@nathanfaucett/mathf
Version:
math polyfills and helper functions
414 lines (327 loc) • 9 kB
JavaScript
var keys = require("@nathanfaucett/keys"),
clamp = require("@nathanfaucett/clamp"),
isNaNPolyfill = require("@nathanfaucett/is_nan");
var mathf = exports,
NativeMath = global.Math,
hasFloat32Array = typeof(Float32Array) !== "undefined",
NativeFloat32Array = hasFloat32Array ? Float32Array : Array;
mathf.ArrayType = NativeFloat32Array;
mathf.PI = NativeMath.PI;
mathf.TAU = mathf.PI * 2;
mathf.TWO_PI = mathf.TAU;
mathf.HALF_PI = mathf.PI * 0.5;
mathf.FOURTH_PI = mathf.PI * 0.25;
mathf.EPSILON = Number.EPSILON || NativeMath.pow(2, -52);
mathf.TO_RADS = mathf.PI / 180;
mathf.TO_DEGS = 180 / mathf.PI;
mathf.E = NativeMath.E;
mathf.LN2 = NativeMath.LN2;
mathf.LN10 = NativeMath.LN10;
mathf.LOG2E = NativeMath.LOG2E;
mathf.LOG10E = NativeMath.LOG10E;
mathf.SQRT1_2 = NativeMath.SQRT1_2;
mathf.SQRT2 = NativeMath.SQRT2;
mathf.abs = NativeMath.abs;
mathf.acos = NativeMath.acos;
mathf.acosh = NativeMath.acosh || function acosh(x) {
return mathf.log(x + mathf.sqrt(x * x - 1));
};
mathf.asin = NativeMath.asin;
mathf.asinh = NativeMath.asinh || function asinh(x) {
if (x === -Infinity) {
return x;
} else {
return mathf.log(x + mathf.sqrt(x * x + 1));
}
};
mathf.atan = NativeMath.atan;
mathf.atan2 = NativeMath.atan2;
mathf.atanh = NativeMath.atanh || function atanh(x) {
return mathf.log((1 + x) / (1 - x)) / 2;
};
mathf.cbrt = NativeMath.cbrt || function cbrt(x) {
var y = mathf.pow(mathf.abs(x), 1 / 3);
return x < 0 ? -y : y;
};
mathf.ceil = NativeMath.ceil;
mathf.clz32 = NativeMath.clz32 || function clz32(value) {
value = +value >>> 0;
return value ? 32 - value.toString(2).length : 32;
};
mathf.cos = NativeMath.cos;
mathf.cosh = NativeMath.cosh || function cosh(x) {
return (mathf.exp(x) + mathf.exp(-x)) / 2;
};
mathf.exp = NativeMath.exp;
mathf.expm1 = NativeMath.expm1 || function expm1(x) {
return mathf.exp(x) - 1;
};
mathf.floor = NativeMath.floor;
mathf.fround = NativeMath.fround || (hasFloat32Array ?
function fround(x) {
return new NativeFloat32Array([x])[0];
} :
function fround(x) {
return x;
}
);
mathf.hypot = NativeMath.hypot || function hypot() {
var y = 0,
i = -1,
il = arguments.length - 1,
value;
while (i++ < il) {
value = arguments[i];
if (value === Infinity || value === -Infinity) {
return Infinity;
} else {
y += value * value;
}
}
return mathf.sqrt(y);
};
mathf.imul = NativeMath.imul || function imul(a, b) {
var ah = (a >>> 16) & 0xffff,
al = a & 0xffff,
bh = (b >>> 16) & 0xffff,
bl = b & 0xffff;
return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0);
};
mathf.log = NativeMath.log;
mathf.log1p = NativeMath.log1p || function log1p(x) {
return mathf.log(1 + x);
};
mathf.log10 = NativeMath.log10 || function log10(x) {
return mathf.log(x) / mathf.LN10;
};
mathf.log2 = NativeMath.log2 || function log2(x) {
return mathf.log(x) / mathf.LN2;
};
mathf.fac = function fac(n) {
if (n < 2) {
return 1;
} else {
return n * fac(n - 1);
}
};
mathf.max = NativeMath.max;
mathf.min = NativeMath.min;
mathf.pow = NativeMath.pow;
mathf.random = NativeMath.random;
mathf.round = NativeMath.round;
mathf.sign = NativeMath.sign || function sign(x) {
x = +x;
if (x === 0 || isNaNPolyfill(x)) {
return x;
} else {
return x > 0 ? 1 : -1;
}
};
mathf.sin = NativeMath.sin;
mathf.sinh = NativeMath.sinh || function sinh(x) {
return (mathf.exp(x) - mathf.exp(-x)) / 2;
};
mathf.sqrt = NativeMath.sqrt;
mathf.tan = NativeMath.tan;
mathf.tanh = NativeMath.tanh || function tanh(x) {
if (x === Infinity) {
return 1;
} else if (x === -Infinity) {
return -1;
} else {
return (mathf.exp(x) - mathf.exp(-x)) / (mathf.exp(x) + mathf.exp(-x));
}
};
mathf.trunc = NativeMath.trunc || function trunc(x) {
return x < 0 ? mathf.ceil(x) : mathf.floor(x);
};
mathf.equals = function(a, b, e) {
return mathf.abs(a - b) < (e !== void(0) ? e : mathf.EPSILON);
};
mathf.modulo = function(a, b) {
var r = a % b;
return (r * b < 0) ? r + b : r;
};
mathf.standardRadian = function(x) {
return mathf.modulo(x, mathf.TWO_PI);
};
mathf.standardAngle = function(x) {
return mathf.modulo(x, 360);
};
mathf.snap = function(x, y) {
var m = x % y;
return m < (y * 0.5) ? x - m : x + y - m;
};
mathf.clamp = clamp;
mathf.clampBottom = function(x, min) {
return x < min ? min : x;
};
mathf.clampTop = function(x, max) {
return x > max ? max : x;
};
mathf.clamp01 = function(x) {
if (x < 0) {
return 0;
} else if (x > 1) {
return 1;
} else {
return x;
}
};
mathf.truncate = function(x, n) {
var p = mathf.pow(10, n),
num = x * p;
return (num < 0 ? mathf.ceil(num) : mathf.floor(num)) / p;
};
mathf.lerp = function(a, b, x) {
return a + (b - a) * x;
};
mathf.lerpRadian = function(a, b, x) {
return mathf.standardRadian(a + (b - a) * x);
};
mathf.lerpAngle = function(a, b, x) {
return mathf.standardAngle(a + (b - a) * x);
};
mathf.lerpCos = function(a, b, x) {
var ft = x * mathf.PI,
f = (1 - mathf.cos(ft)) * 0.5;
return a * (1 - f) + b * f;
};
mathf.lerpCubic = function(v0, v1, v2, v3, x) {
var P, Q, R, S, Px, Qx, Rx;
v0 = v0 || v1;
v3 = v3 || v2;
P = (v3 - v2) - (v0 - v1);
Q = (v0 - v1) - P;
R = v2 - v0;
S = v1;
Px = P * x;
Qx = Q * x;
Rx = R * x;
return (Px * Px * Px) + (Qx * Qx) + Rx + S;
};
mathf.smoothStep = function(x, min, max) {
if (x <= min) {
return 0;
} else {
if (x >= max) {
return 1;
} else {
x = (x - min) / (max - min);
return x * x * (3 - 2 * x);
}
}
};
mathf.smootherStep = function(x, min, max) {
if (x <= min) {
return 0;
} else {
if (x >= max) {
return 1;
} else {
x = (x - min) / (max - min);
return x * x * x * (x * (x * 6 - 15) + 10);
}
}
};
mathf.pingPong = function(x, length) {
length = +length || 1;
return length - mathf.abs(x % (2 * length) - length);
};
mathf.degsToRads = function(x) {
return mathf.standardRadian(x * mathf.TO_RADS);
};
mathf.radsToDegs = function(x) {
return mathf.standardAngle(x * mathf.TO_DEGS);
};
mathf.randInt = function(min, max) {
return mathf.round(min + (mathf.random() * (max - min)));
};
mathf.randFloat = function(min, max) {
return min + (mathf.random() * (max - min));
};
mathf.randSign = function() {
return mathf.random() < 0.5 ? 1 : -1;
};
mathf.shuffle = function(array) {
var i = array.length,
j, x;
while (i) {
j = (mathf.random() * i--) | 0;
x = array[i];
array[i] = array[j];
array[j] = x;
}
return array;
};
mathf.randArg = function() {
return arguments[(mathf.random() * arguments.length) | 0];
};
mathf.randChoice = function(array) {
return array[(mathf.random() * array.length) | 0];
};
mathf.randChoiceObject = function(object) {
var objectKeys = keys(object);
return object[objectKeys[(mathf.random() * objectKeys.length) | 0]];
};
mathf.isPowerOfTwo = function(x) {
return (x & -x) === x;
};
mathf.floorPowerOfTwo = function(x) {
var i = 2,
prev;
while (i < x) {
prev = i;
i *= 2;
}
return prev;
};
mathf.ceilPowerOfTwo = function(x) {
var i = 2;
while (i < x) {
i *= 2;
}
return i;
};
var n225 = 0.39269908169872414,
n675 = 1.1780972450961724,
n1125 = 1.9634954084936207,
n1575 = 2.748893571891069,
n2025 = 3.5342917352885173,
n2475 = 4.319689898685966,
n2925 = 5.105088062083414,
n3375 = 5.8904862254808625,
RIGHT = "right",
UP_RIGHT = "up_right",
UP = "up",
UP_LEFT = "up_left",
LEFT = "left",
DOWN_LEFT = "down_left",
DOWN = "down",
DOWN_RIGHT = "down_right";
mathf.directionAngle = function(a) {
a = mathf.standardRadian(a);
return (
(a >= n225 && a < n675) ? UP_RIGHT :
(a >= n675 && a < n1125) ? UP :
(a >= n1125 && a < n1575) ? UP_LEFT :
(a >= n1575 && a < n2025) ? LEFT :
(a >= n2025 && a < n2475) ? DOWN_LEFT :
(a >= n2475 && a < n2925) ? DOWN :
(a >= n2925 && a < n3375) ? DOWN_RIGHT :
RIGHT
);
};
mathf.direction = function(x, y) {
var a = mathf.standardRadian(mathf.atan2(y, x));
return (
(a >= n225 && a < n675) ? UP_RIGHT :
(a >= n675 && a < n1125) ? UP :
(a >= n1125 && a < n1575) ? UP_LEFT :
(a >= n1575 && a < n2025) ? LEFT :
(a >= n2025 && a < n2475) ? DOWN_LEFT :
(a >= n2475 && a < n2925) ? DOWN :
(a >= n2925 && a < n3375) ? DOWN_RIGHT :
RIGHT
);
};