UNPKG

p5

Version:

[![npm version](https://badge.fury.io/js/p5.svg)](https://www.npmjs.com/package/p5)

953 lines (889 loc) 28.8 kB
import nj from '@d4c/numjs/build/module/numjs.min.js'; import { Vector } from '../p5.Vector.js'; import { MatrixInterface } from './MatrixInterface.js'; import '../../constants-BRcElHU3.js'; let isMatrixArray = (x) => Array.isArray(x); if (typeof Float32Array !== "undefined") { isMatrixArray = (x) => Array.isArray(x) || x instanceof Float32Array; } /** * A class to describe a matrix, which can be either a 3×3 or 4×4 matrix, * for various matrix manipulations in the p5.js webgl renderer. * This class provides methods for common matrix operations such as * multiplication, inversion, transposition, and transformation. * It supports both 3×3 matrices, typically used for normal transformations, * and 4×4 matrices, commonly used for model, view, and projection transformations. * * @class MatrixNumjs * @private * @param {Array} [mat4] column-major array literal of our 4×4 matrix * @param {Array} [mat3] column-major array literal of our 3×3 matrix * @example * <div> * <code> * function setup() { * createCanvas(100, 100); * * background(200); * * // Create Vector objects. * let p1 = createMatrix(1,1,1,1,1,1,1,1,1); * console.log(p1); * } * </code> * </div> */ // const matrixEngine = "numjs"; class MatrixNumjs extends MatrixInterface{ constructor(...args) { // This is default behavior when object super(...args); if (args[0] === 3) { this._mat3 = Array.isArray(args[1]) ? nj.array(args[1]) : nj.identity(3); } else { this._mat4 = Array.isArray(args[0]) ? nj.array(args[0]) : nj.identity(4); } return this; } get mat3() { return this._mat3?.flatten().tolist(); } get mat4() { return this._mat4?.flatten().tolist(); } /** * Resets the matrix to the identity matrix. * * If the matrix is a 3x3 matrix (`mat3`), it sets the matrix to: * [1, 0, 0, * 0, 1, 0, * 0, 0, 1] * * If the matrix is a 4x4 matrix (`mat4`), it sets the matrix to: * [1, 0, 0, 0, * 0, 1, 0, 0, * 0, 0, 1, 0, * 0, 0, 0, 1] * * @returns {this} The current instance for chaining. */ reset() { if (this._mat3) { this._mat3 = nj.identity(3).flatten(); } else if (this._mat4) { this._mat4 = nj.identity(4).flatten(); } return this; } /** * Replace the entire contents of a 4x4 matrix. * If providing an array or a MatrixNumjs, the values will be copied without * referencing the source object. * Can also provide 16 numbers as individual arguments. * * @param {MatrixNumjs|Float32Array|Number[]} [inMatrix] the input MatrixNumjs or * an Array of length 16 * @chainable */ /** * @param {Number[]} elements 16 numbers passed by value to avoid * array copying. * @chainable */ set(inMatrix) { let refArray = [...arguments]; if (inMatrix instanceof MatrixNumjs) { refArray = inMatrix.mat4; } else if (isMatrixArray(inMatrix)) { refArray = inMatrix; } if (refArray.length !== 16) { // p5._friendlyError( // `Expected 16 values but received ${refArray.length}.`, // "MatrixNumjs.set" // ); return this; } this._mat4 = nj.array(refArray); return this; } setElement(index, value) { if (this._mat3) { this._mat3.set(index, value); } return this; } /** * Gets a copy of the vector, returns a MatrixNumjs object. * * @return {MatrixNumjs} the copy of the MatrixNumjs object */ get() { new MatrixNumjs(this.mat4); return new MatrixNumjs(this.mat4); } /** * return a copy of this matrix. * If this matrix is 4x4, a 4x4 matrix with exactly the same entries will be * generated. The same is true if this matrix is 3x3. * * @return {MatrixNumjs} the result matrix */ copy() { if (this._mat3 !== undefined) { const copied3x3 = new MatrixNumjs(3, this._mat3.tolist()); copied3x3._mat3 = copied3x3._mat3.flatten(); return copied3x3; } const copied = new MatrixNumjs(this._mat4.tolist()); // copied.set(this); copied._mat4 = copied._mat4.flatten(); return copied; } /** * Creates a copy of the current matrix. * * @returns {MatrixNumjs} A new matrix that is a copy of the current matrix. */ clone() { return this.copy(); } /** * return an identity matrix * @return {MatrixNumjs} the result matrix */ static identity(pInst) { return new MatrixNumjs(pInst); } /** * transpose according to a given matrix * @param {MatrixNumjs|Float32Array|Number[]} a the matrix to be * based on to transpose * @chainable */ transpose(a) { if (a instanceof MatrixNumjs) { if (a._mat3) { this._mat3 = nj.array(a.mat3).reshape(3, 3).transpose().flatten(); } else if (a._mat4) { this._mat4 = nj.array(a.mat4).reshape(4, 4).transpose().flatten(); } } else if (isMatrixArray(a)) { if (a.length === 9) { let temp3 = new MatrixNumjs(3, a); this._mat3 = nj.array(temp3.mat3).reshape(3, 3).transpose().flatten(); } else if (a.length === 16) { let temp4 = new MatrixNumjs(a); this._mat4 = nj.array(temp4.mat4).reshape(4, 4).transpose().flatten(); } } return this; } /** * invert matrix according to a give matrix * @param {MatrixNumjs|Float32Array|Number[]} a the matrix to be * based on to invert * @chainable */ invert(a) { let a00, a01, a02, a03, a10, a11, a12, a13; let a20, a21, a22, a23, a30, a31, a32, a33; if (a instanceof MatrixNumjs) { a00 = a._mat4.get(0); a01 = a._mat4.get(1); a02 = a._mat4.get(2); a03 = a._mat4.get(3); a10 = a._mat4.get(4); a11 = a._mat4.get(5); a12 = a._mat4.get(6); a13 = a._mat4.get(7); a20 = a._mat4.get(8); a21 = a._mat4.get(9); a22 = a._mat4.get(10); a23 = a._mat4.get(11); a30 = a._mat4.get(12); a31 = a._mat4.get(13); a32 = a._mat4.get(14); a33 = a._mat4.get(15); } else if (isMatrixArray(a)) { a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; a30 = a[12]; a31 = a[13]; a32 = a[14]; a33 = a[15]; } const b00 = a00 * a11 - a01 * a10; const b01 = a00 * a12 - a02 * a10; const b02 = a00 * a13 - a03 * a10; const b03 = a01 * a12 - a02 * a11; const b04 = a01 * a13 - a03 * a11; const b05 = a02 * a13 - a03 * a12; const b06 = a20 * a31 - a21 * a30; const b07 = a20 * a32 - a22 * a30; const b08 = a20 * a33 - a23 * a30; const b09 = a21 * a32 - a22 * a31; const b10 = a21 * a33 - a23 * a31; const b11 = a22 * a33 - a23 * a32; // Calculate the determinant let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; if (!det) { return null; } det = 1.0 / det; this._mat4.set(0, (a11 * b11 - a12 * b10 + a13 * b09) * det); this._mat4.set(1, (a02 * b10 - a01 * b11 - a03 * b09) * det); this._mat4.set(2, (a31 * b05 - a32 * b04 + a33 * b03) * det); this._mat4.set(3, (a22 * b04 - a21 * b05 - a23 * b03) * det); this._mat4.set(4, (a12 * b08 - a10 * b11 - a13 * b07) * det); this._mat4.set(5, (a00 * b11 - a02 * b08 + a03 * b07) * det); this._mat4.set(6, (a32 * b02 - a30 * b05 - a33 * b01) * det); this._mat4.set(7, (a20 * b05 - a22 * b02 + a23 * b01) * det); this._mat4.set(8, (a10 * b10 - a11 * b08 + a13 * b06) * det); this._mat4.set(9, (a01 * b08 - a00 * b10 - a03 * b06) * det); this._mat4.set(10, (a30 * b04 - a31 * b02 + a33 * b00) * det); this._mat4.set(11, (a21 * b02 - a20 * b04 - a23 * b00) * det); this._mat4.set(12, (a11 * b07 - a10 * b09 - a12 * b06) * det); this._mat4.set(13, (a00 * b09 - a01 * b07 + a02 * b06) * det); this._mat4.set(14, (a31 * b01 - a30 * b03 - a32 * b00) * det); this._mat4.set(15, (a20 * b03 - a21 * b01 + a22 * b00) * det); return this; } /** * Inverts a 3×3 matrix * @chainable */ invert3x3() { const a00 = this._mat3.get(0); const a01 = this._mat3.get(1); const a02 = this._mat3.get(2); const a10 = this._mat3.get(3); const a11 = this._mat3.get(4); const a12 = this._mat3.get(5); const a20 = this._mat3.get(6); const a21 = this._mat3.get(7); const a22 = this._mat3.get(8); const b01 = a22 * a11 - a12 * a21; const b11 = -a22 * a10 + a12 * a20; const b21 = a21 * a10 - a11 * a20; // Calculate the determinant let det = a00 * b01 + a01 * b11 + a02 * b21; if (!det) { return null; } det = 1.0 / det; this._mat3.set(0, b01 * det); this._mat3.set(1, (-a22 * a01 + a02 * a21) * det); this._mat3.set(2, (a12 * a01 - a02 * a11) * det); this._mat3.set(3, b11 * det); this._mat3.set(4, (a22 * a00 - a02 * a20) * det); this._mat3.set(5, (-a12 * a00 + a02 * a10) * det); this._mat3.set(6, b21 * det); this._mat3.set(7, (-a21 * a00 + a01 * a20) * det); this._mat3.set(8, (a11 * a00 - a01 * a10) * det); return this; } /** * This function is only for 3x3 matrices. * transposes a 3×3 MatrixNumjs by a mat3 * If there is an array of arguments, the matrix obtained by transposing * the 3x3 matrix generated based on that array is set. * If no arguments, it transposes itself and returns it. * * @param {Number[]} mat3 1-dimensional array * @chainable */ transpose3x3(mat3) { if (mat3 === undefined) { mat3 = this._mat3; this._mat3 = this._mat3.reshape(3, 3).transpose().flatten(); } else { const temp = new MatrixNumjs(3, mat3); temp._mat3 = temp._mat3.reshape(3, 3).transpose().flatten(); this._mat3 = temp._mat3; } return this; } /** * converts a 4×4 matrix to its 3×3 inverse transform * commonly used in MVMatrix to NMatrix conversions. * @param {MatrixNumjs} mat4 the matrix to be based on to invert * @chainable * @todo finish implementation */ inverseTranspose({ mat4 }) { if (this._mat3 === undefined) ; else { //convert mat4 -> mat3 this._mat3 = this._mat3.flatten(); this._mat3.set(0, mat4[0]); this._mat3.set(1, mat4[1]); this._mat3.set(2, mat4[2]); this._mat3.set(3, mat4[4]); this._mat3.set(4, mat4[5]); this._mat3.set(5, mat4[6]); this._mat3.set(6, mat4[8]); this._mat3.set(7, mat4[9]); this._mat3.set(8, mat4[10]); } const inverse = this.invert3x3(); // check inverse succeeded if (inverse) { inverse.transpose3x3(this._mat3); } else { // in case of singularity, just zero the matrix for (let i = 0; i < 9; i++) { this._mat3.set(i, 0); } } return this; } /** * inspired by Toji's mat4 determinant * @return {Number} Determinant of our 4×4 matrix */ determinant() { const d00 = this._mat4.get(0) * this._mat4.get(5) - this._mat4.get(1) * this._mat4.get(4), d01 = this._mat4.get(0) * this._mat4.get(6) - this._mat4.get(2) * this._mat4.get(4), d02 = this._mat4.get(0) * this._mat4.get(7) - this._mat4.get(3) * this._mat4.get(4), d03 = this._mat4.get(1) * this._mat4.get(6) - this._mat4.get(2) * this._mat4.get(5), d04 = this._mat4.get(1) * this._mat4.get(7) - this._mat4.get(3) * this._mat4.get(5), d05 = this._mat4.get(2) * this._mat4.get(7) - this._mat4.get(3) * this._mat4.get(6), d06 = this._mat4.get(8) * this._mat4.get(13) - this._mat4.get(9) * this._mat4.get(12), d07 = this._mat4.get(8) * this._mat4.get(14) - this._mat4.get(10) * this._mat4.get(12), d08 = this._mat4.get(8) * this._mat4.get(15) - this._mat4.get(11) * this._mat4.get(12), d09 = this._mat4.get(9) * this._mat4.get(14) - this._mat4.get(10) * this._mat4.get(13), d10 = this._mat4.get(9) * this._mat4.get(15) - this._mat4.get(11) * this._mat4.get(13), d11 = this._mat4.get(10) * this._mat4.get(15) - this._mat4.get(11) * this._mat4.get(14); // Calculate the determinant return ( d00 * d11 - d01 * d10 + d02 * d09 + d03 * d08 - d04 * d07 + d05 * d06 ); } /** * multiply two mat4s * @param {MatrixNumjs|Float32Array|Number[]} multMatrix The matrix * we want to multiply by * @chainable */ mult(multMatrix) { if (isMatrixArray(multMatrix)) { multMatrix = new MatrixNumjs(multMatrix); } if (this._mat3 !== undefined) { let a = this._mat3.reshape(3, 3); a = a.dot(multMatrix._mat3?.reshape(3, 3)).flatten(); this._mat3 = a; } else if (this._mat4 !== undefined) { let a = this._mat4.reshape(4, 4); a = a.dot(multMatrix._mat4?.reshape(4, 4)).flatten(); this._mat4 = a; } return this; } apply(multMatrix) { let _src; if (multMatrix === this || multMatrix === this._mat4) { _src = this.copy().mat4; // only need to allocate in this rare case } else if (multMatrix instanceof MatrixNumjs) { _src = multMatrix.mat4; } else if (isMatrixArray(multMatrix)) { _src = multMatrix; } else if (arguments.length === 16) { _src = arguments; } else { return; // nothing to do. } const mat4 = this._mat4.tolist(); // each row is used for the multiplier const m0 = mat4[0]; const m4 = mat4[4]; const m8 = mat4[8]; const m12 = mat4[12]; mat4[0] = _src[0] * m0 + _src[1] * m4 + _src[2] * m8 + _src[3] * m12; mat4[4] = _src[4] * m0 + _src[5] * m4 + _src[6] * m8 + _src[7] * m12; mat4[8] = _src[8] * m0 + _src[9] * m4 + _src[10] * m8 + _src[11] * m12; mat4[12] = _src[12] * m0 + _src[13] * m4 + _src[14] * m8 + _src[15] * m12; const m1 = mat4[1]; const m5 = mat4[5]; const m9 = mat4[9]; const m13 = mat4[13]; mat4[1] = _src[0] * m1 + _src[1] * m5 + _src[2] * m9 + _src[3] * m13; mat4[5] = _src[4] * m1 + _src[5] * m5 + _src[6] * m9 + _src[7] * m13; mat4[9] = _src[8] * m1 + _src[9] * m5 + _src[10] * m9 + _src[11] * m13; mat4[13] = _src[12] * m1 + _src[13] * m5 + _src[14] * m9 + _src[15] * m13; const m2 = mat4[2]; const m6 = mat4[6]; const m10 = mat4[10]; const m14 = mat4[14]; mat4[2] = _src[0] * m2 + _src[1] * m6 + _src[2] * m10 + _src[3] * m14; mat4[6] = _src[4] * m2 + _src[5] * m6 + _src[6] * m10 + _src[7] * m14; mat4[10] = _src[8] * m2 + _src[9] * m6 + _src[10] * m10 + _src[11] * m14; mat4[14] = _src[12] * m2 + _src[13] * m6 + _src[14] * m10 + _src[15] * m14; const m3 = mat4[3]; const m7 = mat4[7]; const m11 = mat4[11]; const m15 = mat4[15]; mat4[3] = _src[0] * m3 + _src[1] * m7 + _src[2] * m11 + _src[3] * m15; mat4[7] = _src[4] * m3 + _src[5] * m7 + _src[6] * m11 + _src[7] * m15; mat4[11] = _src[8] * m3 + _src[9] * m7 + _src[10] * m11 + _src[11] * m15; mat4[15] = _src[12] * m3 + _src[13] * m7 + _src[14] * m11 + _src[15] * m15; this._mat4 = nj.array(mat4); return this; } /** * scales a MatrixNumjs by scalars or a vector * @param {Vector|Float32Array|Number[]} s vector to scale by * @chainable */ scale(x, y, z) { if (x instanceof Vector) { // x is a vector, extract the components from it. y = x.y; z = x.z; x = x.x; // must be last } else if (x instanceof Array) { // x is an array, extract the components from it. y = x[1]; z = x[2]; x = x[0]; // must be last } this._mat4 = this._mat4.flatten(); nj.array([x, y, z, 1]); this._mat4.set(0, x * this._mat4.get(0)); this._mat4.set(1, x * this._mat4.get(1)); this._mat4.set(2, x * this._mat4.get(2)); this._mat4.set(3, x * this._mat4.get(3)); this._mat4.set(4, y * this._mat4.get(4)); this._mat4.set(5, y * this._mat4.get(5)); this._mat4.set(6, y * this._mat4.get(6)); this._mat4.set(7, y * this._mat4.get(7)); this._mat4.set(8, z * this._mat4.get(8)); this._mat4.set(9, z * this._mat4.get(9)); this._mat4.set(10, z * this._mat4.get(10)); this._mat4.set(11, z * this._mat4.get(11)); return this; } /** * rotate our Matrix around an axis by the given angle. * @param {Number} a The angle of rotation in radians * @param {Vector|Number[]} axis the axis(es) to rotate around * @chainable * inspired by Toji's gl-matrix lib, mat4 rotation */ rotate(a, x, y, z) { if (x instanceof Vector) { // x is a vector, extract the components from it. y = x.y; z = x.z; x = x.x; //must be last } else if (x instanceof Array) { // x is an array, extract the components from it. y = x[1]; z = x[2]; x = x[0]; //must be last } const len = Math.sqrt(x * x + y * y + z * z); x *= 1 / len; y *= 1 / len; z *= 1 / len; // const aMat = this._mat4.reshape(4,4) this._mat4 = this._mat4.flatten(); const a00 = this._mat4.get(0); const a01 = this._mat4.get(1); const a02 = this._mat4.get(2); const a03 = this._mat4.get(3); const a10 = this._mat4.get(4); const a11 = this._mat4.get(5); const a12 = this._mat4.get(6); const a13 = this._mat4.get(7); const a20 = this._mat4.get(8); const a21 = this._mat4.get(9); const a22 = this._mat4.get(10); const a23 = this._mat4.get(11); //sin,cos, and tan of respective angle const sA = Math.sin(a); const cA = Math.cos(a); const tA = 1 - cA; // Construct the elements of the rotation matrix const b00 = x * x * tA + cA; const b01 = y * x * tA + z * sA; const b02 = z * x * tA - y * sA; const b10 = x * y * tA - z * sA; const b11 = y * y * tA + cA; const b12 = z * y * tA + x * sA; const b20 = x * z * tA + y * sA; const b21 = y * z * tA - x * sA; const b22 = z * z * tA + cA; // rotation-specific matrix multiplication this._mat4.set(0, a00 * b00 + a10 * b01 + a20 * b02); this._mat4.set(1, a01 * b00 + a11 * b01 + a21 * b02); this._mat4.set(2, a02 * b00 + a12 * b01 + a22 * b02); this._mat4.set(3, a03 * b00 + a13 * b01 + a23 * b02); this._mat4.set(4, a00 * b10 + a10 * b11 + a20 * b12); this._mat4.set(5, a01 * b10 + a11 * b11 + a21 * b12); this._mat4.set(6, a02 * b10 + a12 * b11 + a22 * b12); this._mat4.set(7, a03 * b10 + a13 * b11 + a23 * b12); this._mat4.set(8, a00 * b20 + a10 * b21 + a20 * b22); this._mat4.set(9, a01 * b20 + a11 * b21 + a21 * b22); this._mat4.set(10, a02 * b20 + a12 * b21 + a22 * b22); this._mat4.set(11, a03 * b20 + a13 * b21 + a23 * b22); return this; } /** * @todo finish implementing this method! * translates * @param {Number[]} v vector to translate by * @chainable */ translate(v) { const x = v[0], y = v[1], z = v[2] || 0; this._mat4 = this._mat4.flatten(); this._mat4.set( 12, this._mat4.get(0) * x + this._mat4.get(4) * y + this._mat4.get(8) * z + this._mat4.get(12) ); this._mat4.set( 13, this._mat4.get(1) * x + this._mat4.get(5) * y + this._mat4.get(9) * z + this._mat4.get(13) ); this._mat4.set( 14, this._mat4.get(2) * x + this._mat4.get(6) * y + this._mat4.get(10) * z + this._mat4.get(14) ); this._mat4.set( 15, this._mat4.get(3) * x + this._mat4.get(7) * y + this._mat4.get(11) * z + this._mat4.get(15) ); } rotateX(a) { this.rotate(a, 1, 0, 0); } rotateY(a) { this.rotate(a, 0, 1, 0); } rotateZ(a) { this.rotate(a, 0, 0, 1); } /** * sets the perspective matrix * @param {Number} fovy [description] * @param {Number} aspect [description] * @param {Number} near near clipping plane * @param {Number} far far clipping plane * @chainable */ perspective(fovy, aspect, near, far) { const f = 1.0 / Math.tan(fovy / 2), nf = 1 / (near - far); this._mat4 = this._mat4.flatten(); this._mat4.set(0, f / aspect); this._mat4.set(1, 0); this._mat4.set(2, 0); this._mat4.set(3, 0); this._mat4.set(4, 0); this._mat4.set(5, f); this._mat4.set(6, 0); this._mat4.set(7, 0); this._mat4.set(8, 0); this._mat4.set(9, 0); this._mat4.set(10, (far + near) * nf); this._mat4.set(11, -1); this._mat4.set(12, 0); this._mat4.set(13, 0); this._mat4.set(14, 2 * far * near * nf); this._mat4.set(15, 0); return this; } /** * sets the ortho matrix * @param {Number} left [description] * @param {Number} right [description] * @param {Number} bottom [description] * @param {Number} top [description] * @param {Number} near near clipping plane * @param {Number} far far clipping plane * @chainable */ ortho(left, right, bottom, top, near, far) { const lr = 1 / (left - right), bt = 1 / (bottom - top), nf = 1 / (near - far); this._mat4 = this._mat4.flatten(); this._mat4.set(0, -2 * lr); this._mat4.set(1, 0); this._mat4.set(2, 0); this._mat4.set(3, 0); this._mat4.set(4, 0); this._mat4.set(5, -2 * bt); this._mat4.set(6, 0); this._mat4.set(7, 0); this._mat4.set(8, 0); this._mat4.set(9, 0); this._mat4.set(10, 2 * nf); this._mat4.set(11, 0); this._mat4.set(12, (left + right) * lr); this._mat4.set(13, (top + bottom) * bt); this._mat4.set(14, (far + near) * nf); this._mat4.set(15, 1); return this; } /** * apply a matrix to a vector with x,y,z,w components * get the results in the form of an array * @param {Number} * @return {Number[]} */ multiplyVec4(x, y, z, w) { const result = new Array(4); const m = this._mat4; result[0] = m.get(0) * x + m.get(4) * y + m.get(8) * z + m.get(12) * w; result[1] = m.get(1) * x + m.get(5) * y + m.get(9) * z + m.get(13) * w; result[2] = m.get(2) * x + m.get(6) * y + m.get(10) * z + m.get(14) * w; result[3] = m.get(3) * x + m.get(7) * y + m.get(11) * z + m.get(15) * w; return result; } /** * Applies a matrix to a vector. * The fourth component is set to 1. * Returns a vector consisting of the first * through third components of the result. * * @param {Vector} * @return {Vector} */ multiplyPoint({ x, y, z }) { const array = this.multiplyVec4(x, y, z, 1); return new Vector(array[0], array[1], array[2]); } /** * Applies a matrix to a vector. * The fourth component is set to 1. * Returns the result of dividing the 1st to 3rd components * of the result by the 4th component as a vector. * * @param {Vector} * @return {Vector} */ multiplyAndNormalizePoint({ x, y, z }) { const array = this.multiplyVec4(x, y, z, 1); array[0] /= array[3]; array[1] /= array[3]; array[2] /= array[3]; return new Vector(array[0], array[1], array[2]); } /** * Applies a matrix to a vector. * The fourth component is set to 0. * Returns a vector consisting of the first * through third components of the result. * * @param {Vector} * @return {Vector} */ multiplyDirection({ x, y, z }) { const array = this.multiplyVec4(x, y, z, 0); return new Vector(array[0], array[1], array[2]); } /** * This function is only for 3x3 matrices. * multiply two mat3s. It is an operation to multiply the 3x3 matrix of * the argument from the right. Arguments can be a 3x3 MatrixNumjs, * a Float32Array of length 9, or a javascript array of length 9. * In addition, it can also be done by enumerating 9 numbers. * * @param {MatrixNumjs|Float32Array|Number[]} multMatrix The matrix * we want to multiply by * @chainable */ mult3x3(multMatrix) { let tempMatrix = multMatrix; if (multMatrix === this || multMatrix === this._mat3) ; else if (multMatrix instanceof MatrixNumjs) { multMatrix.mat3; } else if (isMatrixArray(multMatrix)) { multMatrix._mat3 = nj.array(arguments); } else if (arguments.length === 9) { tempMatrix = new MatrixNumjs(3, Array.from(arguments)); } else { return; // nothing to do. } let a = this._mat3.reshape(3, 3); a = a.dot(tempMatrix._mat3.reshape(3, 3)).flatten(); this._mat3 = a; return this; } /** * This function is only for 3x3 matrices. * A function that returns a column vector of a 3x3 matrix. * * @param {Number} columnIndex matrix column number * @return {Vector} */ column(columnIndex) { // let temp = this._mat3.reshape(3,3) let vect = new Vector( this._mat3.tolist()[3 * columnIndex], this._mat3.tolist()[3 * columnIndex + 1], this._mat3.tolist()[3 * columnIndex + 2] ); return vect; } /** * This function is only for 3x3 matrices. * A function that returns a row vector of a 3x3 matrix. * * @param {Number} rowIndex matrix row number * @return {Vector} */ row(rowIndex) { return new Vector( this._mat3.tolist()[rowIndex], this._mat3.tolist()[rowIndex + 3], this._mat3.tolist()[rowIndex + 6] ); } /** * Returns the diagonal elements of the matrix in the form of an array. * A 3x3 matrix will return an array of length 3. * A 4x4 matrix will return an array of length 4. * * @return {Number[]} An array obtained by arranging the diagonal elements * of the matrix in ascending order of index */ diagonal() { if (this._mat3 !== undefined) { return this._mat3.reshape(3, 3).diag().tolist(); } return this._mat4.reshape(4, 4).diag().tolist(); } /** * This function is only for 3x3 matrices. * Takes a vector and returns the vector resulting from multiplying to * that vector by this matrix from left. * * @param {Vector} multVector the vector to which this matrix applies * @param {Vector} [target] The vector to receive the result * @return {Vector} */ multiplyVec3(multVector, target) { if (target === undefined) { target = multVector.copy(); } target.x = this.row(0).dot(multVector); target.y = this.row(1).dot(multVector); target.z = this.row(2).dot(multVector); return target; } /** * This function is only for 4x4 matrices. * Creates a 3x3 matrix whose entries are the top left 3x3 part and returns it. * * @return {MatrixNumjs} */ createSubMatrix3x3() { const result = new MatrixNumjs(3); result._mat3 = result._mat3.flatten(); result._mat3.set(0, this._mat4.get(0)); result._mat3.set(1, this._mat4.get(1)); result._mat3.set(2, this._mat4.get(2)); result._mat3.set(3, this._mat4.get(4)); result._mat3.set(4, this._mat4.get(5)); result._mat3.set(5, this._mat4.get(6)); result._mat3.set(6, this._mat4.get(8)); result._mat3.set(7, this._mat4.get(9)); result._mat3.set(8, this._mat4.get(10)); return result; } /** * PRIVATE */ // matrix methods adapted from: // https://developer.mozilla.org/en-US/docs/Web/WebGL/ // gluPerspective // // function _makePerspective(fovy, aspect, znear, zfar){ // const ymax = znear * Math.tan(fovy * Math.PI / 360.0); // const ymin = -ymax; // const xmin = ymin * aspect; // const xmax = ymax * aspect; // return _makeFrustum(xmin, xmax, ymin, ymax, znear, zfar); // } //// //// glFrustum //// //function _makeFrustum(left, right, bottom, top, znear, zfar){ // const X = 2*znear/(right-left); // const Y = 2*znear/(top-bottom); // const A = (right+left)/(right-left); // const B = (top+bottom)/(top-bottom); // const C = -(zfar+znear)/(zfar-znear); // const D = -2*zfar*znear/(zfar-znear); // const frustrumMatrix =[ // X, 0, A, 0, // 0, Y, B, 0, // 0, 0, C, D, // 0, 0, -1, 0 //]; //return frustrumMatrix; // } // function _setMVPMatrices(){ ////an identity matrix ////@TODO use the MatrixNumjs class to abstract away our MV matrices and ///other math //const _mvMatrix = //[ // 1.0,0.0,0.0,0.0, // 0.0,1.0,0.0,0.0, // 0.0,0.0,1.0,0.0, // 0.0,0.0,0.0,1.0 //]; } export { MatrixNumjs };