UNPKG

wgpu-matrix

Version:

fast matrix math library for WebGPU

1,514 lines (1,510 loc) 200 kB
/* wgpu-matrix@3.4.2, license MIT */ function wrapConstructor(OriginalConstructor, modifier) { return class extends OriginalConstructor { constructor(...args) { super(...args); modifier(this); } }; // Type assertion is necessary here } const ZeroArray = wrapConstructor((Array), a => a.fill(0)); /* * Copyright 2022 Gregg Tavares * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ let EPSILON = 0.000001; /** * Set the value for EPSILON for various checks * @param v - Value to use for EPSILON. * @returns previous value of EPSILON; */ function setEpsilon(v) { const old = EPSILON; EPSILON = v; return old; } /** * Convert degrees to radians * @param degrees - Angle in degrees * @returns angle converted to radians */ function degToRad(degrees) { return degrees * Math.PI / 180; } /** * Convert radians to degrees * @param radians - Angle in radians * @returns angle converted to degrees */ function radToDeg(radians) { return radians * 180 / Math.PI; } /** * Lerps between a and b via t * @param a - starting value * @param b - ending value * @param t - value where 0 = a and 1 = b * @returns a + (b - a) * t */ function lerp(a, b, t) { return a + (b - a) * t; } /** * Compute the opposite of lerp. Given a and b and a value between * a and b returns a value between 0 and 1. 0 if a, 1 if b. * Note: no clamping is done. * @param a - start value * @param b - end value * @param v - value between a and b * @returns (v - a) / (b - a) */ function inverseLerp(a, b, v) { const d = b - a; return (Math.abs(b - a) < EPSILON) ? a : (v - a) / d; } /** * Compute the euclidean modulo * * ``` * // table for n / 3 * -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 <- n * ------------------------------------ * -2 -1 -0 -2 -1 0, 1, 2, 0, 1, 2 <- n % 3 * 1 2 0 1 2 0, 1, 2, 0, 1, 2 <- euclideanModule(n, 3) * ``` * * @param n - dividend * @param m - divisor * @returns the euclidean modulo of n / m */ function euclideanModulo(n, m) { return ((n % m) + m) % m; } var utils = { __proto__: null, get EPSILON () { return EPSILON; }, degToRad: degToRad, euclideanModulo: euclideanModulo, inverseLerp: inverseLerp, lerp: lerp, radToDeg: radToDeg, setEpsilon: setEpsilon }; /* * Copyright 2022 Gregg Tavares * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * Generates am typed API for Vec3 */ function getAPIImpl$5(Ctor) { /** * Creates a Vec2; may be called with x, y, z to set initial values. * * Note: Since passing in a raw JavaScript array * is valid in all circumstances, if you want to * force a JavaScript array into a Vec2's specified type * it would be faster to use * * ``` * const v = vec2.clone(someJSArray); * ``` * * @param x - Initial x value. * @param y - Initial y value. * @returns the created vector */ function create(x = 0, y = 0) { const newDst = new Ctor(2); if (x !== undefined) { newDst[0] = x; if (y !== undefined) { newDst[1] = y; } } return newDst; } /** * Creates a Vec2; may be called with x, y, z to set initial values. (same as create) * @param x - Initial x value. * @param y - Initial y value. * @returns the created vector */ const fromValues = create; /** * Sets the values of a Vec2 * Also see {@link vec2.create} and {@link vec2.copy} * * @param x first value * @param y second value * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector with its elements set. */ function set(x, y, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = x; newDst[1] = y; return newDst; } /** * Applies Math.ceil to each element of vector * @param v - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the ceil of each element of v. */ function ceil(v, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = Math.ceil(v[0]); newDst[1] = Math.ceil(v[1]); return newDst; } /** * Applies Math.floor to each element of vector * @param v - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the floor of each element of v. */ function floor(v, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = Math.floor(v[0]); newDst[1] = Math.floor(v[1]); return newDst; } /** * Applies Math.round to each element of vector * @param v - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the round of each element of v. */ function round(v, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = Math.round(v[0]); newDst[1] = Math.round(v[1]); return newDst; } /** * Clamp each element of vector between min and max * @param v - Operand vector. * @param max - Min value, default 0 * @param min - Max value, default 1 * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that the clamped value of each element of v. */ function clamp(v, min = 0, max = 1, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = Math.min(max, Math.max(min, v[0])); newDst[1] = Math.min(max, Math.max(min, v[1])); return newDst; } /** * Adds two vectors; assumes a and b have the same dimension. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the sum of a and b. */ function add(a, b, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = a[0] + b[0]; newDst[1] = a[1] + b[1]; return newDst; } /** * Adds two vectors, scaling the 2nd; assumes a and b have the same dimension. * @param a - Operand vector. * @param b - Operand vector. * @param scale - Amount to scale b * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the sum of a + b * scale. */ function addScaled(a, b, scale, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = a[0] + b[0] * scale; newDst[1] = a[1] + b[1] * scale; return newDst; } /** * Returns the angle in radians between two vectors. * @param a - Operand vector. * @param b - Operand vector. * @returns The angle in radians between the 2 vectors. */ function angle(a, b) { const ax = a[0]; const ay = a[1]; const bx = b[0]; const by = b[1]; const mag1 = Math.sqrt(ax * ax + ay * ay); const mag2 = Math.sqrt(bx * bx + by * by); const mag = mag1 * mag2; const cosine = mag && dot(a, b) / mag; return Math.acos(cosine); } /** * Subtracts two vectors. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the difference of a and b. */ function subtract(a, b, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = a[0] - b[0]; newDst[1] = a[1] - b[1]; return newDst; } /** * Subtracts two vectors. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the difference of a and b. */ const sub = subtract; /** * Check if 2 vectors are approximately equal * @param a - Operand vector. * @param b - Operand vector. * @returns true if vectors are approximately equal */ function equalsApproximately(a, b) { return Math.abs(a[0] - b[0]) < EPSILON && Math.abs(a[1] - b[1]) < EPSILON; } /** * Check if 2 vectors are exactly equal * @param a - Operand vector. * @param b - Operand vector. * @returns true if vectors are exactly equal */ function equals(a, b) { return a[0] === b[0] && a[1] === b[1]; } /** * Performs linear interpolation on two vectors. * Given vectors a and b and interpolation coefficient t, returns * a + t * (b - a). * @param a - Operand vector. * @param b - Operand vector. * @param t - Interpolation coefficient. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The linear interpolated result. */ function lerp(a, b, t, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = a[0] + t * (b[0] - a[0]); newDst[1] = a[1] + t * (b[1] - a[1]); return newDst; } /** * Performs linear interpolation on two vectors. * Given vectors a and b and interpolation coefficient vector t, returns * a + t * (b - a). * @param a - Operand vector. * @param b - Operand vector. * @param t - Interpolation coefficients vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns the linear interpolated result. */ function lerpV(a, b, t, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = a[0] + t[0] * (b[0] - a[0]); newDst[1] = a[1] + t[1] * (b[1] - a[1]); return newDst; } /** * Return max values of two vectors. * Given vectors a and b returns * [max(a[0], b[0]), max(a[1], b[1]), max(a[2], b[2])]. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The max components vector. */ function max(a, b, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = Math.max(a[0], b[0]); newDst[1] = Math.max(a[1], b[1]); return newDst; } /** * Return min values of two vectors. * Given vectors a and b returns * [min(a[0], b[0]), min(a[1], b[1]), min(a[2], b[2])]. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The min components vector. */ function min(a, b, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = Math.min(a[0], b[0]); newDst[1] = Math.min(a[1], b[1]); return newDst; } /** * Multiplies a vector by a scalar. * @param v - The vector. * @param k - The scalar. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ function mulScalar(v, k, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = v[0] * k; newDst[1] = v[1] * k; return newDst; } /** * Multiplies a vector by a scalar. (same as mulScalar) * @param v - The vector. * @param k - The scalar. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ const scale = mulScalar; /** * Divides a vector by a scalar. * @param v - The vector. * @param k - The scalar. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ function divScalar(v, k, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = v[0] / k; newDst[1] = v[1] / k; return newDst; } /** * Inverse a vector. * @param v - The vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The inverted vector. */ function inverse(v, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = 1 / v[0]; newDst[1] = 1 / v[1]; return newDst; } /** * Invert a vector. (same as inverse) * @param v - The vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The inverted vector. */ const invert = inverse; /** * Computes the cross product of two vectors; assumes both vectors have * three entries. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of a cross b. */ function cross(a, b, dst) { const newDst = (dst ?? new Ctor(3)); const z = a[0] * b[1] - a[1] * b[0]; newDst[0] = 0; newDst[1] = 0; newDst[2] = z; return newDst; } /** * Computes the dot product of two vectors; assumes both vectors have * three entries. * @param a - Operand vector. * @param b - Operand vector. * @returns dot product */ function dot(a, b) { return a[0] * b[0] + a[1] * b[1]; } /** * Computes the length of vector * @param v - vector. * @returns length of vector. */ function length(v) { const v0 = v[0]; const v1 = v[1]; return Math.sqrt(v0 * v0 + v1 * v1); } /** * Computes the length of vector (same as length) * @param v - vector. * @returns length of vector. */ const len = length; /** * Computes the square of the length of vector * @param v - vector. * @returns square of the length of vector. */ function lengthSq(v) { const v0 = v[0]; const v1 = v[1]; return v0 * v0 + v1 * v1; } /** * Computes the square of the length of vector (same as lengthSq) * @param v - vector. * @returns square of the length of vector. */ const lenSq = lengthSq; /** * Computes the distance between 2 points * @param a - vector. * @param b - vector. * @returns distance between a and b */ function distance(a, b) { const dx = a[0] - b[0]; const dy = a[1] - b[1]; return Math.sqrt(dx * dx + dy * dy); } /** * Computes the distance between 2 points (same as distance) * @param a - vector. * @param b - vector. * @returns distance between a and b */ const dist = distance; /** * Computes the square of the distance between 2 points * @param a - vector. * @param b - vector. * @returns square of the distance between a and b */ function distanceSq(a, b) { const dx = a[0] - b[0]; const dy = a[1] - b[1]; return dx * dx + dy * dy; } /** * Computes the square of the distance between 2 points (same as distanceSq) * @param a - vector. * @param b - vector. * @returns square of the distance between a and b */ const distSq = distanceSq; /** * Divides a vector by its Euclidean length and returns the quotient. * @param v - The vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The normalized vector. */ function normalize(v, dst) { const newDst = (dst ?? new Ctor(2)); const v0 = v[0]; const v1 = v[1]; const lenSq = v0 * v0 + v1 * v1; const scale = lenSq > 0 ? 1 / Math.sqrt(lenSq) : 1; newDst[0] = v0 * scale; newDst[1] = v1 * scale; return newDst; } /** * Negates a vector. * @param v - The vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns -v. */ function negate(v, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = -v[0]; newDst[1] = -v[1]; return newDst; } /** * Copies a vector. (same as {@link vec2.clone}) * Also see {@link vec2.create} and {@link vec2.set} * @param v - The vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A copy of v. */ function copy(v, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = v[0]; newDst[1] = v[1]; return newDst; } /** * Clones a vector. (same as {@link vec2.copy}) * Also see {@link vec2.create} and {@link vec2.set} * @param v - The vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A copy of v. */ const clone = copy; /** * Multiplies a vector by another vector (component-wise); assumes a and * b have the same length. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of products of entries of a and b. */ function multiply(a, b, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = a[0] * b[0]; newDst[1] = a[1] * b[1]; return newDst; } /** * Multiplies a vector by another vector (component-wise); assumes a and * b have the same length. (same as mul) * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of products of entries of a and b. */ const mul = multiply; /** * Divides a vector by another vector (component-wise); assumes a and * b have the same length. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of quotients of entries of a and b. */ function divide(a, b, dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = a[0] / b[0]; newDst[1] = a[1] / b[1]; return newDst; } /** * Divides a vector by another vector (component-wise); assumes a and * b have the same length. (same as divide) * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of quotients of entries of a and b. */ const div = divide; /** * Creates a random unit vector * scale * @param scale - Default 1 * @param dst - vector to hold result. If not passed in a new one is created. * @returns The random vector. */ function random(scale = 1, dst) { const newDst = (dst ?? new Ctor(2)); const angle = Math.random() * 2 * Math.PI; newDst[0] = Math.cos(angle) * scale; newDst[1] = Math.sin(angle) * scale; return newDst; } /** * Zero's a vector * @param dst - vector to hold result. If not passed in a new one is created. * @returns The zeroed vector. */ function zero(dst) { const newDst = (dst ?? new Ctor(2)); newDst[0] = 0; newDst[1] = 0; return newDst; } /** * Transform Vec2 by 4x4 matrix * @param v - the vector * @param m - The matrix. * @param dst - optional Vec2 to store result. If not passed a new one is created. * @returns the transformed vector */ function transformMat4(v, m, dst) { const newDst = (dst ?? new Ctor(2)); const x = v[0]; const y = v[1]; newDst[0] = x * m[0] + y * m[4] + m[12]; newDst[1] = x * m[1] + y * m[5] + m[13]; return newDst; } /** * Transform Vec2 by 3x3 matrix * * @param v - the vector * @param m - The matrix. * @param dst - optional Vec2 to store result. If not passed a new one is created. * @returns the transformed vector */ function transformMat3(v, m, dst) { const newDst = (dst ?? new Ctor(2)); const x = v[0]; const y = v[1]; newDst[0] = m[0] * x + m[4] * y + m[8]; newDst[1] = m[1] * x + m[5] * y + m[9]; return newDst; } /** * Rotate a 2D vector * * @param a The vec2 point to rotate * @param b The origin of the rotation * @param rad The angle of rotation in radians * @returns the rotated vector */ function rotate(a, b, rad, dst) { const newDst = (dst ?? new Ctor(2)); // Translate point to the origin const p0 = a[0] - b[0]; const p1 = a[1] - b[1]; const sinC = Math.sin(rad); const cosC = Math.cos(rad); //perform rotation and translate to correct position newDst[0] = p0 * cosC - p1 * sinC + b[0]; newDst[1] = p0 * sinC + p1 * cosC + b[1]; return newDst; } /** * Treat a 2D vector as a direction and set it's length * * @param a The vec2 to lengthen * @param len The length of the resulting vector * @returns The lengthened vector */ function setLength(a, len, dst) { const newDst = (dst ?? new Ctor(2)); normalize(a, newDst); return mulScalar(newDst, len, newDst); } /** * Ensure a vector is not longer than a max length * * @param a The vec2 to limit * @param maxLen The longest length of the resulting vector * @returns The vector, shortened to maxLen if it's too long */ function truncate(a, maxLen, dst) { const newDst = (dst ?? new Ctor(2)); if (length(a) > maxLen) { return setLength(a, maxLen, newDst); } return copy(a, newDst); } /** * Return the vector exactly between 2 endpoint vectors * * @param a Endpoint 1 * @param b Endpoint 2 * @returns The vector exactly residing between endpoints 1 and 2 */ function midpoint(a, b, dst) { const newDst = (dst ?? new Ctor(2)); return lerp(a, b, 0.5, newDst); } return { create, fromValues, set, ceil, floor, round, clamp, add, addScaled, angle, subtract, sub, equalsApproximately, equals, lerp, lerpV, max, min, mulScalar, scale, divScalar, inverse, invert, cross, dot, length, len, lengthSq, lenSq, distance, dist, distanceSq, distSq, normalize, negate, copy, clone, multiply, mul, divide, div, random, zero, transformMat4, transformMat3, rotate, setLength, truncate, midpoint, }; } const cache$5 = new Map(); function getAPI$5(Ctor) { let api = cache$5.get(Ctor); if (!api) { api = getAPIImpl$5(Ctor); cache$5.set(Ctor, api); } return api; } /* * Copyright 2022 Gregg Tavares * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /** * Generates am typed API for Vec3 * */ function getAPIImpl$4(Ctor) { /** * Creates a vec3; may be called with x, y, z to set initial values. * @param x - Initial x value. * @param y - Initial y value. * @param z - Initial z value. * @returns the created vector */ function create(x, y, z) { const newDst = new Ctor(3); if (x !== undefined) { newDst[0] = x; if (y !== undefined) { newDst[1] = y; if (z !== undefined) { newDst[2] = z; } } } return newDst; } /** * Creates a vec3; may be called with x, y, z to set initial values. (same as create) * @param x - Initial x value. * @param y - Initial y value. * @param z - Initial z value. * @returns the created vector */ const fromValues = create; /** * Sets the values of a Vec3 * Also see {@link vec3.create} and {@link vec3.copy} * * @param x first value * @param y second value * @param z third value * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector with its elements set. */ function set(x, y, z, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = x; newDst[1] = y; newDst[2] = z; return newDst; } /** * Applies Math.ceil to each element of vector * @param v - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the ceil of each element of v. */ function ceil(v, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = Math.ceil(v[0]); newDst[1] = Math.ceil(v[1]); newDst[2] = Math.ceil(v[2]); return newDst; } /** * Applies Math.floor to each element of vector * @param v - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the floor of each element of v. */ function floor(v, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = Math.floor(v[0]); newDst[1] = Math.floor(v[1]); newDst[2] = Math.floor(v[2]); return newDst; } /** * Applies Math.round to each element of vector * @param v - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the round of each element of v. */ function round(v, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = Math.round(v[0]); newDst[1] = Math.round(v[1]); newDst[2] = Math.round(v[2]); return newDst; } /** * Clamp each element of vector between min and max * @param v - Operand vector. * @param max - Min value, default 0 * @param min - Max value, default 1 * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that the clamped value of each element of v. */ function clamp(v, min = 0, max = 1, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = Math.min(max, Math.max(min, v[0])); newDst[1] = Math.min(max, Math.max(min, v[1])); newDst[2] = Math.min(max, Math.max(min, v[2])); return newDst; } /** * Adds two vectors; assumes a and b have the same dimension. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the sum of a and b. */ function add(a, b, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = a[0] + b[0]; newDst[1] = a[1] + b[1]; newDst[2] = a[2] + b[2]; return newDst; } /** * Adds two vectors, scaling the 2nd; assumes a and b have the same dimension. * @param a - Operand vector. * @param b - Operand vector. * @param scale - Amount to scale b * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the sum of a + b * scale. */ function addScaled(a, b, scale, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = a[0] + b[0] * scale; newDst[1] = a[1] + b[1] * scale; newDst[2] = a[2] + b[2] * scale; return newDst; } /** * Returns the angle in radians between two vectors. * @param a - Operand vector. * @param b - Operand vector. * @returns The angle in radians between the 2 vectors. */ function angle(a, b) { const ax = a[0]; const ay = a[1]; const az = a[2]; const bx = b[0]; const by = b[1]; const bz = b[2]; const mag1 = Math.sqrt(ax * ax + ay * ay + az * az); const mag2 = Math.sqrt(bx * bx + by * by + bz * bz); const mag = mag1 * mag2; const cosine = mag && dot(a, b) / mag; return Math.acos(cosine); } /** * Subtracts two vectors. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the difference of a and b. */ function subtract(a, b, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = a[0] - b[0]; newDst[1] = a[1] - b[1]; newDst[2] = a[2] - b[2]; return newDst; } /** * Subtracts two vectors. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A vector that is the difference of a and b. */ const sub = subtract; /** * Check if 2 vectors are approximately equal * @param a - Operand vector. * @param b - Operand vector. * @returns true if vectors are approximately equal */ function equalsApproximately(a, b) { return Math.abs(a[0] - b[0]) < EPSILON && Math.abs(a[1] - b[1]) < EPSILON && Math.abs(a[2] - b[2]) < EPSILON; } /** * Check if 2 vectors are exactly equal * @param a - Operand vector. * @param b - Operand vector. * @returns true if vectors are exactly equal */ function equals(a, b) { return a[0] === b[0] && a[1] === b[1] && a[2] === b[2]; } /** * Performs linear interpolation on two vectors. * Given vectors a and b and interpolation coefficient t, returns * a + t * (b - a). * @param a - Operand vector. * @param b - Operand vector. * @param t - Interpolation coefficient. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The linear interpolated result. */ function lerp(a, b, t, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = a[0] + t * (b[0] - a[0]); newDst[1] = a[1] + t * (b[1] - a[1]); newDst[2] = a[2] + t * (b[2] - a[2]); return newDst; } /** * Performs linear interpolation on two vectors. * Given vectors a and b and interpolation coefficient vector t, returns * a + t * (b - a). * @param a - Operand vector. * @param b - Operand vector. * @param t - Interpolation coefficients vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns the linear interpolated result. */ function lerpV(a, b, t, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = a[0] + t[0] * (b[0] - a[0]); newDst[1] = a[1] + t[1] * (b[1] - a[1]); newDst[2] = a[2] + t[2] * (b[2] - a[2]); return newDst; } /** * Return max values of two vectors. * Given vectors a and b returns * [max(a[0], b[0]), max(a[1], b[1]), max(a[2], b[2])]. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The max components vector. */ function max(a, b, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = Math.max(a[0], b[0]); newDst[1] = Math.max(a[1], b[1]); newDst[2] = Math.max(a[2], b[2]); return newDst; } /** * Return min values of two vectors. * Given vectors a and b returns * [min(a[0], b[0]), min(a[1], b[1]), min(a[2], b[2])]. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The min components vector. */ function min(a, b, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = Math.min(a[0], b[0]); newDst[1] = Math.min(a[1], b[1]); newDst[2] = Math.min(a[2], b[2]); return newDst; } /** * Multiplies a vector by a scalar. * @param v - The vector. * @param k - The scalar. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ function mulScalar(v, k, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = v[0] * k; newDst[1] = v[1] * k; newDst[2] = v[2] * k; return newDst; } /** * Multiplies a vector by a scalar. (same as mulScalar) * @param v - The vector. * @param k - The scalar. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ const scale = mulScalar; /** * Divides a vector by a scalar. * @param v - The vector. * @param k - The scalar. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The scaled vector. */ function divScalar(v, k, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = v[0] / k; newDst[1] = v[1] / k; newDst[2] = v[2] / k; return newDst; } /** * Inverse a vector. * @param v - The vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The inverted vector. */ function inverse(v, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = 1 / v[0]; newDst[1] = 1 / v[1]; newDst[2] = 1 / v[2]; return newDst; } /** * Invert a vector. (same as inverse) * @param v - The vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The inverted vector. */ const invert = inverse; /** * Computes the cross product of two vectors; assumes both vectors have * three entries. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of a cross b. */ function cross(a, b, dst) { const newDst = (dst ?? new Ctor(3)); const t1 = a[2] * b[0] - a[0] * b[2]; const t2 = a[0] * b[1] - a[1] * b[0]; newDst[0] = a[1] * b[2] - a[2] * b[1]; newDst[1] = t1; newDst[2] = t2; return newDst; } /** * Computes the dot product of two vectors; assumes both vectors have * three entries. * @param a - Operand vector. * @param b - Operand vector. * @returns dot product */ function dot(a, b) { return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]); } /** * Computes the length of vector * @param v - vector. * @returns length of vector. */ function length(v) { const v0 = v[0]; const v1 = v[1]; const v2 = v[2]; return Math.sqrt(v0 * v0 + v1 * v1 + v2 * v2); } /** * Computes the length of vector (same as length) * @param v - vector. * @returns length of vector. */ const len = length; /** * Computes the square of the length of vector * @param v - vector. * @returns square of the length of vector. */ function lengthSq(v) { const v0 = v[0]; const v1 = v[1]; const v2 = v[2]; return v0 * v0 + v1 * v1 + v2 * v2; } /** * Computes the square of the length of vector (same as lengthSq) * @param v - vector. * @returns square of the length of vector. */ const lenSq = lengthSq; /** * Computes the distance between 2 points * @param a - vector. * @param b - vector. * @returns distance between a and b */ function distance(a, b) { const dx = a[0] - b[0]; const dy = a[1] - b[1]; const dz = a[2] - b[2]; return Math.sqrt(dx * dx + dy * dy + dz * dz); } /** * Computes the distance between 2 points (same as distance) * @param a - vector. * @param b - vector. * @returns distance between a and b */ const dist = distance; /** * Computes the square of the distance between 2 points * @param a - vector. * @param b - vector. * @returns square of the distance between a and b */ function distanceSq(a, b) { const dx = a[0] - b[0]; const dy = a[1] - b[1]; const dz = a[2] - b[2]; return dx * dx + dy * dy + dz * dz; } /** * Computes the square of the distance between 2 points (same as distanceSq) * @param a - vector. * @param b - vector. * @returns square of the distance between a and b */ const distSq = distanceSq; /** * Divides a vector by its Euclidean length and returns the quotient. * @param v - The vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The normalized vector. */ function normalize(v, dst) { const newDst = (dst ?? new Ctor(3)); const v0 = v[0]; const v1 = v[1]; const v2 = v[2]; const lenSq = v0 * v0 + v1 * v1 + v2 * v2; const scale = lenSq > 0 ? 1 / Math.sqrt(lenSq) : 1; newDst[0] = v0 * scale; newDst[1] = v1 * scale; newDst[2] = v2 * scale; return newDst; } /** * Negates a vector. * @param v - The vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns -v. */ function negate(v, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = -v[0]; newDst[1] = -v[1]; newDst[2] = -v[2]; return newDst; } /** * Copies a vector. (same as {@link vec3.clone}) * Also see {@link vec3.create} and {@link vec3.set} * @param v - The vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A copy of v. */ function copy(v, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = v[0]; newDst[1] = v[1]; newDst[2] = v[2]; return newDst; } /** * Clones a vector. (same as {@link vec3.copy}) * Also see {@link vec3.create} and {@link vec3.set} * @param v - The vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns A copy of v. */ const clone = copy; /** * Multiplies a vector by another vector (component-wise); assumes a and * b have the same length. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of products of entries of a and b. */ function multiply(a, b, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = a[0] * b[0]; newDst[1] = a[1] * b[1]; newDst[2] = a[2] * b[2]; return newDst; } /** * Multiplies a vector by another vector (component-wise); assumes a and * b have the same length. (same as mul) * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of products of entries of a and b. */ const mul = multiply; /** * Divides a vector by another vector (component-wise); assumes a and * b have the same length. * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of quotients of entries of a and b. */ function divide(a, b, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = a[0] / b[0]; newDst[1] = a[1] / b[1]; newDst[2] = a[2] / b[2]; return newDst; } /** * Divides a vector by another vector (component-wise); assumes a and * b have the same length. (same as divide) * @param a - Operand vector. * @param b - Operand vector. * @param dst - vector to hold result. If not passed in a new one is created. * @returns The vector of quotients of entries of a and b. */ const div = divide; /** * Creates a random vector * @param scale - Default 1 * @param dst - vector to hold result. If not passed in a new one is created. * @returns The random vector. */ function random(scale = 1, dst) { const newDst = (dst ?? new Ctor(3)); const angle = Math.random() * 2 * Math.PI; const z = Math.random() * 2 - 1; const zScale = Math.sqrt(1 - z * z) * scale; newDst[0] = Math.cos(angle) * zScale; newDst[1] = Math.sin(angle) * zScale; newDst[2] = z * scale; return newDst; } /** * Zero's a vector * @param dst - vector to hold result. If not passed in a new one is created. * @returns The zeroed vector. */ function zero(dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = 0; newDst[1] = 0; newDst[2] = 0; return newDst; } /** * transform vec3 by 4x4 matrix * @param v - the vector * @param m - The matrix. * @param dst - optional vec3 to store result. If not passed a new one is created. * @returns the transformed vector */ function transformMat4(v, m, dst) { const newDst = (dst ?? new Ctor(3)); const x = v[0]; const y = v[1]; const z = v[2]; const w = (m[3] * x + m[7] * y + m[11] * z + m[15]) || 1; newDst[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w; newDst[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w; newDst[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w; return newDst; } /** * Transform vec3 by upper 3x3 matrix inside 4x4 matrix. * @param v - The direction. * @param m - The matrix. * @param dst - optional vec3 to store result. If not passed a new one is created. * @returns The transformed vector. */ function transformMat4Upper3x3(v, m, dst) { const newDst = (dst ?? new Ctor(3)); const v0 = v[0]; const v1 = v[1]; const v2 = v[2]; newDst[0] = v0 * m[0 * 4 + 0] + v1 * m[1 * 4 + 0] + v2 * m[2 * 4 + 0]; newDst[1] = v0 * m[0 * 4 + 1] + v1 * m[1 * 4 + 1] + v2 * m[2 * 4 + 1]; newDst[2] = v0 * m[0 * 4 + 2] + v1 * m[1 * 4 + 2] + v2 * m[2 * 4 + 2]; return newDst; } /** * Transforms vec3 by 3x3 matrix * * @param v - the vector * @param m - The matrix. * @param dst - optional vec3 to store result. If not passed a new one is created. * @returns the transformed vector */ function transformMat3(v, m, dst) { const newDst = (dst ?? new Ctor(3)); const x = v[0]; const y = v[1]; const z = v[2]; newDst[0] = x * m[0] + y * m[4] + z * m[8]; newDst[1] = x * m[1] + y * m[5] + z * m[9]; newDst[2] = x * m[2] + y * m[6] + z * m[10]; return newDst; } /** * Transforms vec3 by Quaternion * @param v - the vector to transform * @param q - the quaternion to transform by * @param dst - optional vec3 to store result. If not passed a new one is created. * @returns the transformed */ function transformQuat(v, q, dst) { const newDst = (dst ?? new Ctor(3)); const qx = q[0]; const qy = q[1]; const qz = q[2]; const w2 = q[3] * 2; const x = v[0]; const y = v[1]; const z = v[2]; const uvX = qy * z - qz * y; const uvY = qz * x - qx * z; const uvZ = qx * y - qy * x; newDst[0] = x + uvX * w2 + (qy * uvZ - qz * uvY) * 2; newDst[1] = y + uvY * w2 + (qz * uvX - qx * uvZ) * 2; newDst[2] = z + uvZ * w2 + (qx * uvY - qy * uvX) * 2; return newDst; } /** * Returns the translation component of a 4-by-4 matrix as a vector with 3 * entries. * @param m - The matrix. * @param dst - vector to hold result. If not passed a new one is created. * @returns The translation component of m. */ function getTranslation(m, dst) { const newDst = (dst ?? new Ctor(3)); newDst[0] = m[12]; newDst[1] = m[13]; newDst[2] = m[14]; return newDst; } /** * Returns an axis of a 4x4 matrix as a vector with 3 entries * @param m - The matrix. * @param axis - The axis 0 = x, 1 = y, 2 = z; * @returns The axis component of m. */ function getAxis(m, axis, dst) { const newDst = (dst ?? new Ctor(3)); const off = axis * 4; newDst[0] = m[off + 0]; newDst[1] = m[off + 1]; newDst[2] = m[off + 2]; return newDst; } /** * Returns the scaling component of the matrix * @param m - The Matrix * @param dst - The vector to set. If not passed a new one is created. */ function getScaling(m, dst) { const newDst = (dst ?? new Ctor(3)); const xx = m[0]; const xy = m[1]; const xz = m[2]; const yx = m[4]; const yy = m[5]; const yz = m[6]; const zx = m[8]; const zy = m[9]; const zz = m[10]; newDst[0] = Math.sqrt(xx * xx + xy * xy + xz * xz); newDst[1] = Math.sqrt(yx * yx + yy * yy + yz * yz); newDst[2] = Math.sqrt(zx * zx + zy * zy + zz * zz); return newDst; } /** * Rotate a 3D vector around the x-axis * * @param {ReadonlyVec3} a The vec3 point to rotate * @param {ReadonlyVec3} b The origin of the rotation * @param {Number} rad The angle of rotation in radians * @param dst - The vector to set. If not passed a new one is created. * @returns the rotated vector */ function rotateX(a, b, rad, dst) { const newDst = (dst ?? new Ctor(3)); const p = []; const r = []; //Translate point to the origin p[0] = a[0] - b[0]; p[1] = a[1] - b[1]; p[2] = a[2] - b[2]; //perform rotation r[0] = p[0]; r[1] = p[1] * Math.cos(rad) - p[2] * Math.sin(rad); r[2] = p[1] * Math.sin(rad) + p[2] * Math.cos(rad); //translate to correct position newDst[0] = r[0] + b[0]; newDst[1] = r[1] + b[1]; newDst[2] = r[2