diff --git a/src/common.js b/src/common.js index 37233c59..005bbf0d 100644 --- a/src/common.js +++ b/src/common.js @@ -7,20 +7,18 @@ export const EPSILON = 0.000001; export let ARRAY_TYPE = typeof Float32Array !== "undefined" ? Float32Array : Array; +// If an array is required to initialize to zero. +export let ARRAY_ZERO_INIT_TYPE = ARRAY_TYPE === Array ? _createFastZeroInit(ARRAY_TYPE) : ARRAY_TYPE; export let RANDOM = Math.random; export let ANGLE_ORDER = "zyx"; /** * Symmetric round - * see https://www.npmjs.com/package/round-half-up-symmetric#user-content-detailed-background * * @param {Number} a value to round */ export function round(a) { - if (a >= 0) - return Math.round(a); - - return (a % 0.5 === 0) ? Math.floor(a) : Math.round(a); + return Math.round(Math.abs(a)) * Math.sign(a); } /** @@ -29,7 +27,15 @@ export function round(a) { * @param {Float32ArrayConstructor | ArrayConstructor} type Array type, such as Float32Array or Array */ export function setMatrixArrayType(type) { - ARRAY_TYPE = type; + + ARRAY_TYPE = type; + + // If the Array is not a TypedArray, create a constructor that automatically fills it with zeroes. + if (Array.isArray(type)) { + ARRAY_ZERO_INIT_TYPE = _createFastZeroInit(type); + return; + } + ARRAY_ZERO_INIT_TYPE = type; } const degree = Math.PI / 180; @@ -67,3 +73,20 @@ export function toDegree(a) { export function equals(a, b, tolerance = EPSILON) { return Math.abs(a - b) <= tolerance * Math.max(1, Math.abs(a), Math.abs(b)); } + + + +/** + * Creates a subclass of an Array-like class that initializes all it's elements to zero. + * @private + * @param {ArrayConstructor} type The Array-like class. + * @returns {ArrayConstructor} The zero-initializer subclass. + */ +function _createFastZeroInit(type) { + return class ArrayInitZero extends type { + constructor(...args) { + super(...args); + this.fill(0); + } + } +} diff --git a/src/mat2.js b/src/mat2.js index 2a8cd39f..ddd6e166 100644 --- a/src/mat2.js +++ b/src/mat2.js @@ -11,11 +11,7 @@ import * as glMatrix from "./common.js"; * @returns {mat2} a new 2x2 matrix */ export function create() { - let out = new glMatrix.ARRAY_TYPE(4); - if (glMatrix.ARRAY_TYPE != Float32Array) { - out[1] = 0; - out[2] = 0; - } + let out = new glMatrix.ARRAY_ZERO_INIT_TYPE(4); out[0] = 1; out[3] = 1; return out; @@ -237,16 +233,12 @@ export function rotate(out, a, rad) { * @returns {mat2} out **/ export function scale(out, a, v) { - let a0 = a[0], - a1 = a[1], - a2 = a[2], - a3 = a[3]; let v0 = v[0], v1 = v[1]; - out[0] = a0 * v0; - out[1] = a1 * v0; - out[2] = a2 * v1; - out[3] = a3 * v1; + out[0] = a[0] * v0; + out[1] = a[1] * v0; + out[2] = a[2] * v1; + out[3] = a[3] * v1; return out; } @@ -394,7 +386,7 @@ export function equals(a, b) { glMatrix.EPSILON * Math.max(1.0, Math.abs(a2), Math.abs(b2)) && Math.abs(a3 - b3) <= glMatrix.EPSILON * Math.max(1.0, Math.abs(a3), Math.abs(b3)) - ); + ); } /** diff --git a/src/mat2d.js b/src/mat2d.js index fce8aabd..bfc137f7 100644 --- a/src/mat2d.js +++ b/src/mat2d.js @@ -25,13 +25,7 @@ import * as glMatrix from "./common.js"; * @returns {mat2d} a new 2x3 matrix */ export function create() { - let out = new glMatrix.ARRAY_TYPE(6); - if (glMatrix.ARRAY_TYPE != Float32Array) { - out[1] = 0; - out[2] = 0; - out[4] = 0; - out[5] = 0; - } + let out = new glMatrix.ARRAY_ZERO_INIT_TYPE(6); out[0] = 1; out[3] = 1; return out; @@ -213,17 +207,15 @@ export function rotate(out, a, rad) { let a0 = a[0], a1 = a[1], a2 = a[2], - a3 = a[3], - a4 = a[4], - a5 = a[5]; + a3 = a[3]; let s = Math.sin(rad); let c = Math.cos(rad); out[0] = a0 * c + a2 * s; out[1] = a1 * c + a3 * s; out[2] = a0 * -s + a2 * c; out[3] = a1 * -s + a3 * c; - out[4] = a4; - out[5] = a5; + out[4] = a[4]; + out[5] = a[5]; return out; } diff --git a/src/mat3.js b/src/mat3.js index 2a966eaa..961a3ed1 100644 --- a/src/mat3.js +++ b/src/mat3.js @@ -11,15 +11,7 @@ import * as glMatrix from "./common.js"; * @returns {mat3} a new 3x3 matrix */ export function create() { - let out = new glMatrix.ARRAY_TYPE(9); - if (glMatrix.ARRAY_TYPE != Float32Array) { - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[5] = 0; - out[6] = 0; - out[7] = 0; - } + let out = new glMatrix.ARRAY_ZERO_INIT_TYPE(9); out[0] = 1; out[4] = 1; out[8] = 1; @@ -171,15 +163,15 @@ export function identity(out) { export function transpose(out, a) { // If we are transposing ourselves we can skip a few steps but have to cache some values if (out === a) { - let a01 = a[1], - a02 = a[2], - a12 = a[5]; - out[1] = a[3]; - out[2] = a[6]; - out[3] = a01; + let a01 = a[1], + a02 = a[2], + a12 = a[5]; + out[1] = a[3]; + out[2] = a[6]; + out[3] = a01; out[5] = a[7]; - out[6] = a02; - out[7] = a12; + out[6] = a02; + out[7] = a12; } else { out[0] = a[0]; out[1] = a[3]; @@ -189,7 +181,7 @@ export function transpose(out, a) { out[5] = a[7]; out[6] = a[2]; out[7] = a[5]; - out[8] = a[8]; + out[8] = a[8]; } return out; @@ -700,10 +692,10 @@ export function frob(a) { a[2] * a[2] + a[3] * a[3] + a[4] * a[4] + - a[5] * a[5] + - a[6] * a[6] + - a[7] * a[7] + - a[8] * a[8] + a[5] * a[5] + + a[6] * a[6] + + a[7] * a[7] + + a[8] * a[8] ); } diff --git a/src/mat4.js b/src/mat4.js index 206f7680..48077c4f 100644 --- a/src/mat4.js +++ b/src/mat4.js @@ -11,21 +11,7 @@ import * as glMatrix from "./common.js"; * @returns {mat4} a new 4x4 matrix */ export function create() { - let out = new glMatrix.ARRAY_TYPE(16); - if (glMatrix.ARRAY_TYPE != Float32Array) { - out[1] = 0; - out[2] = 0; - out[3] = 0; - out[4] = 0; - out[6] = 0; - out[7] = 0; - out[8] = 0; - out[9] = 0; - out[11] = 0; - out[12] = 0; - out[13] = 0; - out[14] = 0; - } + let out = new glMatrix.ARRAY_ZERO_INIT_TYPE(16); out[0] = 1; out[5] = 1; out[10] = 1; @@ -242,25 +228,25 @@ export function identity(out) { export function transpose(out, a) { // If we are transposing ourselves we can skip a few steps but have to cache some values if (out === a) { - let a01 = a[1], - a02 = a[2], - a03 = a[3]; - let a12 = a[6], - a13 = a[7]; - let a23 = a[11]; - - out[1] = a[4]; - out[2] = a[8]; - out[3] = a[12]; - out[4] = a01; - out[6] = a[9]; - out[7] = a[13]; - out[8] = a02; - out[9] = a12; - out[11] = a[14]; - out[12] = a03; - out[13] = a13; - out[14] = a23; + let a01 = a[1], + a02 = a[2], + a03 = a[3]; + let a12 = a[6], + a13 = a[7]; + let a23 = a[11]; + + out[1] = a[4]; + out[2] = a[8]; + out[3] = a[12]; + out[4] = a01; + out[6] = a[9]; + out[7] = a[13]; + out[8] = a02; + out[9] = a12; + out[11] = a[14]; + out[12] = a03; + out[13] = a13; + out[14] = a23; } else { out[0] = a[0]; out[1] = a[4]; @@ -277,7 +263,7 @@ export function transpose(out, a) { out[12] = a[3]; out[13] = a[7]; out[14] = a[11]; - out[15] = a[15]; + out[15] = a[15]; } return out; diff --git a/src/quat.js b/src/quat.js index 6f917a1c..960b0c77 100644 --- a/src/quat.js +++ b/src/quat.js @@ -14,12 +14,7 @@ import * as vec4 from "./vec4.js"; * @returns {quat} a new quaternion */ export function create() { - let out = new glMatrix.ARRAY_TYPE(4); - if (glMatrix.ARRAY_TYPE != Float32Array) { - out[0] = 0; - out[1] = 0; - out[2] = 0; - } + let out = new glMatrix.ARRAY_ZERO_INIT_TYPE(4); out[3] = 1; return out; } diff --git a/src/quat2.js b/src/quat2.js index 8774a094..dd35ef1a 100644 --- a/src/quat2.js +++ b/src/quat2.js @@ -16,16 +16,7 @@ import * as mat4 from "./mat4.js"; * @returns {quat2} a new dual quaternion [real -> rotation, dual -> translation] */ export function create() { - let dq = new glMatrix.ARRAY_TYPE(8); - if (glMatrix.ARRAY_TYPE != Float32Array) { - dq[0] = 0; - dq[1] = 0; - dq[2] = 0; - dq[4] = 0; - dq[5] = 0; - dq[6] = 0; - dq[7] = 0; - } + let dq = new glMatrix.ARRAY_ZERO_INIT_TYPE(8); dq[3] = 1; return dq; } diff --git a/src/vec2.js b/src/vec2.js index 76b851a8..e71a851b 100644 --- a/src/vec2.js +++ b/src/vec2.js @@ -11,12 +11,7 @@ import * as glMatrix from "./common.js"; * @returns {vec2} a new 2D vector */ export function create() { - let out = new glMatrix.ARRAY_TYPE(2); - if (glMatrix.ARRAY_TYPE != Float32Array) { - out[0] = 0; - out[1] = 0; - } - return out; + return new glMatrix.ARRAY_ZERO_INIT_TYPE(2); } /** @@ -233,9 +228,7 @@ export function scaleAndAdd(out, a, b, scale) { * @returns {Number} distance between a and b */ export function distance(a, b) { - var x = b[0] - a[0], - y = b[1] - a[1]; - return Math.sqrt(x * x + y * y); + return Math.hypot(b[0] - a[0], b[1] - a[1]); } /** @@ -258,9 +251,7 @@ export function squaredDistance(a, b) { * @returns {Number} length of a */ export function length(a) { - var x = a[0], - y = a[1]; - return Math.sqrt(x * x + y * y); + return Math.hypot(a[0], a[1]); } /** @@ -309,16 +300,11 @@ export function inverse(out, a) { * @returns {vec2} out */ export function normalize(out, a) { - var x = a[0], - y = a[1]; - var len = x * x + y * y; - if (len > 0) { - //TODO: evaluate use of glm_invsqrt here? - len = 1 / Math.sqrt(len); - } - out[0] = a[0] * len; - out[1] = a[1] * len; - return out; + const len = Math.max(Math.hypot(a[0], a[1]), glMatrix.EPSILON); + + out[0] = a[0] / len; + out[1] = a[1] / len; + return out; } /** @@ -372,8 +358,7 @@ export function lerp(out, a, b, t) { * @param {Number} [scale] Length of the resulting vector. If omitted, a unit vector will be returned * @returns {vec2} out */ -export function random(out, scale) { - scale = scale === undefined ? 1.0 : scale; +export function random(out, scale = 1.0) { var r = glMatrix.RANDOM() * 2.0 * Math.PI; out[0] = Math.cos(r) * scale; out[1] = Math.sin(r) * scale; @@ -483,7 +468,7 @@ export function angle(a, b) { /** * Get the signed angle in the interval [-pi,pi] between two 2D vectors (positive if `a` is to the right of `b`) - * + * * @param {ReadonlyVec2} a The first vector * @param {ReadonlyVec2} b The second vector * @returns {number} The signed angle in radians @@ -605,15 +590,9 @@ export const sqrLen = squaredLength; export const forEach = (function() { let vec = create(); - return function(a, stride, offset, count, fn, arg) { + return function(a, stride, offset = 0, count, fn, arg) { let i, l; - if (!stride) { - stride = 2; - } - - if (!offset) { - offset = 0; - } + stride = stride || 2; if (count) { l = Math.min(count * stride + offset, a.length); diff --git a/src/vec3.js b/src/vec3.js index c54c5fab..b02ab1d7 100644 --- a/src/vec3.js +++ b/src/vec3.js @@ -11,13 +11,7 @@ import * as glMatrix from "./common.js"; * @returns {vec3} a new 3D vector */ export function create() { - let out = new glMatrix.ARRAY_TYPE(3); - if (glMatrix.ARRAY_TYPE != Float32Array) { - out[0] = 0; - out[1] = 0; - out[2] = 0; - } - return out; + return new glMatrix.ARRAY_ZERO_INIT_TYPE(3); } /** @@ -41,10 +35,7 @@ export function clone(a) { * @returns {Number} length of a */ export function length(a) { - let x = a[0]; - let y = a[1]; - let z = a[2]; - return Math.sqrt(x * x + y * y + z * z); + return Math.hypot(a[0], a[1], a[2]); } /** @@ -264,10 +255,7 @@ export function scaleAndAdd(out, a, b, scale) { * @returns {Number} distance between a and b */ export function distance(a, b) { - let x = b[0] - a[0]; - let y = b[1] - a[1]; - let z = b[2] - a[2]; - return Math.sqrt(x * x + y * y + z * z); + return Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]); } /** @@ -278,10 +266,7 @@ export function distance(a, b) { * @returns {Number} squared distance between a and b */ export function squaredDistance(a, b) { - let x = b[0] - a[0]; - let y = b[1] - a[1]; - let z = b[2] - a[2]; - return x * x + y * y + z * z; + return Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]) ** 2; } /** @@ -291,10 +276,7 @@ export function squaredDistance(a, b) { * @returns {Number} squared length of a */ export function squaredLength(a) { - let x = a[0]; - let y = a[1]; - let z = a[2]; - return x * x + y * y + z * z; + return a[0] ** 2 + a[1] ** 2 + a[2] ** 2; } /** @@ -333,17 +315,10 @@ export function inverse(out, a) { * @returns {vec3} out */ export function normalize(out, a) { - let x = a[0]; - let y = a[1]; - let z = a[2]; - let len = x * x + y * y + z * z; - if (len > 0) { - //TODO: evaluate use of glm_invsqrt here? - len = 1 / Math.sqrt(len); - } - out[0] = a[0] * len; - out[1] = a[1] * len; - out[2] = a[2] * len; + let len = Math.max(Math.hypot(a[0], a[1], a[2]), glMatrix.EPSILON); + out[0] = a[0] / len; + out[1] = a[1] / len; + out[2] = a[2] / len; return out; } @@ -480,9 +455,7 @@ export function bezier(out, a, b, c, d, t) { * @param {Number} [scale] Length of the resulting vector. If omitted, a unit vector will be returned * @returns {vec3} out */ -export function random(out, scale) { - scale = scale === undefined ? 1.0 : scale; - +export function random(out, scale = 1.0) { let r = glMatrix.RANDOM() * 2.0 * Math.PI; let z = glMatrix.RANDOM() * 2.0 - 1.0; let zScale = Math.sqrt(1.0 - z * z) * scale; @@ -793,15 +766,9 @@ export const sqrLen = squaredLength; export const forEach = (function () { let vec = create(); - return function (a, stride, offset, count, fn, arg) { + return function (a, stride, offset = 0, count, fn, arg) { let i, l; - if (!stride) { - stride = 3; - } - - if (!offset) { - offset = 0; - } + stride = stride || 3; if (count) { l = Math.min(count * stride + offset, a.length); diff --git a/src/vec4.js b/src/vec4.js index 6044df0a..6c951ca1 100644 --- a/src/vec4.js +++ b/src/vec4.js @@ -11,14 +11,7 @@ import * as glMatrix from "./common.js"; * @returns {vec4} a new 4D vector */ export function create() { - let out = new glMatrix.ARRAY_TYPE(4); - if (glMatrix.ARRAY_TYPE != Float32Array) { - out[0] = 0; - out[1] = 0; - out[2] = 0; - out[3] = 0; - } - return out; + return new glMatrix.ARRAY_ZERO_INIT_TYPE(4); } /** @@ -269,11 +262,7 @@ export function scaleAndAdd(out, a, b, scale) { * @returns {Number} distance between a and b */ export function distance(a, b) { - let x = b[0] - a[0]; - let y = b[1] - a[1]; - let z = b[2] - a[2]; - let w = b[3] - a[3]; - return Math.sqrt(x * x + y * y + z * z + w * w); + return Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2], b[3] - a[3]); } /** @@ -284,11 +273,7 @@ export function distance(a, b) { * @returns {Number} squared distance between a and b */ export function squaredDistance(a, b) { - let x = b[0] - a[0]; - let y = b[1] - a[1]; - let z = b[2] - a[2]; - let w = b[3] - a[3]; - return x * x + y * y + z * z + w * w; + return Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2], b[3] - a[3]) ** 2; } /** @@ -298,11 +283,7 @@ export function squaredDistance(a, b) { * @returns {Number} length of a */ export function length(a) { - let x = a[0]; - let y = a[1]; - let z = a[2]; - let w = a[3]; - return Math.sqrt(x * x + y * y + z * z + w * w); + return Math.hypot(a[0], a[1], a[2], a[3]);; } /** @@ -312,11 +293,7 @@ export function length(a) { * @returns {Number} squared length of a */ export function squaredLength(a) { - let x = a[0]; - let y = a[1]; - let z = a[2]; - let w = a[3]; - return x * x + y * y + z * z + w * w; + return Math.hypot(a[0], a[1], a[2], a[3]) ** 2; } /** @@ -357,18 +334,15 @@ export function inverse(out, a) { * @returns {vec4} out */ export function normalize(out, a) { - let x = a[0]; - let y = a[1]; - let z = a[2]; - let w = a[3]; - let len = x * x + y * y + z * z + w * w; - if (len > 0) { - len = 1 / Math.sqrt(len); - } - out[0] = x * len; - out[1] = y * len; - out[2] = z * len; - out[3] = w * len; + let x = a[0], + y = a[1], + z = a[2], + w = a[3]; + let len = Math.max(Math.hypot(x, y, z, w), glMatrix.EPSILON); + out[0] = x / len; + out[1] = y / len; + out[2] = z / len; + out[3] = w / len; return out; } @@ -648,15 +622,9 @@ export const sqrLen = squaredLength; export const forEach = (function() { let vec = create(); - return function(a, stride, offset, count, fn, arg) { + return function(a, stride, offset = 0, count, fn, arg) { let i, l; - if (!stride) { - stride = 4; - } - - if (!offset) { - offset = 0; - } + stride = stride || 4; if (count) { l = Math.min(count * stride + offset, a.length);