@tubular/math
Version:
Miscellaneous math functions
276 lines • 8.13 kB
JavaScript
const debug = false;
export const abs = Math.abs;
export const acos = Math.acos;
export const acosh = Math.acosh;
export function acos_deg(x) {
return Math.acos(x) / Math.PI * 180.0;
}
export function acot(x) {
return Math.PI / 2.0 - Math.atan(x);
}
export function acot2(a, b) {
return Math.PI / 2.0 - Math.atan2(a, b);
}
export function acot_deg(x) {
return 180.0 - Math.atan(x) / Math.PI * 180.0;
}
export function acot2_deg(a, b) {
return 180.0 - Math.atan2(a, b) / Math.PI * 180.0;
}
export const asin = Math.asin;
export const asinh = Math.asinh;
export function asin_deg(x) {
return Math.asin(x) / Math.PI * 180.0;
}
export const atan = Math.atan;
export const atan2 = Math.atan2;
export const atanh = Math.atanh;
export function atan_deg(x) {
return Math.atan(x) / Math.PI * 180.0;
}
export function atan2_deg(a, b) {
return Math.atan2(a, b) / Math.PI * 180.0;
}
export const cbrt = Math.cbrt;
export function ceil(x, multiple = 1) {
if (multiple === 1)
return Math.ceil(x);
return -(-x - mod(-x, multiple));
}
export const clz32 = Math.clz32;
export const cos = Math.cos;
export const cosh = Math.cosh;
export function cos_deg(x) {
return Math.cos(x / 180.0 * Math.PI);
}
/**
* Integer division with "truncation toward zero".
*/
export function div_tt0(x, y) {
const d = x / y;
if (d >= 0)
return Math.floor(d);
else
return Math.ceil(d);
}
/**
* Integer division, always rounding down to the nearest integer regardless of sign of the result.
*/
export function div_rd(x, y) {
return Math.floor(x / y);
}
export const exp = Math.exp;
export const expm1 = Math.expm1;
export function floor(x, multiple = 1) {
if (multiple === 1)
return Math.floor(x);
return x - mod(x, multiple);
}
export const fround = Math.fround;
export const hypot = Math.hypot;
export const imul = Math.imul;
export function interpolate(x0, x, x1, y0, y1) {
if (x0 === x1)
return y0;
return y0 + (x - x0) * (y1 - y0) / (x1 - x0);
}
export function interpolateModular(x0, x, x1, y0, y1, modulus, signedResult = false) {
const m2 = modulus / 2;
if (y0 < 0 || y0 >= modulus)
// noinspection JSSuspiciousNameCombination
y0 = mod(y0, modulus);
while (y1 < y0 - m2)
y1 += modulus;
while (y1 >= y0 + m2)
y1 -= modulus;
let y = interpolate(x0, x, x1, y0, y1);
if (signedResult && (y < m2 || y >= m2))
// noinspection JSSuspiciousNameCombination
y = mod2(y, modulus);
else if (y < 0 || y >= modulus)
// noinspection JSSuspiciousNameCombination
y = mod(y, modulus);
return y;
}
function interpolateOverIntRange(xx, yy, x, a, b) {
let y = 0.0;
for (let i = a; i <= b; ++i) {
let c = 1.0;
for (let j = a; j <= b; ++j) {
if (j !== i)
c *= (x - xx[j]) / (xx[i] - xx[j]);
}
y += c * yy[i];
}
return y;
}
function interpolateAux(xx, yy, x, maxSpan, xc) {
const n = Math.min(xx.length, yy.length);
let a = -1;
let b = n - 1;
for (let i = 0; i < n; ++i) {
const xi = xx[i];
if (a < 0 && xi >= xc - maxSpan)
a = i;
if (xi >= xc + maxSpan) {
b = i;
break;
}
}
return interpolateOverIntRange(xx, yy, x, Math.max(a, 0), b);
}
// Note: To use maxSpan (by specifying a positive value) array data must be pre-sorted
// by ascending x value.
//
export function interpolateTabular(xx, yy, x, maxSpan) {
const n = Math.min(xx.length, yy.length);
if (maxSpan <= 0.0)
return interpolateOverIntRange(xx, yy, x, 0, n - 1);
// To avoid sudden jumps in the return value of this function as we shift
// from one range of table values to another, we'll construct two ranges
// based on maxSpan -- one for the tabular xx value just below x, another
// for the xx value just above x. An interpolation will be performed over
// both ranges, then a weighted average of the two interpolations, based
// on the relative position of x between the two bounding xx values, will
// be returned.
let ca = -1;
let cb = -1;
for (let i = 0; i < n; ++i) {
const xi = xx[i];
if (xi === x)
return yy[i];
if (xi < x)
ca = i;
else if (xi > x) {
cb = i;
break;
}
}
if (ca < 0)
return yy[0];
else if (cb < 0)
return yy[n - 1];
const xa = xx[ca];
const xb = xx[cb];
const weight = (x - xa) / (xb - xa);
const ya = interpolateAux(xx, yy, x, maxSpan, xa);
const yb = interpolateAux(xx, yy, x, maxSpan, xb);
return ya * (1.0 - weight) + yb * weight;
}
export function intersects(r1, r2) {
return (r1.x < r2.x + r2.w && r1.x + r1.w > r2.x &&
r1.y < r2.y + r2.h && r1.y + r1.h > r2.y);
}
export function irandom(lowestOrMax, highest) {
if (arguments.length === 1)
return 1 + Math.floor(Math.random() * lowestOrMax);
return lowestOrMax + Math.floor(Math.random() * (highest - lowestOrMax + 1));
}
export function limitNeg1to1(x, tolerance = 0.01) {
if (x < -1 - tolerance) {
if (debug) {
console.debug('Value out of range: ' + x + ' < -1.0');
console.trace();
}
return -1.0;
}
else if (x > 1 + tolerance) {
if (debug) {
console.debug('Value out of range: ' + x + ' > 1.0');
console.trace();
}
return 1.0;
}
else if (x < -1.0)
return -1.0;
else if (x > 1.0)
return 1.0;
else
return x;
}
export const HALF_PI = Math.PI / 2.0;
export const log = Math.log;
export const log10 = Math.log10;
export const log1p = Math.log1p;
export const log2 = Math.log2;
export const max = Math.max;
export const min = Math.min;
/**
* A modulo operation complementary to div_rd(), differing from % in how the relationship between
* the signs of the arguments and the result is handled.
*/
export function mod(x, y) {
const m = x % y;
if ((m < 0 && y > 0) || (m > 0 && y < 0)) {
return y + m;
}
return m;
}
/**
* For use with values such as angles, this modulo function splits the range of the modulus
* between negative and positive numbers, [-y/2, y/2), e.g. for a y of 360, the result ranges
* from -180 up to (but not including) 180. This function can return non-integer values.
*/
export function mod2(x, y) {
let result = x - Math.floor(x / y) * y;
if (result >= y / 2.0)
result -= y;
return result;
}
export const PI = Math.PI;
export const pow = Math.pow;
export function random(lowestOrMax, highest) {
switch (arguments.length) {
case 1: return Math.random() * lowestOrMax;
case 2: return lowestOrMax + Math.random() * (highest - lowestOrMax);
}
return Math.random();
}
export function round(x, multiple = 1) {
if (multiple === 1)
return Math.round(x);
return x + multiple / 2 - mod(x + multiple / 2, multiple);
}
export const sign = Math.sign;
export function signZN(x) {
if (x > 0.0)
return 1.0;
else
return -1.0;
}
export function signZP(x) {
if (x < 0.0)
return -1.0;
else
return 1.0;
}
export const sin = Math.sin;
export const sinh = Math.sinh;
export function sin_deg(x) {
return Math.sin(x / 180.0 * Math.PI);
}
export const sqrt = Math.sqrt;
export function squared(x) {
return x * x;
}
export const tan = Math.tan;
export const tanh = Math.tanh;
export function tan_deg(x) {
return Math.tan(x / 180.0 * Math.PI);
}
export function to_degree(x) {
return x * 180.0 / Math.PI;
}
export function to_radian(x) {
return x * Math.PI / 180.0;
}
export const trunc = Math.trunc;
export const TWO_PI = Math.PI * 2;
export function union(r1, r2) {
const minX = min(r1.x, r2.x);
const minY = min(r1.y, r2.y);
const maxX = max(r1.x + r1.w, r2.x + r2.w);
const maxY = max(r1.y + r1.h, r2.y + r2.h);
return { x: minX, y: minY, w: maxX - minX, h: maxY - minY };
}
//# sourceMappingURL=math.js.map