blob: 1818d456b955746ac844e11d424a7265590af6dd [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright (c) 2014 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<link rel="import" href="/tracing/base/base.html">
<link rel="import" href="/tracing/base/iteration_helpers.html">
<link rel="import" href="/tracing/base/math.html">
<script>
'use strict';
/**
* @fileoverview Quick range computations.
*/
tr.exportTo('tr.b', function() {
function Range() {
this.isEmpty_ = true;
this.min_ = undefined;
this.max_ = undefined;
}
Range.prototype = {
__proto__: Object.prototype,
reset: function() {
this.isEmpty_ = true;
this.min_ = undefined;
this.max_ = undefined;
},
get isEmpty() {
return this.isEmpty_;
},
addRange: function(range) {
if (range.isEmpty)
return;
this.addValue(range.min);
this.addValue(range.max);
},
addValue: function(value) {
if (this.isEmpty_) {
this.max_ = value;
this.min_ = value;
this.isEmpty_ = false;
return;
}
this.max_ = Math.max(this.max_, value);
this.min_ = Math.min(this.min_, value);
},
set min(min) {
this.isEmpty_ = false;
this.min_ = min;
},
get min() {
if (this.isEmpty_)
return undefined;
return this.min_;
},
get max() {
if (this.isEmpty_)
return undefined;
return this.max_;
},
set max(max) {
this.isEmpty_ = false;
this.max_ = max;
},
get range() {
if (this.isEmpty_)
return undefined;
return this.max_ - this.min_;
},
get center() {
return (this.min_ + this.max_) * 0.5;
},
get duration() {
if (this.isEmpty_)
return 0;
return this.max_ - this.min_;
},
normalize: function(x) {
return tr.b.normalize(x, this.min, this.max);
},
lerp: function(x) {
return tr.b.lerp(x, this.min, this.max);
},
equals: function(that) {
if (this.isEmpty && that.isEmpty)
return true;
if (this.isEmpty != that.isEmpty)
return false;
return (tr.b.approximately(this.min, that.min) &&
tr.b.approximately(this.max, that.max));
},
containsExplicitRangeInclusive: function(min, max) {
if (this.isEmpty)
return false;
return this.min_ <= min && max <= this.max_;
},
containsExplicitRangeExclusive: function(min, max) {
if (this.isEmpty)
return false;
return this.min_ < min && max < this.max_;
},
intersectsExplicitRangeInclusive: function(min, max) {
if (this.isEmpty)
return false;
return this.min_ <= max && min <= this.max_;
},
intersectsExplicitRangeExclusive: function(min, max) {
if (this.isEmpty)
return false;
return this.min_ < max && min < this.max_;
},
containsRangeInclusive: function(range) {
if (range.isEmpty)
return false;
return this.containsExplicitRangeInclusive(range.min_, range.max_);
},
containsRangeExclusive: function(range) {
if (range.isEmpty)
return false;
return this.containsExplicitRangeExclusive(range.min_, range.max_);
},
intersectsRangeInclusive: function(range) {
if (range.isEmpty)
return false;
return this.intersectsExplicitRangeInclusive(range.min_, range.max_);
},
intersectsRangeExclusive: function(range) {
if (range.isEmpty)
return false;
return this.intersectsExplicitRangeExclusive(range.min_, range.max_);
},
findExplicitIntersectionDuration: function(min, max) {
var min = Math.max(this.min, min);
var max = Math.min(this.max, max);
if (max < min)
return 0;
return max - min;
},
findIntersection: function(range) {
if (this.isEmpty || range.isEmpty)
return new Range();
var min = Math.max(this.min, range.min);
var max = Math.min(this.max, range.max);
if (max < min)
return new Range();
return Range.fromExplicitRange(min, max);
},
toJSON: function() {
if (this.isEmpty_)
return {isEmpty: true};
return {
isEmpty: false,
max: this.max,
min: this.min
};
},
/**
* Returns a slice of the input array that intersects with this range
* inclusively.
* If the range does not have a min, it is treated as unbounded from below.
* Similarly, if max is undefined, the range is unbounded from above.
*
* @param {Array} array The array of elements to be filtered.
* @param {Funcation=} opt_keyFunc A function that extracts a numeric value,
* to be used in comparisons, from an element of the array. If not
* specified, array elements themselves will be used.
* @param {Object=} opt_this An optional this argument to be passed to
* opt_keyFunc.
*/
filterArray: function(array, opt_keyFunc, opt_this) {
if (this.isEmpty_)
return [];
// Binary search. |test| is a function that should return true when we
// need to explore the left branch and false to explore the right branch.
function binSearch(test) {
var i0 = 0;
var i1 = array.length;
while (i0 < i1) {
var i = Math.trunc((i0 + i1) / 2);
if (test(i))
i1 = i; // Explore the left branch.
else
i0 = i + 1; // Explore the right branch.
}
return i1;
}
var keyFunc = opt_keyFunc || tr.b.identity;
function getValue(index) {
return keyFunc.call(opt_this, array[index]);
}
var first = binSearch(function(i) {
return this.min_ === undefined || this.min_ <= getValue(i);
}.bind(this));
var last = binSearch(function(i) {
return this.max_ !== undefined && this.max_ < getValue(i);
}.bind(this));
return array.slice(first, last);
}
};
Range.fromDict = function(d) {
if (d.isEmpty === true) {
return new Range();
} else if (d.isEmpty === false) {
var range = new Range();
range.min = d.min;
range.max = d.max;
return range;
} else {
throw new Error('Not a range');
}
};
Range.fromExplicitRange = function(min, max) {
var range = new Range();
range.min = min;
range.max = max;
return range;
};
Range.compareByMinTimes = function(a, b) {
if (!a.isEmpty && !b.isEmpty)
return a.min_ - b.min_;
if (a.isEmpty && !b.isEmpty)
return -1;
if (!a.isEmpty && b.isEmpty)
return 1;
return 0;
};
Range.PERCENT_RANGE = Range.fromExplicitRange(0, 1);
Object.freeze(Range.PERCENT_RANGE);
return {
Range: Range
};
});
</script>