blob: e0658a4160bfecadbc1e49334f3c48f43c4a55a7 [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">
<script src="/gl-matrix-min.js"></script>
<script>
'use strict';
// In node, the script-src for gl-matrix-min above brings in glmatrix into
// a module, instead of into the global scope. Whereas, Tracing code
// assumes that glMatrix is in the global scope. So, in Node only, we
// require() it in, and then take all its exports and shove them into the
// global scope by hand.
(function(global) {
if (tr.isNode) {
var glMatrixAbsPath = HTMLImportsLoader.hrefToAbsolutePath(
'/gl-matrix-min.js');
var glMatrixModule = require(glMatrixAbsPath);
for (var exportName in glMatrixModule) {
global[exportName] = glMatrixModule[exportName];
}
}
})(this);
</script>
<script>
'use strict';
tr.exportTo('tr.b.math', function() {
var PREFERRED_NUMBER_SERIES_MULTIPLIERS = [1, 2, 5, 10];
/* Returns true when x and y are within delta of each other. */
function approximately(x, y, delta) {
if (delta === undefined)
delta = 1e-9;
return Math.abs(x - y) < delta;
}
function clamp(x, lo, hi) {
return Math.min(Math.max(x, lo), hi);
}
function lerp(percentage, lo, hi) {
var range = hi - lo;
return lo + percentage * range;
}
function normalize(value, lo, hi) {
return (value - lo) / (hi - lo);
}
function deg2rad(deg) {
return (Math.PI * deg) / 180.0;
}
/* The Gauss error function gives the probability that a measurement (which is
* under the influence of normally distributed errors with standard deviation
* sigma = 1) is less than x from the mean value of the standard normal
* distribution.
* https://www.desmos.com/calculator/t1v4bdpske
*
* @param {number} x A tolerance for error.
* @return {number} The probability that a measurement is less than |x| from
* the mean value of the standard normal distribution.
*/
function erf(x) {
// save the sign of x
// erf(-x) = -erf(x);
var sign = (x >= 0) ? 1 : -1;
x = Math.abs(x);
// constants
var a1 = 0.254829592;
var a2 = -0.284496736;
var a3 = 1.421413741;
var a4 = -1.453152027;
var a5 = 1.061405429;
var p = 0.3275911;
// Abramowitz and Stegun formula 7.1.26
// maximum error: 1.5e-7
var t = 1.0 / (1.0 + p * x);
var y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t *
Math.exp(-x * x);
return sign * y;
}
var tmpVec2 = vec2.create();
var tmpVec2b = vec2.create();
var tmpVec4 = vec4.create();
var tmpMat2d = mat2d.create();
vec2.createFromArray = function(arr) {
if (arr.length !== 2)
throw new Error('Should be length 2');
var v = vec2.create();
vec2.set(v, arr[0], arr[1]);
return v;
};
vec2.createXY = function(x, y) {
var v = vec2.create();
vec2.set(v, x, y);
return v;
};
vec2.toString = function(a) {
return '[' + a[0] + ', ' + a[1] + ']';
};
vec2.addTwoScaledUnitVectors = function(out, u1, scale1, u2, scale2) {
// out = u1 * scale1 + u2 * scale2
vec2.scale(tmpVec2, u1, scale1);
vec2.scale(tmpVec2b, u2, scale2);
vec2.add(out, tmpVec2, tmpVec2b);
};
vec2.interpolatePiecewiseFunction = function(points, x) {
if (x < points[0][0])
return points[0][1];
for (var i = 1; i < points.length; ++i) {
if (x < points[i][0]) {
var percent = normalize(x, points[i - 1][0], points[i][0]);
return lerp(percent, points[i - 1][1], points[i][1]);
}
}
return points[points.length - 1][1];
};
vec3.createXYZ = function(x, y, z) {
var v = vec3.create();
vec3.set(v, x, y, z);
return v;
};
vec3.toString = function(a) {
return 'vec3(' + a[0] + ', ' + a[1] + ', ' + a[2] + ')';
};
mat2d.translateXY = function(out, x, y) {
vec2.set(tmpVec2, x, y);
mat2d.translate(out, out, tmpVec2);
};
mat2d.scaleXY = function(out, x, y) {
vec2.set(tmpVec2, x, y);
mat2d.scale(out, out, tmpVec2);
};
vec4.unitize = function(out, a) {
out[0] = a[0] / a[3];
out[1] = a[1] / a[3];
out[2] = a[2] / a[3];
out[3] = 1;
return out;
};
vec2.copyFromVec4 = function(out, a) {
vec4.unitize(tmpVec4, a);
vec2.copy(out, tmpVec4);
};
/**
* @param {number} x
* @param {number=} opt_base Defaults to 10
* @return {number}
*/
function logOrLog10(x, base) {
if (base === 10) return Math.log10(x);
return Math.log(x) / Math.log(base);
}
/**
* @param {number} x
* @param {number=} opt_base Defaults to 10
* @return {number}
*/
function lesserPower(x, opt_base) {
var base = opt_base || 10;
return Math.pow(base, Math.floor(logOrLog10(x, base)));
}
/**
* @param {number} x
* @param {number=} opt_base Defaults to 10
* @return {number}
*/
function greaterPower(x, opt_base) {
var base = opt_base || 10;
return Math.pow(base, Math.ceil(logOrLog10(x, base)));
}
function lesserWholeNumber(x) {
if (x === 0) return 0;
const pow10 = (x < 0) ? -lesserPower(-x) : lesserPower(x);
return pow10 * Math.floor(x / pow10);
}
function greaterWholeNumber(x) {
if (x === 0) return 0;
const pow10 = (x < 0) ? -lesserPower(-x) : lesserPower(x);
return pow10 * Math.ceil(x / pow10);
}
/**
* Uses the 1-2-5 series to find the closest prefered number to min
* whose absolute value is at least the absolute value of |min|.
* https://en.wikipedia.org/wiki/Preferred_number
*/
function preferredNumberLargerThanMin(min) {
var absMin = Math.abs(min);
// The conservative guess is the largest power of 10 less than
// or equal to |absMin|.
var conservativeGuess = tr.b.math.lesserPower(absMin);
var minPreferedNumber = undefined;
for (var multiplier of PREFERRED_NUMBER_SERIES_MULTIPLIERS) {
var tightenedGuess = conservativeGuess * multiplier;
if (tightenedGuess >= absMin) {
minPreferedNumber = tightenedGuess;
break;
}
}
if (minPreferedNumber === undefined) {
throw new Error('Could not compute preferred number for ' + min);
}
if (min < 0) minPreferedNumber *= -1;
return minPreferedNumber;
}
return {
approximately,
clamp,
lerp,
normalize,
deg2rad,
erf,
lesserPower,
greaterPower,
lesserWholeNumber,
greaterWholeNumber,
preferredNumberLargerThanMin,
};
});
</script>