diff --git a/src/math/Matrices/Matrix.js b/src/math/Matrices/Matrix.js
new file mode 100644
index 0000000000..58ee51fd26
--- /dev/null
+++ b/src/math/Matrices/Matrix.js
@@ -0,0 +1,1322 @@
+import { Vector } from "../p5.Vector";
+import { MatrixInterface } from "./MatrixInterface";
+
+const isPerfectSquare = (arr) => {
+ const sqDimention = Math.sqrt(Array.from(arr).length);
+ if (sqDimention % 1 !== 0) {
+ throw new Error("Array length must be a perfect square.");
+ }
+ return true;
+};
+
+export let GLMAT_ARRAY_TYPE = Array;
+export let isMatrixArray = (x) => Array.isArray(x);
+if (typeof Float32Array !== "undefined") {
+ GLMAT_ARRAY_TYPE = Float32Array;
+ isMatrixArray = (x) => Array.isArray(x) || x instanceof Float32Array;
+}
+/**
+ * The `Matrix` class represents a mathematical matrix and provides various methods for matrix operations.
+ *
+ * This class extends the `MatrixInterface` and includes methods for creating, manipulating, and performing
+ * operations on matrices. It supports both 3x3 and 4x4 matrices, as well as general NxN matrices.
+ *
+ * @class
+ * @extends MatrixInterface
+ *
+ * @example
+ * // Creating a 3x3 matrix from an array
+ * const matrix = new Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ *
+ * // Creating a 4x4 identity matrix
+ * const identityMatrix = new Matrix(4);
+ *
+ * // Adding two matrices
+ * const matrix1 = new Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ * const matrix2 = new Matrix([9, 8, 7, 6, 5, 4, 3, 2, 1]);
+ * matrix1.add(matrix2); // matrix1 is now [10, 10, 10, 10, 10, 10, 10, 10, 10]
+ *
+ * // Setting an element in the matrix
+ * matrix.setElement(0, 10); // matrix is now [10, 2, 3, 4, 5, 6, 7, 8, 9]
+ *
+ * // Resetting the matrix to an identity matrix
+ * matrix.reset();
+ *
+ * // Getting the diagonal elements of the matrix
+ * const diagonal = matrix.diagonal(); // [1, 1, 1]
+ *
+ * // Transposing the matrix
+ * matrix.transpose();
+ *
+ * // Multiplying two matrices
+ * matrix1.mult(matrix2);
+ *
+ * // Inverting the matrix
+ * matrix.invert();
+ *
+ * // Scaling the matrix
+ * matrix.scale(2, 2, 2);
+ *
+ * // Rotating the matrix around an axis
+ * matrix.rotate4x4(Math.PI / 4, 1, 0, 0);
+ *
+ * // Applying a perspective transformation
+ * matrix.perspective(Math.PI / 4, 1, 0.1, 100);
+ *
+ * // Applying an orthographic transformation
+ * matrix.ortho(-1, 1, -1, 1, 0.1, 100);
+ *
+ * // Multiplying a vector by the matrix
+ * const vector = new Vector(1, 2, 3);
+ * const result = matrix.multiplyPoint(vector);
+ */
+export class Matrix extends MatrixInterface {
+ matrix;
+ #sqDimention;
+
+ constructor(...args) {
+ super(...args);
+ // This is default behavior when object
+ // instantiated using createMatrix()
+ if (isMatrixArray(args[0]) && isPerfectSquare(args[0])) {
+ const sqDimention = Math.sqrt(Array.from(args[0]).length);
+ this.#sqDimention = sqDimention;
+ this.matrix = Array.from(args[0]);
+ } else if (typeof args[0] === "number") {
+ this.#sqDimention = Number(args[0]);
+ this.matrix = this.#createIdentityMatrix(args[0]);
+ }
+ return this;
+ }
+
+ /**
+ * Getter for a 3x3 matrix.
+ *
+ * This method returns the matrix if its dimensions are 3x3.
+ * If the matrix is not 3x3, it returns `undefined`.
+ *
+ * @returns {Array|undefined} The 3x3 matrix or `undefined` if the matrix is not 3x3.
+ */
+ get mat3() {
+ if (this.#sqDimention === 3) {
+ return this.matrix;
+ } else {
+ return undefined;
+ }
+ }
+
+ /**
+ * Getter for a 4x4 matrix.
+ *
+ * This method returns the matrix if its dimensions are 4x4.
+ * If the matrix is not 4x4, it returns `undefined`.
+ *
+ * @returns {Array|undefined} The 4x4 matrix or `undefined` if the matrix is not 4x4.
+ */
+ get mat4() {
+ if (this.#sqDimention === 4) {
+ return this.matrix;
+ } else {
+ return undefined;
+ }
+ }
+
+ /**
+ * Adds the corresponding elements of the given matrix to this matrix.
+ *
+ * @param {Matrix} matrix - The matrix to add to this matrix. It must have the same dimensions as this matrix.
+ * @returns {Matrix} The resulting matrix after addition.
+ * @throws {Error} If the matrices do not have the same dimensions.
+ *
+ * @example
+ * const matrix1 = new Matrix([1, 2, 3]);
+ * const matrix2 = new Matrix([4, 5, 6]);
+ * matrix1.add(matrix2); // matrix1 is now [5, 7, 9]
+ */
+ add(matrix) {
+ if (this.matrix.length !== matrix.matrix.length) {
+ throw new Error("Matrices must be of the same dimension to add.");
+ }
+ for (let i = 0; i < this.matrix.length; i++) {
+ this.matrix[i] += matrix.matrix[i];
+ }
+ return this;
+ }
+
+ /**
+ * Sets the value of a specific element in the matrix.
+ *
+ * @param {number} index - The position in the matrix where the value should be set.
+ * Must be a non-negative integer less than the length of the matrix.
+ * @param {*} value - The new value to be assigned to the specified position in the matrix.
+ * @returns {Matrix} The current instance of the Matrix, allowing for method chaining.
+ *
+ * @example
+ * // Assuming matrix is an instance of Matrix with initial values [1, 2, 3]
+ * matrix.setElement(1, 10);
+ * // Now the matrix values are [1, 10, 3]
+ */
+ setElement(index, value) {
+ if (index >= 0 && index < this.matrix.length) {
+ this.matrix[index] = value;
+ }
+ return this;
+ }
+
+ /**
+ * Resets the current matrix to an identity matrix.
+ *
+ * This method replaces the current matrix with an identity matrix of the same dimensions.
+ * An identity matrix is a square matrix with ones on the main diagonal and zeros elsewhere.
+ *
+ * @returns {Matrix} The current instance of the Matrix class, allowing for method chaining.
+ */
+ reset() {
+ this.matrix = this.#createIdentityMatrix(this.#sqDimention);
+ return this;
+ }
+
+ /**
+ * Replace the entire contents of a NxN matrix.
+ * If providing an array or a p5.Matrix, the values will be copied without
+ * referencing the source object.
+ * Can also provide NxN numbers as individual arguments.
+ *
+ * @param {p5.Matrix|Float32Array|Number[]} [inMatrix] the input p5.Matrix or
+ * an Array of length 16
+ * @chainable
+ */
+ /**
+ * @param {Number[]} elements 16 numbers passed by value to avoid
+ * array copying.
+ * @chainable
+ */
+ set(inMatrix) {
+ let refArray = Array.from([...arguments]);
+ if (inMatrix instanceof Matrix) {
+ refArray = inMatrix.matrix;
+ } else if (isMatrixArray(inMatrix)) {
+ refArray = inMatrix;
+ }
+ if (refArray.length !== this.matrix.length) {
+ p5._friendlyError(
+ `Expected same dimentions values but received different ${refArray.length}.`,
+ "p5.Matrix.set"
+ );
+ return this;
+ }
+ this.matrix = [...refArray];
+ return this;
+ }
+
+ /**
+ * Gets a copy of the vector, returns a p5.Matrix object.
+ *
+ * @return {p5.Matrix} the copy of the p5.Matrix object
+ */
+ get() {
+ return new Matrix(this.matrix); // TODO: Pass p5
+ }
+
+ /**
+ * 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 {p5.Matrix} the result matrix
+ */
+ copy() {
+ return new Matrix(this.matrix);
+ }
+
+ /**
+ * Creates a copy of the current matrix instance.
+ * This method is useful when you need a duplicate of the matrix
+ * without modifying the original one.
+ *
+ * @returns {Matrix} A new matrix instance that is a copy of the current matrix.
+ */
+ clone() {
+ return this.copy();
+ }
+ /**
+ * Returns the diagonal elements of the matrix in the form of an array.
+ * A NxN matrix will return an array of length N.
+ *
+ * @return {Number[]} An array obtained by arranging the diagonal elements
+ * of the matrix in ascending order of index
+ */
+ diagonal() {
+ const diagonal = [];
+ for (let i = 0; i < this.#sqDimention; i++) {
+ diagonal.push(this.matrix[i * (this.#sqDimention + 1)]);
+ }
+ return diagonal;
+ }
+
+ /**
+ * This function is only for 3x3 matrices.
+ * A function that returns a row vector of a NxN matrix.
+ *
+ * @param {Number} columnIndex matrix column number
+ * @return {p5.Vector}
+ */
+ row(columnIndex) {
+ const columnVector = [];
+ for (let i = 0; i < this.#sqDimention; i++) {
+ columnVector.push(this.matrix[i * this.#sqDimention + columnIndex]);
+ }
+ return new Vector(...columnVector);
+ }
+
+ /**
+ * A function that returns a column vector of a NxN matrix.
+ *
+ * @param {Number} rowIndex matrix row number
+ * @return {p5.Vector}
+ */
+ column(rowIndex) {
+ const rowVector = [];
+ for (let i = 0; i < this.#sqDimention; i++) {
+ rowVector.push(this.matrix[rowIndex * this.#sqDimention + i]);
+ }
+ return new Vector(...rowVector);
+ }
+
+
+
+
+ /**
+ * Transposes the given matrix `a` based on the square dimension of the matrix.
+ *
+ * This method rearranges the elements of the matrix such that the rows become columns
+ * and the columns become rows. It handles matrices of different dimensions (4x4, 3x3, NxN)
+ * by delegating to specific transpose methods for each case.
+ *
+ * @param {Array} a - The matrix to be transposed. It should be a 2D array where each sub-array represents a row.
+ * @returns {Array} - The transposed matrix.
+ */
+ transpose(a) {
+ // TODO: Cristian: What does passing an argument to a transpose mean?
+ // In the codebase this is never done in any reference
+ // Actually transposse of a 4x4 is never done dierectly,
+ // I'm thinking it is incorrect, transpose3x3 is only used for inverseTranspose4x4
+ if (this.#sqDimention === 4) {
+ return this.#transpose4x4(a);
+ } else if (this.#sqDimention === 3) {
+ return this.#transpose3x3(a);
+ } else {
+ return this.#transposeNxN(a);
+ }
+ }
+
+
+ /**
+ * Multiplies the current matrix with another matrix or matrix-like array.
+ *
+ * This method supports several types of input:
+ * - Another Matrix instance
+ * - A matrix-like array (must be a perfect square, e.g., 4x4 or 3x3)
+ * - Multiple arguments that form a perfect square matrix
+ *
+ * If the input is the same as the current matrix, a copy is made to avoid modifying the original matrix.
+ *
+ * @param {Matrix|Array|...number} multMatrix - The matrix or matrix-like array to multiply with.
+ * @returns {Matrix|undefined} The resulting matrix after multiplication, or undefined if the input is invalid.
+ * @chainable
+ */
+ mult(multMatrix) {
+ let _src;
+ if (multMatrix === this || multMatrix === this.matrix) {
+ _src = this.copy().matrix; // only need to allocate in this rare case
+ } else if (multMatrix instanceof Matrix) {
+ _src = multMatrix.matrix;
+ } else if (isMatrixArray(multMatrix) && isPerfectSquare(multMatrix)) {
+ _src = multMatrix;
+ } else if (isPerfectSquare(arguments)) {
+ _src = Array.from(arguments);
+ } else {
+ return; // nothing to do.
+ }
+ if (this.#sqDimention === 4 && _src.length === 16) {
+ return this.#mult4x4(_src);
+ } else if (this.#sqDimention === 3 && _src.length === 9) {
+ return this.#mult3x3(_src);
+ } else {
+ return this.#multNxN(_src);
+ }
+ }
+
+ /**
+ * 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 {p5.Vector} multVector the vector to which this matrix applies
+ * @param {p5.Vector} [target] The vector to receive the result
+ * @return {p5.Vector}
+ */
+ multiplyVec(multVector, target) {
+ if (target === undefined) {
+ target = multVector.copy();
+ }
+ for (let i = 0; i < this.#sqDimention; i++) {
+ target.values[i] = this.row(i).dot(multVector);
+ }
+ return target;
+ }
+
+ /**
+ * Inverts a given matrix.
+ *
+ * This method inverts a matrix based on its dimensions. Currently, it supports
+ * 3x3 and 4x4 matrices. If the matrix dimension is greater than 4, an error is thrown.
+ *
+ * @param {Array} a - The matrix to be inverted. It should be a 2D array representing the matrix.
+ * @returns {Array} - The inverted matrix.
+ * @throws {Error} - Throws an error if the matrix dimension is greater than 4.
+ */
+ invert(a) {
+ if (this.#sqDimention === 4) {
+ return this.#invert4x4(a);
+ } else if (this.#sqDimention === 3) {
+ return this.#invert3x3(a);
+ } else {
+ throw new Error(
+ "Invert is not implemented for N>4 at the moment, we are working on it"
+ );
+ }
+ }
+
+ /**
+ * This function is only for 4x4 matrices.
+ * Creates a 3x3 matrix whose entries are the top left 3x3 part and returns it.
+ *
+ * @return {p5.Matrix}
+ */
+ createSubMatrix3x3() {
+ if (this.#sqDimention === 4) {
+ const result = new Matrix(3);
+ result.mat3[0] = this.matrix[0];
+ result.mat3[1] = this.matrix[1];
+ result.mat3[2] = this.matrix[2];
+ result.mat3[3] = this.matrix[4];
+ result.mat3[4] = this.matrix[5];
+ result.mat3[5] = this.matrix[6];
+ result.mat3[6] = this.matrix[8];
+ result.mat3[7] = this.matrix[9];
+ result.mat3[8] = this.matrix[10];
+ return result;
+ } else {
+ throw new Error("Matrix dimension must be 4 to create a 3x3 submatrix.");
+ }
+ }
+
+ /**
+ * Converts a 4×4 matrix to its 3×3 inverse transform
+ * commonly used in MVMatrix to NMatrix conversions.
+ * @param {p5.Matrix} mat4 the matrix to be based on to invert
+ * @chainable
+ * @todo finish implementation
+ */
+ inverseTranspose4x4({ mat4 }) {
+ if (this.#sqDimention !== 3) {
+ p5._friendlyError("sorry, this function only works with mat3");
+ } else {
+ //convert mat4 -> mat3
+ this.matrix[0] = mat4[0];
+ this.matrix[1] = mat4[1];
+ this.matrix[2] = mat4[2];
+ this.matrix[3] = mat4[4];
+ this.matrix[4] = mat4[5];
+ this.matrix[5] = mat4[6];
+ this.matrix[6] = mat4[8];
+ this.matrix[7] = mat4[9];
+ this.matrix[8] = mat4[10];
+ }
+
+ const inverse = this.invert();
+ // check inverse succeeded
+ if (inverse) {
+ inverse.transpose(this.matrix);
+ } else {
+ // in case of singularity, just zero the matrix
+ for (let i = 0; i < 9; i++) {
+ this.matrix[i] = 0;
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Applies a transformation matrix to the current matrix.
+ *
+ * This method multiplies the current matrix by another matrix, which can be provided
+ * in several forms: another Matrix instance, an array representing a matrix, or as
+ * individual arguments representing the elements of a 4x4 matrix.
+ *
+ * @param {Matrix|Array|number} multMatrix - The matrix to multiply with. This can be:
+ * - An instance of the Matrix class.
+ * - An array of 16 numbers representing a 4x4 matrix.
+ * - 16 individual numbers representing the elements of a 4x4 matrix.
+ * @returns {Matrix} The current matrix after applying the transformation.
+ *
+ * @example
+ * // Assuming `matrix` is an instance of Matrix
+ * const anotherMatrix = new Matrix();
+ * matrix.apply(anotherMatrix);
+ *
+ * @example
+ * // Applying a transformation using an array
+ * const matrixArray = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
+ * matrix.apply(matrixArray);
+ *
+ * @example
+ * // Applying a transformation using individual arguments
+ * matrix.apply(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+ */
+ apply(multMatrix) {
+ let _src;
+
+ if (multMatrix === this || multMatrix === this.matrix) {
+ _src = this.copy().matrix; // only need to allocate in this rare case
+ } else if (multMatrix instanceof Matrix) {
+ _src = multMatrix.matrix;
+ } else if (isMatrixArray(multMatrix)) {
+ _src = multMatrix;
+ } else if (arguments.length === 16) {
+ _src = arguments;
+ } else {
+ return; // nothing to do.
+ }
+
+ const mat4 = this.matrix;
+
+ // 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;
+
+ return this;
+ }
+
+ /**
+ * scales a p5.Matrix by scalars or a vector
+ * @param {p5.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.matrix[0] *= x;
+ this.matrix[1] *= x;
+ this.matrix[2] *= x;
+ this.matrix[3] *= x;
+ this.matrix[4] *= y;
+ this.matrix[5] *= y;
+ this.matrix[6] *= y;
+ this.matrix[7] *= y;
+ this.matrix[8] *= z;
+ this.matrix[9] *= z;
+ this.matrix[10] *= z;
+ this.matrix[11] *= z;
+
+ return this;
+ }
+
+ /**
+ * rotate our Matrix around an axis by the given angle.
+ * @param {Number} a The angle of rotation in radians
+ * @param {p5.Vector|Number[]} axis the axis(es) to rotate around
+ * @chainable
+ * inspired by Toji's gl-matrix lib, mat4 rotation
+ */
+ rotate4x4(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 a00 = this.matrix[0];
+ const a01 = this.matrix[1];
+ const a02 = this.matrix[2];
+ const a03 = this.matrix[3];
+ const a10 = this.matrix[4];
+ const a11 = this.matrix[5];
+ const a12 = this.matrix[6];
+ const a13 = this.matrix[7];
+ const a20 = this.matrix[8];
+ const a21 = this.matrix[9];
+ const a22 = this.matrix[10];
+ const a23 = this.matrix[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.matrix[0] = a00 * b00 + a10 * b01 + a20 * b02;
+ this.matrix[1] = a01 * b00 + a11 * b01 + a21 * b02;
+ this.matrix[2] = a02 * b00 + a12 * b01 + a22 * b02;
+ this.matrix[3] = a03 * b00 + a13 * b01 + a23 * b02;
+ this.matrix[4] = a00 * b10 + a10 * b11 + a20 * b12;
+ this.matrix[5] = a01 * b10 + a11 * b11 + a21 * b12;
+ this.matrix[6] = a02 * b10 + a12 * b11 + a22 * b12;
+ this.matrix[7] = a03 * b10 + a13 * b11 + a23 * b12;
+ this.matrix[8] = a00 * b20 + a10 * b21 + a20 * b22;
+ this.matrix[9] = a01 * b20 + a11 * b21 + a21 * b22;
+ this.matrix[10] = a02 * b20 + a12 * b21 + a22 * b22;
+ this.matrix[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.matrix[12] +=
+ this.matrix[0] * x + this.matrix[4] * y + this.matrix[8] * z;
+ this.matrix[13] +=
+ this.matrix[1] * x + this.matrix[5] * y + this.matrix[9] * z;
+ this.matrix[14] +=
+ this.matrix[2] * x + this.matrix[6] * y + this.matrix[10] * z;
+ this.matrix[15] +=
+ this.matrix[3] * x + this.matrix[7] * y + this.matrix[11] * z;
+ }
+
+ /**
+ * Rotates the matrix around the X-axis by a given angle.
+ *
+ * This method modifies the current matrix to apply a rotation transformation
+ * around the X-axis. The rotation angle is specified in radians.
+ *
+ * @param {number} a - The angle in radians to rotate the matrix by.
+ */
+ rotateX(a) {
+ this.rotate4x4(a, 1, 0, 0);
+ }
+
+ /**
+ * Rotates the matrix around the Y-axis by a given angle.
+ *
+ * This method modifies the current matrix to apply a rotation transformation
+ * around the Y-axis. The rotation is performed in 3D space, and the angle
+ * is specified in radians.
+ *
+ * @param {number} a - The angle in radians to rotate the matrix by. Positive
+ * values rotate the matrix counterclockwise, and negative values rotate it
+ * clockwise.
+ */
+ rotateY(a) {
+ this.rotate4x4(a, 0, 1, 0);
+ }
+
+ /**
+ * Rotates the matrix around the Z-axis by a given angle.
+ *
+ * @param {number} a - The angle in radians to rotate the matrix by.
+ *
+ * This method modifies the current matrix to apply a rotation transformation
+ * around the Z-axis. The rotation is performed in a 4x4 matrix context, which
+ * is commonly used in 3D graphics to handle transformations.
+ *
+ * Example usage:
+ * ```
+ * const matrix = new Matrix();
+ * matrix.rotateZ(Math.PI / 4); // Rotates the matrix 45 degrees around the Z-axis
+ * ```
+ */
+ rotateZ(a) {
+ this.rotate4x4(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.matrix[0] = f / aspect;
+ this.matrix[1] = 0;
+ this.matrix[2] = 0;
+ this.matrix[3] = 0;
+ this.matrix[4] = 0;
+ this.matrix[5] = f;
+ this.matrix[6] = 0;
+ this.matrix[7] = 0;
+ this.matrix[8] = 0;
+ this.matrix[9] = 0;
+ this.matrix[10] = (far + near) * nf;
+ this.matrix[11] = -1;
+ this.matrix[12] = 0;
+ this.matrix[13] = 0;
+ this.matrix[14] = 2 * far * near * nf;
+ this.matrix[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.matrix[0] = -2 * lr;
+ this.matrix[1] = 0;
+ this.matrix[2] = 0;
+ this.matrix[3] = 0;
+ this.matrix[4] = 0;
+ this.matrix[5] = -2 * bt;
+ this.matrix[6] = 0;
+ this.matrix[7] = 0;
+ this.matrix[8] = 0;
+ this.matrix[9] = 0;
+ this.matrix[10] = 2 * nf;
+ this.matrix[11] = 0;
+ this.matrix[12] = (left + right) * lr;
+ this.matrix[13] = (top + bottom) * bt;
+ this.matrix[14] = (far + near) * nf;
+ this.matrix[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.matrix;
+
+ result[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
+ result[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
+ result[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
+ result[3] = m[3] * x + m[7] * y + m[11] * z + m[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 {p5.Vector}
+ * @return {p5.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 {p5.Vector}
+ * @return {p5.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 {p5.Vector}
+ * @return {p5.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.
+ * Takes a vector and returns the vector resulting from multiplying to
+ * that vector by this matrix from left.
+ *
+ * @param {p5.Vector} multVector the vector to which this matrix applies
+ * @param {p5.Vector} [target] The vector to receive the result
+ * @return {p5.Vector}
+ */
+ /**
+ * 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 {p5.Vector} multVector the vector to which this matrix applies
+ * @param {p5.Vector} [target] The vector to receive the result
+ * @return {p5.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;
+ }
+
+ // ====================
+ // PRIVATE
+ #createIdentityMatrix(dimension) {
+ // This it to prevent loops in the most common 3x3 and 4x4 cases
+ // TODO: check performance if it actually helps
+ if (dimension === 3)
+ return new GLMAT_ARRAY_TYPE([1, 0, 0, 0, 1, 0, 0, 0, 1]);
+ if (dimension === 4)
+ return new GLMAT_ARRAY_TYPE([
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
+ ]);
+ const identityMatrix = new GLMAT_ARRAY_TYPE(dimension * dimension).fill(0);
+ for (let i = 0; i < dimension; i++) {
+ identityMatrix[i * dimension + i] = 1;
+ }
+ return identityMatrix;
+ }
+
+ /**
+ * Multiplies the current 4x4 matrix with another 4x4 matrix.
+ * This method updates the current matrix with the result of the multiplication.
+ *
+ * @private
+ * @param {number[]} _src - A 16-element array representing the 4x4 matrix to multiply with.
+ *
+ * @returns {this} The current instance with the updated matrix.
+ *
+ * @example
+ * // Assuming `matrix` is an instance of the Matrix class
+ * const srcMatrix = [
+ * 1, 0, 0, 0,
+ * 0, 1, 0, 0,
+ * 0, 0, 1, 0,
+ * 0, 0, 0, 1
+ * ];
+ * matrix.#mult4x4(srcMatrix);
+ */
+ #mult4x4(_src) {
+ // each row is used for the multiplier
+ let b0 = this.matrix[0],
+ b1 = this.matrix[1],
+ b2 = this.matrix[2],
+ b3 = this.matrix[3];
+ this.matrix[0] = b0 * _src[0] + b1 * _src[4] + b2 * _src[8] + b3 * _src[12];
+ this.matrix[1] = b0 * _src[1] + b1 * _src[5] + b2 * _src[9] + b3 * _src[13];
+ this.matrix[2] =
+ b0 * _src[2] + b1 * _src[6] + b2 * _src[10] + b3 * _src[14];
+ this.matrix[3] =
+ b0 * _src[3] + b1 * _src[7] + b2 * _src[11] + b3 * _src[15];
+
+ b0 = this.matrix[4];
+ b1 = this.matrix[5];
+ b2 = this.matrix[6];
+ b3 = this.matrix[7];
+ this.matrix[4] = b0 * _src[0] + b1 * _src[4] + b2 * _src[8] + b3 * _src[12];
+ this.matrix[5] = b0 * _src[1] + b1 * _src[5] + b2 * _src[9] + b3 * _src[13];
+ this.matrix[6] =
+ b0 * _src[2] + b1 * _src[6] + b2 * _src[10] + b3 * _src[14];
+ this.matrix[7] =
+ b0 * _src[3] + b1 * _src[7] + b2 * _src[11] + b3 * _src[15];
+
+ b0 = this.matrix[8];
+ b1 = this.matrix[9];
+ b2 = this.matrix[10];
+ b3 = this.matrix[11];
+ this.matrix[8] = b0 * _src[0] + b1 * _src[4] + b2 * _src[8] + b3 * _src[12];
+ this.matrix[9] = b0 * _src[1] + b1 * _src[5] + b2 * _src[9] + b3 * _src[13];
+ this.matrix[10] =
+ b0 * _src[2] + b1 * _src[6] + b2 * _src[10] + b3 * _src[14];
+ this.matrix[11] =
+ b0 * _src[3] + b1 * _src[7] + b2 * _src[11] + b3 * _src[15];
+
+ b0 = this.matrix[12];
+ b1 = this.matrix[13];
+ b2 = this.matrix[14];
+ b3 = this.matrix[15];
+ this.matrix[12] =
+ b0 * _src[0] + b1 * _src[4] + b2 * _src[8] + b3 * _src[12];
+ this.matrix[13] =
+ b0 * _src[1] + b1 * _src[5] + b2 * _src[9] + b3 * _src[13];
+ this.matrix[14] =
+ b0 * _src[2] + b1 * _src[6] + b2 * _src[10] + b3 * _src[14];
+ this.matrix[15] =
+ b0 * _src[3] + b1 * _src[7] + b2 * _src[11] + b3 * _src[15];
+
+ return this;
+ }
+
+ /**
+ * @param {p5.Matrix|Float32Array|Number[]} multMatrix The matrix
+ * we want to multiply by
+ * @chainable
+ */
+ #multNxN(multMatrix) {
+ if (multMatrix.length !== this.matrix.length) {
+ throw new Error("Matrices must be of the same dimension to multiply.");
+ }
+ const result = new GLMAT_ARRAY_TYPE(this.matrix.length).fill(0);
+ for (let i = 0; i < this.#sqDimention; i++) {
+ for (let j = 0; j < this.#sqDimention; j++) {
+ for (let k = 0; k < this.#sqDimention; k++) {
+ result[i * this.#sqDimention + j] +=
+ this.matrix[i * this.#sqDimention + k] *
+ multMatrix[k * this.#sqDimention + j];
+ }
+ }
+ }
+ this.matrix = result;
+ return this;
+ }
+
+ /**
+ * 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 p5.Matrix,
+ * a Float32Array of length 9, or a javascript array of length 9.
+ * In addition, it can also be done by enumerating 9 numbers.
+ *
+ * @param {p5.Matrix|Float32Array|Number[]} multMatrix The matrix
+ * we want to multiply by
+ * @chainable
+ */
+ #mult3x3(_src) {
+ // each row is used for the multiplier
+ let b0 = this.mat3[0];
+ let b1 = this.mat3[1];
+ let b2 = this.mat3[2];
+ this.mat3[0] = b0 * _src[0] + b1 * _src[3] + b2 * _src[6];
+ this.mat3[1] = b0 * _src[1] + b1 * _src[4] + b2 * _src[7];
+ this.mat3[2] = b0 * _src[2] + b1 * _src[5] + b2 * _src[8];
+
+ b0 = this.mat3[3];
+ b1 = this.mat3[4];
+ b2 = this.mat3[5];
+ this.mat3[3] = b0 * _src[0] + b1 * _src[3] + b2 * _src[6];
+ this.mat3[4] = b0 * _src[1] + b1 * _src[4] + b2 * _src[7];
+ this.mat3[5] = b0 * _src[2] + b1 * _src[5] + b2 * _src[8];
+
+ b0 = this.mat3[6];
+ b1 = this.mat3[7];
+ b2 = this.mat3[8];
+ this.mat3[6] = b0 * _src[0] + b1 * _src[3] + b2 * _src[6];
+ this.mat3[7] = b0 * _src[1] + b1 * _src[4] + b2 * _src[7];
+ this.mat3[8] = b0 * _src[2] + b1 * _src[5] + b2 * _src[8];
+
+ return this;
+ }
+
+ /**
+ * Transposes a square matrix in place.
+ * This method swaps the rows and columns of the matrix, effectively flipping it over its diagonal.
+ *
+ * @private
+ * @returns {Matrix} The current instance of the Matrix, with the transposed values.
+ */
+ #transposeNxN() {
+ const n = this.#sqDimention;
+ for (let i = 0; i < n; i++) {
+ for (let j = 0; j < n; j++) {
+ this.matrix[i * n + j] = this.matrix[j * n + i];
+ }
+ }
+ return this;
+ }
+
+ /**
+ * transpose according to a given matrix
+ * @param {p5.Matrix|Float32Array|Number[]} a the matrix to be
+ * based on to transpose
+ * @chainable
+ */
+ #transpose4x4(a) {
+ console.log("====> 4x4");
+ let a01, a02, a03, a12, a13, a23;
+ if (a instanceof Matrix) {
+ a01 = a.matrix[1];
+ a02 = a.matrix[2];
+ a03 = a.matrix[3];
+ a12 = a.matrix[6];
+ a13 = a.matrix[7];
+ a23 = a.matrix[11];
+
+ this.matrix[0] = a.matrix[0];
+ this.matrix[1] = a.matrix[4];
+ this.matrix[2] = a.matrix[8];
+ this.matrix[3] = a.matrix[12];
+ this.matrix[4] = a01;
+ this.matrix[5] = a.matrix[5];
+ this.matrix[6] = a.matrix[9];
+ this.matrix[7] = a.matrix[13];
+ this.matrix[8] = a02;
+ this.matrix[9] = a12;
+ this.matrix[10] = a.matrix[10];
+ this.matrix[11] = a.matrix[14];
+ this.matrix[12] = a03;
+ this.matrix[13] = a13;
+ this.matrix[14] = a23;
+ this.matrix[15] = a.matrix[15];
+ } else if (isMatrixArray(a)) {
+ a01 = a[1];
+ a02 = a[2];
+ a03 = a[3];
+ a12 = a[6];
+ a13 = a[7];
+ a23 = a[11];
+
+ this.matrix[0] = a[0];
+ this.matrix[1] = a[4];
+ this.matrix[2] = a[8];
+ this.matrix[3] = a[12];
+ this.matrix[4] = a01;
+ this.matrix[5] = a[5];
+ this.matrix[6] = a[9];
+ this.matrix[7] = a[13];
+ this.matrix[8] = a02;
+ this.matrix[9] = a12;
+ this.matrix[10] = a[10];
+ this.matrix[11] = a[14];
+ this.matrix[12] = a03;
+ this.matrix[13] = a13;
+ this.matrix[14] = a23;
+ this.matrix[15] = a[15];
+ }
+ return this;
+ }
+
+ /**
+ * This function is only for 3x3 matrices.
+ * transposes a 3×3 p5.Matrix 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;
+ }
+ const a01 = mat3[1];
+ const a02 = mat3[2];
+ const a12 = mat3[5];
+ this.mat3[0] = mat3[0];
+ this.mat3[1] = mat3[3];
+ this.mat3[2] = mat3[6];
+ this.mat3[3] = a01;
+ this.mat3[4] = mat3[4];
+ this.mat3[5] = mat3[7];
+ this.mat3[6] = a02;
+ this.mat3[7] = a12;
+ this.mat3[8] = mat3[8];
+
+ return this;
+ }
+
+ /**
+ * Only 4x4 becasuse determinant is only 4x4 currently
+ * invert matrix according to a give matrix
+ * @param {p5.Matrix|Float32Array|Number[]} a the matrix to be
+ * based on to invert
+ * @chainable
+ */
+ #invert4x4(a) {
+ let a00, a01, a02, a03, a10, a11, a12, a13;
+ let a20, a21, a22, a23, a30, a31, a32, a33;
+ if (a instanceof Matrix) {
+ a00 = a.matrix[0];
+ a01 = a.matrix[1];
+ a02 = a.matrix[2];
+ a03 = a.matrix[3];
+ a10 = a.matrix[4];
+ a11 = a.matrix[5];
+ a12 = a.matrix[6];
+ a13 = a.matrix[7];
+ a20 = a.matrix[8];
+ a21 = a.matrix[9];
+ a22 = a.matrix[10];
+ a23 = a.matrix[11];
+ a30 = a.matrix[12];
+ a31 = a.matrix[13];
+ a32 = a.matrix[14];
+ a33 = a.matrix[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.matrix[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
+ this.matrix[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
+ this.matrix[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
+ this.matrix[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
+ this.matrix[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
+ this.matrix[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
+ this.matrix[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
+ this.matrix[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
+ this.matrix[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
+ this.matrix[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
+ this.matrix[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
+ this.matrix[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
+ this.matrix[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
+ this.matrix[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
+ this.matrix[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
+ this.matrix[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
+
+ return this;
+ }
+
+ /**
+ * Inverts a 3×3 matrix
+ * @chainable
+ */
+ #invert3x3() {
+ const a00 = this.mat3[0];
+ const a01 = this.mat3[1];
+ const a02 = this.mat3[2];
+ const a10 = this.mat3[3];
+ const a11 = this.mat3[4];
+ const a12 = this.mat3[5];
+ const a20 = this.mat3[6];
+ const a21 = this.mat3[7];
+ const a22 = this.mat3[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[0] = b01 * det;
+ this.mat3[1] = (-a22 * a01 + a02 * a21) * det;
+ this.mat3[2] = (a12 * a01 - a02 * a11) * det;
+ this.mat3[3] = b11 * det;
+ this.mat3[4] = (a22 * a00 - a02 * a20) * det;
+ this.mat3[5] = (-a12 * a00 + a02 * a10) * det;
+ this.mat3[6] = b21 * det;
+ this.mat3[7] = (-a21 * a00 + a01 * a20) * det;
+ this.mat3[8] = (a11 * a00 - a01 * a10) * det;
+ return this;
+ }
+
+ /**
+ * inspired by Toji's mat4 determinant
+ * @return {Number} Determinant of our 4×4 matrix
+ */
+ #determinant4x4() {
+ if (this.#sqDimention !== 4) {
+ throw new Error(
+ "Determinant is only implemented for 4x4 matrices. We are working on it."
+ );
+ }
+
+ const d00 =
+ this.matrix[0] * this.matrix[5] - this.matrix[1] * this.matrix[4],
+ d01 = this.matrix[0] * this.matrix[6] - this.matrix[2] * this.matrix[4],
+ d02 = this.matrix[0] * this.matrix[7] - this.matrix[3] * this.matrix[4],
+ d03 = this.matrix[1] * this.matrix[6] - this.matrix[2] * this.matrix[5],
+ d04 = this.matrix[1] * this.matrix[7] - this.matrix[3] * this.matrix[5],
+ d05 = this.matrix[2] * this.matrix[7] - this.matrix[3] * this.matrix[6],
+ d06 = this.matrix[8] * this.matrix[13] - this.matrix[9] * this.matrix[12],
+ d07 =
+ this.matrix[8] * this.matrix[14] - this.matrix[10] * this.matrix[12],
+ d08 =
+ this.matrix[8] * this.matrix[15] - this.matrix[11] * this.matrix[12],
+ d09 =
+ this.matrix[9] * this.matrix[14] - this.matrix[10] * this.matrix[13],
+ d10 =
+ this.matrix[9] * this.matrix[15] - this.matrix[11] * this.matrix[13],
+ d11 =
+ this.matrix[10] * this.matrix[15] - this.matrix[11] * this.matrix[14];
+
+ // Calculate the determinant
+ return (
+ d00 * d11 - d01 * d10 + d02 * d09 + d03 * d08 - d04 * d07 + d05 * d06
+ );
+ }
+
+ /**
+ * 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 p5.Matrix 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
+ //];
+}
diff --git a/src/math/Matrices/MatrixInterface.js b/src/math/Matrices/MatrixInterface.js
new file mode 100644
index 0000000000..4ab4a0b63a
--- /dev/null
+++ b/src/math/Matrices/MatrixInterface.js
@@ -0,0 +1,53 @@
+export let GLMAT_ARRAY_TYPE = Array;
+export let isMatrixArray = (x) => Array.isArray(x);
+if (typeof Float32Array !== "undefined") {
+ GLMAT_ARRAY_TYPE = Float32Array;
+ isMatrixArray = (x) => Array.isArray(x) || x instanceof Float32Array;
+}
+export class MatrixInterface {
+ // Private field to store the matrix
+ #matrix = null;
+ constructor(...args) {
+ if (this.constructor === MatrixInterface) {
+ throw new Error("Class is of abstract type and can't be instantiated");
+ }
+ const methods = [
+ "add",
+ "setElement",
+ "reset",
+ "set",
+ "get",
+ "copy",
+ "clone",
+ "diagonal",
+ "row",
+ "column",
+ "transpose",
+ "mult",
+ "multiplyVec",
+ "invert",
+ "createSubMatrix3x3",
+ "inverseTranspose4x4",
+ "apply",
+ "scale",
+ "rotate4x4",
+ "translate",
+ "rotateX",
+ "rotateY",
+ "rotateZ",
+ "perspective",
+ "ortho",
+ "multiplyVec4",
+ "multiplyPoint",
+ "multiplyAndNormalizePoint",
+ "multiplyDirection",
+ "multiplyVec3",
+ ];
+
+ methods.forEach((method) => {
+ if (this[method] === undefined) {
+ throw new Error(`${method}() method must be implemented`);
+ }
+ });
+ }
+}
diff --git a/src/math/Matrices/MatrixNumjs.js b/src/math/Matrices/MatrixNumjs.js
new file mode 100644
index 0000000000..75d731dbee
--- /dev/null
+++ b/src/math/Matrices/MatrixNumjs.js
@@ -0,0 +1,966 @@
+import nj from "@d4c/numjs/build/module/numjs.min.js";
+import { Vector } from "../p5.Vector";
+import { MatrixInterface } from "./MatrixInterface";
+
+/**
+ * @requires constants
+ * @todo see methods below needing further implementation.
+ * future consideration: implement SIMD optimizations
+ * when browser compatibility becomes available
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/
+ * Reference/Global_Objects/SIMD
+ */
+
+let GLMAT_ARRAY_TYPE = Array;
+let isMatrixArray = (x) => Array.isArray(x);
+if (typeof Float32Array !== "undefined") {
+ GLMAT_ARRAY_TYPE = Float32Array;
+ 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
+ *
+ *
+ * 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);
+ * }
+ *
+ *
+ */
+// const matrixEngine = "numjs";
+export 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() {
+ let temp = 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) {
+ // p5._friendlyError("sorry, this function only works with mat3");
+ } 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();
+ const vect = 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 _src;
+ let tempMatrix = multMatrix;
+ if (multMatrix === this || multMatrix === this._mat3) {
+ // mat3; // only need to allocate in this rare case
+ } else if (multMatrix instanceof MatrixNumjs) {
+ _src = 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
+ //];
+}
+
diff --git a/src/math/math.js b/src/math/math.js
index 55a88b4541..c3e9103da4 100644
--- a/src/math/math.js
+++ b/src/math/math.js
@@ -93,7 +93,7 @@ function math(p5, fn){
*
*
*/
- fn.createVector = function(x, y, z) {
+ fn.createVector = function (x, y, z) {
if (this instanceof p5) {
return new p5.Vector(
this._fromRadians.bind(this),
@@ -104,6 +104,32 @@ function math(p5, fn){
return new p5.Vector(x, y, z);
}
};
+
+ /**
+ * Creates a new p5.Matrix object.
+ *
+ * A matrix is a mathematical concept that is useful in many fields, including
+ * computer graphics. In p5.js, matrices are used to perform transformations
+ * on shapes and images.
+ *
+ * @method createMatrix
+ * @return {p5.Matrix} new p5.Matrix object.
+ *
+ * @example
+ *
+ *
+ * function setup() {
+ * createCanvas(100, 100);
+ * let matrix = createMatrix();
+ * console.log(matrix);
+ * describe('Logs a new p5.Matrix object to the console.');
+ * }
+ *
+ *
+ */
+ fn.createMatrix = function (...args) {
+ return new p5.Matrix(...args);
+ };
}
export default math;
diff --git a/src/math/p5.Matrix.js b/src/math/p5.Matrix.js
new file mode 100644
index 0000000000..704544dc86
--- /dev/null
+++ b/src/math/p5.Matrix.js
@@ -0,0 +1,30 @@
+/**
+ * @requires constants
+ * @todo see methods below needing further implementation.
+ * future consideration: implement SIMD optimizations
+ * when browser compatibility becomes available
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/
+ * Reference/Global_Objects/SIMD
+ */
+import { Matrix } from './Matrices/Matrix'
+// import { MatrixNumjs as Matrix } from './Matrices/MatrixNumjs'
+
+
+
+function matrix(p5, fn){
+ /**
+ * A class to describe a 4×4 matrix
+ * for model and view matrix manipulation in the p5js webgl renderer.
+ * @class p5.Matrix
+ * @private
+ * @param {Array} [mat4] column-major array literal of our 4×4 matrix
+ */
+ p5.Matrix = Matrix
+}
+
+export default matrix;
+export { Matrix };
+
+if(typeof p5 !== 'undefined'){
+ matrix(p5, p5.prototype);
+}
diff --git a/src/math/p5.Vector.js b/src/math/p5.Vector.js
index 738b966f8d..9b983acfc6 100644
--- a/src/math/p5.Vector.js
+++ b/src/math/p5.Vector.js
@@ -34,23 +34,223 @@ class Vector {
// This is how it comes in with createVector()
// This check if the first argument is a function
constructor(...args) {
- let x, y, z;
- if (typeof args[0] === 'function') {
+ let dimensions = args.length; // TODO: make default 3 if no arguments
+ let values = args.map((arg) => arg || 0);
+ if (typeof args[0] === "function") {
this.isPInst = true;
this._fromRadians = args[0];
this._toRadians = args[1];
- x = args[2] || 0;
- y = args[3] || 0;
- z = args[4] || 0;
- // This is what we'll get with new Vector()
+ values = args.slice(2).map((arg) => arg || 0);
+ }
+ if (dimensions === 0) {
+ this.dimensions = 2;
+ this._values = [0, 0, 0];
+ } else {
+ this.dimensions = dimensions;
+ this._values = values;
+ }
+ }
+
+ /**
+ * Gets the values of the vector.
+ *
+ * This method returns an array of numbers that represent the vector.
+ * Each number in the array corresponds to a different component of the vector,
+ * like its position in different directions (e.g., x, y, z).
+ *
+ * @returns {Array} The array of values representing the vector.
+ */
+ get values() {
+ return this._values;
+ }
+
+ /**
+ * Sets the values of the vector.
+ *
+ * This method allows you to update the entire vector with a new set of values.
+ * You need to provide an array of numbers, where each number represents a component
+ * of the vector (e.g., x, y, z). The length of the array should match the number of
+ * dimensions of the vector. If the array is shorter, the missing components will be
+ * set to 0. If the array is longer, the extra values will be ignored.
+ *
+ * @param {Array} newValues - An array of numbers representing the new values for the vector.
+ *
+ */
+ set values(newValues) {
+ let dimensions = newValues.length;
+ if (dimensions === 0) {
+ this.dimensions = 2;
+ this._values = [0, 0, 0];
+ } else {
+ this.dimensions = dimensions;
+ this._values = newValues.slice();
+ }
+ }
+
+ /**
+ * Gets the x component of the vector.
+ *
+ * This method returns the value of the x component of the vector.
+ * Think of the x component as the horizontal position or the first number in the vector.
+ * If the x component is not defined, it will return 0.
+ *
+ * @returns {number} The x component of the vector. Returns 0 if the value is not defined.
+ */
+ get x() {
+ return this._values[0] || 0;
+ }
+
+ /**
+ * Retrieves the value at the specified index from the vector.
+ *
+ * This method allows you to get the value of a specific component of the vector
+ * by providing its index. Think of the vector as a list of numbers, where each
+ * number represents a different direction (like x, y, or z). The index is just
+ * the position of the number in that list.
+ *
+ * For example, if you have a vector with values 10, 20, 30 the index 0 would
+ * give you the first value 10, index 1 would give you the second value 20,
+ * and so on.
+ *
+ * @param {number} index - The position of the value you want to get from the vector.
+ * @returns {number} The value at the specified position in the vector.
+ * @throws Will throw an error if the index is out of bounds, meaning if you try to
+ * get a value from a position that doesn't exist in the vector.
+ */
+ getValue(index) {
+ if (index < this._values.length) {
+ return this._values[index];
+ } else {
+ p5._friendlyError(
+ "The index parameter is trying to set a value outside the bounds of the vector",
+ "p5.Vector.setValue"
+ );
+ }
+ }
+
+ /**
+ * Sets the value at the specified index of the vector.
+ *
+ * This method allows you to change a specific component of the vector by providing its index and the new value you want to set.
+ * Think of the vector as a list of numbers, where each number represents a different direction (like x, y, or z).
+ * The index is just the position of the number in that list.
+ *
+ * For example, if you have a vector with values [0, 20, 30], and you want to change the second value (20) to 50,
+ * you would use this method with index 1 (since indexes start at 0) and value 50.
+ *
+ * @param {number} index - The position in the vector where you want to set the new value.
+ * @param {number} value - The new value you want to set at the specified position.
+ * @throws Will throw an error if the index is outside the bounds of the vector, meaning if you try to set a value at a position that doesn't exist in the vector.
+ */
+
+ setValue(index, value) {
+ if (index < this._values.length) {
+ this._values[index] = value;
} else {
- x = args[0] || 0;
- y = args[1] || 0;
- z = args[2] || 0;
+ p5._friendlyError(
+ "The index parameter is trying to set a value outside the bounds of the vector",
+ "p5.Vector.setValue"
+ );
+ }
+ }
+
+ /**
+ * Gets the y component of the vector.
+ *
+ * This method returns the value of the y component of the vector.
+ * Think of the y component as the vertical position or the second number in the vector.
+ * If the y component is not defined, it will return 0.
+ *
+ * @returns {number} The y component of the vector. Returns 0 if the value is not defined.
+ */
+ get y() {
+ return this._values[1] || 0;
+ }
+
+ /**
+ * Gets the z component of the vector.
+ *
+ * This method returns the value of the z component of the vector.
+ * Think of the z component as the depth or the third number in the vector.
+ * If the z component is not defined, it will return 0.
+ *
+ * @returns {number} The z component of the vector. Returns 0 if the value is not defined.
+ */
+ get z() {
+ return this._values[2] || 0;
+ }
+
+ /**
+ * Gets the w component of the vector.
+ *
+ * This method returns the value of the w component of the vector.
+ * Think of the w component as the fourth number in the vector.
+ * If the w component is not defined, it will return 0.
+ *
+ * @returns {number} The w component of the vector. Returns 0 if the value is not defined.
+ */
+ get w() {
+ return this._values[3] || 0;
+ }
+
+ /**
+ * Sets the x component of the vector.
+ *
+ * This method allows you to change the x value of the vector.
+ * The x value is the first number in the vector, representing the horizontal position.
+ * By calling this method, you can update the x value to a new number.
+ *
+ * @param {number} xVal - The new value for the x component.
+ */
+ set x(xVal) {
+ if (this._values.length > 1) {
+ this._values[0] = xVal;
+ }
+ }
+
+ /**
+ * Sets the y component of the vector.
+ *
+ * This method allows you to change the y value of the vector.
+ * The y value is the second number in the vector, representing the vertical position.
+ * By calling this method, you can update the y value to a new number.
+ *
+ * @param {number} yVal - The new value for the y component.
+ */
+ set y(yVal) {
+ if (this._values.length > 1) {
+ this._values[1] = yVal;
+ }
+ }
+
+ /**
+ * Sets the z component of the vector.
+ *
+ * This method allows you to change the z value of the vector.
+ * The z value is the third number in the vector, representing the depth or the third dimension.
+ * By calling this method, you can update the z value to a new number.
+ *
+ * @param {number} zVal - The new value for the z component.
+ */
+ set z(zVal) {
+ if (this._values.length > 2) {
+ this._values[2] = zVal;
+ }
+ }
+
+ /**
+ * Sets the w component of the vector.
+ *
+ * This method allows you to change the w value of the vector.
+ * The w value is the fourth number in the vector, representing the fourth dimension.
+ * By calling this method, you can update the w value to a new number.
+ *
+ * @param {number} wVal - The new value for the w component.
+ */
+ set w(wVal) {
+ if (this._values.length > 3) {
+ this._values[3] = wVal;
}
- this.x = x;
- this.y = y;
- this.z = z;
}
/**
@@ -74,7 +274,7 @@ class Vector {
*
*/
toString() {
- return `p5.Vector Object : [${this.x}, ${this.y}, ${this.z}]`;
+ return `[${this.values.join(", ")}]`;
}
/**
@@ -134,23 +334,15 @@ class Vector {
* @param {p5.Vector|Number[]} value vector to set.
* @chainable
*/
- set(x, y, z) {
- if (x instanceof Vector) {
- this.x = x.x || 0;
- this.y = x.y || 0;
- this.z = x.z || 0;
- return this;
- }
- if (Array.isArray(x)) {
- this.x = x[0] || 0;
- this.y = x[1] || 0;
- this.z = x[2] || 0;
- return this;
+ set(...args) {
+ if (args[0] instanceof Vector) {
+ this.values = args[0].values.slice();
+ } else if (Array.isArray(args[0])) {
+ this.values = args[0].map((arg) => arg || 0);
+ } else {
+ this.values = args.map((arg) => arg || 0);
}
- this.x = x || 0;
- this.y = y || 0;
- this.z = z || 0;
-
+ this.dimensions = this.values.length;
return this;
}
@@ -184,15 +376,9 @@ class Vector {
*/
copy() {
if (this.isPInst) {
- return new Vector(
- this._fromRadians,
- this._toRadians,
- this.x,
- this.y,
- this.z
- );
+ return new Vector(this._fromRadians, this._toRadians, ...this.values);
} else {
- return new Vector(this.x, this.y, this.z);
+ return new Vector(...this.values);
}
}
@@ -328,22 +514,15 @@ class Vector {
* @param {p5.Vector|Number[]} value The vector to add
* @chainable
*/
- add(x, y, z) {
- if (x instanceof Vector) {
- this.x += x.x || 0;
- this.y += x.y || 0;
- this.z += x.z || 0;
- return this;
+ add(...args) {
+ if (args[0] instanceof Vector) {
+ args = args[0].values;
+ } else if (Array.isArray(args[0])) {
+ args = args[0];
}
- if (Array.isArray(x)) {
- this.x += x[0] || 0;
- this.y += x[1] || 0;
- this.z += x[2] || 0;
- return this;
- }
- this.x += x || 0;
- this.y += y || 0;
- this.z += z || 0;
+ args.forEach((value, index) => {
+ this.values[index] = (this.values[index] || 0) + (value || 0);
+ });
return this;
}
@@ -481,7 +660,7 @@ class Vector {
);
}
} else if (Array.isArray(x)) {
- if (x.every(element => Number.isFinite(element))) {
+ if (x.every((element) => Number.isFinite(element))) {
if (x.length === 2) {
return calculateRemainder2D.call(this, x[0], x[1]);
}
@@ -498,7 +677,7 @@ class Vector {
}
} else if (arguments.length === 2) {
const vectorComponents = [...arguments];
- if (vectorComponents.every(element => Number.isFinite(element))) {
+ if (vectorComponents.every((element) => Number.isFinite(element))) {
if (vectorComponents.length === 2) {
return calculateRemainder2D.call(
this,
@@ -509,7 +688,7 @@ class Vector {
}
} else if (arguments.length === 3) {
const vectorComponents = [...arguments];
- if (vectorComponents.every(element => Number.isFinite(element))) {
+ if (vectorComponents.every((element) => Number.isFinite(element))) {
if (vectorComponents.length === 3) {
return calculateRemainder3D.call(
this,
@@ -651,22 +830,20 @@ class Vector {
* @param {p5.Vector|Number[]} value the vector to subtract
* @chainable
*/
- sub(x, y, z) {
- if (x instanceof Vector) {
- this.x -= x.x || 0;
- this.y -= x.y || 0;
- this.z -= x.z || 0;
- return this;
- }
- if (Array.isArray(x)) {
- this.x -= x[0] || 0;
- this.y -= x[1] || 0;
- this.z -= x[2] || 0;
- return this;
+ sub(...args) {
+ if (args[0] instanceof Vector) {
+ args[0].values.forEach((value, index) => {
+ this.values[index] -= value || 0;
+ });
+ } else if (Array.isArray(args[0])) {
+ args[0].forEach((value, index) => {
+ this.values[index] -= value || 0;
+ });
+ } else {
+ args.forEach((value, index) => {
+ this.values[index] -= value || 0;
+ });
}
- this.x -= x || 0;
- this.y -= y || 0;
- this.z -= z || 0;
return this;
}
@@ -862,82 +1039,44 @@ class Vector {
* @param {p5.Vector} v vector to multiply with the components of the original vector.
* @chainable
*/
- mult(x, y, z) {
- if (x instanceof Vector) {
- // new p5.Vector will check that values are valid upon construction but it's possible
- // that someone could change the value of a component after creation, which is why we still
- // perform this check
- if (
- Number.isFinite(x.x) &&
- Number.isFinite(x.y) &&
- Number.isFinite(x.z) &&
- typeof x.x === 'number' &&
- typeof x.y === 'number' &&
- typeof x.z === 'number'
- ) {
- this.x *= x.x;
- this.y *= x.y;
- this.z *= x.z;
- } else {
- console.warn(
- 'p5.Vector.prototype.mult:',
- 'x contains components that are either undefined or not finite numbers'
- );
+ mult(...args) {
+ if (args.length === 1 && args[0] instanceof Vector) {
+ const v = args[0];
+ const maxLen = Math.min(this.values.length, v.values.length);
+ for (let i = 0; i < maxLen; i++) {
+ if (Number.isFinite(v.values[i]) && typeof v.values[i] === "number") {
+ this._values[i] *= v.values[i];
+ } else {
+ console.warn(
+ "p5.Vector.prototype.mult:",
+ "v contains components that are either undefined or not finite numbers"
+ );
+ return this;
+ }
}
- return this;
- }
- if (Array.isArray(x)) {
- if (
- x.every(element => Number.isFinite(element)) &&
- x.every(element => typeof element === 'number')
- ) {
- if (x.length === 1) {
- this.x *= x[0];
- this.y *= x[0];
- this.z *= x[0];
- } else if (x.length === 2) {
- this.x *= x[0];
- this.y *= x[1];
- } else if (x.length === 3) {
- this.x *= x[0];
- this.y *= x[1];
- this.z *= x[2];
+ } else if (args.length === 1 && Array.isArray(args[0])) {
+ const arr = args[0];
+ const maxLen = Math.min(this.values.length, arr.length);
+ for (let i = 0; i < maxLen; i++) {
+ if (Number.isFinite(arr[i]) && typeof arr[i] === "number") {
+ this._values[i] *= arr[i];
+ } else {
+ console.warn(
+ "p5.Vector.prototype.mult:",
+ "arr contains elements that are either undefined or not finite numbers"
+ );
+ return this;
}
- } else {
- console.warn(
- 'p5.Vector.prototype.mult:',
- 'x contains elements that are either undefined or not finite numbers'
- );
}
- return this;
- }
-
- const vectorComponents = [...arguments];
- if (
- vectorComponents.every(element => Number.isFinite(element)) &&
- vectorComponents.every(element => typeof element === 'number')
+ } else if (
+ args.length === 1 &&
+ typeof args[0] === "number" &&
+ Number.isFinite(args[0])
) {
- if (arguments.length === 1) {
- this.x *= x;
- this.y *= x;
- this.z *= x;
- }
- if (arguments.length === 2) {
- this.x *= x;
- this.y *= y;
+ for (let i = 0; i < this._values.length; i++) {
+ this._values[i] *= args[0];
}
- if (arguments.length === 3) {
- this.x *= x;
- this.y *= y;
- this.z *= z;
- }
- } else {
- console.warn(
- 'p5.Vector.prototype.mult:',
- 'x, y, or z arguments are either undefined or not a finite number'
- );
}
-
return this;
}
@@ -1135,97 +1274,56 @@ class Vector {
* @param {p5.Vector} v vector to divide the components of the original vector by.
* @chainable
*/
- div(x, y, z) {
- if (x instanceof Vector) {
- // new p5.Vector will check that values are valid upon construction but it's possible
- // that someone could change the value of a component after creation, which is why we still
- // perform this check
+ div(...args) {
+ if (args.length === 0) return this;
+ if (args.length === 1 && args[0] instanceof Vector) {
+ const v = args[0];
if (
- Number.isFinite(x.x) &&
- Number.isFinite(x.y) &&
- Number.isFinite(x.z) &&
- typeof x.x === 'number' &&
- typeof x.y === 'number' &&
- typeof x.z === 'number'
+ v._values.every(
+ (val) => Number.isFinite(val) && typeof val === "number"
+ )
) {
- const isLikely2D = x.z === 0 && this.z === 0;
- if (x.x === 0 || x.y === 0 || (!isLikely2D && x.z === 0)) {
- console.warn('p5.Vector.prototype.div:', 'divide by 0');
+ if (v._values.some((val) => val === 0)) {
+ console.warn("p5.Vector.prototype.div:", "divide by 0");
return this;
}
- this.x /= x.x;
- this.y /= x.y;
- if (!isLikely2D) {
- this.z /= x.z;
- }
+ this._values = this._values.map((val, i) => val / v._values[i]);
} else {
console.warn(
- 'p5.Vector.prototype.div:',
- 'x contains components that are either undefined or not finite numbers'
+ "p5.Vector.prototype.div:",
+ "vector contains components that are either undefined or not finite numbers"
);
}
return this;
}
- if (Array.isArray(x)) {
- if (
- x.every(element => Number.isFinite(element)) &&
- x.every(element => typeof element === 'number')
- ) {
- if (x.some(element => element === 0)) {
- console.warn('p5.Vector.prototype.div:', 'divide by 0');
- return this;
- }
- if (x.length === 1) {
- this.x /= x[0];
- this.y /= x[0];
- this.z /= x[0];
- } else if (x.length === 2) {
- this.x /= x[0];
- this.y /= x[1];
- } else if (x.length === 3) {
- this.x /= x[0];
- this.y /= x[1];
- this.z /= x[2];
+ if (args.length === 1 && Array.isArray(args[0])) {
+ const arr = args[0];
+ if (arr.every((val) => Number.isFinite(val) && typeof val === "number")) {
+ if (arr.some((val) => val === 0)) {
+ console.warn("p5.Vector.prototype.div:", "divide by 0");
+ return this;
}
+ this._values = this._values.map((val, i) => val / arr[i]);
} else {
console.warn(
- 'p5.Vector.prototype.div:',
- 'x contains components that are either undefined or not finite numbers'
+ "p5.Vector.prototype.div:",
+ "array contains components that are either undefined or not finite numbers"
);
}
-
return this;
}
- const vectorComponents = [...arguments];
- if (
- vectorComponents.every(element => Number.isFinite(element)) &&
- vectorComponents.every(element => typeof element === 'number')
- ) {
- if (vectorComponents.some(element => element === 0)) {
- console.warn('p5.Vector.prototype.div:', 'divide by 0');
+ if (args.every((val) => Number.isFinite(val) && typeof val === "number")) {
+ if (args.some((val) => val === 0)) {
+ console.warn("p5.Vector.prototype.div:", "divide by 0");
return this;
}
-
- if (arguments.length === 1) {
- this.x /= x;
- this.y /= x;
- this.z /= x;
- }
- if (arguments.length === 2) {
- this.x /= x;
- this.y /= y;
- }
- if (arguments.length === 3) {
- this.x /= x;
- this.y /= y;
- this.z /= z;
- }
+ this._values = this._values.map((val, i) => val / args[0]);
} else {
console.warn(
- 'p5.Vector.prototype.div:',
- 'x, y, or z arguments are either undefined or not a finite number'
+ "p5.Vector.prototype.div:",
+ "arguments contain components that are either undefined or not finite numbers"
);
}
@@ -1303,10 +1401,10 @@ class Vector {
*
*/
magSq() {
- const x = this.x;
- const y = this.y;
- const z = this.z;
- return x * x + y * y + z * z;
+ return this._values.reduce(
+ (sum, component) => sum + component * component,
+ 0
+ );
}
/**
@@ -1413,11 +1511,13 @@ class Vector {
* @param {p5.Vector} v p5.Vector to be dotted.
* @return {Number}
*/
- dot(x, y, z) {
- if (x instanceof Vector) {
- return this.dot(x.x, x.y, x.z);
+ dot(...args) {
+ if (args[0] instanceof Vector) {
+ return this.dot(...args[0]._values);
}
- return this.x * (x || 0) + this.y * (y || 0) + this.z * (z || 0);
+ return this._values.reduce((sum, component, index) => {
+ return sum + component * (args[index] || 0);
+ }, 0);
}
/**
@@ -1584,10 +1684,7 @@ class Vector {
*
*/
dist(v) {
- return v
- .copy()
- .sub(this)
- .mag();
+ return v.copy().sub(this).mag();
}
/**
@@ -2658,8 +2755,12 @@ class Vector {
*/
slerp(v, amt) {
// edge cases.
- if (amt === 0) { return this; }
- if (amt === 1) { return this.set(v); }
+ if (amt === 0) {
+ return this;
+ }
+ if (amt === 1) {
+ return this.set(v);
+ }
// calculate magnitudes
const selfMag = this.mag();
@@ -2708,7 +2809,7 @@ class Vector {
// Since 'axis' is a unit vector, ey is a vector of the same length as 'this'.
const ey = axis.cross(this);
// interpolate the length with 'this' and 'v'.
- const lerpedMagFactor = (1 - amt) + amt * vMag / selfMag;
+ const lerpedMagFactor = 1 - amt + (amt * vMag) / selfMag;
// imagine a situation where 'axis', 'this', and 'ey' are pointing
// along the z, x, and y axes, respectively.
// rotates 'this' around 'axis' by amt * theta towards 'ey'.
@@ -2928,22 +3029,22 @@ class Vector {
* @param {p5.Vector|Array} value vector to compare.
* @return {Boolean}
*/
- equals(x, y, z) {
- let a, b, c;
- if (x instanceof Vector) {
- a = x.x || 0;
- b = x.y || 0;
- c = x.z || 0;
- } else if (Array.isArray(x)) {
- a = x[0] || 0;
- b = x[1] || 0;
- c = x[2] || 0;
+ equals(...args) {
+ let values;
+ if (args[0] instanceof Vector) {
+ values = args[0]._values;
+ } else if (Array.isArray(args[0])) {
+ values = args[0];
} else {
- a = x || 0;
- b = y || 0;
- c = z || 0;
+ values = args;
}
- return this.x === a && this.y === b && this.z === c;
+
+ for (let i = 0; i < this._values.length; i++) {
+ if (this._values[i] !== (values[i] || 0)) {
+ return false;
+ }
+ }
+ return true;
}
/**
@@ -2960,9 +3061,9 @@ class Vector {
* @chainable
*/
clampToZero() {
- this.x = this._clampToZero(this.x);
- this.y = this._clampToZero(this.y);
- this.z = this._clampToZero(this.z);
+ for (let i = 0; i < this._values.length; i++) {
+ this._values[i] = this._clampToZero(this._values[i]);
+ }
return this;
}
@@ -3047,14 +3148,10 @@ class Vector {
*
*/
static fromAngle(angle, length) {
- if (typeof length === 'undefined') {
+ if (typeof length === "undefined") {
length = 1;
}
- return new Vector(
- length * Math.cos(angle),
- length * Math.sin(angle),
- 0
- );
+ return new Vector(length * Math.cos(angle), length * Math.sin(angle), 0);
}
/**
@@ -3113,7 +3210,7 @@ class Vector {
*
*/
static fromAngles(theta, phi, length) {
- if (typeof length === 'undefined') {
+ if (typeof length === "undefined") {
length = 1;
}
const cosPhi = Math.cos(phi);
@@ -3246,8 +3343,8 @@ class Vector {
target = v1.copy();
if (arguments.length === 3) {
p5._friendlyError(
- 'The target parameter is undefined, it should be of type p5.Vector',
- 'p5.Vector.add'
+ "The target parameter is undefined, it should be of type p5.Vector",
+ "p5.Vector.add"
);
}
} else {
@@ -3293,8 +3390,8 @@ class Vector {
target = v1.copy();
if (arguments.length === 3) {
p5._friendlyError(
- 'The target parameter is undefined, it should be of type p5.Vector',
- 'p5.Vector.sub'
+ "The target parameter is undefined, it should be of type p5.Vector",
+ "p5.Vector.sub"
);
}
} else {
@@ -3337,8 +3434,8 @@ class Vector {
target = v.copy();
if (arguments.length === 3) {
p5._friendlyError(
- 'The target parameter is undefined, it should be of type p5.Vector',
- 'p5.Vector.mult'
+ "The target parameter is undefined, it should be of type p5.Vector",
+ "p5.Vector.mult"
);
}
} else {
@@ -3363,8 +3460,8 @@ class Vector {
} else {
if (!(target instanceof Vector)) {
p5._friendlyError(
- 'The target parameter should be of type p5.Vector',
- 'p5.Vector.rotate'
+ "The target parameter should be of type p5.Vector",
+ "p5.Vector.rotate"
);
}
target.set(v);
@@ -3407,8 +3504,8 @@ class Vector {
if (arguments.length === 3) {
p5._friendlyError(
- 'The target parameter is undefined, it should be of type p5.Vector',
- 'p5.Vector.div'
+ "The target parameter is undefined, it should be of type p5.Vector",
+ "p5.Vector.div"
);
}
} else {
@@ -3445,15 +3542,15 @@ class Vector {
}
/**
- * Calculates the Euclidean distance between two points (considering a
- * point as a vector object).
- */
+ * Calculates the Euclidean distance between two points (considering a
+ * point as a vector object).
+ */
/**
- * @static
- * @param {p5.Vector} v1 The first p5.Vector
- * @param {p5.Vector} v2 The second p5.Vector
- * @return {Number} The distance
- */
+ * @static
+ * @param {p5.Vector} v1 The first p5.Vector
+ * @param {p5.Vector} v2 The second p5.Vector
+ * @return {Number} The distance
+ */
static dist(v1, v2) {
return v1.dist(v2);
}
@@ -3475,8 +3572,8 @@ class Vector {
target = v1.copy();
if (arguments.length === 4) {
p5._friendlyError(
- 'The target parameter is undefined, it should be of type p5.Vector',
- 'p5.Vector.lerp'
+ "The target parameter is undefined, it should be of type p5.Vector",
+ "p5.Vector.lerp"
);
}
} else {
@@ -3505,8 +3602,8 @@ class Vector {
target = v1.copy();
if (arguments.length === 4) {
p5._friendlyError(
- 'The target parameter is undefined, it should be of type p5.Vector',
- 'p5.Vector.slerp'
+ "The target parameter is undefined, it should be of type p5.Vector",
+ "p5.Vector.slerp"
);
}
} else {
@@ -3559,8 +3656,8 @@ class Vector {
} else {
if (!(target instanceof Vector)) {
p5._friendlyError(
- 'The target parameter should be of type p5.Vector',
- 'p5.Vector.normalize'
+ "The target parameter should be of type p5.Vector",
+ "p5.Vector.normalize"
);
}
target.set(v);
@@ -3585,8 +3682,8 @@ class Vector {
} else {
if (!(target instanceof Vector)) {
p5._friendlyError(
- 'The target parameter should be of type p5.Vector',
- 'p5.Vector.limit'
+ "The target parameter should be of type p5.Vector",
+ "p5.Vector.limit"
);
}
target.set(v);
@@ -3611,8 +3708,8 @@ class Vector {
} else {
if (!(target instanceof Vector)) {
p5._friendlyError(
- 'The target parameter should be of type p5.Vector',
- 'p5.Vector.setMag'
+ "The target parameter should be of type p5.Vector",
+ "p5.Vector.setMag"
);
}
target.set(v);
@@ -3667,8 +3764,8 @@ class Vector {
} else {
if (!(target instanceof Vector)) {
p5._friendlyError(
- 'The target parameter should be of type p5.Vector',
- 'p5.Vector.reflect'
+ "The target parameter should be of type p5.Vector",
+ "p5.Vector.reflect"
);
}
target.set(incidentVector);
@@ -3708,8 +3805,8 @@ class Vector {
v = new Vector().set(v1);
} else {
p5._friendlyError(
- 'The v1 parameter should be of type Array or p5.Vector',
- 'p5.Vector.equals'
+ "The v1 parameter should be of type Array or p5.Vector",
+ "p5.Vector.equals"
);
}
return v.equals(v2);
diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js
index 8f8a4f5252..a2af7f3476 100644
--- a/src/webgl/3d_primitives.js
+++ b/src/webgl/3d_primitives.js
@@ -10,7 +10,7 @@ import * as constants from '../core/constants';
import { RendererGL } from './p5.RendererGL';
import { Vector } from '../math/p5.Vector';
import { Geometry } from './p5.Geometry';
-import { Matrix } from './p5.Matrix';
+import { Matrix } from '../math/p5.Matrix';
function primitives3D(p5, fn){
/**
diff --git a/src/webgl/GeometryBuilder.js b/src/webgl/GeometryBuilder.js
index 4dd66c8fdd..9d5783dbf9 100644
--- a/src/webgl/GeometryBuilder.js
+++ b/src/webgl/GeometryBuilder.js
@@ -1,5 +1,5 @@
import * as constants from '../core/constants';
-import { Matrix } from './p5.Matrix';
+import { Matrix } from '../math/p5.Matrix';
import { Geometry } from './p5.Geometry';
/**
@@ -11,8 +11,8 @@ class GeometryBuilder {
constructor(renderer) {
this.renderer = renderer;
renderer._pInst.push();
- this.identityMatrix = new Matrix();
- renderer.states.uModelMatrix = new Matrix();
+ this.identityMatrix = new Matrix(4);
+ renderer.states.uModelMatrix = new Matrix(4);
this.geometry = new Geometry(undefined, undefined, undefined, this.renderer);
this.geometry.gid = `_p5_GeometryBuilder_${GeometryBuilder.nextGeometryId}`;
GeometryBuilder.nextGeometryId++;
@@ -37,7 +37,7 @@ class GeometryBuilder {
if (!this.hasTransform) return normals;
return normals.map(
- v => this.renderer.states.uNMatrix.multiplyVec3(v)
+ v => this.renderer.states.uNMatrix.multiplyVec(v) // this is a vec3
);
}
@@ -51,7 +51,7 @@ class GeometryBuilder {
.every((v, i) => v === this.identityMatrix.mat4[i]);
if (this.hasTransform) {
- this.renderer.states.uNMatrix.inverseTranspose(this.renderer.states.uModelMatrix);
+ this.renderer.states.uNMatrix.inverseTranspose4x4(this.renderer.states.uModelMatrix);
}
let startIdx = this.geometry.vertices.length;
diff --git a/src/webgl/index.js b/src/webgl/index.js
index adcf04631c..c2515fce5a 100644
--- a/src/webgl/index.js
+++ b/src/webgl/index.js
@@ -6,7 +6,7 @@ import material from './material';
import text from './text';
import renderBuffer from './p5.RenderBuffer';
import quat from './p5.Quat';
-import matrix from './p5.Matrix';
+import matrix from '../math/p5.Matrix';
import geometry from './p5.Geometry';
import framebuffer from './p5.Framebuffer';
import dataArray from './p5.DataArray';
diff --git a/src/webgl/p5.Camera.js b/src/webgl/p5.Camera.js
index 7a906bc83c..c85d14f335 100644
--- a/src/webgl/p5.Camera.js
+++ b/src/webgl/p5.Camera.js
@@ -4,7 +4,7 @@
* @requires core
*/
-import { Matrix } from './p5.Matrix';
+import { Matrix } from '../math/p5.Matrix';
import { Vector } from '../math/p5.Vector';
import { Quat } from './p5.Quat';
import { RendererGL } from './p5.RendererGL';
@@ -15,8 +15,8 @@ class Camera {
this.cameraType = 'default';
this.useLinePerspective = true;
- this.cameraMatrix = new Matrix();
- this.projMatrix = new Matrix();
+ this.cameraMatrix = new Matrix(4);
+ this.projMatrix = new Matrix(4);
this.yScale = 1;
}
/**
@@ -1236,7 +1236,7 @@ class Camera {
this.cameraNear = near;
this.cameraFar = far;
- this.projMatrix = Matrix.identity();
+ this.projMatrix = new Matrix(4);
const f = 1.0 / Math.tan(this.cameraFOV / 2);
const nf = 1.0 / (this.cameraNear - this.cameraFar);
@@ -1428,7 +1428,7 @@ class Camera {
const tx = -(right + left) / w;
const ty = -(top + bottom) / h;
const tz = -(far + near) / d;
- this.projMatrix = Matrix.identity();
+ this.projMatrix = new Matrix(4);
/* eslint-disable indent */
this.projMatrix.set(x, 0, 0, 0,
0, -y, 0, 0,
@@ -1564,7 +1564,7 @@ class Camera {
const ty = (top + bottom) / h;
const tz = -(far + near) / d;
- this.projMatrix = Matrix.identity();
+ this.projMatrix = new Matrix(4);
/* eslint-disable indent */
this.projMatrix.set(x, 0, 0, 0,
@@ -1599,8 +1599,8 @@ class Camera {
centerY -= this.eyeY;
centerZ -= this.eyeZ;
- const rotation = Matrix.identity(this._renderer._pInst);
- rotation.rotate(this._renderer._pInst._toRadians(a), x, y, z);
+ const rotation = new Matrix(4); // TODO Maybe pass p5
+ rotation.rotate4x4(this._renderer._pInst._toRadians(a), x, y, z);
/* eslint-disable max-len */
const rotatedCenter = [
@@ -2566,12 +2566,16 @@ class Camera {
// and interpolate the elements of the projection matrix.
// Use logarithmic interpolation for interpolation.
if (this.projMatrix.mat4[15] !== 0) {
- this.projMatrix.mat4[0] =
- cam0.projMatrix.mat4[0] *
- Math.pow(cam1.projMatrix.mat4[0] / cam0.projMatrix.mat4[0], amt);
- this.projMatrix.mat4[5] =
- cam0.projMatrix.mat4[5] *
- Math.pow(cam1.projMatrix.mat4[5] / cam0.projMatrix.mat4[5], amt);
+ this.projMatrix.setElement(
+ 0,
+ cam0.projMatrix.mat4[0] *
+ Math.pow(cam1.projMatrix.mat4[0] / cam0.projMatrix.mat4[0], amt)
+ );
+ this.projMatrix.setElement(
+ 5,
+ cam0.projMatrix.mat4[5] *
+ Math.pow(cam1.projMatrix.mat4[5] / cam0.projMatrix.mat4[5], amt)
+ );
// If the camera is active, make uPMatrix reflect changes in projMatrix.
if (this._isActive()) {
this._renderer.states.uPMatrix.mat4 = this.projMatrix.mat4.slice();
@@ -2640,7 +2644,7 @@ class Camera {
// and multiply it to mat1 from the right.
// This matrix represents the difference between the two.
// 'deltaRot' means 'difference of rotation matrices'.
- const deltaRot = rotMat1.mult3x3(rotMat0.copy().transpose3x3());
+ const deltaRot = rotMat1.mult(rotMat0.copy().transpose()); // mat1 is 3x3
// Calculate the trace and from it the cos value of the angle.
// An orthogonal matrix is just an orthonormal basis. If this is not the identity
@@ -2716,7 +2720,8 @@ class Camera {
const ab = a * b;
const bc = b * c;
const ca = c * a;
- const lerpedRotMat = new Matrix('mat3', [
+ // 3x3
+ const lerpedRotMat = new Matrix( [
cosAngle + oneMinusCosAngle * a * a,
oneMinusCosAngle * ab + sinAngle * c,
oneMinusCosAngle * ca - sinAngle * b,
@@ -2730,12 +2735,12 @@ class Camera {
// Multiply this to mat0 from left to get the interpolated front vector.
// calculate newEye, newCenter with newFront vector.
- lerpedRotMat.multiplyVec3(front0, newFront);
+ lerpedRotMat.multiplyVec(front0, newFront); // this is vec3
newEye.set(newFront).mult(ratio * lerpedDist).add(lerpedMedium);
newCenter.set(newFront).mult((ratio - 1) * lerpedDist).add(lerpedMedium);
- lerpedRotMat.multiplyVec3(up0, newUp);
+ lerpedRotMat.multiplyVec(up0, newUp); // this is vec3
// We also get the up vector in the same way and set the camera.
// The eye position and center position are calculated based on the front vector.
diff --git a/src/webgl/p5.Matrix.js b/src/webgl/p5.Matrix.js
deleted file mode 100644
index b358dd3098..0000000000
--- a/src/webgl/p5.Matrix.js
+++ /dev/null
@@ -1,994 +0,0 @@
-/**
- * @requires constants
- * @todo see methods below needing further implementation.
- * future consideration: implement SIMD optimizations
- * when browser compatibility becomes available
- * https://developer.mozilla.org/en-US/docs/Web/JavaScript/
- * Reference/Global_Objects/SIMD
- */
-
-import { Vector } from '../math/p5.Vector';
-
-let GLMAT_ARRAY_TYPE = Array;
-let isMatrixArray = x => Array.isArray(x);
-if (typeof Float32Array !== 'undefined') {
- GLMAT_ARRAY_TYPE = Float32Array;
- isMatrixArray = x => Array.isArray(x) || x instanceof Float32Array;
-}
-
-class Matrix {
- constructor(...args){
-
- // This is default behavior when object
- // instantiated using createMatrix()
- // @todo implement createMatrix() in core/math.js
- // if (args.length && args[args.length - 1] instanceof p5) {
- // this.p5 = args[args.length - 1];
- // }
-
- if (args[0] === 'mat3') {
- this.mat3 = Array.isArray(args[1])
- ? args[1]
- : new GLMAT_ARRAY_TYPE([1, 0, 0, 0, 1, 0, 0, 0, 1]);
- } else {
- this.mat4 = Array.isArray(args[0])
- ? args[0]
- : new GLMAT_ARRAY_TYPE(
- [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
- }
- return this;
- }
-
- reset() {
- if (this.mat3) {
- this.mat3.set([1, 0, 0, 0, 1, 0, 0, 0, 1]);
- } else if (this.mat4) {
- this.mat4.set([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
- }
- return this;
- }
-
- /**
- * Replace the entire contents of a 4x4 matrix.
- * If providing an array or a p5.Matrix, the values will be copied without
- * referencing the source object.
- * Can also provide 16 numbers as individual arguments.
- *
- * @param {p5.Matrix|Float32Array|Number[]} [inMatrix] the input p5.Matrix 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 Matrix) {
- refArray = inMatrix.mat4;
- } else if (isMatrixArray(inMatrix)) {
- refArray = inMatrix;
- }
- if (refArray.length !== 16) {
- p5._friendlyError(
- `Expected 16 values but received ${refArray.length}.`,
- 'p5.Matrix.set'
- );
- return this;
- }
- for (let i = 0; i < 16; i++) {
- this.mat4[i] = refArray[i];
- }
- return this;
- }
-
- /**
- * Gets a copy of the vector, returns a p5.Matrix object.
- *
- * @return {p5.Matrix} the copy of the p5.Matrix object
- */
- get() {
- return new Matrix(this.mat4, this.p5);
- }
-
- /**
- * 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 {p5.Matrix} the result matrix
- */
- copy() {
- if (this.mat3 !== undefined) {
- const copied3x3 = new Matrix('mat3', this.p5);
- copied3x3.mat3[0] = this.mat3[0];
- copied3x3.mat3[1] = this.mat3[1];
- copied3x3.mat3[2] = this.mat3[2];
- copied3x3.mat3[3] = this.mat3[3];
- copied3x3.mat3[4] = this.mat3[4];
- copied3x3.mat3[5] = this.mat3[5];
- copied3x3.mat3[6] = this.mat3[6];
- copied3x3.mat3[7] = this.mat3[7];
- copied3x3.mat3[8] = this.mat3[8];
- return copied3x3;
- }
- const copied = new Matrix(this.p5);
- copied.mat4[0] = this.mat4[0];
- copied.mat4[1] = this.mat4[1];
- copied.mat4[2] = this.mat4[2];
- copied.mat4[3] = this.mat4[3];
- copied.mat4[4] = this.mat4[4];
- copied.mat4[5] = this.mat4[5];
- copied.mat4[6] = this.mat4[6];
- copied.mat4[7] = this.mat4[7];
- copied.mat4[8] = this.mat4[8];
- copied.mat4[9] = this.mat4[9];
- copied.mat4[10] = this.mat4[10];
- copied.mat4[11] = this.mat4[11];
- copied.mat4[12] = this.mat4[12];
- copied.mat4[13] = this.mat4[13];
- copied.mat4[14] = this.mat4[14];
- copied.mat4[15] = this.mat4[15];
- return copied;
- }
-
- clone() {
- return this.copy();
- }
-
- /**
- * return an identity matrix
- * @return {p5.Matrix} the result matrix
- */
- static identity(pInst){
- return new Matrix(pInst);
- }
-
- /**
- * transpose according to a given matrix
- * @param {p5.Matrix|Float32Array|Number[]} a the matrix to be
- * based on to transpose
- * @chainable
- */
- transpose(a) {
- let a01, a02, a03, a12, a13, a23;
- if (a instanceof Matrix) {
- a01 = a.mat4[1];
- a02 = a.mat4[2];
- a03 = a.mat4[3];
- a12 = a.mat4[6];
- a13 = a.mat4[7];
- a23 = a.mat4[11];
-
- this.mat4[0] = a.mat4[0];
- this.mat4[1] = a.mat4[4];
- this.mat4[2] = a.mat4[8];
- this.mat4[3] = a.mat4[12];
- this.mat4[4] = a01;
- this.mat4[5] = a.mat4[5];
- this.mat4[6] = a.mat4[9];
- this.mat4[7] = a.mat4[13];
- this.mat4[8] = a02;
- this.mat4[9] = a12;
- this.mat4[10] = a.mat4[10];
- this.mat4[11] = a.mat4[14];
- this.mat4[12] = a03;
- this.mat4[13] = a13;
- this.mat4[14] = a23;
- this.mat4[15] = a.mat4[15];
- } else if (isMatrixArray(a)) {
- a01 = a[1];
- a02 = a[2];
- a03 = a[3];
- a12 = a[6];
- a13 = a[7];
- a23 = a[11];
-
- this.mat4[0] = a[0];
- this.mat4[1] = a[4];
- this.mat4[2] = a[8];
- this.mat4[3] = a[12];
- this.mat4[4] = a01;
- this.mat4[5] = a[5];
- this.mat4[6] = a[9];
- this.mat4[7] = a[13];
- this.mat4[8] = a02;
- this.mat4[9] = a12;
- this.mat4[10] = a[10];
- this.mat4[11] = a[14];
- this.mat4[12] = a03;
- this.mat4[13] = a13;
- this.mat4[14] = a23;
- this.mat4[15] = a[15];
- }
- return this;
- }
-
- /**
- * invert matrix according to a give matrix
- * @param {p5.Matrix|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 Matrix) {
- a00 = a.mat4[0];
- a01 = a.mat4[1];
- a02 = a.mat4[2];
- a03 = a.mat4[3];
- a10 = a.mat4[4];
- a11 = a.mat4[5];
- a12 = a.mat4[6];
- a13 = a.mat4[7];
- a20 = a.mat4[8];
- a21 = a.mat4[9];
- a22 = a.mat4[10];
- a23 = a.mat4[11];
- a30 = a.mat4[12];
- a31 = a.mat4[13];
- a32 = a.mat4[14];
- a33 = a.mat4[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[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
- this.mat4[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
- this.mat4[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
- this.mat4[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
- this.mat4[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
- this.mat4[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
- this.mat4[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
- this.mat4[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
- this.mat4[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
- this.mat4[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
- this.mat4[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
- this.mat4[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
- this.mat4[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
- this.mat4[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
- this.mat4[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
- this.mat4[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
-
- return this;
- }
-
- /**
- * Inverts a 3×3 matrix
- * @chainable
- */
- invert3x3() {
- const a00 = this.mat3[0];
- const a01 = this.mat3[1];
- const a02 = this.mat3[2];
- const a10 = this.mat3[3];
- const a11 = this.mat3[4];
- const a12 = this.mat3[5];
- const a20 = this.mat3[6];
- const a21 = this.mat3[7];
- const a22 = this.mat3[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[0] = b01 * det;
- this.mat3[1] = (-a22 * a01 + a02 * a21) * det;
- this.mat3[2] = (a12 * a01 - a02 * a11) * det;
- this.mat3[3] = b11 * det;
- this.mat3[4] = (a22 * a00 - a02 * a20) * det;
- this.mat3[5] = (-a12 * a00 + a02 * a10) * det;
- this.mat3[6] = b21 * det;
- this.mat3[7] = (-a21 * a00 + a01 * a20) * det;
- this.mat3[8] = (a11 * a00 - a01 * a10) * det;
- return this;
- }
-
- /**
- * This function is only for 3x3 matrices.
- * transposes a 3×3 p5.Matrix 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;
- }
- const a01 = mat3[1];
- const a02 = mat3[2];
- const a12 = mat3[5];
- this.mat3[0] = mat3[0];
- this.mat3[1] = mat3[3];
- this.mat3[2] = mat3[6];
- this.mat3[3] = a01;
- this.mat3[4] = mat3[4];
- this.mat3[5] = mat3[7];
- this.mat3[6] = a02;
- this.mat3[7] = a12;
- this.mat3[8] = mat3[8];
-
- return this;
- }
-
- /**
- * converts a 4×4 matrix to its 3×3 inverse transform
- * commonly used in MVMatrix to NMatrix conversions.
- * @param {p5.Matrix} mat4 the matrix to be based on to invert
- * @chainable
- * @todo finish implementation
- */
- inverseTranspose({ mat4 }) {
- if (this.mat3 === undefined) {
- p5._friendlyError('sorry, this function only works with mat3');
- } else {
- //convert mat4 -> mat3
- this.mat3[0] = mat4[0];
- this.mat3[1] = mat4[1];
- this.mat3[2] = mat4[2];
- this.mat3[3] = mat4[4];
- this.mat3[4] = mat4[5];
- this.mat3[5] = mat4[6];
- this.mat3[6] = mat4[8];
- this.mat3[7] = mat4[9];
- this.mat3[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[i] = 0;
- }
- }
- return this;
- }
-
- /**
- * inspired by Toji's mat4 determinant
- * @return {Number} Determinant of our 4×4 matrix
- */
- determinant() {
- const d00 = this.mat4[0] * this.mat4[5] - this.mat4[1] * this.mat4[4],
- d01 = this.mat4[0] * this.mat4[6] - this.mat4[2] * this.mat4[4],
- d02 = this.mat4[0] * this.mat4[7] - this.mat4[3] * this.mat4[4],
- d03 = this.mat4[1] * this.mat4[6] - this.mat4[2] * this.mat4[5],
- d04 = this.mat4[1] * this.mat4[7] - this.mat4[3] * this.mat4[5],
- d05 = this.mat4[2] * this.mat4[7] - this.mat4[3] * this.mat4[6],
- d06 = this.mat4[8] * this.mat4[13] - this.mat4[9] * this.mat4[12],
- d07 = this.mat4[8] * this.mat4[14] - this.mat4[10] * this.mat4[12],
- d08 = this.mat4[8] * this.mat4[15] - this.mat4[11] * this.mat4[12],
- d09 = this.mat4[9] * this.mat4[14] - this.mat4[10] * this.mat4[13],
- d10 = this.mat4[9] * this.mat4[15] - this.mat4[11] * this.mat4[13],
- d11 = this.mat4[10] * this.mat4[15] - this.mat4[11] * this.mat4[14];
-
- // Calculate the determinant
- return d00 * d11 - d01 * d10 + d02 * d09 +
- d03 * d08 - d04 * d07 + d05 * d06;
- }
-
- /**
- * multiply two mat4s
- * @param {p5.Matrix|Float32Array|Number[]} multMatrix The matrix
- * we want to multiply by
- * @chainable
- */
- mult(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 Matrix) {
- _src = multMatrix.mat4;
- } else if (isMatrixArray(multMatrix)) {
- _src = multMatrix;
- } else if (arguments.length === 16) {
- _src = arguments;
- } else {
- return; // nothing to do.
- }
-
- // each row is used for the multiplier
- let b0 = this.mat4[0],
- b1 = this.mat4[1],
- b2 = this.mat4[2],
- b3 = this.mat4[3];
- this.mat4[0] = b0 * _src[0] + b1 * _src[4] + b2 * _src[8] + b3 * _src[12];
- this.mat4[1] = b0 * _src[1] + b1 * _src[5] + b2 * _src[9] + b3 * _src[13];
- this.mat4[2] = b0 * _src[2] + b1 * _src[6] + b2 * _src[10] + b3 * _src[14];
- this.mat4[3] = b0 * _src[3] + b1 * _src[7] + b2 * _src[11] + b3 * _src[15];
-
- b0 = this.mat4[4];
- b1 = this.mat4[5];
- b2 = this.mat4[6];
- b3 = this.mat4[7];
- this.mat4[4] = b0 * _src[0] + b1 * _src[4] + b2 * _src[8] + b3 * _src[12];
- this.mat4[5] = b0 * _src[1] + b1 * _src[5] + b2 * _src[9] + b3 * _src[13];
- this.mat4[6] = b0 * _src[2] + b1 * _src[6] + b2 * _src[10] + b3 * _src[14];
- this.mat4[7] = b0 * _src[3] + b1 * _src[7] + b2 * _src[11] + b3 * _src[15];
-
- b0 = this.mat4[8];
- b1 = this.mat4[9];
- b2 = this.mat4[10];
- b3 = this.mat4[11];
- this.mat4[8] = b0 * _src[0] + b1 * _src[4] + b2 * _src[8] + b3 * _src[12];
- this.mat4[9] = b0 * _src[1] + b1 * _src[5] + b2 * _src[9] + b3 * _src[13];
- this.mat4[10] = b0 * _src[2] + b1 * _src[6] + b2 * _src[10] + b3 * _src[14];
- this.mat4[11] = b0 * _src[3] + b1 * _src[7] + b2 * _src[11] + b3 * _src[15];
-
- b0 = this.mat4[12];
- b1 = this.mat4[13];
- b2 = this.mat4[14];
- b3 = this.mat4[15];
- this.mat4[12] = b0 * _src[0] + b1 * _src[4] + b2 * _src[8] + b3 * _src[12];
- this.mat4[13] = b0 * _src[1] + b1 * _src[5] + b2 * _src[9] + b3 * _src[13];
- this.mat4[14] = b0 * _src[2] + b1 * _src[6] + b2 * _src[10] + b3 * _src[14];
- this.mat4[15] = b0 * _src[3] + b1 * _src[7] + b2 * _src[11] + b3 * _src[15];
-
- 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 Matrix) {
- _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;
-
- // 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;
-
- return this;
- }
-
- /**
- * scales a p5.Matrix by scalars or a vector
- * @param {p5.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[0] *= x;
- this.mat4[1] *= x;
- this.mat4[2] *= x;
- this.mat4[3] *= x;
- this.mat4[4] *= y;
- this.mat4[5] *= y;
- this.mat4[6] *= y;
- this.mat4[7] *= y;
- this.mat4[8] *= z;
- this.mat4[9] *= z;
- this.mat4[10] *= z;
- this.mat4[11] *= z;
-
- return this;
- }
-
- /**
- * rotate our Matrix around an axis by the given angle.
- * @param {Number} a The angle of rotation in radians
- * @param {p5.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 a00 = this.mat4[0];
- const a01 = this.mat4[1];
- const a02 = this.mat4[2];
- const a03 = this.mat4[3];
- const a10 = this.mat4[4];
- const a11 = this.mat4[5];
- const a12 = this.mat4[6];
- const a13 = this.mat4[7];
- const a20 = this.mat4[8];
- const a21 = this.mat4[9];
- const a22 = this.mat4[10];
- const a23 = this.mat4[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[0] = a00 * b00 + a10 * b01 + a20 * b02;
- this.mat4[1] = a01 * b00 + a11 * b01 + a21 * b02;
- this.mat4[2] = a02 * b00 + a12 * b01 + a22 * b02;
- this.mat4[3] = a03 * b00 + a13 * b01 + a23 * b02;
- this.mat4[4] = a00 * b10 + a10 * b11 + a20 * b12;
- this.mat4[5] = a01 * b10 + a11 * b11 + a21 * b12;
- this.mat4[6] = a02 * b10 + a12 * b11 + a22 * b12;
- this.mat4[7] = a03 * b10 + a13 * b11 + a23 * b12;
- this.mat4[8] = a00 * b20 + a10 * b21 + a20 * b22;
- this.mat4[9] = a01 * b20 + a11 * b21 + a21 * b22;
- this.mat4[10] = a02 * b20 + a12 * b21 + a22 * b22;
- this.mat4[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[12] += this.mat4[0] * x + this.mat4[4] * y + this.mat4[8] * z;
- this.mat4[13] += this.mat4[1] * x + this.mat4[5] * y + this.mat4[9] * z;
- this.mat4[14] += this.mat4[2] * x + this.mat4[6] * y + this.mat4[10] * z;
- this.mat4[15] += this.mat4[3] * x + this.mat4[7] * y + this.mat4[11] * z;
- }
-
- 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[0] = f / aspect;
- this.mat4[1] = 0;
- this.mat4[2] = 0;
- this.mat4[3] = 0;
- this.mat4[4] = 0;
- this.mat4[5] = f;
- this.mat4[6] = 0;
- this.mat4[7] = 0;
- this.mat4[8] = 0;
- this.mat4[9] = 0;
- this.mat4[10] = (far + near) * nf;
- this.mat4[11] = -1;
- this.mat4[12] = 0;
- this.mat4[13] = 0;
- this.mat4[14] = 2 * far * near * nf;
- this.mat4[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[0] = -2 * lr;
- this.mat4[1] = 0;
- this.mat4[2] = 0;
- this.mat4[3] = 0;
- this.mat4[4] = 0;
- this.mat4[5] = -2 * bt;
- this.mat4[6] = 0;
- this.mat4[7] = 0;
- this.mat4[8] = 0;
- this.mat4[9] = 0;
- this.mat4[10] = 2 * nf;
- this.mat4[11] = 0;
- this.mat4[12] = (left + right) * lr;
- this.mat4[13] = (top + bottom) * bt;
- this.mat4[14] = (far + near) * nf;
- this.mat4[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[0] * x + m[4] * y + m[8] * z + m[12] * w;
- result[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
- result[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
- result[3] = m[3] * x + m[7] * y + m[11] * z + m[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 {p5.Vector}
- * @return {p5.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 {p5.Vector}
- * @return {p5.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 {p5.Vector}
- * @return {p5.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 p5.Matrix,
- * a Float32Array of length 9, or a javascript array of length 9.
- * In addition, it can also be done by enumerating 9 numbers.
- *
- * @param {p5.Matrix|Float32Array|Number[]} multMatrix The matrix
- * we want to multiply by
- * @chainable
- */
- mult3x3(multMatrix) {
- let _src;
-
- if (multMatrix === this || multMatrix === this.mat3) {
- _src = this.copy().mat3; // only need to allocate in this rare case
- } else if (multMatrix instanceof Matrix) {
- _src = multMatrix.mat3;
- } else if (isMatrixArray(multMatrix)) {
- _src = multMatrix;
- } else if (arguments.length === 9) {
- _src = arguments;
- } else {
- return; // nothing to do.
- }
-
- // each row is used for the multiplier
- let b0 = this.mat3[0];
- let b1 = this.mat3[1];
- let b2 = this.mat3[2];
- this.mat3[0] = b0 * _src[0] + b1 * _src[3] + b2 * _src[6];
- this.mat3[1] = b0 * _src[1] + b1 * _src[4] + b2 * _src[7];
- this.mat3[2] = b0 * _src[2] + b1 * _src[5] + b2 * _src[8];
-
- b0 = this.mat3[3];
- b1 = this.mat3[4];
- b2 = this.mat3[5];
- this.mat3[3] = b0 * _src[0] + b1 * _src[3] + b2 * _src[6];
- this.mat3[4] = b0 * _src[1] + b1 * _src[4] + b2 * _src[7];
- this.mat3[5] = b0 * _src[2] + b1 * _src[5] + b2 * _src[8];
-
- b0 = this.mat3[6];
- b1 = this.mat3[7];
- b2 = this.mat3[8];
- this.mat3[6] = b0 * _src[0] + b1 * _src[3] + b2 * _src[6];
- this.mat3[7] = b0 * _src[1] + b1 * _src[4] + b2 * _src[7];
- this.mat3[8] = b0 * _src[2] + b1 * _src[5] + b2 * _src[8];
-
- 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 {p5.Vector}
- */
- column(columnIndex) {
- return new Vector(
- this.mat3[3 * columnIndex],
- this.mat3[3 * columnIndex + 1],
- this.mat3[3 * columnIndex + 2]
- );
- }
-
- /**
- * 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 {p5.Vector}
- */
- row(rowIndex) {
- return new Vector(
- this.mat3[rowIndex],
- this.mat3[rowIndex + 3],
- this.mat3[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[0], this.mat3[4], this.mat3[8]];
- }
- return [this.mat4[0], this.mat4[5], this.mat4[10], this.mat4[15]];
- }
-
- /**
- * 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 {p5.Vector} multVector the vector to which this matrix applies
- * @param {p5.Vector} [target] The vector to receive the result
- * @return {p5.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 {p5.Matrix}
- */
- createSubMatrix3x3() {
- const result = new Matrix('mat3');
- result.mat3[0] = this.mat4[0];
- result.mat3[1] = this.mat4[1];
- result.mat3[2] = this.mat4[2];
- result.mat3[3] = this.mat4[4];
- result.mat3[4] = this.mat4[5];
- result.mat3[5] = this.mat4[6];
- result.mat3[6] = this.mat4[8];
- result.mat3[7] = this.mat4[9];
- result.mat3[8] = this.mat4[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 p5.Matrix 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
-//];
-};
-
-function matrix(p5, fn){
- /**
- * A class to describe a 4×4 matrix
- * for model and view matrix manipulation in the p5js webgl renderer.
- * @class p5.Matrix
- * @private
- * @param {Array} [mat4] column-major array literal of our 4×4 matrix
- */
- p5.Matrix = Matrix
-}
-
-export default matrix;
-export { Matrix };
-
-if(typeof p5 !== 'undefined'){
- matrix(p5, p5.prototype);
-}
diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js
index 6756ebc62d..c2d97f1a01 100644
--- a/src/webgl/p5.RendererGL.js
+++ b/src/webgl/p5.RendererGL.js
@@ -1,7 +1,7 @@
import * as constants from "../core/constants";
import GeometryBuilder from "./GeometryBuilder";
import { Renderer } from "../core/p5.Renderer";
-import { Matrix } from "./p5.Matrix";
+import { Matrix } from "../math/p5.Matrix";
import { Camera } from "./p5.Camera";
import { Vector } from "../math/p5.Vector";
import { RenderBuffer } from "./p5.RenderBuffer";
@@ -175,12 +175,12 @@ class RendererGL extends Renderer {
this.geometryBuilder = undefined;
// Push/pop state
- this.states.uModelMatrix = new Matrix();
- this.states.uViewMatrix = new Matrix();
- this.states.uMVMatrix = new Matrix();
- this.states.uPMatrix = new Matrix();
- this.states.uNMatrix = new Matrix("mat3");
- this.states.curMatrix = new Matrix("mat3");
+ this.states.uModelMatrix = new Matrix(4);
+ this.states.uViewMatrix = new Matrix(4);
+ this.states.uMVMatrix = new Matrix(4);
+ this.states.uPMatrix = new Matrix(4);
+ this.states.uNMatrix = new Matrix(3);
+ this.states.curMatrix = new Matrix(3);
this.states.curCamera = new Camera(this);
@@ -1609,6 +1609,7 @@ class RendererGL extends Renderer {
applyMatrix(a, b, c, d, e, f) {
if (arguments.length === 16) {
+ // this.states.uModelMatrix.apply(arguments);
Matrix.prototype.apply.apply(this.states.uModelMatrix, arguments);
} else {
this.states.uModelMatrix.apply([
@@ -1668,7 +1669,7 @@ class RendererGL extends Renderer {
if (typeof axis === "undefined") {
return this.rotateZ(rad);
}
- Matrix.prototype.rotate.apply(this.states.uModelMatrix, arguments);
+ Matrix.prototype.rotate4x4.apply(this.states.uModelMatrix, arguments);
return this;
}
@@ -1738,14 +1739,11 @@ class RendererGL extends Renderer {
if (!this.sphereMapping) {
this.sphereMapping = this._pInst.createFilterShader(sphereMapping);
}
- this.states.uNMatrix.inverseTranspose(this.states.uViewMatrix);
- this.states.uNMatrix.invert3x3(this.states.uNMatrix);
+ this.states.uNMatrix.inverseTranspose4x4(this.states.uViewMatrix);
+ this.states.uNMatrix.invert(this.states.uNMatrix); // uNMMatrix is 3x3
this.sphereMapping.setUniform("uFovY", this.states.curCamera.cameraFOV);
this.sphereMapping.setUniform("uAspect", this.states.curCamera.aspectRatio);
- this.sphereMapping.setUniform(
- "uNewNormalMatrix",
- this.states.uNMatrix.mat3,
- );
+ this.sphereMapping.setUniform("uNewNormalMatrix", this.states.uNMatrix.mat3);
this.sphereMapping.setUniform("uSampler", img);
return this.sphereMapping;
}
@@ -2212,11 +2210,11 @@ class RendererGL extends Renderer {
modelViewProjectionMatrix.mat4,
);
if (shader.uniforms.uNormalMatrix) {
- this.states.uNMatrix.inverseTranspose(this.states.uMVMatrix);
+ this.states.uNMatrix.inverseTranspose4x4(this.states.uMVMatrix);
shader.setUniform("uNormalMatrix", this.states.uNMatrix.mat3);
}
if (shader.uniforms.uCameraRotation) {
- this.states.curMatrix.inverseTranspose(this.states.uViewMatrix);
+ this.states.curMatrix.inverseTranspose4x4(this.states.uViewMatrix);
shader.setUniform("uCameraRotation", this.states.curMatrix.mat3);
}
shader.setUniform("uViewport", this._viewport);
diff --git a/test/unit/core/param_errors.js b/test/unit/core/param_errors.js
index 6456ceb6d3..03fe4e5117 100644
--- a/test/unit/core/param_errors.js
+++ b/test/unit/core/param_errors.js
@@ -195,7 +195,7 @@ suite('Validate Params', function () {
});
});
- suite('validateParams: web API objects', function () {
+ suite('validateParams: web API objects', function () { // TODO: fix this p5 error
const audioContext = new AudioContext();
const gainNode = audioContext.createGain();
diff --git a/test/unit/math/p5.Matrix.js b/test/unit/math/p5.Matrix.js
new file mode 100644
index 0000000000..51e684db18
--- /dev/null
+++ b/test/unit/math/p5.Matrix.js
@@ -0,0 +1,528 @@
+import { describe, it, expect, beforeAll, afterAll, test } from "vitest";
+import p5 from "../../../src/app.js";
+
+const toArray = (typedArray) => Array.from(typedArray);
+/* eslint-disable indent */
+var mat4 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
+
+var other = [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16];
+
+var mat3 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+/* eslint-enable indent */
+
+suite("p5.Matrix", function () {
+ var myp5;
+
+ beforeAll(function () {
+ new p5(function (p) {
+ p.setup = function () {
+ myp5 = p;
+ };
+ });
+ });
+
+ afterAll(function () {
+ myp5.remove();
+ });
+
+ suite("construction", function () {
+ test("new p5.Matrix(4)", function () {
+ var m = new p5.Matrix(4);
+ assert.instanceOf(m, p5.Matrix);
+ assert.isUndefined(m.mat3);
+ /* eslint-disable indent */
+ assert.deepEqual(
+ [].slice.call(m.mat4),
+ [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
+ );
+ /* eslint-enable indent */
+ });
+
+ test("new p5.Matrix(array)", function () {
+ var m = new p5.Matrix(mat4);
+ assert.instanceOf(m, p5.Matrix);
+ assert.isUndefined(m.mat3);
+ expect(m.mat4).toEqual(mat4);
+ });
+
+ test("new p5.Matrix(mat3)", function () {
+ var m = new p5.Matrix(mat3);
+ assert.instanceOf(m, p5.Matrix);
+ assert.isUndefined(m.mat4);
+ assert.deepEqual([].slice.call(m.mat3), mat3);
+ });
+
+ test("identity()", function () {
+ var m = new p5.Matrix(4);
+ assert.instanceOf(m, p5.Matrix);
+ assert.isUndefined(m.mat3);
+ /* eslint-disable indent */
+ expect(toArray(m.mat4)).toEqual([
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
+ ]);
+ /* eslint-enable indent */
+ });
+ });
+
+ describe("reset", function () {
+ it("should reset a 4x4 matrix to the identity matrix", function () {
+ const m = new p5.Matrix([
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ ]);
+ m.reset();
+ expect(toArray(m.mat4)).toEqual([
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
+ ]);
+ });
+
+ it("should reset a 3x3 matrix to the identity matrix", function () {
+ const m = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ m.reset();
+ expect(toArray(m.mat3)).toEqual([1, 0, 0, 0, 1, 0, 0, 0, 1]);
+ });
+ });
+
+ suite("set", function () {
+ test("p5.Matrix", function () {
+ var m = new p5.Matrix(4);
+ m.set(new p5.Matrix(mat4));
+ expect(m.mat4).toEqual(mat4);
+ // assert.deepEqual([].slice.call(m.mat4), mat4);
+ });
+
+ test("array", function () {
+ var m = new p5.Matrix(4);
+ m.set(mat4);
+ assert.deepEqual([].slice.call(m.mat4), mat4);
+ });
+
+ test("arguments", function () {
+ var m = new p5.Matrix(4);
+ m.set(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6);
+ expect(m.mat4).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]);
+ });
+ });
+
+ it("should clone a 4x4 matrix correctly", () => {
+ const original = new p5.Matrix([
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ ]);
+ const clone = original.clone();
+
+ expect(clone).not.toBe(original);
+ expect(toArray(clone.mat4)).toEqual(toArray(original.mat4));
+ });
+
+ it("should clone a 3x3 matrix correctly", () => {
+ const original = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ const clone = original.clone();
+
+ expect(clone).not.toBe(original);
+ expect(toArray(clone.mat3)).toEqual(original.mat3);
+ });
+
+ it("should clone an identity matrix correctly", () => {
+ const original = new p5.Matrix(4);
+ const clone = original.clone();
+
+ expect(clone).not.toBe(original);
+ expect(toArray(clone.mat4)).toEqual(toArray(original.mat4));
+ });
+
+ suite("get / copy", function () {
+ test("get", function () {
+ var m = new p5.Matrix(mat4);
+ var m2 = m.get();
+ assert.notEqual(m, m2);
+ expect(m.mat4).toEqual(m2.mat4);
+ });
+ test("copy", function () {
+ var m = new p5.Matrix(mat4);
+ var m2 = m.copy();
+ assert.notEqual(m, m2);
+ assert.notEqual(m.mat4, m2.mat4);
+ assert.deepEqual([].slice.call(m.mat4), [].slice.call(m2.mat4));
+ });
+ });
+
+ suite.todo("add", () => {});
+
+ suite("mult", function () {
+ /* eslint-disable indent */
+ var mm = [
+ 30, 70, 110, 150, 70, 174, 278, 382, 110, 278, 446, 614, 150, 382, 614,
+ 846,
+ ];
+ /* eslint-enable indent */
+
+ test("self", function () {
+ var m = new p5.Matrix(mat4.slice());
+ m.mult(m);
+ /* eslint-disable indent */
+ assert.deepEqual(
+ [].slice.call(m.mat4),
+ [
+ 90, 100, 110, 120, 202, 228, 254, 280, 314, 356, 398, 440, 426, 484,
+ 542, 600,
+ ]
+ );
+ /* eslint-enable indent */
+ });
+
+ test("p5.Matrix", function () {
+ var m1 = new p5.Matrix(mat4.slice());
+ var m2 = new p5.Matrix(other);
+ m1.mult(m2);
+ assert.deepEqual([].slice.call(m1.mat4), mm);
+ });
+
+ test("array", function () {
+ var m = new p5.Matrix(mat4.slice());
+ m.mult(other);
+ assert.deepEqual([].slice.call(m.mat4), mm);
+ });
+
+ test.todo("arguments", function () {
+ var m = new p5.Matrix(mat4.slice());
+ m.mult.apply(m, other);
+ assert.deepEqual([].slice.call(m.mat4), mm);
+ });
+ });
+
+ suite("apply", function () {
+ /* eslint-disable indent */
+ var am = [
+ 276, 304, 332, 360, 304, 336, 368, 400, 332, 368, 404, 440, 360, 400, 440,
+ 480,
+ ];
+ /* eslint-enable indent */
+
+ test("self", function () {
+ var m = new p5.Matrix(mat4.slice());
+ m.apply(m);
+ /* eslint-disable indent */
+ assert.deepEqual(
+ [].slice.call(m.mat4),
+ [
+ 90, 100, 110, 120, 202, 228, 254, 280, 314, 356, 398, 440, 426, 484,
+ 542, 600,
+ ]
+ );
+ /* eslint-enable indent */
+ });
+
+ test("p5.Matrix", function () {
+ var m1 = new p5.Matrix(mat4.slice());
+ var m2 = new p5.Matrix(other);
+ m1.apply(m2);
+ assert.deepEqual([].slice.call(m1.mat4), am);
+ });
+
+ test("array", function () {
+ var m = new p5.Matrix(mat4.slice());
+ m.apply(other);
+ assert.deepEqual([].slice.call(m.mat4), am);
+ });
+
+ test("arguments", function () {
+ var m = new p5.Matrix(mat4.slice());
+ m.apply.apply(m, other);
+ assert.deepEqual([].slice.call(m.mat4), am);
+ });
+ });
+
+ suite("scale", function () {
+ /* eslint-disable indent */
+ var sm = [2, 4, 6, 8, 15, 18, 21, 24, 45, 50, 55, 60, 13, 14, 15, 16];
+ /* eslint-enable indent */
+
+ var mat4 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
+ test("p5.Vector", function () {
+ var m = new p5.Matrix(mat4.slice());
+ var v = myp5.createVector(2, 3, 5);
+ m.scale(v);
+ assert.notEqual(m.mat4, mat4);
+ assert.deepEqual([].slice.call(m.mat4), sm);
+ });
+
+ test("array", function () {
+ var m = new p5.Matrix(mat4.slice());
+ m.scale([2, 3, 5]);
+ assert.notEqual(m.mat4, mat4);
+ assert.deepEqual([].slice.call(m.mat4), sm);
+ });
+
+ test("arguments", function () {
+ var m = new p5.Matrix(mat4.slice());
+ m.scale(2, 3, 5);
+ assert.notEqual(m.mat4, mat4);
+ assert.deepEqual([].slice.call(m.mat4), sm);
+ });
+ });
+
+ suite("rotate", function () {
+ /* eslint-disable max-len */
+ var rm = [
+ 1.433447866601989, 2.5241247073503885, 3.6148015480987885,
+ 4.7054783888471885, 6.460371405020393, 7.054586073938033,
+ 7.648800742855675, 8.243015411773316, 7.950398010346969,
+ 9.157598472697025, 10.36479893504708, 11.571999397397136, 13, 14, 15, 16,
+ ];
+ /* eslint-enable max-len */
+
+ test("p5.Vector", function () {
+ var m = new p5.Matrix(mat4.slice());
+ var v = myp5.createVector(2, 3, 5);
+ m.rotate4x4(45 * myp5.DEG_TO_RAD, v);
+ assert.deepEqual([].slice.call(m.mat4), rm);
+ });
+
+ test("array", function () {
+ var m = new p5.Matrix(mat4.slice());
+ m.rotate4x4(45 * myp5.DEG_TO_RAD, [2, 3, 5]);
+ assert.deepEqual([].slice.call(m.mat4), rm);
+ });
+
+ test("arguments", function () {
+ var m = new p5.Matrix(mat4.slice());
+ m.rotate4x4(45 * myp5.DEG_TO_RAD, 2, 3, 5);
+ assert.deepEqual([].slice.call(m.mat4), rm);
+ });
+ });
+
+ suite("p5.Matrix3x3", function () {
+ test("apply copy() to 3x3Matrix", function () {
+ const m = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ const mCopy = m.copy();
+
+ // The matrix created by copying is different from the original matrix
+ assert.notEqual(m, mCopy);
+ assert.notEqual(m.mat3, mCopy.mat3);
+
+ // The matrix created by copying has the same elements as the original matrix
+ assert.deepEqual([].slice.call(m.mat3), [].slice.call(mCopy.mat3));
+ });
+ test("transpose()", function () {
+ const m = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ const mTp = new p5.Matrix([1, 4, 7, 2, 5, 8, 3, 6, 9]);
+
+ // If no arguments, transpose itself
+ m.transpose();
+ assert.deepEqual([].slice.call(m.mat3), [].slice.call(mTp.mat3));
+
+ // // If there is an array of arguments, set it by transposing it
+ m.transpose([1, 2, 3, 10, 20, 30, 100, 200, 300]);
+ assert.deepEqual(
+ [].slice.call(m.mat3),
+ [1, 10, 100, 2, 20, 200, 3, 30, 300]
+ );
+ });
+
+ test("mult a 3x3 matrix with matrix as argument", function () {
+ const m = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ const multMatrix = new p5.Matrix([1, 1, 1, 0, 1, 1, 1, 0, 1]);
+ // When taking a matrix as an argument
+ m.mult(multMatrix);
+ expect(m.mat3).toEqual([ 4, 3, 6, 10, 9, 15, 16, 15, 24 ]);
+ });
+
+ test("mult a 3x3 matrix with array as argument", function () {
+ const m = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ m.mult([1, 1, 1, 0, 1, 1, 1, 0, 1])
+ expect(m.mat3).toEqual([ 4, 3, 6, 10, 9, 15, 16, 15, 24 ]);
+ });
+
+ test("mult a 3x3 matrix with arguments non array", function () {
+ const m = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ m.mult(1, 1, 1, 0, 1, 1, 1, 0, 1)
+ expect(m.mat3).toEqual([ 4, 3, 6, 10, 9, 15, 16, 15, 24 ]);
+ });
+
+ test("column() and row()", function () {
+ // The matrix data is stored column-major, so each line below is
+ // a column rather than a row. Imagine you are looking at the
+ // transpose of the matrix in the source code.
+ const m = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ const column0 = m.column(0);
+ const column1 = m.column(1);
+ const column2 = m.column(2);
+ expect(column0.array()).toStrictEqual([1, 2, 3]);
+ expect(column1.array()).toStrictEqual([4, 5, 6]);
+ expect(column2.array()).toStrictEqual([7, 8, 9]);
+ const row0 = m.row(0);
+ const row1 = m.row(1);
+ const row2 = m.row(2);
+ expect(row0.array()).toStrictEqual([1, 4, 7]);
+ expect(row1.array()).toStrictEqual([2, 5, 8]);
+ expect(row2.array()).toStrictEqual([3, 6, 9]);
+ });
+ test("diagonal()", function () {
+ const m = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ const m4x4 = new p5.Matrix([
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ ]);
+ assert.deepEqual(m.diagonal(), [1, 5, 9]);
+ assert.deepEqual(m4x4.diagonal(), [1, 6, 11, 16]);
+ });
+ test("multiplyVec version 3", function () {
+ const m = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ const multVector = new p5.Vector(3, 2, 1);
+ const result = m.multiplyVec(multVector);
+ assert.deepEqual(result.array(), [18, 24, 30]);
+ // If there is a target, set result and return that.
+ const target = new p5.Vector();
+ m.multiplyVec(multVector, target);
+ assert.deepEqual(target.array(), [18, 24, 30]);
+ });
+ test("createSubMatrix3x3", function () {
+ const m4x4 = new p5.Matrix([
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ ]);
+ const result = new p5.Matrix([1, 2, 3, 5, 6, 7, 9, 10, 11]);
+ const subMatrix3x3 = m4x4.createSubMatrix3x3();
+ assert.deepEqual(
+ [].slice.call(result.mat3),
+ [].slice.call(subMatrix3x3.mat3)
+ );
+ });
+ });
+
+ ///
+ describe("transpose", () => {
+ it("should transpose a 4x4 matrix correctly", () => {
+ const mat = new p5.Matrix([
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ ]);
+
+ mat.transpose(mat);
+
+ expect(mat.mat4).toEqual([
+ 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16,
+ ]);
+ });
+
+ it("should transpose a 4x4 matrix from an array correctly", () => {
+ const mat = new p5.Matrix([
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ ]);
+
+ mat.transpose([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
+
+ expect(mat.mat4).toEqual([
+ 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16,
+ ]);
+ });
+
+ // TODO: matrix transpose This needs to be added to the legacy tests
+ it.skip("should transpose a 3x3 matrix correctly", () => {
+ const mat = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ mat.transpose(mat);
+ expect(mat.mat3).toEqual([1, 4, 7, 2, 5, 8, 3, 6, 9]);
+ });
+
+ // TODO: matrix transpose This needs to be added to the legacy tests
+ it.skip("should transpose a 3x3 matrix from an array correctly", () => {
+ const mat = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+
+ mat.transpose([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+
+ expect(mat.mat3).toEqual([1, 4, 7, 2, 5, 8, 3, 6, 9]);
+ });
+ });
+ describe.skip("Determinant", () => { // TODO: Cristian, when this is public we'll add tests
+ it("should calculate the determinant of a 4x4 matrix", () => {
+ const mat4 = new p5.Matrix([
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
+ ]);
+ const det = mat4.determinant4x4();
+ expect(det).toBeCloseTo(1);
+ });
+
+ it("should return 0 for a singular 4x4 matrix", () => {
+ const mat4 = new p5.Matrix([
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ ]);
+ const det = mat4.determinant4x4();
+ expect(det).toBeCloseTo(0);
+ });
+ });
+
+ describe("invert", () => {
+ it("should correctly invert a 4x4 matrix", () => {
+ const matrix = new p5.Matrix([
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
+ ]);
+
+ const invertedMatrix = matrix.invert(matrix);
+
+ expect(invertedMatrix.mat4).toEqual([
+ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
+ ]);
+ });
+
+ it("should return null for a non-invertible matrix", () => {
+ const matrix = new p5.Matrix([
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ ]);
+
+ const invertedMatrix = matrix.invert(matrix);
+
+ expect(invertedMatrix).toBeNull();
+ });
+
+ it("should correctly invert a non-identity 4x4 matrix", () => {
+ const matrix = new p5.Matrix([
+ 1, 1, 1, 1, 1, -1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0,
+ ]);
+
+ const invertedMatrix = matrix.invert(matrix);
+
+ expect(invertedMatrix.mat4).toEqual([
+ 0, 0, 0, 1, 0, 0, 1, -1, 0, 1, 1, -2, 1, -1, -2, 2,
+ ]);
+ });
+ });
+ //
+ describe("invert", () => {
+ it("should correctly invert a 3x3 matrix", () => {
+ const matrix = new p5.Matrix([1, 2, 3, 0, 1, 4, 5, 6, 0]);
+ const invertedMatrix = matrix.invert();
+
+ expect(invertedMatrix.mat3).toEqual([-24, 18, 5, 20, -15, -4, -5, 4, 1]);
+ });
+
+ it("should return null for a non-invertible 3x3 matrix", () => {
+ const matrix = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ const invertedMatrix = matrix.invert();
+
+ expect(invertedMatrix).toBeNull();
+ });
+
+ it("should return the identity matrix when inverting the identity matrix", () => {
+ const matrix = new p5.Matrix([1, 0, 0, 0, 1, 0, 0, 0, 1]);
+ const invertedMatrix = matrix.invert();
+
+ expect(invertedMatrix.mat3).toEqual([1, 0, 0, 0, 1, 0, 0, 0, 1]);
+ });
+ });
+
+ describe("mat set element", () => {
+ it("should set element of mat4 matrix", () => {
+ const matrix = new p5.Matrix([
+ 1, 2, 3, 5, 0, 1, 4, 5, 5, 6, 0, 5, 5, 6, 0, 5,
+ ]);
+ const invertedMatrix = matrix.setElement(2, 0);
+
+ expect(invertedMatrix.mat4).toEqual([
+ 1, 2, 0, 5, 0, 1, 4, 5, 5, 6, 0, 5, 5, 6, 0, 5,
+ ]);
+ });
+
+ it("should set element of mat3 matrix", () => {
+ const matrix = new p5.Matrix([1, 2, 3, 0, 1, 4, 5, 6, 0]);
+ const invertedMatrix = matrix.setElement(2, 0);
+
+ expect(invertedMatrix.mat3).toEqual([1, 2, 0, 0, 1, 4, 5, 6, 0]);
+ });
+ });
+});
diff --git a/test/unit/math/p5.Vector.js b/test/unit/math/p5.Vector.js
index 020f7a60b4..a2dc405716 100644
--- a/test/unit/math/p5.Vector.js
+++ b/test/unit/math/p5.Vector.js
@@ -1,124 +1,123 @@
-import vector from '../../../src/math/p5.Vector.js';
-import { vi } from 'vitest';
+import vector from "../../../src/math/p5.Vector.js";
+import { vi } from "vitest";
-suite('p5.Vector', function() {
+suite("p5.Vector", function () {
var v;
const mockP5 = {
- _validateParameters: vi.fn()
+ _validateParameters: vi.fn(),
};
const mockP5Prototype = {};
- beforeEach(async function() {
+ beforeEach(async function () {
vector(mockP5, mockP5Prototype);
});
- afterEach(function() {
- });
+ afterEach(function () {});
- suite.todo('p5.prototype.setHeading() RADIANS', function() {
- beforeEach(function() {
+ suite.todo("p5.prototype.setHeading() RADIANS", function () {
+ beforeEach(function () {
mockP5Prototype.angleMode(mockP5.RADIANS);
v = mockP5Prototype.createVector(1, 1);
v.setHeading(1);
});
- test('should have heading() value of 1 (RADIANS)', function() {
+ test("should have heading() value of 1 (RADIANS)", function () {
assert.closeTo(v.heading(), 1, 0.001);
});
});
- suite.todo('p5.prototype.setHeading() DEGREES', function() {
- beforeEach(function() {
+ suite.todo("p5.prototype.setHeading() DEGREES", function () {
+ beforeEach(function () {
mockP5Prototype.angleMode(mockP5.DEGREES);
v = mockP5Prototype.createVector(1, 1);
v.setHeading(1);
});
- test('should have heading() value of 1 (DEGREES)', function() {
+ test("should have heading() value of 1 (DEGREES)", function () {
assert.closeTo(v.heading(), 1, 0.001);
});
});
// NOTE: test this in a separate file or move `createVector` to p5.Vector file
// Prefer latter
- suite.todo('p5.prototype.createVector()', function() {
- beforeEach(function() {
+ suite.todo("p5.prototype.createVector()", function () {
+ beforeEach(function () {
v = mockP5Prototype.createVector();
});
- test('should create instance of p5.Vector', function() {
+ test("should create instance of p5.Vector", function () {
assert.instanceOf(v, mockP5.Vector);
});
- test('should have x, y, z be initialized to 0', function() {
+ test("should have x, y, z be initialized to 0", function () {
assert.equal(v.x, 0);
assert.equal(v.y, 0);
assert.equal(v.z, 0);
});
});
- suite.todo('p5.prototype.createVector(1, 2, 3)', function() {
- beforeEach(function() {
+ suite.todo("p5.prototype.createVector(1, 2, 3)", function () {
+ beforeEach(function () {
v = mockP5Prototype.createVector(1, 2, 3);
});
- test('should have x, y, z be initialized to 1,2,3', function() {
+ test("should have x, y, z be initialized to 1,2,3", function () {
assert.equal(v.x, 1);
assert.equal(v.y, 2);
assert.equal(v.z, 3);
});
});
- suite('new p5.Vector()', function() {
- beforeEach(function() {
+ suite("new p5.Vector()", function () {
+ beforeEach(function () {
v = new mockP5.Vector();
});
- test('should set constant to DEGREES', function() {
+ test("should set constant to DEGREES", function () {
assert.instanceOf(v, mockP5.Vector);
});
- test('should have x, y, z be initialized to 0', function() {
+ test("should have x, y, z be initialized to 0", function () {
assert.equal(v.x, 0);
assert.equal(v.y, 0);
assert.equal(v.z, 0);
});
});
- suite('new p5.Vector(1, 2, 3)', function() {
- beforeEach(function() {
+ suite("new p5.Vector(1, 2, 3)", function () {
+ beforeEach(function () {
v = new mockP5.Vector(1, 2, 3);
});
- test('should have x, y, z be initialized to 1,2,3', function() {
+ test("should have x, y, z be initialized to 1,2,3", function () {
assert.equal(v.x, 1);
assert.equal(v.y, 2);
assert.equal(v.z, 3);
});
});
- suite('new p5.Vector(1,2,undefined)', function() {
- beforeEach(function() {
+ suite("new p5.Vector(1,2,undefined)", function () {
+ beforeEach(function () {
v = new mockP5.Vector(1, 2, undefined);
});
- test('should have x, y, z be initialized to 1,2,0', function() {
+ test("should have x, y, z be initialized to 1,2,0", function () {
assert.equal(v.x, 1);
assert.equal(v.y, 2);
assert.equal(v.z, 0);
});
});
- suite('rotate', function() {
- suite('p5.Vector.prototype.rotate() [INSTANCE]', function() {
- test('should return the same object', function() {
+ suite("rotate", function () {
+ suite("p5.Vector.prototype.rotate() [INSTANCE]", function () {
+ test("should return the same object", function () {
v = new mockP5.Vector(0, 1);
expect(v.rotate(Math.PI)).to.eql(v);
});
- suite.todo('radians', function() {
- beforeEach(function() {
+ suite.todo("radians", function () {
+ beforeEach(function () {
mockP5Prototype.angleMode(mockP5.RADIANS);
});
- test('should rotate the vector [0, 1, 0] by pi radians to [0, -1, 0]', function() {
+ test("should rotate the vector [0, 1, 0] by pi radians to [0, -1, 0]", function () {
v = mockP5Prototype.createVector(0, 1, 0);
v.rotate(Math.PI);
expect(v.x).to.be.closeTo(0, 0.01);
@@ -126,7 +125,7 @@ suite('p5.Vector', function() {
expect(v.z).to.be.closeTo(0, 0.01);
});
- test('should rotate the vector [1, 0, 0] by -pi/2 radians to [0, -1, 0]', function() {
+ test("should rotate the vector [1, 0, 0] by -pi/2 radians to [0, -1, 0]", function () {
v = mockP5Prototype.createVector(1, 0, 0);
v.rotate(-Math.PI / 2);
expect(v.x).to.be.closeTo(0, 0.01);
@@ -134,7 +133,7 @@ suite('p5.Vector', function() {
expect(v.z).to.be.closeTo(0, 0.01);
});
- test('should rotate the vector [1, 0, 0] by pi radians to [-1, 0, 0]', function() {
+ test("should rotate the vector [1, 0, 0] by pi radians to [-1, 0, 0]", function () {
v = mockP5Prototype.createVector(1, 0, 0);
v.rotate(Math.PI);
expect(v.x).to.be.closeTo(-1, 0.01);
@@ -143,12 +142,12 @@ suite('p5.Vector', function() {
});
});
- suite.todo('degrees', function() {
- beforeEach(function() {
+ suite.todo("degrees", function () {
+ beforeEach(function () {
mockP5Prototype.angleMode(mockP5.DEGREES);
});
- test('should rotate the vector [0, 1, 0] by 180 degrees to [0, -1, 0]', function() {
+ test("should rotate the vector [0, 1, 0] by 180 degrees to [0, -1, 0]", function () {
v = mockP5Prototype.createVector(0, 1, 0);
v.rotate(180);
expect(v.x).to.be.closeTo(0, 0.01);
@@ -156,7 +155,7 @@ suite('p5.Vector', function() {
expect(v.z).to.be.closeTo(0, 0.01);
});
- test('should rotate the vector [1, 0, 0] by -90 degrees to [0, -1, 0]', function() {
+ test("should rotate the vector [1, 0, 0] by -90 degrees to [0, -1, 0]", function () {
v = mockP5Prototype.createVector(1, 0, 0);
v.rotate(-90);
expect(v.x).to.be.closeTo(0, 0.01);
@@ -166,12 +165,12 @@ suite('p5.Vector', function() {
});
});
- suite.todo('p5.Vector.rotate() [CLASS]', function() {
- beforeEach(function() {
+ suite.todo("p5.Vector.rotate() [CLASS]", function () {
+ beforeEach(function () {
mockP5Prototype.angleMode(mockP5.RADIANS);
});
- test('should not change the original object', function() {
+ test("should not change the original object", function () {
v = mockP5Prototype.createVector(1, 0, 0);
mockP5.Vector.rotate(v, Math.PI / 2);
expect(v.x).to.equal(1);
@@ -179,7 +178,7 @@ suite('p5.Vector', function() {
expect(v.z).to.equal(0);
});
- test('should rotate the vector [0, 1, 0] by pi radians to [0, -1, 0]', function() {
+ test("should rotate the vector [0, 1, 0] by pi radians to [0, -1, 0]", function () {
v = mockP5Prototype.createVector(0, 1, 0);
const v1 = mockP5.Vector.rotate(v, Math.PI);
expect(v1.x).to.be.closeTo(0, 0.01);
@@ -187,7 +186,7 @@ suite('p5.Vector', function() {
expect(v1.z).to.be.closeTo(0, 0.01);
});
- test('should rotate the vector [1, 0, 0] by -pi/2 radians to [0, -1, 0]', function() {
+ test("should rotate the vector [1, 0, 0] by -pi/2 radians to [0, -1, 0]", function () {
v = mockP5Prototype.createVector(1, 0, 0);
const v1 = mockP5.Vector.rotate(v, -Math.PI / 2);
expect(v1.x).to.be.closeTo(0, 0.01);
@@ -197,20 +196,20 @@ suite('p5.Vector', function() {
});
});
- suite('angleBetween', function() {
+ suite("angleBetween", function () {
let v1, v2;
- beforeEach(function() {
+ beforeEach(function () {
v1 = new mockP5.Vector(1, 0, 0);
v2 = new mockP5.Vector(2, 2, 0);
});
- suite('p5.Vector.prototype.angleBetween() [INSTANCE]', function() {
- test('should return a Number', function() {
+ suite("p5.Vector.prototype.angleBetween() [INSTANCE]", function () {
+ test("should return a Number", function () {
const res = v1.angleBetween(v2);
- expect(typeof res).to.eql('number');
+ expect(typeof res).to.eql("number");
});
- test('should not trip on rounding issues in 2D space', function() {
+ test("should not trip on rounding issues in 2D space", function () {
v1 = new mockP5.Vector(-11, -20);
v2 = new mockP5.Vector(-5.5, -10);
const v3 = new mockP5.Vector(5.5, 10);
@@ -219,47 +218,50 @@ suite('p5.Vector', function() {
expect(v1.angleBetween(v3)).to.be.closeTo(Math.PI, 0.00001);
});
- test('should not trip on rounding issues in 3D space', function() {
+ test("should not trip on rounding issues in 3D space", function () {
v1 = new mockP5.Vector(1, 1.1, 1.2);
v2 = new mockP5.Vector(2, 2.2, 2.4);
expect(v1.angleBetween(v2)).to.be.closeTo(0, 0.00001);
});
- test('should return NaN for zero vector', function() {
+ test("should return NaN for zero vector", function () {
v1 = new mockP5.Vector(0, 0, 0);
v2 = new mockP5.Vector(2, 3, 4);
expect(v1.angleBetween(v2)).to.be.NaN;
expect(v2.angleBetween(v1)).to.be.NaN;
});
- test.todo('between [1,0,0] and [1,0,0] should be 0 degrees', function() {
+ test.todo("between [1,0,0] and [1,0,0] should be 0 degrees", function () {
mockP5Prototype.angleMode(mockP5.DEGREES);
v1 = new mockP5.Vector(1, 0, 0);
v2 = new mockP5.Vector(1, 0, 0);
expect(v1.angleBetween(v2)).to.equal(0);
});
- test.todo('between [0,3,0] and [0,-3,0] should be 180 degrees', function() {
- mockP5Prototype.angleMode(mockP5.DEGREES);
- v1 = new mockP5.Vector(0, 3, 0);
- v2 = new mockP5.Vector(0, -3, 0);
- expect(v1.angleBetween(v2)).to.be.closeTo(180, 0.01);
- });
+ test.todo(
+ "between [0,3,0] and [0,-3,0] should be 180 degrees",
+ function () {
+ mockP5Prototype.angleMode(mockP5.DEGREES);
+ v1 = new mockP5.Vector(0, 3, 0);
+ v2 = new mockP5.Vector(0, -3, 0);
+ expect(v1.angleBetween(v2)).to.be.closeTo(180, 0.01);
+ }
+ );
- test('between [1,0,0] and [2,2,0] should be 1/4 PI radians', function() {
+ test("between [1,0,0] and [2,2,0] should be 1/4 PI radians", function () {
v1 = new mockP5.Vector(1, 0, 0);
v2 = new mockP5.Vector(2, 2, 0);
expect(v1.angleBetween(v2)).to.be.closeTo(Math.PI / 4, 0.01);
- expect(v2.angleBetween(v1)).to.be.closeTo(-1 * Math.PI / 4, 0.01);
+ expect(v2.angleBetween(v1)).to.be.closeTo((-1 * Math.PI) / 4, 0.01);
});
- test('between [2,0,0] and [-2,0,0] should be PI radians', function() {
+ test("between [2,0,0] and [-2,0,0] should be PI radians", function () {
v1 = new mockP5.Vector(2, 0, 0);
v2 = new mockP5.Vector(-2, 0, 0);
expect(v1.angleBetween(v2)).to.be.closeTo(Math.PI, 0.01);
});
- test('between [2,0,0] and [-2,-2,0] should be -3/4 PI radians ', function() {
+ test("between [2,0,0] and [-2,-2,0] should be -3/4 PI radians ", function () {
v1 = new mockP5.Vector(2, 0, 0);
v2 = new mockP5.Vector(-2, -2, 0);
expect(v1.angleBetween(v2)).to.be.closeTo(
@@ -268,7 +270,7 @@ suite('p5.Vector', function() {
);
});
- test('between [-2,-2,0] and [2,0,0] should be 3/4 PI radians', function() {
+ test("between [-2,-2,0] and [2,0,0] should be 3/4 PI radians", function () {
v1 = new mockP5.Vector(-2, -2, 0);
v2 = new mockP5.Vector(2, 0, 0);
expect(v1.angleBetween(v2)).to.be.closeTo(
@@ -277,35 +279,38 @@ suite('p5.Vector', function() {
);
});
- test('For the same vectors, the angle between them should always be 0.', function() {
+ test("For the same vectors, the angle between them should always be 0.", function () {
v1 = new mockP5.Vector(288, 814);
v2 = new mockP5.Vector(288, 814);
expect(v1.angleBetween(v2)).to.equal(0);
});
- test('The angle between vectors pointing in opposite is always PI.', function() {
+ test("The angle between vectors pointing in opposite is always PI.", function () {
v1 = new mockP5.Vector(219, 560);
v2 = new mockP5.Vector(-219, -560);
expect(v1.angleBetween(v2)).to.be.closeTo(Math.PI, 0.0000001);
});
});
- suite('p5.Vector.angleBetween() [CLASS]', function() {
- test('should return NaN for zero vector', function() {
+ suite("p5.Vector.angleBetween() [CLASS]", function () {
+ test("should return NaN for zero vector", function () {
v1 = new mockP5.Vector(0, 0, 0);
v2 = new mockP5.Vector(2, 3, 4);
expect(mockP5.Vector.angleBetween(v1, v2)).to.be.NaN;
expect(mockP5.Vector.angleBetween(v2, v1)).to.be.NaN;
});
- test.todo('between [1,0,0] and [0,-1,0] should be -90 degrees', function() {
- mockP5Prototype.angleMode(mockP5.DEGREES);
- v1 = new mockP5.Vector(1, 0, 0);
- v2 = new mockP5.Vector(0, -1, 0);
- expect(mockP5.Vector.angleBetween(v1, v2)).to.be.closeTo(-90, 0.01);
- });
+ test.todo(
+ "between [1,0,0] and [0,-1,0] should be -90 degrees",
+ function () {
+ mockP5Prototype.angleMode(mockP5.DEGREES);
+ v1 = new mockP5.Vector(1, 0, 0);
+ v2 = new mockP5.Vector(0, -1, 0);
+ expect(mockP5.Vector.angleBetween(v1, v2)).to.be.closeTo(-90, 0.01);
+ }
+ );
- test('between [0,3,0] and [0,-3,0] should be PI radians', function() {
+ test("between [0,3,0] and [0,-3,0] should be PI radians", function () {
v1 = new mockP5.Vector(0, 3, 0);
v2 = new mockP5.Vector(0, -3, 0);
expect(mockP5.Vector.angleBetween(v1, v2)).to.be.closeTo(Math.PI, 0.01);
@@ -313,9 +318,9 @@ suite('p5.Vector', function() {
});
});
- suite('set()', function() {
- suite('with p5.Vector', function() {
- test("should have x, y, z be initialized to the vector's x, y, z", function() {
+ suite("set()", function () {
+ suite("with p5.Vector", function () {
+ test("should have x, y, z be initialized to the vector's x, y, z", function () {
v.set(new mockP5.Vector(2, 5, 6));
expect(v.x).to.eql(2);
expect(v.y).to.eql(5);
@@ -323,15 +328,15 @@ suite('p5.Vector', function() {
});
});
- suite('with Array', function() {
- test('[2,4] should set x === 2, y === 4, z === 0', function() {
+ suite("with Array", function () {
+ test("[2,4] should set x === 2, y === 4, z === 0", function () {
v.set([2, 4]);
expect(v.x).to.eql(2);
expect(v.y).to.eql(4);
expect(v.z).to.eql(0);
});
- test("should have x, y, z be initialized to the array's 0,1,2 index", function() {
+ test("should have x, y, z be initialized to the array's 0,1,2 index", function () {
v.set([2, 5, 6]);
expect(v.x).to.eql(2);
expect(v.y).to.eql(5);
@@ -339,8 +344,8 @@ suite('p5.Vector', function() {
});
});
- suite('set(1,2,3)', function() {
- test('should have x, y, z be initialized to the 1, 2, 3', function() {
+ suite("set(1,2,3)", function () {
+ test("should have x, y, z be initialized to the 1, 2, 3", function () {
v.set(1, 2, 3);
expect(v.x).to.eql(1);
expect(v.y).to.eql(2);
@@ -349,18 +354,18 @@ suite('p5.Vector', function() {
});
});
- suite('copy', function() {
- beforeEach(function() {
+ suite("copy", function () {
+ beforeEach(function () {
v = new mockP5.Vector(2, 3, 4);
});
- suite('p5.Vector.prototype.copy() [INSTANCE]', function() {
- test('should not return the same instance', function() {
+ suite("p5.Vector.prototype.copy() [INSTANCE]", function () {
+ test("should not return the same instance", function () {
var newObject = v.copy();
expect(newObject).to.not.equal(v);
});
- test("should return the calling object's x, y, z", function() {
+ test("should return the calling object's x, y, z", function () {
var newObject = v.copy();
expect(newObject.x).to.eql(2);
expect(newObject.y).to.eql(3);
@@ -368,13 +373,13 @@ suite('p5.Vector', function() {
});
});
- suite('p5.Vector.copy() [CLASS]', function() {
- test('should not return the same instance', function() {
+ suite("p5.Vector.copy() [CLASS]", function () {
+ test("should not return the same instance", function () {
var newObject = mockP5.Vector.copy(v);
expect(newObject).to.not.equal(v);
});
- test("should return the passed object's x, y, z", function() {
+ test("should return the passed object's x, y, z", function () {
var newObject = mockP5.Vector.copy(v);
expect(newObject.x).to.eql(2);
expect(newObject.y).to.eql(3);
@@ -383,13 +388,13 @@ suite('p5.Vector', function() {
});
});
- suite('add()', function() {
- beforeEach(function() {
+ suite("add()", function () {
+ beforeEach(function () {
v = new mockP5.Vector();
});
- suite('with p5.Vector', function() {
- test('should add x, y, z from the vector argument', function() {
+ suite("with p5.Vector", function () {
+ test("should add x, y, z from the vector argument", function () {
v.add(new mockP5.Vector(1, 5, 6));
expect(v.x).to.eql(1);
expect(v.y).to.eql(5);
@@ -397,9 +402,9 @@ suite('p5.Vector', function() {
});
});
- suite('with Array', function() {
- suite('add([2, 4])', function() {
- test('should add the x and y components', function() {
+ suite("with Array", function () {
+ suite("add([2, 4])", function () {
+ test("should add the x and y components", function () {
v.add([2, 4]);
expect(v.x).to.eql(2);
expect(v.y).to.eql(4);
@@ -407,7 +412,7 @@ suite('p5.Vector', function() {
});
});
- test("should add the array's 0,1,2 index", function() {
+ test("should add the array's 0,1,2 index", function () {
v.add([2, 5, 6]);
expect(v.x).to.eql(2);
expect(v.y).to.eql(5);
@@ -415,8 +420,8 @@ suite('p5.Vector', function() {
});
});
- suite('add(3,5)', function() {
- test('should add the x and y components', function() {
+ suite("add(3,5)", function () {
+ test("should add the x and y components", function () {
v.add(3, 5);
expect(v.x).to.eql(3);
expect(v.y).to.eql(5);
@@ -424,8 +429,8 @@ suite('p5.Vector', function() {
});
});
- suite('add(2,3,4)', function() {
- test('should add the x, y, z components', function() {
+ suite("add(2,3,4)", function () {
+ test("should add the x, y, z components", function () {
v.add(5, 5, 5);
expect(v.x).to.eql(5);
expect(v.y).to.eql(5);
@@ -433,20 +438,28 @@ suite('p5.Vector', function() {
});
});
- suite('p5.Vector.add(v1, v2)', function() {
+ suite("add(2,3,4)", function () {
+ test("should add the x, y, z components", function () {
+ v.add([1, 2, 3]);
+ expect(v.x).to.eql(1);
+ expect(v.y).to.eql(2);
+ });
+ });
+
+ suite("p5.Vector.add(v1, v2)", function () {
var v1, v2, res;
- beforeEach(function() {
+ beforeEach(function () {
v1 = new mockP5.Vector(2, 0, 3);
v2 = new mockP5.Vector(0, 1, 3);
res = mockP5.Vector.add(v1, v2);
});
- test('should return neither v1 nor v2', function() {
+ test("should return neither v1 nor v2", function () {
expect(res).to.not.eql(v1);
expect(res).to.not.eql(v2);
});
- test('should be sum of the two p5.Vectors', function() {
+ test("should be sum of the two p5.Vectors", function () {
expect(res.x).to.eql(v1.x + v2.x);
expect(res.y).to.eql(v1.y + v2.y);
expect(res.z).to.eql(v1.z + v2.z);
@@ -454,62 +467,62 @@ suite('p5.Vector', function() {
});
});
- suite('rem()', function() {
- beforeEach(function() {
+ suite("rem()", function () {
+ beforeEach(function () {
v = new mockP5.Vector(3, 4, 5);
});
- test('should give same vector if nothing passed as parameter', function() {
+ test("should give same vector if nothing passed as parameter", function () {
v.rem();
expect(v.x).to.eql(3);
expect(v.y).to.eql(4);
expect(v.z).to.eql(5);
});
- test('should give correct output if passed only one numeric value', function() {
+ test("should give correct output if passed only one numeric value", function () {
v.rem(2);
expect(v.x).to.eql(1);
expect(v.y).to.eql(0);
expect(v.z).to.eql(1);
});
- test('should give correct output if passed two numeric value', function() {
+ test("should give correct output if passed two numeric value", function () {
v.rem(2, 3);
expect(v.x).to.eql(1);
expect(v.y).to.eql(1);
expect(v.z).to.eql(5);
});
- test('should give correct output if passed three numeric value', function() {
+ test("should give correct output if passed three numeric value", function () {
v.rem(2, 3, 4);
expect(v.x).to.eql(1);
expect(v.y).to.eql(1);
expect(v.z).to.eql(1);
});
- suite('with p5.Vector', function() {
- test('should return correct output if only one component is non-zero', function() {
+ suite("with p5.Vector", function () {
+ test("should return correct output if only one component is non-zero", function () {
v.rem(new mockP5.Vector(0, 0, 4));
expect(v.x).to.eql(3);
expect(v.y).to.eql(4);
expect(v.z).to.eql(1);
});
- test('should return correct output if x component is zero', () => {
+ test("should return correct output if x component is zero", () => {
v.rem(new mockP5.Vector(0, 3, 4));
expect(v.x).to.eql(3);
expect(v.y).to.eql(1);
expect(v.z).to.eql(1);
});
- test('should return correct output if all components are non-zero', () => {
+ test("should return correct output if all components are non-zero", () => {
v.rem(new mockP5.Vector(2, 3, 4));
expect(v.x).to.eql(1);
expect(v.y).to.eql(1);
expect(v.z).to.eql(1);
});
- test('should return same vector if all components are zero', () => {
+ test("should return same vector if all components are zero", () => {
v.rem(new mockP5.Vector(0, 0, 0));
expect(v.x).to.eql(3);
expect(v.y).to.eql(4);
@@ -517,12 +530,12 @@ suite('p5.Vector', function() {
});
});
- suite('with negative vectors', function() {
+ suite("with negative vectors", function () {
let v;
- beforeEach(function() {
+ beforeEach(function () {
v = new mockP5.Vector(-15, -5, -2);
});
- test('should return correct output', () => {
+ test("should return correct output", () => {
v.rem(new mockP5.Vector(2, 3, 3));
expect(v.x).to.eql(-1);
expect(v.y).to.eql(-2);
@@ -530,28 +543,28 @@ suite('p5.Vector', function() {
});
});
- suite('with Arrays', function() {
- test('should return remainder of vector components for 3D vector', function() {
+ suite("with Arrays", function () {
+ test("should return remainder of vector components for 3D vector", function () {
v.rem([2, 3, 0]);
expect(v.x).to.eql(1);
expect(v.y).to.eql(1);
expect(v.z).to.eql(5);
});
- test('should return remainder of vector components for 2D vector', function() {
+ test("should return remainder of vector components for 2D vector", function () {
v.rem([2, 3]);
expect(v.x).to.eql(1);
expect(v.y).to.eql(1);
expect(v.z).to.eql(5);
});
- test('should return correct output if x,y components are zero for 2D vector', () => {
+ test("should return correct output if x,y components are zero for 2D vector", () => {
v.rem([0, 0]);
expect(v.x).to.eql(3);
expect(v.y).to.eql(4);
expect(v.z).to.eql(5);
});
- test('should return same vector if any vector component is non-finite number', () => {
+ test("should return same vector if any vector component is non-finite number", () => {
v.rem([2, 3, Infinity]);
expect(v.x).to.eql(3);
expect(v.y).to.eql(4);
@@ -559,20 +572,20 @@ suite('p5.Vector', function() {
});
});
- suite('p5.Vector.rem(v1,v2)', function() {
+ suite("p5.Vector.rem(v1,v2)", function () {
let v1, v2, res;
- beforeEach(function() {
+ beforeEach(function () {
v1 = new mockP5.Vector(2, 3, 4);
v2 = new mockP5.Vector(1, 2, 3);
res = mockP5.Vector.rem(v1, v2);
});
- test('should return neither v1 nor v2', function() {
+ test("should return neither v1 nor v2", function () {
expect(res).to.not.eql(v1);
expect(res).to.not.eql(v2);
});
- test('should be v1 % v2', function() {
+ test("should be v1 % v2", function () {
expect(res.x).to.eql(v1.x % v2.x);
expect(res.y).to.eql(v1.y % v2.y);
expect(res.z).to.eql(v1.z % v2.z);
@@ -580,14 +593,14 @@ suite('p5.Vector', function() {
});
});
- suite('sub()', function() {
- beforeEach(function() {
+ suite("sub()", function () {
+ beforeEach(function () {
v.x = 0;
v.y = 0;
v.z = 0;
});
- suite('with p5.Vector', function() {
- test('should sub x, y, z from the vector argument', function() {
+ suite("with p5.Vector", function () {
+ test("should sub x, y, z from the vector argument", function () {
v.sub(new mockP5.Vector(2, 5, 6));
expect(v.x).to.eql(-2);
expect(v.y).to.eql(-5);
@@ -595,9 +608,9 @@ suite('p5.Vector', function() {
});
});
- suite('with Array', function() {
- suite('sub([2, 4])', function() {
- test('should sub the x and y components', function() {
+ suite("with Array", function () {
+ suite("sub([2, 4])", function () {
+ test("should sub the x and y components", function () {
v.sub([2, 4]);
expect(v.x).to.eql(-2);
expect(v.y).to.eql(-4);
@@ -605,7 +618,7 @@ suite('p5.Vector', function() {
});
});
- test("should subtract from the array's 0,1,2 index", function() {
+ test("should subtract from the array's 0,1,2 index", function () {
v.sub([2, 5, 6]);
expect(v.x).to.eql(-2);
expect(v.y).to.eql(-5);
@@ -613,8 +626,8 @@ suite('p5.Vector', function() {
});
});
- suite('sub(3,5)', function() {
- test('should subtract the x and y components', function() {
+ suite("sub(3,5)", function () {
+ test("should subtract the x and y components", function () {
v.sub(3, 5);
expect(v.x).to.eql(-3);
expect(v.y).to.eql(-5);
@@ -622,8 +635,8 @@ suite('p5.Vector', function() {
});
});
- suite('sub(2,3,4)', function() {
- test('should subtract the x, y, z components', function() {
+ suite("sub(2,3,4)", function () {
+ test("should subtract the x, y, z components", function () {
v.sub(5, 5, 5);
expect(v.x).to.eql(-5);
expect(v.y).to.eql(-5);
@@ -631,20 +644,20 @@ suite('p5.Vector', function() {
});
});
- suite('p5.Vector.sub(v1, v2)', function() {
+ suite("p5.Vector.sub(v1, v2)", function () {
var v1, v2, res;
- beforeEach(function() {
+ beforeEach(function () {
v1 = new mockP5.Vector(2, 0, 3);
v2 = new mockP5.Vector(0, 1, 3);
res = mockP5.Vector.sub(v1, v2);
});
- test('should return neither v1 nor v2', function() {
+ test("should return neither v1 nor v2", function () {
expect(res).to.not.eql(v1);
expect(res).to.not.eql(v2);
});
- test('should be v1 - v2', function() {
+ test("should be v1 - v2", function () {
expect(res.x).to.eql(v1.x - v2.x);
expect(res.y).to.eql(v1.y - v2.y);
expect(res.z).to.eql(v1.z - v2.z);
@@ -652,31 +665,31 @@ suite('p5.Vector', function() {
});
});
- suite('mult()', function() {
- beforeEach(function() {
+ suite("mult()", function () {
+ beforeEach(function () {
v = new mockP5.Vector(1, 1, 1);
});
- test('should return the same object', function() {
+ test("should return the same object", function () {
expect(v.mult(1)).to.eql(v);
});
- test('should not change x, y, z if no argument is given', function() {
+ test("should not change x, y, z if no argument is given", function () {
v.mult();
expect(v.x).to.eql(1);
expect(v.y).to.eql(1);
expect(v.z).to.eql(1);
});
- test('should not change x, y, z if n is not a finite number', function() {
+ test("should not change x, y, z if n is not a finite number", function () {
v.mult(NaN);
expect(v.x).to.eql(1);
expect(v.y).to.eql(1);
expect(v.z).to.eql(1);
});
- suite('with scalar', function() {
- test('multiply the x, y, z with the scalar', function() {
+ suite("with scalar", function () {
+ test("multiply the x, y, z with the scalar", function () {
v.mult(2);
expect(v.x).to.eql(2);
expect(v.y).to.eql(2);
@@ -684,78 +697,78 @@ suite('p5.Vector', function() {
});
});
- suite('v0.mult(v1)', function() {
+ suite("v0.mult(v1)", function () {
var v0, v1;
- beforeEach(function() {
+ beforeEach(function () {
v0 = new mockP5.Vector(1, 2, 3);
v1 = new mockP5.Vector(2, 3, 4);
v0.mult(v1);
});
- test('should do component wise multiplication', function() {
+ test("should do component wise multiplication", function () {
expect(v0.x).to.eql(2);
expect(v0.y).to.eql(6);
expect(v0.z).to.eql(12);
});
});
- suite('v0.mult(arr)', function() {
+ suite("v0.mult(arr)", function () {
var v0, arr;
- beforeEach(function() {
+ beforeEach(function () {
v0 = new mockP5.Vector(1, 2, 3);
arr = [2, 3, 4];
v0.mult(arr);
});
- test('should do component wise multiplication from an array', function() {
+ test("should do component wise multiplication from an array", function () {
expect(v0.x).to.eql(2);
expect(v0.y).to.eql(6);
expect(v0.z).to.eql(12);
});
});
- suite('p5.Vector.mult(v, n)', function() {
+ suite("p5.Vector.mult(v, n)", function () {
var v, res;
- beforeEach(function() {
+ beforeEach(function () {
v = new mockP5.Vector(1, 2, 3);
res = mockP5.Vector.mult(v, 4);
});
- test('should return a new p5.Vector', function() {
+ test("should return a new p5.Vector", function () {
expect(res).to.not.eql(v);
});
- test('should multiply the scalar', function() {
+ test("should multiply the scalar", function () {
expect(res.x).to.eql(4);
expect(res.y).to.eql(8);
expect(res.z).to.eql(12);
});
});
- suite('p5.Vector.mult(v, v', function() {
+ suite("p5.Vector.mult(v, v", function () {
var v0, v1, res;
- beforeEach(function() {
+ beforeEach(function () {
v0 = new mockP5.Vector(1, 2, 3);
v1 = new mockP5.Vector(2, 3, 4);
res = mockP5.Vector.mult(v0, v1);
});
- test('should return new vector from component wise multiplication', function() {
+ test("should return new vector from component wise multiplication", function () {
expect(res.x).to.eql(2);
expect(res.y).to.eql(6);
expect(res.z).to.eql(12);
});
});
- suite('p5.Vector.mult(v, arr', function() {
+ suite("p5.Vector.mult(v, arr", function () {
var v0, arr, res;
- beforeEach(function() {
+ beforeEach(function () {
v0 = new mockP5.Vector(1, 2, 3);
arr = [2, 3, 4];
res = mockP5.Vector.mult(v0, arr);
});
- test('should return new vector from component wise multiplication with an array', function() {
+ test("should return new vector from component wise multiplication with an array", function () {
expect(res.x).to.eql(2);
expect(res.y).to.eql(6);
expect(res.z).to.eql(12);
@@ -763,38 +776,38 @@ suite('p5.Vector', function() {
});
});
- suite('div()', function() {
- beforeEach(function() {
+ suite("div()", function () {
+ beforeEach(function () {
v = new mockP5.Vector(1, 1, 1);
});
- test('should return the same object', function() {
+ test("should return the same object", function () {
expect(v.div(1)).to.eql(v);
});
- test('should not change x, y, z if no argument is given', function() {
+ test("should not change x, y, z if no argument is given", function () {
v.div();
expect(v.x).to.eql(1);
expect(v.y).to.eql(1);
expect(v.z).to.eql(1);
});
- test('should not change x, y, z if n is not a finite number', function() {
+ test("should not change x, y, z if n is not a finite number", function () {
v.div(NaN);
expect(v.x).to.eql(1);
expect(v.y).to.eql(1);
expect(v.z).to.eql(1);
});
- suite('with scalar', function() {
- test('divide the x, y, z with the scalar', function() {
+ suite("with scalar", function () {
+ test("divide the x, y, z with the scalar", function () {
v.div(2);
expect(v.x).to.be.closeTo(0.5, 0.01);
expect(v.y).to.be.closeTo(0.5, 0.01);
expect(v.z).to.be.closeTo(0.5, 0.01);
});
- test('should not change x, y, z if n is 0', function() {
+ test("should not change x, y, z if n is 0", function () {
v.div(0);
expect(v.x).to.eql(1);
expect(v.y).to.eql(1);
@@ -802,31 +815,31 @@ suite('p5.Vector', function() {
});
});
- suite('p5.Vector.div(v, n)', function() {
+ suite("p5.Vector.div(v, n)", function () {
var v, res;
- beforeEach(function() {
+ beforeEach(function () {
v = new mockP5.Vector(1, 1, 1);
res = mockP5.Vector.div(v, 4);
});
- test('should not be undefined', function() {
+ test("should not be undefined", function () {
expect(res).to.not.eql(undefined);
});
- test('should return a new p5.Vector', function() {
+ test("should return a new p5.Vector", function () {
expect(res).to.not.eql(v);
});
- test('should divide the scalar', function() {
+ test("should divide the scalar", function () {
expect(res.x).to.eql(0.25);
expect(res.y).to.eql(0.25);
expect(res.z).to.eql(0.25);
});
});
- suite('v0.div(v1)', function() {
+ suite("v0.div(v1)", function () {
var v0, v1, v2, v3;
- beforeEach(function() {
+ beforeEach(function () {
v0 = new mockP5.Vector(2, 6, 9);
v1 = new mockP5.Vector(2, 2, 3);
v2 = new mockP5.Vector(1, 1, 1);
@@ -835,20 +848,20 @@ suite('p5.Vector', function() {
v0.div(v1);
});
- test('should do component wise division', function() {
+ test("should do component wise division", function () {
expect(v0.x).to.eql(1);
expect(v0.y).to.eql(3);
expect(v0.z).to.eql(3);
});
- test('should not change x, y, z if v3 is all 0', function() {
+ test("should not change x, y, z if v3 is all 0", function () {
v2.div(v3);
expect(v2.x).to.eql(1);
expect(v2.y).to.eql(1);
expect(v2.z).to.eql(1);
});
- test('should work on 2D vectors', function() {
+ test("should work on 2D vectors", function () {
const v = new mockP5.Vector(1, 1);
const divisor = new mockP5.Vector(2, 2);
v.div(divisor);
@@ -857,7 +870,7 @@ suite('p5.Vector', function() {
expect(v.z).to.eql(0);
});
- test('should work when the dividend has 0', function() {
+ test("should work when the dividend has 0", function () {
const v = new mockP5.Vector(1, 0);
const divisor = new mockP5.Vector(2, 2);
v.div(divisor);
@@ -866,7 +879,7 @@ suite('p5.Vector', function() {
expect(v.z).to.eql(0);
});
- test('should do nothing when the divisor has 0', function() {
+ test("should do nothing when the divisor has 0", function () {
const v = new mockP5.Vector(1, 1);
const divisor = new mockP5.Vector(0, 2);
v.div(divisor);
@@ -876,22 +889,22 @@ suite('p5.Vector', function() {
});
});
- suite('v0.div(arr)', function() {
+ suite("v0.div(arr)", function () {
var v0, v1, arr;
- beforeEach(function() {
+ beforeEach(function () {
v0 = new mockP5.Vector(2, 6, 9);
v1 = new mockP5.Vector(1, 1, 1);
arr = [2, 2, 3];
v0.div(arr);
});
- test('should do component wise division with an array', function() {
+ test("should do component wise division with an array", function () {
expect(v0.x).to.eql(1);
expect(v0.y).to.eql(3);
expect(v0.z).to.eql(3);
});
- test('should not change x, y, z if array contains 0', function() {
+ test("should not change x, y, z if array contains 0", function () {
v1.div([0, 0, 0]);
expect(v1.x).to.eql(1);
expect(v1.y).to.eql(1);
@@ -899,30 +912,30 @@ suite('p5.Vector', function() {
});
});
- suite('p5.Vector.div(v, v', function() {
+ suite("p5.Vector.div(v, v", function () {
var v0, v1, res;
- beforeEach(function() {
+ beforeEach(function () {
v0 = new mockP5.Vector(2, 6, 9);
v1 = new mockP5.Vector(2, 2, 3);
res = mockP5.Vector.div(v0, v1);
});
- test('should return new vector from component wise division', function() {
+ test("should return new vector from component wise division", function () {
expect(res.x).to.eql(1);
expect(res.y).to.eql(3);
expect(res.z).to.eql(3);
});
});
- suite('p5.Vector.div(v, arr', function() {
+ suite("p5.Vector.div(v, arr", function () {
var v0, arr, res;
- beforeEach(function() {
+ beforeEach(function () {
v0 = new mockP5.Vector(2, 6, 9);
arr = [2, 2, 3];
res = mockP5.Vector.div(v0, arr);
});
- test('should return new vector from component wise division with an array', function() {
+ test("should return new vector from component wise division with an array", function () {
expect(res.x).to.eql(1);
expect(res.y).to.eql(3);
expect(res.z).to.eql(3);
@@ -930,65 +943,65 @@ suite('p5.Vector', function() {
});
});
- suite('dot', function() {
- beforeEach(function() {
+ suite("dot", function () {
+ beforeEach(function () {
v.x = 1;
v.y = 1;
v.z = 1;
});
- test('should return a number', function() {
- expect(typeof v.dot(new mockP5.Vector()) === 'number').to.eql(true);
+ test("should return a number", function () {
+ expect(typeof v.dot(new mockP5.Vector()) === "number").to.eql(true);
});
- suite('with p5.Vector', function() {
- test('should be the dot product of the vector', function() {
+ suite("with p5.Vector", function () {
+ test("should be the dot product of the vector", function () {
expect(v.dot(new mockP5.Vector(2, 2))).to.eql(4);
});
});
- suite('with x, y, z', function() {
- test('should be the dot product with x, y', function() {
+ suite("with x, y, z", function () {
+ test("should be the dot product with x, y", function () {
expect(v.dot(2, 2)).to.eql(4);
});
- test('should be the dot product with x, y, z', function() {
+ test("should be the dot product with x, y, z", function () {
expect(v.dot(2, 2, 2)).to.eql(6);
});
});
- suite('p5.Vector.dot(v, n)', function() {
+ suite("p5.Vector.dot(v, n)", function () {
var v1, v2, res;
- beforeEach(function() {
+ beforeEach(function () {
v1 = new mockP5.Vector(1, 1, 1);
v2 = new mockP5.Vector(2, 3, 4);
res = mockP5.Vector.dot(v1, v2);
});
- test('should return a number', function() {
- expect(typeof res === 'number').to.eql(true);
+ test("should return a number", function () {
+ expect(typeof res === "number").to.eql(true);
});
- test('should be the dot product of the two vectors', function() {
+ test("should be the dot product of the two vectors", function () {
expect(res).to.eql(9);
});
});
});
- suite('cross', function() {
+ suite("cross", function () {
var res;
- beforeEach(function() {
+ beforeEach(function () {
v.x = 1;
v.y = 1;
v.z = 1;
});
- test('should return a new product', function() {
+ test("should return a new product", function () {
expect(v.cross(new mockP5.Vector())).to.not.eql(v);
});
- suite('with p5.Vector', function() {
- test('should cross x, y, z from the vector argument', function() {
+ suite("with p5.Vector", function () {
+ test("should cross x, y, z from the vector argument", function () {
res = v.cross(new mockP5.Vector(2, 5, 6));
expect(res.x).to.eql(1); //this.y * v.z - this.z * v.y
expect(res.y).to.eql(-4); //this.z * v.x - this.x * v.z
@@ -996,24 +1009,24 @@ suite('p5.Vector', function() {
});
});
- suite('p5.Vector.cross(v1, v2)', function() {
+ suite("p5.Vector.cross(v1, v2)", function () {
var v1, v2, res;
- beforeEach(function() {
+ beforeEach(function () {
v1 = new mockP5.Vector(3, 6, 9);
v2 = new mockP5.Vector(1, 1, 1);
res = mockP5.Vector.cross(v1, v2);
});
- test('should not be undefined', function() {
+ test("should not be undefined", function () {
expect(res).to.not.eql(undefined);
});
- test('should return neither v1 nor v2', function() {
+ test("should return neither v1 nor v2", function () {
expect(res).to.not.eql(v1);
expect(res).to.not.eql(v2);
});
- test('should the cross product of v1 and v2', function() {
+ test("should the cross product of v1 and v2", function () {
expect(res.x).to.eql(-3);
expect(res.y).to.eql(6);
expect(res.z).to.eql(-3);
@@ -1021,58 +1034,58 @@ suite('p5.Vector', function() {
});
});
- suite('dist', function() {
+ suite("dist", function () {
var b, c;
- beforeEach(function() {
+ beforeEach(function () {
v = new mockP5.Vector(0, 0, 1);
b = new mockP5.Vector(0, 0, 5);
c = new mockP5.Vector(3, 4, 1);
});
- test('should return a number', function() {
- expect(typeof v.dist(b) === 'number').to.eql(true);
+ test("should return a number", function () {
+ expect(typeof v.dist(b) === "number").to.eql(true);
});
- test('should return distance between two vectors', function() {
+ test("should return distance between two vectors", function () {
expect(v.dist(b)).to.eql(4);
});
- test('should return distance between two vectors', function() {
+ test("should return distance between two vectors", function () {
expect(v.dist(c)).to.eql(5);
});
- test('should be commutative', function() {
+ test("should be commutative", function () {
expect(b.dist(c)).to.eql(c.dist(b));
});
});
- suite('p5.Vector.dist(v1, v2)', function() {
+ suite("p5.Vector.dist(v1, v2)", function () {
var v1, v2;
- beforeEach(function() {
+ beforeEach(function () {
v1 = new mockP5.Vector(0, 0, 0);
v2 = new mockP5.Vector(0, 3, 4);
});
- test('should return a number', function() {
- expect(typeof mockP5.Vector.dist(v1, v2) === 'number').to.eql(true);
+ test("should return a number", function () {
+ expect(typeof mockP5.Vector.dist(v1, v2) === "number").to.eql(true);
});
- test('should be commutative', function() {
+ test("should be commutative", function () {
expect(mockP5.Vector.dist(v1, v2)).to.eql(mockP5.Vector.dist(v2, v1));
});
});
- suite('normalize', function() {
- suite('p5.Vector.prototype.normalize() [INSTANCE]', function() {
- beforeEach(function() {
+ suite("normalize", function () {
+ suite("p5.Vector.prototype.normalize() [INSTANCE]", function () {
+ beforeEach(function () {
v = new mockP5.Vector(1, 1, 1);
});
- test('should return the same object', function() {
+ test("should return the same object", function () {
expect(v.normalize()).to.eql(v);
});
- test('unit vector should not change values', function() {
+ test("unit vector should not change values", function () {
v.x = 1;
v.y = 0;
v.z = 0;
@@ -1082,7 +1095,7 @@ suite('p5.Vector', function() {
expect(v.z).to.eql(0);
});
- test('2,2,1 should normalize to ~0.66,0.66,0.33', function() {
+ test("2,2,1 should normalize to ~0.66,0.66,0.33", function () {
v.x = 2;
v.y = 2;
v.z = 1;
@@ -1093,28 +1106,28 @@ suite('p5.Vector', function() {
});
});
- suite('p5.Vector.normalize(v) [CLASS]', function() {
+ suite("p5.Vector.normalize(v) [CLASS]", function () {
var res;
- beforeEach(function() {
+ beforeEach(function () {
v = new mockP5.Vector(1, 0, 0);
res = mockP5.Vector.normalize(v);
});
- test('should not be undefined', function() {
+ test("should not be undefined", function () {
expect(res).to.not.eql(undefined);
});
- test('should not return same object v', function() {
+ test("should not return same object v", function () {
expect(res).to.not.equal(v);
});
- test('unit vector 1,0,0 should normalize to 1,0,0', function() {
+ test("unit vector 1,0,0 should normalize to 1,0,0", function () {
expect(res.x).to.eql(1);
expect(res.y).to.eql(0);
expect(res.z).to.eql(0);
});
- test('2,2,1 should normalize to ~0.66,0.66,0.33', function() {
+ test("2,2,1 should normalize to ~0.66,0.66,0.33", function () {
v.x = 2;
v.y = 2;
v.z = 1;
@@ -1126,20 +1139,20 @@ suite('p5.Vector', function() {
});
});
- suite('limit', function() {
+ suite("limit", function () {
let v;
- beforeEach(function() {
+ beforeEach(function () {
v = new mockP5.Vector(5, 5, 5);
});
- suite('p5.Vector.prototype.limit() [INSTANCE]', function() {
- test('should return the same object', function() {
+ suite("p5.Vector.prototype.limit() [INSTANCE]", function () {
+ test("should return the same object", function () {
expect(v.limit()).to.equal(v);
});
- suite('with a vector larger than the limit', function() {
- test('should limit the vector', function() {
+ suite("with a vector larger than the limit", function () {
+ test("should limit the vector", function () {
v.limit(1);
expect(v.x).to.be.closeTo(0.5773, 0.01);
expect(v.y).to.be.closeTo(0.5773, 0.01);
@@ -1147,8 +1160,8 @@ suite('p5.Vector', function() {
});
});
- suite('with a vector smaller than the limit', function() {
- test('should not limit the vector', function() {
+ suite("with a vector smaller than the limit", function () {
+ test("should not limit the vector", function () {
v.limit(8.67);
expect(v.x).to.eql(5);
expect(v.y).to.eql(5);
@@ -1157,13 +1170,13 @@ suite('p5.Vector', function() {
});
});
- suite('p5.Vector.limit() [CLASS]', function() {
- test('should not return the same object', function() {
+ suite("p5.Vector.limit() [CLASS]", function () {
+ test("should not return the same object", function () {
expect(mockP5.Vector.limit(v)).to.not.equal(v);
});
- suite('with a vector larger than the limit', function() {
- test('should limit the vector', function() {
+ suite("with a vector larger than the limit", function () {
+ test("should limit the vector", function () {
const res = mockP5.Vector.limit(v, 1);
expect(res.x).to.be.closeTo(0.5773, 0.01);
expect(res.y).to.be.closeTo(0.5773, 0.01);
@@ -1171,8 +1184,8 @@ suite('p5.Vector', function() {
});
});
- suite('with a vector smaller than the limit', function() {
- test('should not limit the vector', function() {
+ suite("with a vector smaller than the limit", function () {
+ test("should not limit the vector", function () {
const res = mockP5.Vector.limit(v, 8.67);
expect(res.x).to.eql(5);
expect(res.y).to.eql(5);
@@ -1180,8 +1193,8 @@ suite('p5.Vector', function() {
});
});
- suite('when given a target vector', function() {
- test('should store limited vector in the target', function() {
+ suite("when given a target vector", function () {
+ test("should store limited vector in the target", function () {
const target = new mockP5.Vector(0, 0, 0);
mockP5.Vector.limit(v, 1, target);
expect(target.x).to.be.closeTo(0.5773, 0.01);
@@ -1192,26 +1205,26 @@ suite('p5.Vector', function() {
});
});
- suite('setMag', function() {
+ suite("setMag", function () {
let v;
- beforeEach(function() {
+ beforeEach(function () {
v = new mockP5.Vector(1, 0, 0);
});
- suite('p5.Vector.setMag() [INSTANCE]', function() {
- test('should return the same object', function() {
+ suite("p5.Vector.setMag() [INSTANCE]", function () {
+ test("should return the same object", function () {
expect(v.setMag(2)).to.equal(v);
});
- test('should set the magnitude of the vector', function() {
+ test("should set the magnitude of the vector", function () {
v.setMag(4);
expect(v.x).to.eql(4);
expect(v.y).to.eql(0);
expect(v.z).to.eql(0);
});
- test('should set the magnitude of the vector', function() {
+ test("should set the magnitude of the vector", function () {
v.x = 2;
v.y = 3;
v.z = -6;
@@ -1222,19 +1235,19 @@ suite('p5.Vector', function() {
});
});
- suite('p5.Vector.prototype.setMag() [CLASS]', function() {
- test('should not return the same object', function() {
+ suite("p5.Vector.prototype.setMag() [CLASS]", function () {
+ test("should not return the same object", function () {
expect(mockP5.Vector.setMag(v, 2)).to.not.equal(v);
});
- test('should set the magnitude of the vector', function() {
+ test("should set the magnitude of the vector", function () {
const res = mockP5.Vector.setMag(v, 4);
expect(res.x).to.eql(4);
expect(res.y).to.eql(0);
expect(res.z).to.eql(0);
});
- test('should set the magnitude of the vector', function() {
+ test("should set the magnitude of the vector", function () {
v.x = 2;
v.y = 3;
v.z = -6;
@@ -1244,8 +1257,8 @@ suite('p5.Vector', function() {
expect(res.z).to.eql(-12);
});
- suite('when given a target vector', function() {
- test('should set the magnitude on the target', function() {
+ suite("when given a target vector", function () {
+ test("should set the magnitude on the target", function () {
const target = new mockP5.Vector(0, 1, 0);
const res = mockP5.Vector.setMag(v, 4, target);
expect(target).to.equal(res);
@@ -1257,57 +1270,57 @@ suite('p5.Vector', function() {
});
});
- suite('heading', function() {
- beforeEach(function() {
+ suite("heading", function () {
+ beforeEach(function () {
v = new mockP5.Vector();
});
- suite('p5.Vector.prototype.heading() [INSTANCE]', function() {
- test('should return a number', function() {
- expect(typeof v.heading() === 'number').to.eql(true);
+ suite("p5.Vector.prototype.heading() [INSTANCE]", function () {
+ test("should return a number", function () {
+ expect(typeof v.heading() === "number").to.eql(true);
});
- test('heading for vector pointing right is 0', function() {
+ test("heading for vector pointing right is 0", function () {
v.x = 1;
v.y = 0;
v.z = 0;
expect(v.heading()).to.be.closeTo(0, 0.01);
});
- test('heading for vector pointing down is PI/2', function() {
+ test("heading for vector pointing down is PI/2", function () {
v.x = 0;
v.y = 1;
v.z = 0;
expect(v.heading()).to.be.closeTo(Math.PI / 2, 0.01);
});
- test('heading for vector pointing left is PI', function() {
+ test("heading for vector pointing left is PI", function () {
v.x = -1;
v.y = 0;
v.z = 0;
expect(v.heading()).to.be.closeTo(Math.PI, 0.01);
});
- suite.todo('with `angleMode(DEGREES)`', function() {
- beforeEach(function() {
+ suite.todo("with `angleMode(DEGREES)`", function () {
+ beforeEach(function () {
mockP5Prototype.angleMode(mockP5.DEGREES);
});
- test('heading for vector pointing right is 0', function() {
+ test("heading for vector pointing right is 0", function () {
v.x = 1;
v.y = 0;
v.z = 0;
expect(v.heading()).to.equal(0);
});
- test('heading for vector pointing down is 90', function() {
+ test("heading for vector pointing down is 90", function () {
v.x = 0;
v.y = 1;
v.z = 0;
expect(v.heading()).to.equal(90);
});
- test('heading for vector pointing left is 180', function() {
+ test("heading for vector pointing left is 180", function () {
v.x = -1;
v.y = 0;
v.z = 0;
@@ -1316,26 +1329,26 @@ suite('p5.Vector', function() {
});
});
- suite('p5.Vector.heading() [CLASS]', function() {
- test('should return a number', function() {
- expect(typeof mockP5.Vector.heading(v) === 'number').to.eql(true);
+ suite("p5.Vector.heading() [CLASS]", function () {
+ test("should return a number", function () {
+ expect(typeof mockP5.Vector.heading(v) === "number").to.eql(true);
});
- test('heading for vector pointing right is 0', function() {
+ test("heading for vector pointing right is 0", function () {
v.x = 1;
v.y = 0;
v.z = 0;
expect(mockP5.Vector.heading(v)).to.be.closeTo(0, 0.01);
});
- test('heading for vector pointing down is PI/2', function() {
+ test("heading for vector pointing down is PI/2", function () {
v.x = 0;
v.y = 1;
v.z = 0;
expect(mockP5.Vector.heading(v)).to.be.closeTo(Math.PI / 2, 0.01);
});
- test('heading for vector pointing left is PI', function() {
+ test("heading for vector pointing left is PI", function () {
v.x = -1;
v.y = 0;
v.z = 0;
@@ -1344,8 +1357,8 @@ suite('p5.Vector', function() {
});
});
- suite('lerp', function() {
- test('should return the same object', function() {
+ suite("lerp", function () {
+ test("should return the same object", function () {
expect(v.lerp()).to.eql(v);
});
@@ -1358,29 +1371,29 @@ suite('p5.Vector', function() {
// });
// });
- suite('with x, y, z, amt', function() {
- beforeEach(function() {
+ suite("with x, y, z, amt", function () {
+ beforeEach(function () {
v.x = 0;
v.y = 0;
v.z = 0;
v.lerp(2, 2, 2, 0.5);
});
- test('should lerp x by amt', function() {
+ test("should lerp x by amt", function () {
expect(v.x).to.eql(1);
});
- test('should lerp y by amt', function() {
+ test("should lerp y by amt", function () {
expect(v.y).to.eql(1);
});
- test('should lerp z by amt', function() {
+ test("should lerp z by amt", function () {
expect(v.z).to.eql(1);
});
});
- suite('with no amt', function() {
- test('should assume 0 amt', function() {
+ suite("with no amt", function () {
+ test("should assume 0 amt", function () {
v.x = 0;
v.y = 0;
v.z = 0;
@@ -1392,56 +1405,56 @@ suite('p5.Vector', function() {
});
});
- suite('p5.Vector.lerp(v1, v2, amt)', function() {
+ suite("p5.Vector.lerp(v1, v2, amt)", function () {
var res, v1, v2;
- beforeEach(function() {
+ beforeEach(function () {
v1 = new mockP5.Vector(0, 0, 0);
v2 = new mockP5.Vector(2, 2, 2);
res = mockP5.Vector.lerp(v1, v2, 0.5);
});
- test('should not be undefined', function() {
+ test("should not be undefined", function () {
expect(res).to.not.eql(undefined);
});
- test('should be a p5.Vector', function() {
+ test("should be a p5.Vector", function () {
expect(res).to.be.an.instanceof(mockP5.Vector);
});
- test('should return neither v1 nor v2', function() {
+ test("should return neither v1 nor v2", function () {
expect(res).to.not.eql(v1);
expect(res).to.not.eql(v2);
});
- test('should res to be [1, 1, 1]', function() {
+ test("should res to be [1, 1, 1]", function () {
expect(res.x).to.eql(1);
expect(res.y).to.eql(1);
expect(res.z).to.eql(1);
});
});
- suite('v.slerp(w, amt)', function() {
+ suite("v.slerp(w, amt)", function () {
var w;
- beforeEach(function() {
+ beforeEach(function () {
v.set(1, 2, 3);
w = new mockP5.Vector(4, 6, 8);
});
- test('if amt is 0, returns original vector', function() {
+ test("if amt is 0, returns original vector", function () {
v.slerp(w, 0);
expect(v.x).to.eql(1);
expect(v.y).to.eql(2);
expect(v.z).to.eql(3);
});
- test('if amt is 1, returns argument vector', function() {
+ test("if amt is 1, returns argument vector", function () {
v.slerp(w, 1);
expect(v.x).to.eql(4);
expect(v.y).to.eql(6);
expect(v.z).to.eql(8);
});
- test('if both v and w are 2D, then result will also be 2D.', function() {
+ test("if both v and w are 2D, then result will also be 2D.", function () {
v.set(2, 3, 0);
w.set(3, -2, 0);
v.slerp(w, 0.3);
@@ -1453,7 +1466,7 @@ suite('p5.Vector', function() {
expect(v.z).to.eql(0);
});
- test('if one side is a zero vector, linearly interpolate.', function() {
+ test("if one side is a zero vector, linearly interpolate.", function () {
v.set(0, 0, 0);
w.set(2, 4, 6);
v.slerp(w, 0.5);
@@ -1462,7 +1475,7 @@ suite('p5.Vector', function() {
expect(v.z).to.eql(3);
});
- test('If they are pointing in the same direction, linearly interpolate.', function() {
+ test("If they are pointing in the same direction, linearly interpolate.", function () {
v.set(5, 11, 16);
w.set(15, 33, 48);
v.slerp(w, 0.5);
@@ -1472,121 +1485,126 @@ suite('p5.Vector', function() {
});
});
- suite('p5.Vector.slerp(v1, v2, amt)', function() {
+ suite("p5.Vector.slerp(v1, v2, amt)", function () {
var res, v1, v2;
- beforeEach(function() {
+ beforeEach(function () {
v1 = new mockP5.Vector(1, 0, 0);
v2 = new mockP5.Vector(0, 0, 1);
- res = mockP5.Vector.slerp(v1, v2, 1/3);
+ res = mockP5.Vector.slerp(v1, v2, 1 / 3);
});
- test('should not be undefined', function() {
+ test("should not be undefined", function () {
expect(res).to.not.eql(undefined);
});
- test('should be a p5.Vector', function() {
+ test("should be a p5.Vector", function () {
expect(res).to.be.an.instanceof(mockP5.Vector);
});
- test('should return neither v1 nor v2', function() {
+ test("should return neither v1 nor v2", function () {
expect(res).to.not.eql(v1);
expect(res).to.not.eql(v2);
});
- test('Make sure the interpolation in 1/3 is correct', function() {
- expect(res.x).to.be.closeTo(Math.cos(Math.PI/6), 0.00001);
+ test("Make sure the interpolation in 1/3 is correct", function () {
+ expect(res.x).to.be.closeTo(Math.cos(Math.PI / 6), 0.00001);
expect(res.y).to.be.closeTo(0, 0.00001);
- expect(res.z).to.be.closeTo(Math.sin(Math.PI/6), 0.00001);
+ expect(res.z).to.be.closeTo(Math.sin(Math.PI / 6), 0.00001);
});
- test('Make sure the interpolation in -1/3 is correct', function() {
- mockP5.Vector.slerp(v1, v2, -1/3, res);
- expect(res.x).to.be.closeTo(Math.cos(-Math.PI/6), 0.00001);
+ test("Make sure the interpolation in -1/3 is correct", function () {
+ mockP5.Vector.slerp(v1, v2, -1 / 3, res);
+ expect(res.x).to.be.closeTo(Math.cos(-Math.PI / 6), 0.00001);
expect(res.y).to.be.closeTo(0, 0.00001);
- expect(res.z).to.be.closeTo(Math.sin(-Math.PI/6), 0.00001);
+ expect(res.z).to.be.closeTo(Math.sin(-Math.PI / 6), 0.00001);
});
- test('Make sure the interpolation in 5/3 is correct', function() {
- mockP5.Vector.slerp(v1, v2, 5/3, res);
- expect(res.x).to.be.closeTo(Math.cos(5*Math.PI/6), 0.00001);
+ test("Make sure the interpolation in 5/3 is correct", function () {
+ mockP5.Vector.slerp(v1, v2, 5 / 3, res);
+ expect(res.x).to.be.closeTo(Math.cos((5 * Math.PI) / 6), 0.00001);
expect(res.y).to.be.closeTo(0, 0.00001);
- expect(res.z).to.be.closeTo(Math.sin(5*Math.PI/6), 0.00001);
+ expect(res.z).to.be.closeTo(Math.sin((5 * Math.PI) / 6), 0.00001);
});
});
- suite('p5.Vector.fromAngle(angle)', function() {
+ suite("p5.Vector.fromAngle(angle)", function () {
var res, angle;
- beforeEach(function() {
+ beforeEach(function () {
angle = Math.PI / 2;
res = mockP5.Vector.fromAngle(angle);
});
- test('should be a p5.Vector with values (0,1)', function() {
+ test("should be a p5.Vector with values (0,1)", function () {
expect(res.x).to.be.closeTo(0, 0.01);
expect(res.y).to.be.closeTo(1, 0.01);
});
});
- suite('p5.Vector.random2D()', function() {
+ suite("p5.Vector.random2D()", function () {
var res;
- beforeEach(function() {
+ beforeEach(function () {
res = mockP5.Vector.random2D();
});
- test('should be a unit p5.Vector', function() {
+ test("should be a unit p5.Vector", function () {
expect(res.mag()).to.be.closeTo(1, 0.01);
});
});
- suite('p5.Vector.random3D()', function() {
+ suite("p5.Vector.random3D()", function () {
var res;
- beforeEach(function() {
+ beforeEach(function () {
res = mockP5.Vector.random3D();
});
- test('should be a unit p5.Vector', function() {
+ test("should be a unit p5.Vector", function () {
expect(res.mag()).to.be.closeTo(1, 0.01);
});
});
- suite('array', function() {
- beforeEach(function() {
+ suite("array", function () {
+ beforeEach(function () {
v = new mockP5.Vector(1, 23, 4);
});
- suite('p5.Vector.prototype.array() [INSTANCE]', function() {
- test('should return an array', function() {
+ suite("p5.Vector.prototype.array() [INSTANCE]", function () {
+ test("should return an array", function () {
expect(v.array()).to.be.instanceof(Array);
});
- test('should return an with the x y and z components', function() {
+ test("should return an with the x y and z components", function () {
expect(v.array()).to.eql([1, 23, 4]);
});
});
- suite('p5.Vector.array() [CLASS]', function() {
- test('should return an array', function() {
+ suite("p5.Vector.array() [CLASS]", function () {
+ test("should return an array", function () {
expect(mockP5.Vector.array(v)).to.be.instanceof(Array);
});
- test('should return an with the x y and z components', function() {
+ test("should return an with the x y and z components", function () {
expect(mockP5.Vector.array(v)).to.eql([1, 23, 4]);
});
});
});
- suite('reflect', function() {
- suite('p5.Vector.prototype.reflect() [INSTANCE]', function() {
+ suite("reflect", function () {
+ suite("p5.Vector.prototype.reflect() [INSTANCE]", function () {
let incoming_x, incoming_y, incoming_z, original_incoming;
let x_normal, y_normal, z_normal;
- let x_bounce_incoming, x_bounce_outgoing,
- y_bounce_incoming, y_bounce_outgoing,
- z_bounce_incoming, z_bounce_outgoing;
- beforeEach(function() {
+ let x_bounce_incoming,
+ x_bounce_outgoing,
+ y_bounce_incoming,
+ y_bounce_outgoing,
+ z_bounce_incoming,
+ z_bounce_outgoing;
+ beforeEach(function () {
incoming_x = 1;
incoming_y = 1;
incoming_z = 1;
original_incoming = new mockP5.Vector(
- incoming_x, incoming_y, incoming_z
+ incoming_x,
+ incoming_y,
+ incoming_z
);
x_normal = new mockP5.Vector(3, 0, 0);
@@ -1594,50 +1612,56 @@ suite('p5.Vector', function() {
z_normal = new mockP5.Vector(0, 0, 3);
x_bounce_incoming = new mockP5.Vector(
- incoming_x, incoming_y, incoming_z
+ incoming_x,
+ incoming_y,
+ incoming_z
);
x_bounce_outgoing = x_bounce_incoming.reflect(x_normal);
y_bounce_incoming = new mockP5.Vector(
- incoming_x, incoming_y, incoming_z
+ incoming_x,
+ incoming_y,
+ incoming_z
);
y_bounce_outgoing = y_bounce_incoming.reflect(y_normal);
z_bounce_incoming = new mockP5.Vector(
- incoming_x, incoming_y, incoming_z
+ incoming_x,
+ incoming_y,
+ incoming_z
);
z_bounce_outgoing = z_bounce_incoming.reflect(z_normal);
});
- test('should return a p5.Vector', function() {
+ test("should return a p5.Vector", function () {
expect(x_bounce_incoming).to.be.an.instanceof(mockP5.Vector);
expect(y_bounce_incoming).to.be.an.instanceof(mockP5.Vector);
expect(z_bounce_incoming).to.be.an.instanceof(mockP5.Vector);
});
- test('should update this', function() {
+ test("should update this", function () {
assert.equal(x_bounce_incoming, x_bounce_outgoing);
assert.equal(y_bounce_incoming, y_bounce_outgoing);
assert.equal(z_bounce_incoming, z_bounce_outgoing);
});
- test('x-normal should flip incoming x component and maintain y,z components', function() {
+ test("x-normal should flip incoming x component and maintain y,z components", function () {
expect(x_bounce_outgoing.x).to.be.closeTo(-1, 0.01);
expect(x_bounce_outgoing.y).to.be.closeTo(1, 0.01);
expect(x_bounce_outgoing.z).to.be.closeTo(1, 0.01);
});
- test('y-normal should flip incoming y component and maintain x,z components', function() {
+ test("y-normal should flip incoming y component and maintain x,z components", function () {
expect(y_bounce_outgoing.x).to.be.closeTo(1, 0.01);
expect(y_bounce_outgoing.y).to.be.closeTo(-1, 0.01);
expect(y_bounce_outgoing.z).to.be.closeTo(1, 0.01);
});
- test('z-normal should flip incoming z component and maintain x,y components', function() {
+ test("z-normal should flip incoming z component and maintain x,y components", function () {
expect(z_bounce_outgoing.x).to.be.closeTo(1, 0.01);
expect(z_bounce_outgoing.y).to.be.closeTo(1, 0.01);
expect(z_bounce_outgoing.z).to.be.closeTo(-1, 0.01);
});
- test('angle of incidence should match angle of reflection', function() {
+ test("angle of incidence should match angle of reflection", function () {
expect(
Math.abs(x_normal.angleBetween(original_incoming))
).to.be.closeTo(
@@ -1657,7 +1681,7 @@ suite('p5.Vector', function() {
0.01
);
});
- test('should not update surface normal', function() {
+ test("should not update surface normal", function () {
const tolerance = 0.001;
assert.closeTo(x_normal.x, 3, tolerance);
assert.closeTo(x_normal.y, 0, tolerance);
@@ -1671,23 +1695,27 @@ suite('p5.Vector', function() {
assert.closeTo(z_normal.y, 0, tolerance);
assert.closeTo(z_normal.z, 3, tolerance);
});
-
});
- suite('p5.Vector.reflect() [CLASS]', function() {
+ suite("p5.Vector.reflect() [CLASS]", function () {
let incoming_x, incoming_y, incoming_z, original_incoming;
let x_target, y_target, z_target;
let x_normal, y_normal, z_normal;
- let x_bounce_incoming, x_bounce_outgoing,
- y_bounce_incoming, y_bounce_outgoing,
- z_bounce_incoming, z_bounce_outgoing;
-
- beforeEach(function() {
+ let x_bounce_incoming,
+ x_bounce_outgoing,
+ y_bounce_incoming,
+ y_bounce_outgoing,
+ z_bounce_incoming,
+ z_bounce_outgoing;
+
+ beforeEach(function () {
incoming_x = 1;
incoming_y = 1;
incoming_z = 1;
original_incoming = new mockP5.Vector(
- incoming_x, incoming_y, incoming_z
+ incoming_x,
+ incoming_y,
+ incoming_z
);
x_target = new mockP5.Vector();
y_target = new mockP5.Vector();
@@ -1698,7 +1726,9 @@ suite('p5.Vector', function() {
z_normal = new mockP5.Vector(0, 0, 3);
x_bounce_incoming = new mockP5.Vector(
- incoming_x, incoming_y, incoming_z
+ incoming_x,
+ incoming_y,
+ incoming_z
);
x_bounce_outgoing = mockP5.Vector.reflect(
x_bounce_incoming,
@@ -1707,7 +1737,9 @@ suite('p5.Vector', function() {
);
y_bounce_incoming = new mockP5.Vector(
- incoming_x, incoming_y, incoming_z
+ incoming_x,
+ incoming_y,
+ incoming_z
);
y_bounce_outgoing = mockP5.Vector.reflect(
y_bounce_incoming,
@@ -1716,7 +1748,9 @@ suite('p5.Vector', function() {
);
z_bounce_incoming = new mockP5.Vector(
- incoming_x, incoming_y, incoming_z
+ incoming_x,
+ incoming_y,
+ incoming_z
);
z_bounce_outgoing = mockP5.Vector.reflect(
z_bounce_incoming,
@@ -1725,19 +1759,19 @@ suite('p5.Vector', function() {
);
});
- test('should return a p5.Vector', function() {
+ test("should return a p5.Vector", function () {
expect(x_bounce_incoming).to.be.an.instanceof(mockP5.Vector);
expect(y_bounce_incoming).to.be.an.instanceof(mockP5.Vector);
expect(z_bounce_incoming).to.be.an.instanceof(mockP5.Vector);
});
- test('should not update this', function() {
+ test("should not update this", function () {
expect(x_bounce_incoming).to.not.equal(x_bounce_outgoing);
expect(y_bounce_incoming).to.not.equal(y_bounce_outgoing);
expect(z_bounce_incoming).to.not.equal(z_bounce_outgoing);
});
- test('should not update surface normal', function() {
+ test("should not update surface normal", function () {
const tolerance = 0.001;
assert.closeTo(x_normal.x, 3, tolerance);
assert.closeTo(x_normal.y, 0, tolerance);
@@ -1752,30 +1786,29 @@ suite('p5.Vector', function() {
assert.closeTo(z_normal.z, 3, tolerance);
});
-
- test('should update target', function() {
+ test("should update target", function () {
assert.equal(x_target, x_bounce_outgoing);
assert.equal(y_target, y_bounce_outgoing);
assert.equal(z_target, z_bounce_outgoing);
});
- test('x-normal should flip incoming x component and maintain y,z components', function() {
+ test("x-normal should flip incoming x component and maintain y,z components", function () {
expect(x_bounce_outgoing.x).to.be.closeTo(-1, 0.01);
expect(x_bounce_outgoing.y).to.be.closeTo(1, 0.01);
expect(x_bounce_outgoing.z).to.be.closeTo(1, 0.01);
});
- test('y-normal should flip incoming y component and maintain x,z components', function() {
+ test("y-normal should flip incoming y component and maintain x,z components", function () {
expect(y_bounce_outgoing.x).to.be.closeTo(1, 0.01);
expect(y_bounce_outgoing.y).to.be.closeTo(-1, 0.01);
expect(y_bounce_outgoing.z).to.be.closeTo(1, 0.01);
});
- test('z-normal should flip incoming z component and maintain x,y components', function() {
+ test("z-normal should flip incoming z component and maintain x,y components", function () {
expect(z_bounce_outgoing.x).to.be.closeTo(1, 0.01);
expect(z_bounce_outgoing.y).to.be.closeTo(1, 0.01);
expect(z_bounce_outgoing.z).to.be.closeTo(-1, 0.01);
});
- test('angle of incidence should match angle of reflection', function() {
+ test("angle of incidence should match angle of reflection", function () {
expect(
Math.abs(x_normal.angleBetween(original_incoming))
).to.be.closeTo(
@@ -1798,61 +1831,61 @@ suite('p5.Vector', function() {
});
});
- suite('mag', function() {
+ suite("mag", function () {
const MAG = 3.7416573867739413; // sqrt(1*1 + 2*2 + 3*3)
let v0;
let v1;
- beforeEach(function() {
+ beforeEach(function () {
v0 = new mockP5.Vector(0, 0, 0);
v1 = new mockP5.Vector(1, 2, 3);
});
- suite('p5.Vector.prototype.mag() [INSTANCE]', function() {
- test('should return the magnitude of the vector', function() {
+ suite("p5.Vector.prototype.mag() [INSTANCE]", function () {
+ test("should return the magnitude of the vector", function () {
expect(v0.mag()).to.eql(0);
expect(v1.mag()).to.eql(MAG);
});
});
- suite('p5.Vector.mag() [CLASS]', function() {
- test('should return the magnitude of the vector', function() {
+ suite("p5.Vector.mag() [CLASS]", function () {
+ test("should return the magnitude of the vector", function () {
expect(mockP5.Vector.mag(v0)).to.eql(0);
expect(mockP5.Vector.mag(v1)).to.eql(MAG);
});
});
});
- suite('magSq', function() {
+ suite("magSq", function () {
const MAG = 14; // 1*1 + 2*2 + 3*3
let v0;
let v1;
- beforeEach(function() {
+ beforeEach(function () {
v0 = new mockP5.Vector(0, 0, 0);
v1 = new mockP5.Vector(1, 2, 3);
});
- suite('p5.Vector.prototype.magSq() [INSTANCE]', function() {
- test('should return the magnitude of the vector', function() {
+ suite("p5.Vector.prototype.magSq() [INSTANCE]", function () {
+ test("should return the magnitude of the vector", function () {
expect(v0.magSq()).to.eql(0);
expect(v1.magSq()).to.eql(MAG);
});
});
- suite('p5.Vector.magSq() [CLASS]', function() {
- test('should return the magnitude of the vector', function() {
+ suite("p5.Vector.magSq() [CLASS]", function () {
+ test("should return the magnitude of the vector", function () {
expect(mockP5.Vector.magSq(v0)).to.eql(0);
expect(mockP5.Vector.magSq(v1)).to.eql(MAG);
});
});
});
- suite('equals', function() {
- suite('p5.Vector.prototype.equals() [INSTANCE]', function() {
- test('should return false for parameters inequal to the vector', function() {
+ suite("equals", function () {
+ suite("p5.Vector.prototype.equals() [INSTANCE]", function () {
+ test("should return false for parameters inequal to the vector", function () {
const v1 = new mockP5.Vector(0, -1, 1);
const v2 = new mockP5.Vector(1, 2, 3);
const a2 = [1, 2, 3];
@@ -1861,26 +1894,26 @@ suite('p5.Vector', function() {
expect(v1.equals(1, 2, 3)).to.be.false;
});
- test('should return true for equal vectors', function() {
+ test("should return true for equal vectors", function () {
const v1 = new mockP5.Vector(0, -1, 1);
const v2 = new mockP5.Vector(0, -1, 1);
expect(v1.equals(v2)).to.be.true;
});
- test('should return true for arrays equal to the vector', function() {
+ test("should return true for arrays equal to the vector", function () {
const v1 = new mockP5.Vector(0, -1, 1);
const a1 = [0, -1, 1];
expect(v1.equals(a1)).to.be.true;
});
- test('should return true for arguments equal to the vector', function() {
+ test("should return true for arguments equal to the vector", function () {
const v1 = new mockP5.Vector(0, -1, 1);
expect(v1.equals(0, -1, 1)).to.be.true;
});
});
- suite('p5.Vector.equals() [CLASS]', function() {
- test('should return false for inequal parameters', function() {
+ suite("p5.Vector.equals() [CLASS]", function () {
+ test("should return false for inequal parameters", function () {
const v1 = new mockP5.Vector(0, -1, 1);
const v2 = new mockP5.Vector(1, 2, 3);
const a2 = [1, 2, 3];
@@ -1889,24 +1922,147 @@ suite('p5.Vector', function() {
expect(mockP5.Vector.equals(a2, v1)).to.be.false;
});
- test('should return true for equal vectors', function() {
+ test("should return true for equal vectors", function () {
const v1 = new mockP5.Vector(0, -1, 1);
const v2 = new mockP5.Vector(0, -1, 1);
expect(mockP5.Vector.equals(v1, v2)).to.be.true;
});
- test('should return true for equal vectors and arrays', function() {
+ test("should return true for equal vectors and arrays", function () {
const v1 = new mockP5.Vector(0, -1, 1);
const a1 = [0, -1, 1];
expect(mockP5.Vector.equals(v1, a1)).to.be.true;
expect(mockP5.Vector.equals(a1, v1)).to.be.true;
});
- test('should return true for equal arrays', function() {
+ test("should return true for equal arrays", function () {
const a1 = [0, -1, 1];
const a2 = [0, -1, 1];
expect(mockP5.Vector.equals(a1, a2)).to.be.true;
});
});
});
+
+ suite("set values", function () {
+ beforeEach(function () {
+ v = new mockP5.Vector();
+ });
+
+ test("should set values to [0,0,0] if values array is empty", function () {
+ v.values = [];
+ assert.equal(v.x, 0);
+ assert.equal(v.y, 0);
+ assert.equal(v.z, 0);
+ assert.equal(v.dimensions, 2);
+ });
+ });
+ suite("get value", function () {
+ test("should return element in range of a non empty vector", function () {
+ let vect = new mockP5.Vector(1, 2, 3, 4);
+ assert.equal(vect.getValue(0), 1);
+ assert.equal(vect.getValue(1), 2);
+ assert.equal(vect.getValue(2), 3);
+ assert.equal(vect.getValue(3), 4);
+ });
+
+ test.fails(
+ "should throw friendly error if attempting to get element outside lenght",
+ function () {
+ let vect = new mockP5.Vector(1, 2, 3, 4);
+ assert.equal(vect.getValue(5), 1);
+ }
+ );
+ });
+
+ suite("set value", function () {
+ test("should set value of element in range", function () {
+ let vect = new mockP5.Vector(1, 2, 3, 4);
+ vect.setValue(0, 7);
+ assert.equal(vect.getValue(0), 7);
+ assert.equal(vect.getValue(1), 2);
+ assert.equal(vect.getValue(2), 3);
+ assert.equal(vect.getValue(3), 4);
+ });
+
+ test.fails(
+ "should throw friendly error if attempting to set element outside lenght",
+ function () {
+ let vect = new mockP5.Vector(1, 2, 3, 4);
+ vect.setValue(100, 7);
+ }
+ );
+ });
+
+ describe("get w", () => {
+ it("should return the w component of the vector", () => {
+ v = new mockP5.Vector(1, 2, 3, 4);
+ expect(v.w).toBe(4);
+ });
+
+ it("should return 0 if w component is not set", () => {
+ v = new mockP5.Vector(1, 2, 3);
+ expect(v.w).toBe(0);
+ });
+ });
+
+ describe("set w", () => {
+ it("should set 4th dimension of vector to w value if it exists", () => {
+ v = new mockP5.Vector(1, 2, 3, 4);
+ v.w = 7;
+ expect(v.x).toBe(1);
+ expect(v.y).toBe(2);
+ expect(v.z).toBe(3);
+ expect(v.w).toBe(7);
+ });
+
+ it("should throw error if trying to set w if vector dimensions is less than 4", () => {
+ v = new mockP5.Vector(1, 2);
+ v.w = 5;
+ console.log(v);
+ console.log(v.w);
+ expect(v.w).toBe(0); //TODO: Check this, maybe this should fail
+ });
+ });
+
+ describe("vector to string", () => {
+ it("should return the string version of a vector", () => {
+ v = new mockP5.Vector(1, 2, 3, 4);
+ expect(v.toString()).toBe("[1, 2, 3, 4]");
+ });
+ });
+
+ describe("set heading", () => {
+ it("should rotate a 2D vector by specified angle without changing magnitude", () => {
+ v = new mockP5.Vector(0, 2);
+ const mag = v.mag();
+ expect(v.setHeading(2 * Math.PI).mag()).toBe(mag);
+ expect(v.x).toBe(2);
+ expect(v.y).toBe(-4.898587196589413e-16);
+ });
+ });
+
+ describe("clamp to zero", () => {
+ it("should clamp values cloze to zero to zero, with Number.epsilon value", () => {
+ v = new mockP5.Vector(0, 1, 0.5, 0.1, 0.0000000000000001);
+ expect(v.clampToZero().values).toEqual([0, 1, 0.5, 0.1, 0]);
+ });
+ });
+
+ suite("p5.Vector.fromAngles()", function () {
+ it("should create a v3ctor froma pair of ISO spherical angles", () => {
+ let vect = mockP5.Vector.fromAngles(0, 0);
+ expect(vect.values).toEqual([0, -1, 0]);
+ });
+ });
+
+ suite("p5.Vector.rotate()", function () {
+ it("should rotate the vector (only 2D vectors) by the given angle; magnitude remains the same.", () => {
+ v = new mockP5.Vector(0, 1, 2);
+ let target = new mockP5.Vector();
+ mockP5.Vector.rotate(v, 1 * Math.PI, target);
+ expect(target.values).toEqual([
+ -4.10759023698152e-16, -2.23606797749979, 2,
+ ]);
+ });
+ });
});
diff --git a/test/unit/webgl/p5.Matrix.js b/test/unit/webgl/p5.Matrix.js
deleted file mode 100644
index f1d1202763..0000000000
--- a/test/unit/webgl/p5.Matrix.js
+++ /dev/null
@@ -1,413 +0,0 @@
-import p5 from '../../../src/app.js';
-
-/* eslint-disable indent */
-var mat4 = [
- 1, 2, 3, 4,
- 5, 6, 7, 8,
- 9, 10, 11, 12,
- 13, 14, 15, 16
-];
-
-var other = [
- 1, 5, 9, 13,
- 2, 6, 10, 14,
- 3, 7, 11, 15,
- 4, 8, 12, 16
-];
-
-var mat3 = [
- 1, 2, 3,
- 4, 5, 6,
- 7, 8, 9
-];
-/* eslint-enable indent */
-
-suite('p5.Matrix', function() {
- var myp5;
-
- beforeAll(function() {
- new p5(function(p) {
- p.setup = function() {
- myp5 = p;
- };
- });
- });
-
- afterAll(function() {
- myp5.remove();
- });
-
- suite('construction', function() {
- test('new p5.Matrix()', function() {
- var m = new p5.Matrix();
- assert.instanceOf(m, p5.Matrix);
- assert.isUndefined(m.mat3);
- /* eslint-disable indent */
- assert.deepEqual([].slice.call(m.mat4), [
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, 0, 1
- ]);
- /* eslint-enable indent */
- });
-
- test('new p5.Matrix(array)', function() {
- var m = new p5.Matrix(mat4);
- assert.instanceOf(m, p5.Matrix);
- assert.isUndefined(m.mat3);
- assert.deepEqual([].slice.call(m.mat4), mat4);
- });
-
- test('new p5.Matrix(mat3)', function() {
- var m = new p5.Matrix('mat3', mat3);
- assert.instanceOf(m, p5.Matrix);
- assert.isUndefined(m.mat4);
- assert.deepEqual([].slice.call(m.mat3), mat3);
- });
-
- test('identity()', function() {
- var m = p5.Matrix.identity();
- assert.instanceOf(m, p5.Matrix);
- assert.isUndefined(m.mat3);
- /* eslint-disable indent */
- assert.deepEqual([].slice.call(m.mat4), [
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, 0, 1
- ]);
- /* eslint-enable indent */
- });
- });
-
- suite('set', function() {
- test('p5.Matrix', function() {
- var m = new p5.Matrix();
- m.set(new p5.Matrix(mat4));
- assert.deepEqual([].slice.call(m.mat4), mat4);
- });
-
- test('array', function() {
- var m = new p5.Matrix();
- m.set(mat4);
- assert.deepEqual([].slice.call(m.mat4), mat4);
- });
-
- test('arguments', function() {
- var m = new p5.Matrix();
- m.set.apply(m, mat4);
- assert.notEqual(m.mat4, mat4);
- assert.deepEqual([].slice.call(m.mat4), mat4);
- });
- });
-
- suite('get / copy', function() {
- test('get', function() {
- var m = new p5.Matrix(mat4);
- var m2 = m.get();
- assert.notEqual(m, m2);
- assert.equal(m.mat4, m2.mat4);
- });
- test('copy', function() {
- var m = new p5.Matrix(mat4);
- var m2 = m.copy();
- assert.notEqual(m, m2);
- assert.notEqual(m.mat4, m2.mat4);
- assert.deepEqual([].slice.call(m.mat4), [].slice.call(m2.mat4));
- });
- });
-
- suite('mult', function() {
- /* eslint-disable indent */
- var mm = [
- 30, 70, 110, 150,
- 70, 174, 278, 382,
- 110, 278, 446, 614,
- 150, 382, 614, 846
- ];
- /* eslint-enable indent */
-
- test('self', function() {
- var m = new p5.Matrix(mat4.slice());
- m.mult(m);
- /* eslint-disable indent */
- assert.deepEqual([].slice.call(m.mat4), [
- 90, 100, 110, 120,
- 202, 228, 254, 280,
- 314, 356, 398, 440,
- 426, 484, 542, 600
- ]);
- /* eslint-enable indent */
- });
-
- test('p5.Matrix', function() {
- var m1 = new p5.Matrix(mat4.slice());
- var m2 = new p5.Matrix(other);
- m1.mult(m2);
- assert.deepEqual([].slice.call(m1.mat4), mm);
- });
-
- test('array', function() {
- var m = new p5.Matrix(mat4.slice());
- m.mult(other);
- assert.deepEqual([].slice.call(m.mat4), mm);
- });
-
- test('arguments', function() {
- var m = new p5.Matrix(mat4.slice());
- m.mult.apply(m, other);
- assert.deepEqual([].slice.call(m.mat4), mm);
- });
- });
-
- suite('apply', function() {
- /* eslint-disable indent */
- var am = [
- 276, 304, 332, 360,
- 304, 336, 368, 400,
- 332, 368, 404, 440,
- 360, 400, 440, 480
- ];
- /* eslint-enable indent */
-
- test('self', function() {
- var m = new p5.Matrix(mat4.slice());
- m.apply(m);
- /* eslint-disable indent */
- assert.deepEqual([].slice.call(m.mat4), [
- 90, 100, 110, 120,
- 202, 228, 254, 280,
- 314, 356, 398, 440,
- 426, 484, 542, 600
- ]);
- /* eslint-enable indent */
- });
-
- test('p5.Matrix', function() {
- var m1 = new p5.Matrix(mat4.slice());
- var m2 = new p5.Matrix(other);
- m1.apply(m2);
- assert.deepEqual([].slice.call(m1.mat4), am);
- });
-
- test('array', function() {
- var m = new p5.Matrix(mat4.slice());
- m.apply(other);
- assert.deepEqual([].slice.call(m.mat4), am);
- });
-
- test('arguments', function() {
- var m = new p5.Matrix(mat4.slice());
- m.apply.apply(m, other);
- assert.deepEqual([].slice.call(m.mat4), am);
- });
- });
-
- suite('scale', function() {
- /* eslint-disable indent */
- var sm = [
- 2, 4, 6, 8,
- 15, 18, 21, 24,
- 45, 50, 55, 60,
- 13, 14, 15, 16
- ];
- /* eslint-enable indent */
-
- test('p5.Vector', function() {
- var m = new p5.Matrix(mat4.slice());
- var v = myp5.createVector(2, 3, 5);
- m.scale(v);
- assert.notEqual(m.mat4, mat4);
- assert.deepEqual([].slice.call(m.mat4), sm);
- });
-
- test('array', function() {
- var m = new p5.Matrix(mat4.slice());
- m.scale([2, 3, 5]);
- assert.notEqual(m.mat4, mat4);
- assert.deepEqual([].slice.call(m.mat4), sm);
- });
-
- test('arguments', function() {
- var m = new p5.Matrix(mat4.slice());
- m.scale(2, 3, 5);
- assert.notEqual(m.mat4, mat4);
- assert.deepEqual([].slice.call(m.mat4), sm);
- });
- });
-
- suite('rotate', function() {
- /* eslint-disable max-len */
- var rm = [
- 1.433447866601989, 2.5241247073503885, 3.6148015480987885, 4.7054783888471885,
- 6.460371405020393, 7.054586073938033, 7.648800742855675, 8.243015411773316,
- 7.950398010346969, 9.157598472697025, 10.36479893504708, 11.571999397397136,
- 13, 14, 15, 16
- ];
- /* eslint-enable max-len */
-
- test('p5.Vector', function() {
- var m = new p5.Matrix(mat4.slice());
- var v = myp5.createVector(2, 3, 5);
- m.rotate(45 * myp5.DEG_TO_RAD, v);
- assert.deepEqual([].slice.call(m.mat4), rm);
- });
-
- test('array', function() {
- var m = new p5.Matrix(mat4.slice());
- m.rotate(45 * myp5.DEG_TO_RAD, [2, 3, 5]);
- assert.deepEqual([].slice.call(m.mat4), rm);
- });
-
- test('arguments', function() {
- var m = new p5.Matrix(mat4.slice());
- m.rotate(45 * myp5.DEG_TO_RAD, 2, 3, 5);
- assert.deepEqual([].slice.call(m.mat4), rm);
- });
- });
-
-
- suite('p5.Matrix3x3', function() {
- test('apply copy() to 3x3Matrix', function() {
- const m = new p5.Matrix('mat3', [
- 1, 2, 3,
- 4, 5, 6,
- 7, 8, 9
- ]);
- const mCopy = m.copy();
-
- // The matrix created by copying is different from the original matrix
- assert.notEqual(m, mCopy);
- assert.notEqual(m.mat3, mCopy.mat3);
-
- // The matrix created by copying has the same elements as the original matrix
- assert.deepEqual([].slice.call(m.mat3), [].slice.call(mCopy.mat3));
- });
- test('transpose3x3()', function() {
- const m = new p5.Matrix('mat3', [
- 1, 2, 3,
- 4, 5, 6,
- 7, 8, 9
- ]);
- const mTp = new p5.Matrix('mat3', [
- 1, 4, 7,
- 2, 5, 8,
- 3, 6, 9
- ]);
-
- // If no arguments, transpose itself
- m.transpose3x3();
- assert.deepEqual([].slice.call(m.mat3), [].slice.call(mTp.mat3));
-
- // If there is an array of arguments, set it by transposing it
- m.transpose3x3([
- 1, 2, 3,
- 10, 20, 30,
- 100, 200, 300
- ]);
- assert.deepEqual([].slice.call(m.mat3), [
- 1, 10, 100,
- 2, 20, 200,
- 3, 30, 300
- ]);
- });
- test('mult3x3()', function() {
- const m = new p5.Matrix('mat3', [
- 1, 2, 3,
- 4, 5, 6,
- 7, 8, 9
- ]);
- const m1 = m.copy();
- const m2 = m.copy();
- const multMatrix = new p5.Matrix('mat3', [
- 1, 1, 1,
- 0, 1, 1,
- 1, 0, 1
- ]);
-
- // When taking a matrix as an argument
- m.mult3x3(multMatrix);
- assert.deepEqual([].slice.call(m.mat3), [
- 4, 3, 6,
- 10, 9, 15,
- 16, 15, 24
- ]);
-
- // if the argument is an array or an enumerated number
- m1.mult3x3(1, 1, 1, 0, 1, 1, 1, 0, 1);
- m2.mult3x3([1, 1, 1, 0, 1, 1, 1, 0, 1]);
- assert.deepEqual([].slice.call(m.mat3), [].slice.call(m1.mat3));
- assert.deepEqual([].slice.call(m.mat3), [].slice.call(m2.mat3));
- });
- test('column() and row()', function(){
- const m = new p5.Matrix('mat3', [
- // The matrix data is stored column-major, so each line below is
- // a column rather than a row. Imagine you are looking at the
- // transpose of the matrix in the source code.
- 1, 2, 3,
- 4, 5, 6,
- 7, 8, 9
- ]);
- const column0 = m.column(0);
- const column1 = m.column(1);
- const column2 = m.column(2);
- assert.deepEqual(column0.array(), [1, 2, 3]);
- assert.deepEqual(column1.array(), [4, 5, 6]);
- assert.deepEqual(column2.array(), [7, 8, 9]);
- const row0 = m.row(0);
- const row1 = m.row(1);
- const row2 = m.row(2);
- assert.deepEqual(row0.array(), [1, 4, 7]);
- assert.deepEqual(row1.array(), [2, 5, 8]);
- assert.deepEqual(row2.array(), [3, 6, 9]);
- });
- test('diagonal()', function() {
- const m = new p5.Matrix('mat3', [
- 1, 2, 3,
- 4, 5, 6,
- 7, 8, 9
- ]);
- const m4x4 = new p5.Matrix([
- 1, 2, 3, 4,
- 5, 6, 7, 8,
- 9, 10, 11, 12,
- 13, 14, 15, 16
- ]);
- assert.deepEqual(m.diagonal(), [1, 5, 9]);
- assert.deepEqual(m4x4.diagonal(), [1, 6, 11, 16]);
- });
- test('multiplyVec3', function() {
- const m = new p5.Matrix('mat3', [
- 1, 2, 3,
- 4, 5, 6,
- 7, 8, 9
- ]);
- const multVector = new p5.Vector(3, 2, 1);
- const result = m.multiplyVec3(multVector);
- assert.deepEqual(result.array(), [18, 24, 30]);
- // If there is a target, set result and return that.
- const target = new p5.Vector();
- m.multiplyVec3(multVector, target);
- assert.deepEqual(target.array(), [18, 24, 30]);
- });
- test('createSubMatrix3x3', function() {
- const m4x4 = new p5.Matrix([
- 1, 2, 3, 4,
- 5, 6, 7, 8,
- 9, 10, 11, 12,
- 13, 14, 15, 16
- ]);
- const result = new p5.Matrix('mat3', [
- 1, 2, 3,
- 5, 6, 7,
- 9, 10, 11
- ]);
- const subMatrix3x3 = m4x4.createSubMatrix3x3();
- assert.deepEqual(
- [].slice.call(result.mat3),
- [].slice.call(subMatrix3x3.mat3)
- );
- });
- });
-});
diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js
index b3abec9cd4..a71a01b2fe 100644
--- a/test/unit/webgl/p5.RendererGL.js
+++ b/test/unit/webgl/p5.RendererGL.js
@@ -1,5 +1,6 @@
import p5 from '../../../src/app.js';
import '../../js/chai_helpers';
+const toArray = (typedArray) => Array.from(typedArray);
suite('p5.RendererGL', function() {
var myp5;
@@ -928,7 +929,7 @@ suite('p5.RendererGL', function() {
// cam1 is applied right now so technically this is redundant
myp5.setCamera(cam1);
const cam1Matrix = cam1.cameraMatrix.copy();
- assert.deepEqual(myp5._renderer.states.uViewMatrix.mat4, cam1Matrix.mat4);
+ assert.deepEqual(toArray(myp5._renderer.states.uViewMatrix.mat4), toArray(cam1Matrix.mat4));
// Translation only changes the model matrix
myp5.translate(100, 0, 0);
@@ -936,12 +937,12 @@ suite('p5.RendererGL', function() {
myp5._renderer.states.uModelMatrix.mat4,
origModelMatrix.mat4
);
- assert.deepEqual(myp5._renderer.states.uViewMatrix.mat4, cam1Matrix.mat4);
+ assert.deepEqual(toArray(myp5._renderer.states.uViewMatrix.mat4), toArray(cam1Matrix.mat4));
// Switchnig cameras only changes the view matrix
const transformedModel = myp5._renderer.states.uModelMatrix.copy();
myp5.setCamera(cam2);
- assert.deepEqual(myp5._renderer.states.uModelMatrix.mat4, transformedModel.mat4);
+ assert.deepEqual(toArray(myp5._renderer.states.uModelMatrix.mat4), toArray(transformedModel.mat4));
assert.notDeepEqual(myp5._renderer.states.uViewMatrix.mat4, cam1Matrix.mat4);
});
});