UNPKG

@lucania/vectorics

Version:
344 lines (339 loc) 13.1 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Vectorics = {})); })(this, (function (exports) { 'use strict'; class Matrix { _data; size; length; constructor(...data) { this._data = data; this.size = Math.sqrt(data.length); this.length = data.length; } get(row, column) { return this._data[row * this.size + column]; } add(value) { const data = this._tuple(value); this._data.map((_, index) => this._data[index] += data[index]); return this; } subtract(value) { const data = this._tuple(value); this._data.forEach((_, index) => this._data[index] -= data[index]); return this; } multiply(value) { const data = this._tuple(value); const result = new Array(data.length).fill(0); for (let resultRow = 0; resultRow < this.size; resultRow++) { for (let resultColumn = 0; resultColumn < this.size; resultColumn++) { let sum = 0; for (let i = 0; i < this.size; i++) { sum += this._data[resultRow * this.size + i] * data[i * this.size + resultColumn]; } result[resultRow * this.size + resultColumn] = sum; } } this._data = result; return this; } multiplyVector(vector) { if (vector.size !== this.size) { throw new Error(`Cannot multiply ${vector.size} component vector by ${this.size}x${this.size} matrix.`); } const result = vector.clone().set(0); for (let row = 0; row < this.size; row++) { for (let column = 0; column < this.size; column++) { result.components[row] += this.get(row, column) * vector.components[column]; } } return result; } divide(value) { const data = this._tuple(value); this._data.forEach((_, index) => this._data[index] /= data[index]); return this; } inverse() { this._data.forEach((value, index) => this._data[index] = 1 / value); return this; } transpose() { const matrix = this.clone(); for (let i = 0; i < this.size; i++) { for (let j = 0; j < this.size; j++) { matrix._data[i * this.size + j] = this._data[j * this.size + i]; } } this._data = matrix._data; return this; } clone() { return new Matrix(...this._data); } _tuple(value) { if (typeof value === "number") { return new Array(this.length).fill(value); } else if (value instanceof Matrix) { return value._data; } else { return value; } } get data() { return this._data; } toString(fractionDigits = 2) { const maximumLength = this._data.reduce((length, value) => { const newLength = value.toFixed(fractionDigits).length; return newLength > length ? newLength : length; }, 0); const lines = []; let line; for (let i = 0; i < this.size; i++) { line = []; for (let j = 0; j < this.size; j++) { line.push(this._data[i * this.size + j].toFixed(fractionDigits).padStart(maximumLength, " ")); } lines.push(`[ ${line.join(", ")} ]`); } return lines.join("\n"); } static tuple(length, source) { if (typeof source === "number") { return new Array(length).fill(source); } else if (source instanceof Matrix) { return source.data; } else { return source; } } static fromSource(size, source) { const tuple = Matrix.tuple(size, source); switch (size) { case 2: return new Matrix2(...tuple); case 3: return new Matrix3(...tuple); case 4: return new Matrix4(...tuple); default: return new Matrix(...tuple); } } } class Matrix2 extends Matrix { static SIZE = 2 * 2; static identity() { return new Matrix2(1, 0, 0, 1); } multiplyVector(vector) { return super.multiplyVector(vector); } clone() { return new Matrix2(...this.data); } } class Matrix3 extends Matrix { static SIZE = 3 * 3; static identity() { return new Matrix3(1, 0, 0, 0, 1, 0, 0, 0, 1); } multiplyVector(vector) { return super.multiplyVector(vector); } clone() { return new Matrix3(...this.data); } } class Matrix4 extends Matrix { static SIZE = 4 * 4; static identity() { return new Matrix4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } multiplyVector(vector) { return super.multiplyVector(vector); } static orthographic(left, right, bottom, top, near, far) { const width = right - left; const height = top - bottom; const depth = far - near; const translationX = -(right + left) / width; const translationY = -(top + bottom) / height; const translationZ = -(far + near) / depth; return new Matrix4(2 / width, 0, 0, translationX, 0, 2 / height, 0, translationY, 0, 0, -2 / depth, translationZ, 0, 0, 0, 1); } static translate(translationX, translationY, translationZ) { return new Matrix4(1, 0, 0, translationX, 0, 1, 0, translationY, 0, 0, 1, translationZ, 0, 0, 0, 1); } static rotate(angleInDegrees, axisX, axisY, axisZ) { const angleInRadians = angleInDegrees * globalThis.Math.PI / 180; const cosAngle = globalThis.Math.cos(angleInRadians); const sinAngle = globalThis.Math.sin(angleInRadians); const oneMinusCos = 1 - cosAngle; const tx = oneMinusCos * axisX; const ty = oneMinusCos * axisY; const tz = oneMinusCos * axisZ; const txy = tx * axisY; const txz = tx * axisZ; const tyz = ty * axisZ; const sinX = sinAngle * axisX; const sinY = sinAngle * axisY; const sinZ = sinAngle * axisZ; return new Matrix4(tx * axisX + cosAngle, txy - sinZ, txz + sinY, 0, txy + sinZ, ty * axisY + cosAngle, tyz - sinX, 0, txz - sinY, tyz + sinX, tz * axisZ + cosAngle, 0, 0, 0, 0, 1); } static scale(scaleX, scaleY, scaleZ) { return new Matrix4(scaleX, 0, 0, 0, 0, scaleY, 0, 0, 0, 0, scaleZ, 0, 0, 0, 0, 1); } clone() { return new Matrix4(...this.data); } } class Vector { _components; constructor(...components) { this._components = components; } add(value) { return this.operation(value, (a, b) => a + b); } subtract(value) { return this.operation(value, (a, b) => a - b); } multiply(value) { return this.operation(value, (a, b) => a * b); } divide(value) { return this.operation(value, (a, b) => a / b); } set(value) { return this.operation(value, (_, b) => b); } normalize() { return this.operation(this.getMagnitude(), (a, b) => a / b); } dot(value) { return this.clone().multiply(value).getSum(); } distance(vector) { const difference = this.clone().subtract(vector); return Math.sqrt(difference.multiply(difference).getSum()); } clone() { return new Vector(...this._components); } getSum() { return this._components.reduce((sum, value) => sum + value); } getMagnitude() { return this.distance(this.clone().set(0)); } isZero() { return this._components.every((component) => component === 0); } operation(value, operation) { if (typeof value === "number") { for (let i = 0; i < this._components.length; i++) { this._components[i] = operation(this._components[i], value); } } else if (value instanceof Vector) { for (let i = 0; i < this._components.length; i++) { this._components[i] = operation(this._components[i], value._components[i]); } } else { for (let i = 0; i < this._components.length; i++) { this._components[i] = operation(this._components[i], value[i]); } } return this; } toString() { return `[ ${this._components.join(", ")} ]`; } get size() { return this._components.length; } get components() { return this._components; } static tuple(size, source) { if (typeof source === "number") { return new Array(size).fill(source); } else if ("components" in source) { return source.components; } else { return source; } } static fromSource(size, source) { const tuple = Vector.tuple(size, source); switch (size) { case 2: return new Vector2(...tuple); case 3: return new Vector3(...tuple); case 4: return new Vector4(...tuple); default: return new Vector(...tuple); } } } class Vector2 extends Vector { static SIZE = 2; get x() { return this.components[0]; } set x(value) { this.components[0] = value; } get y() { return this.components[1]; } set y(value) { this.components[1] = value; } get width() { return this.x; } set width(value) { this.x = value; } get height() { return this.y; } set height(value) { this.y = value; } clone() { return new Vector2(this.x, this.y); } static from(source) { return Vector.fromSource(Vector2.SIZE, source); } } class Vector3 extends Vector { static SIZE = 3; get x() { return this.components[0]; } set x(value) { this.components[0] = value; } get y() { return this.components[1]; } set y(value) { this.components[1] = value; } get z() { return this.components[2]; } set z(value) { this.components[2] = value; } get width() { return this.x; } set width(value) { this.x = value; } get height() { return this.y; } set height(value) { this.y = value; } get depth() { return this.z; } set depth(value) { this.z = value; } cross(vector) { return this.set([ this.y * vector.z - this.z * vector.y, this.z * vector.x - this.x * vector.z, this.x * vector.y - this.y * vector.x ]); } clone() { return new Vector3(this.x, this.y, this.z); } static from(source) { return Vector.fromSource(Vector3.SIZE, source); } } class Vector4 extends Vector { static SIZE = 4; get x() { return this.components[0]; } set x(value) { this.components[0] = value; } get y() { return this.components[1]; } set y(value) { this.components[1] = value; } get z() { return this.components[2]; } set z(value) { this.components[2] = value; } get w() { return this.components[3]; } set w(value) { this.components[3] = value; } get width() { return this.z; } set width(value) { this.z = value; } get height() { return this.w; } set height(value) { this.w = value; } clone() { return new Vector4(this.x, this.y, this.z, this.w); } static from(source) { return Vector.fromSource(Vector4.SIZE, source); } } exports.Matrix = Matrix; exports.Matrix2 = Matrix2; exports.Matrix3 = Matrix3; exports.Matrix4 = Matrix4; exports.Vector = Vector; exports.Vector2 = Vector2; exports.Vector3 = Vector3; exports.Vector4 = Vector4; }));