@openhps/core
Version:
Open Hybrid Positioning System - Core component
641 lines (610 loc) • 22.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.RAD2DEG = exports.MathUtils = exports.DEG2RAD = void 0;
exports.ceilPowerOfTwo = ceilPowerOfTwo;
exports.clamp = clamp;
exports.damp = damp;
exports.degToRad = degToRad;
exports.denormalize = denormalize;
exports.euclideanModulo = euclideanModulo;
exports.floorPowerOfTwo = floorPowerOfTwo;
exports.generateUUID = generateUUID;
exports.inverseLerp = inverseLerp;
exports.isPowerOfTwo = isPowerOfTwo;
exports.lerp = lerp;
exports.mapLinear = mapLinear;
exports.normalize = normalize;
exports.pingpong = pingpong;
exports.radToDeg = radToDeg;
exports.randFloat = randFloat;
exports.randFloatSpread = randFloatSpread;
exports.randInt = randInt;
exports.seededRandom = seededRandom;
exports.setQuaternionFromProperEuler = setQuaternionFromProperEuler;
exports.smootherstep = smootherstep;
exports.smoothstep = smoothstep;
const _lut = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '0a', '0b', '0c', '0d', '0e', '0f', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1a', '1b', '1c', '1d', '1e', '1f', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '3a', '3b', '3c', '3d', '3e', '3f', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a', '5b', '5c', '5d', '5e', '5f', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '7a', '7b', '7c', '7d', '7e', '7f', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '8a', '8b', '8c', '8d', '8e', '8f', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '9a', '9b', '9c', '9d', '9e', '9f', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'aa', 'ab', 'ac', 'ad', 'ae', 'af', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'ba', 'bb', 'bc', 'bd', 'be', 'bf', 'c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'ca', 'cb', 'cc', 'cd', 'ce', 'cf', 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'da', 'db', 'dc', 'dd', 'de', 'df', 'e0', 'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9', 'ea', 'eb', 'ec', 'ed', 'ee', 'ef', 'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'fa', 'fb', 'fc', 'fd', 'fe', 'ff'];
let _seed = 1234567;
const DEG2RAD = exports.DEG2RAD = Math.PI / 180;
const RAD2DEG = exports.RAD2DEG = 180 / Math.PI;
/**
* Generate a [UUID]{@link https://en.wikipedia.org/wiki/Universally_unique_identifier}
* (universally unique identifier).
*
* @return {string} The UUID.
*/
function generateUUID() {
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
const d0 = Math.random() * 0xffffffff | 0;
const d1 = Math.random() * 0xffffffff | 0;
const d2 = Math.random() * 0xffffffff | 0;
const d3 = Math.random() * 0xffffffff | 0;
const uuid = _lut[d0 & 0xff] + _lut[d0 >> 8 & 0xff] + _lut[d0 >> 16 & 0xff] + _lut[d0 >> 24 & 0xff] + '-' + _lut[d1 & 0xff] + _lut[d1 >> 8 & 0xff] + '-' + _lut[d1 >> 16 & 0x0f | 0x40] + _lut[d1 >> 24 & 0xff] + '-' + _lut[d2 & 0x3f | 0x80] + _lut[d2 >> 8 & 0xff] + '-' + _lut[d2 >> 16 & 0xff] + _lut[d2 >> 24 & 0xff] + _lut[d3 & 0xff] + _lut[d3 >> 8 & 0xff] + _lut[d3 >> 16 & 0xff] + _lut[d3 >> 24 & 0xff];
// .toLowerCase() here flattens concatenated strings to save heap memory space.
return uuid.toLowerCase();
}
/**
* Clamps the given value between min and max.
*
* @param {number} value - The value to clamp.
* @param {number} min - The min value.
* @param {number} max - The max value.
* @return {number} The clamped value.
*/
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
/**
* Computes the Euclidean modulo of the given parameters that
* is `( ( n % m ) + m ) % m`.
*
* @param {number} n - The first parameter.
* @param {number} m - The second parameter.
* @return {number} The Euclidean modulo.
*/
function euclideanModulo(n, m) {
// https://en.wikipedia.org/wiki/Modulo_operation
return (n % m + m) % m;
}
/**
* Performs a linear mapping from range `<a1, a2>` to range `<b1, b2>`
* for the given value.
*
* @param {number} x - The value to be mapped.
* @param {number} a1 - Minimum value for range A.
* @param {number} a2 - Maximum value for range A.
* @param {number} b1 - Minimum value for range B.
* @param {number} b2 - Maximum value for range B.
* @return {number} The mapped value.
*/
function mapLinear(x, a1, a2, b1, b2) {
return b1 + (x - a1) * (b2 - b1) / (a2 - a1);
}
/**
* Returns the percentage in the closed interval `[0, 1]` of the given value
* between the start and end point.
*
* @param {number} x - The start point
* @param {number} y - The end point.
* @param {number} value - A value between start and end.
* @return {number} The interpolation factor.
*/
function inverseLerp(x, y, value) {
// https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/
if (x !== y) {
return (value - x) / (y - x);
} else {
return 0;
}
}
/**
* Returns a value linearly interpolated from two known points based on the given interval -
* `t = 0` will return `x` and `t = 1` will return `y`.
*
* @param {number} x - The start point
* @param {number} y - The end point.
* @param {number} t - The interpolation factor in the closed interval `[0, 1]`.
* @return {number} The interpolated value.
*/
function lerp(x, y, t) {
return (1 - t) * x + t * y;
}
/**
* Smoothly interpolate a number from `x` to `y` in a spring-like manner using a delta
* time to maintain frame rate independent movement. For details, see
* [Frame rate independent damping using lerp]{@link http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/}.
*
* @param {number} x - The current point.
* @param {number} y - The target point.
* @param {number} lambda - A higher lambda value will make the movement more sudden,
* and a lower value will make the movement more gradual.
* @param {number} dt - Delta time in seconds.
* @return {number} The interpolated value.
*/
function damp(x, y, lambda, dt) {
return lerp(x, y, 1 - Math.exp(-lambda * dt));
}
/**
* Returns a value that alternates between `0` and the given `length` parameter.
*
* @param {number} x - The value to pingpong.
* @param {number} [length=1] - The positive value the function will pingpong to.
* @return {number} The alternated value.
*/
function pingpong(x, length = 1) {
// https://www.desmos.com/calculator/vcsjnyz7x4
return length - Math.abs(euclideanModulo(x, length * 2) - length);
}
/**
* Returns a value in the range `[0,1]` that represents the percentage that `x` has
* moved between `min` and `max`, but smoothed or slowed down the closer `x` is to
* the `min` and `max`.
*
* See [Smoothstep]{@link http://en.wikipedia.org/wiki/Smoothstep} for more details.
*
* @param {number} x - The value to evaluate based on its position between min and max.
* @param {number} min - The min value. Any x value below min will be `0`.
* @param {number} max - The max value. Any x value above max will be `1`.
* @return {number} The alternated value.
*/
function smoothstep(x, min, max) {
if (x <= min) return 0;
if (x >= max) return 1;
x = (x - min) / (max - min);
return x * x * (3 - 2 * x);
}
/**
* A [variation on smoothstep]{@link https://en.wikipedia.org/wiki/Smoothstep#Variations}
* that has zero 1st and 2nd order derivatives at x=0 and x=1.
*
* @param {number} x - The value to evaluate based on its position between min and max.
* @param {number} min - The min value. Any x value below min will be `0`.
* @param {number} max - The max value. Any x value above max will be `1`.
* @return {number} The alternated value.
*/
function smootherstep(x, min, max) {
if (x <= min) return 0;
if (x >= max) return 1;
x = (x - min) / (max - min);
return x * x * x * (x * (x * 6 - 15) + 10);
}
/**
* Returns a random integer from `<low, high>` interval.
*
* @param {number} low - The lower value boundary.
* @param {number} high - The upper value boundary
* @return {number} A random integer.
*/
function randInt(low, high) {
return low + Math.floor(Math.random() * (high - low + 1));
}
/**
* Returns a random float from `<low, high>` interval.
*
* @param {number} low - The lower value boundary.
* @param {number} high - The upper value boundary
* @return {number} A random float.
*/
function randFloat(low, high) {
return low + Math.random() * (high - low);
}
/**
* Returns a random integer from `<-range/2, range/2>` interval.
*
* @param {number} range - Defines the value range.
* @return {number} A random float.
*/
function randFloatSpread(range) {
return range * (0.5 - Math.random());
}
/**
* Returns a deterministic pseudo-random float in the interval `[0, 1]`.
*
* @param {number} [s] - The integer seed.
* @return {number} A random float.
*/
function seededRandom(s) {
if (s !== undefined) _seed = s;
// Mulberry32 generator
let t = _seed += 0x6D2B79F5;
t = Math.imul(t ^ t >>> 15, t | 1);
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
return ((t ^ t >>> 14) >>> 0) / 4294967296;
}
/**
* Converts degrees to radians.
*
* @param {number} degrees - A value in degrees.
* @return {number} The converted value in radians.
*/
function degToRad(degrees) {
return degrees * DEG2RAD;
}
/**
* Converts radians to degrees.
*
* @param {number} radians - A value in radians.
* @return {number} The converted value in degrees.
*/
function radToDeg(radians) {
return radians * RAD2DEG;
}
/**
* Returns `true` if the given number is a power of two.
*
* @param {number} value - The value to check.
* @return {boolean} Whether the given number is a power of two or not.
*/
function isPowerOfTwo(value) {
return (value & value - 1) === 0 && value !== 0;
}
/**
* Returns the smallest power of two that is greater than or equal to the given number.
*
* @param {number} value - The value to find a POT for.
* @return {number} The smallest power of two that is greater than or equal to the given number.
*/
function ceilPowerOfTwo(value) {
return Math.pow(2, Math.ceil(Math.log(value) / Math.LN2));
}
/**
* Returns the largest power of two that is less than or equal to the given number.
*
* @param {number} value - The value to find a POT for.
* @return {number} The largest power of two that is less than or equal to the given number.
*/
function floorPowerOfTwo(value) {
return Math.pow(2, Math.floor(Math.log(value) / Math.LN2));
}
/**
* Sets the given quaternion from the [Intrinsic Proper Euler Angles]{@link https://en.wikipedia.org/wiki/Euler_angles}
* defined by the given angles and order.
*
* Rotations are applied to the axes in the order specified by order:
* rotation by angle `a` is applied first, then by angle `b`, then by angle `c`.
*
* @param {Quaternion} q - The quaternion to set.
* @param {number} a - The rotation applied to the first axis, in radians.
* @param {number} b - The rotation applied to the second axis, in radians.
* @param {number} c - The rotation applied to the third axis, in radians.
* @param {('XYX'|'XZX'|'YXY'|'YZY'|'ZXZ'|'ZYZ')} order - A string specifying the axes order.
*/
function setQuaternionFromProperEuler(q, a, b, c, order) {
const cos = Math.cos;
const sin = Math.sin;
const c2 = cos(b / 2);
const s2 = sin(b / 2);
const c13 = cos((a + c) / 2);
const s13 = sin((a + c) / 2);
const c1_3 = cos((a - c) / 2);
const s1_3 = sin((a - c) / 2);
const c3_1 = cos((c - a) / 2);
const s3_1 = sin((c - a) / 2);
switch (order) {
case 'XYX':
q.set(c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13);
break;
case 'YZY':
q.set(s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13);
break;
case 'ZXZ':
q.set(s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13);
break;
case 'XZX':
q.set(c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13);
break;
case 'YXY':
q.set(s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13);
break;
case 'ZYZ':
q.set(s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13);
break;
default:
console.warn('THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order);
}
}
/**
* Denormalizes the given value according to the given typed array.
*
* @param {number} value - The value to denormalize.
* @param {TypedArray} array - The typed array that defines the data type of the value.
* @return {number} The denormalize (float) value in the range `[0,1]`.
*/
function denormalize(value, array) {
switch (array.constructor) {
case Float32Array:
return value;
case Uint32Array:
return value / 4294967295.0;
case Uint16Array:
return value / 65535.0;
case Uint8Array:
return value / 255.0;
case Int32Array:
return Math.max(value / 2147483647.0, -1.0);
case Int16Array:
return Math.max(value / 32767.0, -1.0);
case Int8Array:
return Math.max(value / 127.0, -1.0);
default:
throw new Error('Invalid component type.');
}
}
/**
* Normalizes the given value according to the given typed array.
*
* @param {number} value - The float value in the range `[0,1]` to normalize.
* @param {TypedArray} array - The typed array that defines the data type of the value.
* @return {number} The normalize value.
*/
function normalize(value, array) {
switch (array.constructor) {
case Float32Array:
return value;
case Uint32Array:
return Math.round(value * 4294967295.0);
case Uint16Array:
return Math.round(value * 65535.0);
case Uint8Array:
return Math.round(value * 255.0);
case Int32Array:
return Math.round(value * 2147483647.0);
case Int16Array:
return Math.round(value * 32767.0);
case Int8Array:
return Math.round(value * 127.0);
default:
throw new Error('Invalid component type.');
}
}
/**
* @class
* @classdesc A collection of math utility functions.
* @hideconstructor
*/
const MathUtils = exports.MathUtils = {
DEG2RAD: DEG2RAD,
RAD2DEG: RAD2DEG,
/**
* Generate a [UUID]{@link https://en.wikipedia.org/wiki/Universally_unique_identifier}
* (universally unique identifier).
*
* @static
* @method
* @return {string} The UUID.
*/
generateUUID: generateUUID,
/**
* Clamps the given value between min and max.
*
* @static
* @method
* @param {number} value - The value to clamp.
* @param {number} min - The min value.
* @param {number} max - The max value.
* @return {number} The clamped value.
*/
clamp: clamp,
/**
* Computes the Euclidean modulo of the given parameters that
* is `( ( n % m ) + m ) % m`.
*
* @static
* @method
* @param {number} n - The first parameter.
* @param {number} m - The second parameter.
* @return {number} The Euclidean modulo.
*/
euclideanModulo: euclideanModulo,
/**
* Performs a linear mapping from range `<a1, a2>` to range `<b1, b2>`
* for the given value.
*
* @static
* @method
* @param {number} x - The value to be mapped.
* @param {number} a1 - Minimum value for range A.
* @param {number} a2 - Maximum value for range A.
* @param {number} b1 - Minimum value for range B.
* @param {number} b2 - Maximum value for range B.
* @return {number} The mapped value.
*/
mapLinear: mapLinear,
/**
* Returns the percentage in the closed interval `[0, 1]` of the given value
* between the start and end point.
*
* @static
* @method
* @param {number} x - The start point
* @param {number} y - The end point.
* @param {number} value - A value between start and end.
* @return {number} The interpolation factor.
*/
inverseLerp: inverseLerp,
/**
* Returns a value linearly interpolated from two known points based on the given interval -
* `t = 0` will return `x` and `t = 1` will return `y`.
*
* @static
* @method
* @param {number} x - The start point
* @param {number} y - The end point.
* @param {number} t - The interpolation factor in the closed interval `[0, 1]`.
* @return {number} The interpolated value.
*/
lerp: lerp,
/**
* Smoothly interpolate a number from `x` to `y` in a spring-like manner using a delta
* time to maintain frame rate independent movement. For details, see
* [Frame rate independent damping using lerp]{@link http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/}.
*
* @static
* @method
* @param {number} x - The current point.
* @param {number} y - The target point.
* @param {number} lambda - A higher lambda value will make the movement more sudden,
* and a lower value will make the movement more gradual.
* @param {number} dt - Delta time in seconds.
* @return {number} The interpolated value.
*/
damp: damp,
/**
* Returns a value that alternates between `0` and the given `length` parameter.
*
* @static
* @method
* @param {number} x - The value to pingpong.
* @param {number} [length=1] - The positive value the function will pingpong to.
* @return {number} The alternated value.
*/
pingpong: pingpong,
/**
* Returns a value in the range `[0,1]` that represents the percentage that `x` has
* moved between `min` and `max`, but smoothed or slowed down the closer `x` is to
* the `min` and `max`.
*
* See [Smoothstep]{@link http://en.wikipedia.org/wiki/Smoothstep} for more details.
*
* @static
* @method
* @param {number} x - The value to evaluate based on its position between min and max.
* @param {number} min - The min value. Any x value below min will be `0`.
* @param {number} max - The max value. Any x value above max will be `1`.
* @return {number} The alternated value.
*/
smoothstep: smoothstep,
/**
* A [variation on smoothstep]{@link https://en.wikipedia.org/wiki/Smoothstep#Variations}
* that has zero 1st and 2nd order derivatives at x=0 and x=1.
*
* @static
* @method
* @param {number} x - The value to evaluate based on its position between min and max.
* @param {number} min - The min value. Any x value below min will be `0`.
* @param {number} max - The max value. Any x value above max will be `1`.
* @return {number} The alternated value.
*/
smootherstep: smootherstep,
/**
* Returns a random integer from `<low, high>` interval.
*
* @static
* @method
* @param {number} low - The lower value boundary.
* @param {number} high - The upper value boundary
* @return {number} A random integer.
*/
randInt: randInt,
/**
* Returns a random float from `<low, high>` interval.
*
* @static
* @method
* @param {number} low - The lower value boundary.
* @param {number} high - The upper value boundary
* @return {number} A random float.
*/
randFloat: randFloat,
/**
* Returns a random integer from `<-range/2, range/2>` interval.
*
* @static
* @method
* @param {number} range - Defines the value range.
* @return {number} A random float.
*/
randFloatSpread: randFloatSpread,
/**
* Returns a deterministic pseudo-random float in the interval `[0, 1]`.
*
* @static
* @method
* @param {number} [s] - The integer seed.
* @return {number} A random float.
*/
seededRandom: seededRandom,
/**
* Converts degrees to radians.
*
* @static
* @method
* @param {number} degrees - A value in degrees.
* @return {number} The converted value in radians.
*/
degToRad: degToRad,
/**
* Converts radians to degrees.
*
* @static
* @method
* @param {number} radians - A value in radians.
* @return {number} The converted value in degrees.
*/
radToDeg: radToDeg,
/**
* Returns `true` if the given number is a power of two.
*
* @static
* @method
* @param {number} value - The value to check.
* @return {boolean} Whether the given number is a power of two or not.
*/
isPowerOfTwo: isPowerOfTwo,
/**
* Returns the smallest power of two that is greater than or equal to the given number.
*
* @static
* @method
* @param {number} value - The value to find a POT for.
* @return {number} The smallest power of two that is greater than or equal to the given number.
*/
ceilPowerOfTwo: ceilPowerOfTwo,
/**
* Returns the largest power of two that is less than or equal to the given number.
*
* @static
* @method
* @param {number} value - The value to find a POT for.
* @return {number} The largest power of two that is less than or equal to the given number.
*/
floorPowerOfTwo: floorPowerOfTwo,
/**
* Sets the given quaternion from the [Intrinsic Proper Euler Angles]{@link https://en.wikipedia.org/wiki/Euler_angles}
* defined by the given angles and order.
*
* Rotations are applied to the axes in the order specified by order:
* rotation by angle `a` is applied first, then by angle `b`, then by angle `c`.
*
* @static
* @method
* @param {Quaternion} q - The quaternion to set.
* @param {number} a - The rotation applied to the first axis, in radians.
* @param {number} b - The rotation applied to the second axis, in radians.
* @param {number} c - The rotation applied to the third axis, in radians.
* @param {('XYX'|'XZX'|'YXY'|'YZY'|'ZXZ'|'ZYZ')} order - A string specifying the axes order.
*/
setQuaternionFromProperEuler: setQuaternionFromProperEuler,
/**
* Normalizes the given value according to the given typed array.
*
* @static
* @method
* @param {number} value - The float value in the range `[0,1]` to normalize.
* @param {TypedArray} array - The typed array that defines the data type of the value.
* @return {number} The normalize value.
*/
normalize: normalize,
/**
* Denormalizes the given value according to the given typed array.
*
* @static
* @method
* @param {number} value - The value to denormalize.
* @param {TypedArray} array - The typed array that defines the data type of the value.
* @return {number} The denormalize (float) value in the range `[0,1]`.
*/
denormalize: denormalize
};