pex-math
Version:
Array-based vector, quaternion and matrix math with utils for PEX.
350 lines (317 loc) • 8.43 kB
JavaScript
/** @module avec3 */
import * as vec3 from "./vec3.js";
const TEMP_VEC3 = vec3.create();
/**
* Sets a vector components.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {number} x
* @param {number} y
* @param {number} z
*/
export function set3(a, i, x, y, z) {
a[i * 3] = x;
a[i * 3 + 1] = y;
a[i * 3 + 2] = z;
}
/**
* Sets a vector to another vector.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {import("./types.js").avec3} b
* @param {number} j
*/
export function set(a, i, b, j) {
a[i * 3] = b[j * 3];
a[i * 3 + 1] = b[j * 3 + 1];
a[i * 3 + 2] = b[j * 3 + 2];
}
/**
* Compares two vectors.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {import("./types.js").avec3} b
* @param {number} j
* @returns {boolean}
*/
export function equals(a, i, b, j) {
return (
a[i * 3] === b[j * 3] &&
a[i * 3 + 1] === b[j * 3 + 1] &&
a[i * 3 + 2] === b[j * 3 + 2]
);
}
/**
* Adds a vector to another.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {import("./types.js").avec3} b
* @param {number} j
*/
export function add(a, i, b, j) {
a[i * 3] += b[j * 3];
a[i * 3 + 1] += b[j * 3 + 1];
a[i * 3 + 2] += b[j * 3 + 2];
}
/**
* Subtracts a vector from another.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {import("./types.js").avec3} b
* @param {number} j
*/
export function sub(a, i, b, j) {
a[i * 3] -= b[j * 3];
a[i * 3 + 1] -= b[j * 3 + 1];
a[i * 3 + 2] -= b[j * 3 + 2];
}
/**
* Scales a vector by a number.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {number} s
*/
export function scale(a, i, s) {
a[i * 3] *= s;
a[i * 3 + 1] *= s;
a[i * 3 + 2] *= s;
}
/**
* Adds two vectors after scaling the second one.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {import("./types.js").avec3} b
* @param {number} j
* @param {number} s
*/
export function addScaled(a, i, b, j, s) {
a[i * 3] += b[j * 3] * s;
a[i * 3 + 1] += b[j * 3 + 1] * s;
a[i * 3 + 2] += b[j * 3 + 2] * s;
}
/**
* Multiplies a vector by a matrix.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {import("./types.js").amat4} m
* @param {number} j
*/
export function multMat4(a, i, m, j) {
const x = a[i * 3];
const y = a[i * 3 + 1];
const z = a[i * 3 + 2];
a[i * 3] =
m[j * 16] * x + m[j * 16 + 4] * y + m[j * 16 + 8] * z + m[j * 16 + 12];
a[i * 3 + 1] =
m[j * 16 + 1] * x + m[j * 16 + 5] * y + m[j * 16 + 9] * z + m[j * 16 + 13];
a[i * 3 + 2] =
m[j * 16 + 2] * x + m[j * 16 + 6] * y + m[j * 16 + 10] * z + m[j * 16 + 14];
}
/**
* Multiplies a vector by a quaternion.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {import("./types.js").aquat} q
* @param {number} j
*/
export function multQuat(a, i, q, j) {
const x = a[i * 3];
const y = a[i * 3 + 1];
const z = a[i * 3 + 2];
const qx = q[j * 4];
const qy = q[j * 4 + 1];
const qz = q[j * 4 + 2];
const qw = q[j * 4 + 3];
const ix = qw * x + qy * z - qz * y;
const iy = qw * y + qz * x - qx * z;
const iz = qw * z + qx * y - qy * x;
const iw = -qx * x - qy * y - qz * z;
a[i * 3] = ix * qw + iw * -qx + iy * -qz - iz * -qy;
a[i * 3 + 1] = iy * qw + iw * -qy + iz * -qx - ix * -qz;
a[i * 3 + 2] = iz * qw + iw * -qz + ix * -qy - iy * -qx;
}
/**
* Calculates the dot product of two vectors.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {import("./types.js").avec3} b
* @param {number} j
* @returns {number}
*/
export function dot(a, i, b, j) {
return (
a[i * 3] * b[j * 3] +
a[i * 3 + 1] * b[j * 3 + 1] +
a[i * 3 + 2] * b[j * 3 + 2]
);
}
/**
* Calculates the cross product of two vectors.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {import("./types.js").avec3} b
* @param {number} j
*/
export function cross(a, i, b, j) {
const x = a[i * 3];
const y = a[i * 3 + 1];
const z = a[i * 3 + 2];
const vx = b[j * 3];
const vy = b[j * 3 + 1];
const vz = b[j * 3 + 2];
a[i * 3] = y * vz - vy * z;
a[i * 3 + 1] = z * vx - vz * x;
a[i * 3 + 2] = x * vy - vx * y;
}
/**
* Calculates the length of a vector.
* @param {import("./types.js").avec3} a
* @param {number} i
* @returns {number}
*/
export function length(a, i) {
const x = a[i * 3];
const y = a[i * 3 + 1];
const z = a[i * 3 + 2];
return Math.sqrt(x * x + y * y + z * z);
}
/**
* Calculates the squared length of a vector.
* @param {import("./types.js").avec3} a
* @param {number} i
* @returns {number}
*/
export function lengthSq(a, i) {
const x = a[i * 3];
const y = a[i * 3 + 1];
const z = a[i * 3 + 2];
return x * x + y * y + z * z;
}
/**
* Normalises a vector.
* @param {import("./types.js").avec3} a
* @param {number} i
*/
export function normalize(a, i) {
const lenSq =
a[i * 3] * a[i * 3] +
a[i * 3 + 1] * a[i * 3 + 1] +
a[i * 3 + 2] * a[i * 3 + 2];
if (lenSq > 0) {
const len = Math.sqrt(lenSq);
a[i * 3] /= len;
a[i * 3 + 1] /= len;
a[i * 3 + 2] /= len;
}
}
/**
* Calculates the distance between two vectors.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {import("./types.js").avec3} b
* @param {number} j
* @returns {number}
*/
export function distance(a, i, b, j) {
const dx = b[j * 3] - a[i * 3];
const dy = b[j * 3 + 1] - a[i * 3 + 1];
const dz = b[j * 3 + 2] - a[i * 3 + 2];
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
/**
* Calculates the squared distance between two vectors.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {import("./types.js").avec3} b
* @param {number} j
* @returns {number}
*/
export function distanceSq(a, i, b, j) {
const dx = b[j * 3] - a[i * 3];
const dy = b[j * 3 + 1] - a[i * 3 + 1];
const dz = b[j * 3 + 2] - a[i * 3 + 2];
return dx * dx + dy * dy + dz * dz;
}
/**
* Limits a vector to a length.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {number} len
*/
export function limit(a, i, len) {
const x = a[i * 3];
const y = a[i * 3 + 1];
const z = a[i * 3 + 2];
const dsq = x * x + y * y + z * z;
const lsq = len * len;
if (lsq > 0 && dsq > lsq) {
const nd = len / Math.sqrt(dsq);
a[i * 3] *= nd;
a[i * 3 + 1] *= nd;
a[i * 3 + 2] *= nd;
}
}
/**
* Linearly interpolates between two vectors.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {import("./types.js").avec3} b
* @param {number} j
* @param {number} t
*/
export function lerp(a, i, b, j, t) {
const x = a[i * 3];
const y = a[i * 3 + 1];
const z = a[i * 3 + 2];
a[i * 3] = x + (b[j * 3] - x) * t;
a[i * 3 + 1] = y + (b[j * 3 + 1] - y) * t;
a[i * 3 + 2] = z + (b[j * 3 + 2] - z) * t;
}
/**
* Executes a function once for each array element.
* @param {import("./types.js").avec3} a
* @param {import("./types.js").iterativeCallback} callbackFn
*/
export function forEach(a, callbackFn) {
for (let i = 0; i < a.length / 3; i++) {
TEMP_VEC3[0] = a[i * 3];
TEMP_VEC3[1] = a[i * 3 + 1];
TEMP_VEC3[2] = a[i * 3 + 2];
callbackFn(TEMP_VEC3, i, a);
a[i * 3] = TEMP_VEC3[0];
a[i * 3 + 1] = TEMP_VEC3[1];
a[i * 3 + 2] = TEMP_VEC3[2];
}
}
/**
* Creates a new array populated with the results of calling a provided function on every element in the calling array.
* @param {import("./types.js").avec3} a
* @param {import("./types.js").iterativeCallback} callbackFn
* @returns {import("./types.js").avec3}
*/
export function map(a, callbackFn) {
const b = new a.constructor(a.length);
const element = new a.constructor(3);
for (let i = 0; i < a.length / 3; i++) {
element[0] = a[i * 3];
element[1] = a[i * 3 + 1];
element[2] = a[i * 3 + 2];
const returnValue = callbackFn(element, i, a);
b[i * 3] = returnValue[0];
b[i * 3 + 1] = returnValue[1];
b[i * 3 + 2] = returnValue[2];
}
return b;
}
/**
* Prints a vector to a string.
* @param {import("./types.js").avec3} a
* @param {number} i
* @param {number} [precision=4]
* @returns {string}
*/
export function toString(a, i, precision = 4) {
const scale = 10 ** precision;
// prettier-ignore
return `[${Math.floor(a[i * 3] * scale) / scale}, ${Math.floor(a[i * 3 + 1] * scale) / scale}, ${Math.floor(a[i * 3 + 2] * scale) / scale}]`;
}