Finished Mat2

This commit is contained in:
Brandon Jones 2023-03-19 11:41:23 -07:00
parent 81044f2deb
commit fb2bdd48da
5 changed files with 867 additions and 31 deletions

View File

@ -1,4 +1,5 @@
import { EPSILON } from './common.js';
import { Vec2Like } from './vec2.js';
/**
* A 2x2 Matrix given as a {@link Mat2}, a 4-element Float32Array, or an array
@ -60,7 +61,7 @@ export class Mat2 extends Float32Array {
}
//===================
// Instances methods
// Instance methods
//===================
/**
@ -74,6 +75,66 @@ export class Mat2 extends Float32Array {
return this;
}
/**
* Multiplies this {@link Mat2} against another one
* Equivalent to `Mat2.multiply(this, this, b);`
*
* @param out - The receiving Matrix
* @param a - The first operand
* @param b - The second operand
* @returns `this`
*/
multiply(b: Readonly<Mat2Like>): Mat2 {
return Mat2.multiply(this, this, b) as Mat2;
}
/**
* Alias for {@link Mat2.multiply}
*/
mul(b: Readonly<Mat2Like>): Mat2 { return this; }
/**
* Transpose this {@link Mat2}
* Equivalent to `Mat2.transpose(this, this);`
*
* @returns `this`
*/
transpose(): Mat2 {
return Mat2.transpose(this, this) as Mat2;
}
/**
* Inverts this {@link Mat2}
* Equivalent to `Mat4.invert(this, this);`
*
* @returns `this`
*/
invert(): Mat2 {
return Mat2.invert(this, this) as Mat2;
}
/**
* Scales this {@link Mat2} by the dimensions in the given vec3 not using vectorization
* Equivalent to `Mat2.scale(this, this, v);`
*
* @param v - The {@link Vec2} to scale the matrix by
* @returns `this`
*/
scale(v: Readonly<Vec2Like>): Mat2 {
return Mat2.scale(this, this, v) as Mat2;
}
/**
* Rotates this {@link Mat2} by the given angle around the given axis
* Equivalent to `Mat2.rotate(this, this, rad);`
*
* @param rad - the angle to rotate the matrix by
* @returns `out`
*/
rotate(rad: number): Mat2 {
return Mat2.rotate(this, this, rad) as Mat2;
}
//================
// Static methods
//================
@ -126,6 +187,388 @@ export class Mat2 extends Float32Array {
return new Mat2(...values);
}
/**
* Set the components of a {@link Mat2} to the given values
* @category Static
*
* @param out - The receiving matrix
* @param values - Matrix components
* @returns `out`
*/
static set(out: Mat2Like, ...values: number[]): Mat2Like {
out[0] = values[0];
out[1] = values[1];
out[2] = values[2];
out[3] = values[3];
return out;
}
/**
* Set a {@link Mat2} to the identity matrix
* @category Static
*
* @param out - The receiving matrix
* @returns `out`
*/
static identity(out: Mat2Like): Mat2Like {
out[0] = 1;
out[1] = 0;
out[2] = 0;
out[3] = 1;
return out;
}
/**
* Transpose the values of a {@link Mat2}
* @category Static
*
* @param out - the receiving matrix
* @param a - the source matrix
* @returns `out`
*/
static transpose(out: Mat2Like, a: Readonly<Mat2Like>): Mat2Like {
// If we are transposing ourselves we can skip a few steps but have to cache
// some values
if (out === a) {
let a1 = a[1];
out[1] = a[2];
out[2] = a1;
} else {
out[0] = a[0];
out[1] = a[2];
out[2] = a[1];
out[3] = a[3];
}
return out;
}
/**
* Inverts a {@link Mat2}
* @category Static
*
* @param out - the receiving matrix
* @param a - the source matrix
* @returns `out`
*/
static invert(out: Mat2Like, a: Mat2Like): Mat2Like {
const a0 = a[0];
const a1 = a[1];
const a2 = a[2];
const a3 = a[3];
// Calculate the determinant
let det = a0 * a3 - a2 * a1;
if (!det) {
return null;
}
det = 1.0 / det;
out[0] = a3 * det;
out[1] = -a1 * det;
out[2] = -a2 * det;
out[3] = a0 * det;
return out;
}
/**
* Calculates the adjugate of a {@link Mat2}
* @category Static
*
* @param out - the receiving matrix
* @param a - the source matrix
* @returns `out`
*/
static adjoint(out: Mat2Like, a: Mat2Like): Mat2Like {
// Caching this value is necessary if out == a
const a0 = a[0];
out[0] = a[3];
out[1] = -a[1];
out[2] = -a[2];
out[3] = a0;
return out;
}
/**
* Calculates the determinant of a {@link Mat2}
* @category Static
*
* @param a - the source matrix
* @returns determinant of a
*/
static determinant(a: Readonly<Mat2Like>): number {
return a[0] * a[3] - a[2] * a[1];
}
/**
* Adds two {@link Mat2}'s
* @category Static
*
* @param out - the receiving matrix
* @param a - the first operand
* @param b - the second operand
* @returns `out`
*/
static add(out: Mat2Like, a: Readonly<Mat2Like>, b: Readonly<Mat2Like>): Mat2Like {
out[0] = a[0] + b[0];
out[1] = a[1] + b[1];
out[2] = a[2] + b[2];
out[3] = a[3] + b[3];
return out;
}
/**
* Subtracts matrix b from matrix a
* @category Static
*
* @param out - the receiving matrix
* @param a - the first operand
* @param b - the second operand
* @returns `out`
*/
static subtract(out: Mat2Like, a: Readonly<Mat2Like>, b: Readonly<Mat2Like>): Mat2Like {
out[0] = a[0] - b[0];
out[1] = a[1] - b[1];
out[2] = a[2] - b[2];
out[3] = a[3] - b[3];
return out;
}
/**
* Alias for {@link Mat2.subtract}
* @category Static
*/
static sub(out: Mat2Like, a: Readonly<Mat2Like>, b: Readonly<Mat2Like>): Mat2Like { return out; }
/**
* Multiplies two {@link Mat2}s
* @category Static
*
* @param out - The receiving Matrix
* @param a - The first operand
* @param b - The second operand
* @returns `out`
*/
static multiply(out: Mat2Like, a: Readonly<Mat2Like>, b: Readonly<Mat2Like>): Mat2Like {
const a0 = a[0];
const a1 = a[1];
const a2 = a[2];
const a3 = a[3];
const b0 = b[0];
const b1 = b[1];
const b2 = b[2];
const b3 = b[3];
out[0] = a0 * b0 + a2 * b1;
out[1] = a1 * b0 + a3 * b1;
out[2] = a0 * b2 + a2 * b3;
out[3] = a1 * b2 + a3 * b3;
return out;
}
/**
* Alias for {@link Mat2.multiply}
* @category Static
*/
static mul(out: Mat2Like, a: Readonly<Mat2Like>, b: Readonly<Mat2Like>): Mat2Like { return out; }
/**
* Rotates a {@link Mat2} by the given angle
* @category Static
*
* @param out - the receiving matrix
* @param a - the matrix to rotate
* @param rad - the angle to rotate the matrix by
* @returns `out`
*/
static rotate(out: Mat2Like, a: Readonly<Mat2Like>, rad: number): Mat2Like {
const a0 = a[0];
const a1 = a[1];
const a2 = a[2];
const a3 = a[3];
const s = Math.sin(rad);
const c = Math.cos(rad);
out[0] = a0 * c + a2 * s;
out[1] = a1 * c + a3 * s;
out[2] = a0 * -s + a2 * c;
out[3] = a1 * -s + a3 * c;
return out;
}
/**
* Scales the {@link Mat2} by the dimensions in the given {@link Vec2}
* @category Static
*
* @param out - the receiving matrix
* @param a - the matrix to scale
* @param v - the {@link Vec2} to scale the matrix by
* @returns `out`
**/
static scale(out: Mat2Like, a: Readonly<Mat2Like>, v: Readonly<Vec2Like>): Mat2Like {
const a0 = a[0];
const a1 = a[1];
const a2 = a[2];
const a3 = a[3];
const v0 = v[0];
const v1 = v[1];
out[0] = a0 * v0;
out[1] = a1 * v0;
out[2] = a2 * v1;
out[3] = a3 * v1;
return out;
}
/**
* Creates a {@link Mat2} from a given angle around a given axis
* This is equivalent to (but much faster than):
*
* mat2.identity(dest);
* mat2.rotate(dest, dest, rad);
* @category Static
*
* @param out - {@link Mat2} receiving operation result
* @param rad - the angle to rotate the matrix by
* @returns `out`
*/
static fromRotation(out: Mat2Like, rad: number): Mat2Like {
const s = Math.sin(rad);
const c = Math.cos(rad);
out[0] = c;
out[1] = s;
out[2] = -s;
out[3] = c;
return out;
}
/**
* Creates a {@link Mat2} from a vector scaling
* This is equivalent to (but much faster than):
*
* mat2.identity(dest);
* mat2.scale(dest, dest, vec);
* @category Static
*
* @param out - {@link Mat2} receiving operation result
* @param v - Scaling vector
* @returns `out`
*/
static fromScaling(out: Mat2Like, v: Readonly<Vec2Like>): Mat2Like {
out[0] = v[0];
out[1] = 0;
out[2] = 0;
out[3] = v[1];
return out;
}
/**
* Returns Frobenius norm of a {@link Mat2}
* @category Static
*
* @param a - the matrix to calculate Frobenius norm of
* @returns Frobenius norm
*/
static frob(a: Readonly<Mat2Like>): number {
return Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3]);
}
/**
* Multiply each element of a {@link Mat2} by a scalar.
* @category Static
*
* @param out - the receiving matrix
* @param a - the matrix to scale
* @param b - amount to scale the matrix's elements by
* @returns `out`
*/
static multiplyScalar(out: Mat2Like, a: Readonly<Mat2Like>, b: number): Mat2Like {
out[0] = a[0] * b;
out[1] = a[1] * b;
out[2] = a[2] * b;
out[3] = a[3] * b;
return out;
}
/**
* Adds two {@link Mat2}'s after multiplying each element of the second operand by a scalar value.
* @category Static
*
* @param out - the receiving vector
* @param a - the first operand
* @param b - the second operand
* @param scale - the amount to scale b's elements by before adding
* @returns `out`
*/
static multiplyScalarAndAdd(out: Mat2Like, a: Readonly<Mat2Like>, b: Readonly<Mat2Like>, scale: number): Mat2Like {
out[0] = a[0] + b[0] * scale;
out[1] = a[1] + b[1] * scale;
out[2] = a[2] + b[2] * scale;
out[3] = a[3] + b[3] * scale;
return out;
}
/**
* Returns L, D and U matrices (Lower triangular, Diagonal and Upper triangular) by factorizing the input matrix
* @category Static
*
* @param L - the lower triangular matrix
* @param D - the diagonal matrix
* @param U - the upper triangular matrix
* @param a - the input matrix to factorize
*/
static LDU(L: Mat2Like, D: Readonly<Mat2Like>, U: Mat2Like, a: Readonly<Mat2Like>) {
L[2] = a[2] / a[0];
U[0] = a[0];
U[1] = a[1];
U[3] = a[3] - L[2] * U[1];
return [L, D, U];
}
/**
* Returns whether or not two {@link Mat2}s have exactly the same elements in the same position (when compared with ===)
* @category Static
*
* @param a - The first matrix.
* @param b - The second matrix.
* @returns True if the matrices are equal, false otherwise.
*/
static exactEquals(a: Readonly<Mat2Like>, b: Readonly<Mat2Like>): boolean {
return (
a[0] === b[0] &&
a[1] === b[1] &&
a[2] === b[2] &&
a[3] === b[3]
);
}
/**
* Returns whether or not two {@link Mat2}s have approximately the same elements in the same position.
* @category Static
*
* @param a - The first matrix.
* @param b - The second matrix.
* @returns True if the matrices are equal, false otherwise.
*/
static equals(a: Readonly<Mat2Like>, b: Readonly<Mat2Like>): boolean {
const a0 = a[0];
const a1 = a[1];
const a2 = a[2];
const a3 = a[3];
const b0 = b[0];
const b1 = b[1];
const b2 = b[2];
const b3 = b[3];
return (
Math.abs(a0 - b0) <= EPSILON * Math.max(1, Math.abs(a0), Math.abs(b0)) &&
Math.abs(a1 - b1) <= EPSILON * Math.max(1, Math.abs(a1), Math.abs(b1)) &&
Math.abs(a2 - b2) <= EPSILON * Math.max(1, Math.abs(a2), Math.abs(b2)) &&
Math.abs(a3 - b3) <= EPSILON * Math.max(1, Math.abs(a3), Math.abs(b3))
);
}
/**
* Returns a string representation of a {@link Mat2}
* @category Static
@ -139,6 +582,13 @@ export class Mat2 extends Float32Array {
}
// Instance method alias assignments
Mat2.prototype.mul = Mat2.prototype.multiply;
// Static method alias assignments
Mat2.mul = Mat2.multiply;
Mat2.sub = Mat2.subtract;
/**
* {@link Mat2} alias for backwards compatibility
*/

View File

@ -67,7 +67,7 @@ export class Mat3 extends Float32Array {
}
//===================
// Instances methods
// Instance methods
//===================
/**
@ -427,6 +427,7 @@ export class Mat3 extends Float32Array {
out[8] = a[8] - b[8];
return out;
}
/**
* Alias for {@link Mat3.subtract}
* @category Static
@ -835,19 +836,6 @@ export class Mat3 extends Float32Array {
return out;
}
// TODO: Left off at normalFromMat4
/**
* Returns a string representation of a {@link Mat3}
* @category Static
*
* @param a - matrix to represent as a string
* @returns string representation of the matrix
*/
static str(a: Readonly<Mat3Like>): string {
return `Mat3(${a.join(', ')})`;
}
/**
* Returns Frobenius norm of a {@link Mat3}
* @category Static
@ -977,6 +965,17 @@ export class Mat3 extends Float32Array {
Math.abs(a8 - b8) <= EPSILON * Math.max(1, Math.abs(a8), Math.abs(b8))
);
}
/**
* Returns a string representation of a {@link Mat3}
* @category Static
*
* @param a - matrix to represent as a string
* @returns string representation of the matrix
*/
static str(a: Readonly<Mat3Like>): string {
return `Mat3(${a.join(', ')})`;
}
}
// Instance method alias assignments

View File

@ -69,7 +69,7 @@ export class Mat4 extends Float32Array {
}
//===================
// Instances methods
// Instance methods
//===================
/**
@ -2055,17 +2055,6 @@ export class Mat4 extends Float32Array {
return out;
}
/**
* Returns a string representation of a {@link Mat4}
* @category Static
*
* @param a - matrix to represent as a string
* @returns string representation of the matrix
*/
static str(a: Readonly<Mat4Like>): string {
return `Mat4(${a.join(', ')})`;
}
/**
* Returns Frobenius norm of a {@link Mat4}
* @category Static
@ -2307,6 +2296,17 @@ export class Mat4 extends Float32Array {
Math.abs(a15 - b15) <= EPSILON * Math.max(1, Math.abs(a15), Math.abs(b15))
);
}
/**
* Returns a string representation of a {@link Mat4}
* @category Static
*
* @param a - matrix to represent as a string
* @returns string representation of the matrix
*/
static str(a: Readonly<Mat4Like>): string {
return `Mat4(${a.join(', ')})`;
}
}
// Temporary variables to prevent repeated allocations in the algorithms above.

387
tests/mat2.spec.ts Normal file
View File

@ -0,0 +1,387 @@
import { expect, describe, it, beforeEach } from 'vitest';
import "./test-utils"
import { Mat2 } from "../src/mat2"
import { Vec2 } from "../src/vec2"
describe("Mat2", function() {
describe("constructor", () => {
it("should return an identity Mat2 if called with no arguments", () => {
expect(new Mat2()).toBeVec(
1, 0,
0, 1)
});
it("should return Mat2(m0, m1, ...m8) if called with (m0, m1, ...m8)", () => {
expect(new Mat2(
1, 2,
3, 4)).toBeVec(
1, 2,
3, 4);
});
it("should return Mat2(x, x, x, ...) if called with (x)", () => {
expect(new Mat2(1)).toBeVec(
1, 1,
1, 1);
});
it("should return Mat2(m0, m1, ...m8) if called with ([m0, m1, ...m8])", () => {
expect(new Mat2([
1, 2,
3, 4])).toBeVec(
1, 2,
3, 4);
});
it("should return Mat2(m0, m1, ...m8) if called with (Mat4(m0, m1, ...m9))", () => {
let v = new Mat2(
1, 2,
3, 4);
expect(new Mat2(v)).toBeVec(v);
});
it("should return Mat2(m0, m1, ...m8) if called with (Float32Array([m0, m1, ...m8]))", () => {
let arr = new Float32Array([
1, 2,
3, 4]);
expect(new Mat2(arr)).toBeVec(arr);
});
});
describe("static", () => {
let out, matA, matB, identity, result;
beforeEach(function() {
matA = new Float32Array([1, 2,
3, 4]);
matB = new Float32Array([5, 6,
7, 8]);
out = new Float32Array([0, 0,
0, 0]);
identity = new Float32Array([1, 0,
0, 1]);
});
describe("create", function() {
beforeEach(function() { result = Mat2.create(); });
it("should return a 4 element array initialized to a 2x2 identity matrix", function() { expect(result).toBeVec(identity); });
});
describe("clone", function() {
beforeEach(function() { result = Mat2.clone(matA); });
it("should return a 4 element array initialized to the values in matA", function() { expect(result).toBeVec(matA); });
});
describe("copy", function() {
beforeEach(function() { result = Mat2.copy(out, matA); });
it("should place values into out", function() { expect(out).toBeVec(matA); });
it("should return out", function() { expect(result).toBe(out); });
});
describe("identity", function() {
beforeEach(function() { result = Mat2.identity(out); });
it("should place values into out", function() { expect(result).toBeVec(identity); });
it("should return out", function() { expect(result).toBe(out); });
});
describe("transpose", function() {
describe("with a separate output matrix", function() {
beforeEach(function() { result = Mat2.transpose(out, matA); });
it("should place values into out", function() { expect(out).toBeVec(1, 3, 2, 4); });
it("should return out", function() { expect(result).toBe(out); });
it("should not modify matA", function() { expect(matA).toBeVec(1, 2, 3, 4); });
});
describe("when matA is the output matrix", function() {
beforeEach(function() { result = Mat2.transpose(matA, matA); });
it("should place values into matA", function() { expect(matA).toBeVec(1, 3, 2, 4); });
it("should return matA", function() { expect(result).toBe(matA); });
});
});
describe("invert", function() {
describe("with a separate output matrix", function() {
beforeEach(function() { result = Mat2.invert(out, matA); });
it("should place values into out", function() { expect(out).toBeVec(-2, 1, 1.5, -0.5); });
it("should return out", function() { expect(result).toBe(out); });
it("should not modify matA", function() { expect(matA).toBeVec(1, 2, 3, 4); });
});
describe("when matA is the output matrix", function() {
beforeEach(function() { result = Mat2.invert(matA, matA); });
it("should place values into matA", function() { expect(matA).toBeVec(-2, 1, 1.5, -0.5); });
it("should return matA", function() { expect(result).toBe(matA); });
});
});
describe("adjoint", function() {
describe("with a separate output matrix", function() {
beforeEach(function() { result = Mat2.adjoint(out, matA); });
it("should place values into out", function() { expect(out).toBeVec(4, -2, -3, 1); });
it("should return out", function() { expect(result).toBe(out); });
it("should not modify matA", function() { expect(matA).toBeVec(1, 2, 3, 4); });
});
describe("when matA is the output matrix", function() {
beforeEach(function() { result = Mat2.adjoint(matA, matA); });
it("should place values into matA", function() { expect(matA).toBeVec(4, -2, -3, 1); });
it("should return matA", function() { expect(result).toBe(matA); });
});
});
describe("determinant", function() {
beforeEach(function() { result = Mat2.determinant(matA); });
it("should return the determinant", function() { expect(result).toEqual(-2); });
});
describe("multiply", function() {
it("should have an alias called 'mul'", function() { expect(Mat2.mul).toEqual(Mat2.multiply); });
describe("with a separate output matrix", function() {
beforeEach(function() { result = Mat2.multiply(out, matA, matB); });
it("should place values into out", function() { expect(out).toBeVec(23, 34, 31, 46); });
it("should return out", function() { expect(result).toBe(out); });
it("should not modify matA", function() { expect(matA).toBeVec(1, 2, 3, 4); });
it("should not modify matB", function() { expect(matB).toBeVec(5, 6, 7, 8); });
});
describe("when matA is the output matrix", function() {
beforeEach(function() { result = Mat2.multiply(matA, matA, matB); });
it("should place values into matA", function() { expect(matA).toBeVec(23, 34, 31, 46); });
it("should return matA", function() { expect(result).toBe(matA); });
it("should not modify matB", function() { expect(matB).toBeVec(5, 6, 7, 8); });
});
describe("when matB is the output matrix", function() {
beforeEach(function() { result = Mat2.multiply(matB, matA, matB); });
it("should place values into matB", function() { expect(matB).toBeVec(23, 34, 31, 46); });
it("should return matB", function() { expect(result).toBe(matB); });
it("should not modify matA", function() { expect(matA).toBeVec(1, 2, 3, 4); });
});
});
describe("rotate", function() {
describe("with a separate output matrix", function() {
beforeEach(function() { result = Mat2.rotate(out, matA, Math.PI * 0.5); });
it("should place values into out", function() { expect(out).toBeVec(3, 4, -1, -2); });
it("should return out", function() { expect(result).toBe(out); });
it("should not modify matA", function() { expect(matA).toBeVec(1, 2, 3, 4); });
});
describe("when matA is the output matrix", function() {
beforeEach(function() { result = Mat2.rotate(matA, matA, Math.PI * 0.5); });
it("should place values into matA", function() { expect(matA).toBeVec(3, 4, -1, -2); });
it("should return matA", function() { expect(result).toBe(matA); });
});
});
describe("scale", function() {
let vecA;
beforeEach(function() { vecA = [2, 3]; });
describe("with a separate output matrix", function() {
beforeEach(function() { result = Mat2.scale(out, matA, vecA); });
it("should place values into out", function() { expect(out).toBeVec(2, 4, 9, 12); });
it("should return out", function() { expect(result).toBe(out); });
it("should not modify matA", function() { expect(matA).toBeVec(1, 2, 3, 4); });
});
describe("when matA is the output matrix", function() {
beforeEach(function() { result = Mat2.scale(matA, matA, vecA); });
it("should place values into matA", function() { expect(matA).toBeVec(2, 4, 9, 12); });
it("should return matA", function() { expect(result).toBe(matA); });
});
});
describe("str", function() {
beforeEach(function() { result = Mat2.str(matA); });
it("should return a string representation of the matrix", function() { expect(result).toEqual("Mat2(1, 2, 3, 4)"); });
});
describe("frob", function() {
beforeEach(function() { result = Mat2.frob(matA); });
it("should return the Frobenius Norm of the matrix", function() { expect(result).toEqual( Math.sqrt(Math.pow(1, 2) + Math.pow(2, 2) + Math.pow(3, 2) + Math.pow(4, 2))); });
});
describe("LDU", function() {
let L, D, U, L_result, D_result, U_result;
beforeEach(function() {
L = Mat2.create();
D = Mat2.create();
U = Mat2.create();
result = Mat2.LDU(L, D, U, [4,3,6,3]);
L_result = Mat2.create(); L_result[2] = 1.5;
D_result = Mat2.create();
U_result = Mat2.create();
U_result[0] = 4; U_result[1] = 3; U_result[3] = -1.5;
});
it("should return a lower triangular, a diagonal and an upper triangular matrix", function() {
expect(result[0]).toBeVec(L_result);
expect(result[1]).toBeVec(D_result);
expect(result[2]).toBeVec(U_result);
});
});
describe("add", function() {
describe("with a separate output matrix", function() {
beforeEach(function() { result = Mat2.add(out, matA, matB); });
it("should place values into out", function() { expect(out).toBeVec(6, 8, 10, 12); });
it("should return out", function() { expect(result).toBe(out); });
it("should not modify matA", function() { expect(matA).toBeVec(1, 2, 3, 4); });
it("should not modify matB", function() { expect(matB).toBeVec(5, 6, 7, 8); });
});
describe("when matA is the output matrix", function() {
beforeEach(function() { result = Mat2.add(matA, matA, matB); });
it("should place values into matA", function() { expect(matA).toBeVec(6, 8, 10, 12); });
it("should return matA", function() { expect(result).toBe(matA); });
it("should not modify matB", function() { expect(matB).toBeVec(5, 6, 7, 8); });
});
describe("when matB is the output matrix", function() {
beforeEach(function() { result = Mat2.add(matB, matA, matB); });
it("should place values into matB", function() { expect(matB).toBeVec(6, 8, 10, 12); });
it("should return matB", function() { expect(result).toBe(matB); });
it("should not modify matA", function() { expect(matA).toBeVec(1, 2, 3, 4); });
});
});
describe("subtract", function() {
it("should have an alias called 'sub'", function() { expect(Mat2.sub).toEqual(Mat2.subtract); });
describe("with a separate output matrix", function() {
beforeEach(function() { result = Mat2.subtract(out, matA, matB); });
it("should place values into out", function() { expect(out).toBeVec(-4, -4, -4, -4); });
it("should return out", function() { expect(result).toBe(out); });
it("should not modify matA", function() { expect(matA).toBeVec(1, 2, 3, 4); });
it("should not modify matB", function() { expect(matB).toBeVec(5, 6, 7, 8); });
});
describe("when matA is the output matrix", function() {
beforeEach(function() { result = Mat2.subtract(matA, matA, matB); });
it("should place values into matA", function() { expect(matA).toBeVec(-4, -4, -4, -4); });
it("should return matA", function() { expect(result).toBe(matA); });
it("should not modify matB", function() { expect(matB).toBeVec(5, 6, 7, 8); });
});
describe("when matB is the output matrix", function() {
beforeEach(function() { result = Mat2.subtract(matB, matA, matB); });
it("should place values into matB", function() { expect(matB).toBeVec(-4, -4, -4, -4); });
it("should return matB", function() { expect(result).toBe(matB); });
it("should not modify matA", function() { expect(matA).toBeVec(1, 2, 3, 4); });
});
});
describe("fromValues", function() {
beforeEach(function() { result = Mat2.fromValues(1, 2, 3, 4); });
it("should return a 4 element array initialized to the values passed", function() { expect(result).toBeVec(1, 2, 3, 4); });
});
describe("set", function() {
beforeEach(function() { result = Mat2.set(out, 1, 2, 3, 4); });
it("should place values into out", function() { expect(out).toBeVec(1, 2, 3, 4); });
it("should return out", function() { expect(result).toBe(out); });
});
describe("multiplyScalar", function() {
describe("with a separate output matrix", function() {
beforeEach(function() { result = Mat2.multiplyScalar(out, matA, 2); });
it("should place values into out", function() { expect(out).toBeVec(2, 4, 6, 8); });
it("should return out", function() { expect(result).toBe(out); });
it("should not modify matA", function() { expect(matA).toBeVec(1, 2, 3, 4); });
});
describe("when matA is the output matrix", function() {
beforeEach(function() { result = Mat2.multiplyScalar(matA, matA, 2); });
it("should place values into matA", function() { expect(matA).toBeVec(2, 4, 6, 8); });
it("should return matA", function() { expect(result).toBe(matA); });
});
});
describe("multiplyScalarAndAdd", function() {
describe("with a separate output matrix", function() {
beforeEach(function() { result = Mat2.multiplyScalarAndAdd(out, matA, matB, 0.5); });
it("should place values into out", function() { expect(out).toBeVec(3.5, 5, 6.5, 8); });
it("should return out", function() { expect(result).toBe(out); });
it("should not modify matA", function() { expect(matA).toBeVec(1, 2, 3, 4); });
it("should not modify matB", function() { expect(matB).toBeVec(5, 6, 7, 8); });
});
describe("when matA is the output matrix", function() {
beforeEach(function() { result = Mat2.multiplyScalarAndAdd(matA, matA, matB, 0.5); });
it("should place values into matA", function() { expect(matA).toBeVec(3.5, 5, 6.5, 8); });
it("should return matA", function() { expect(result).toBe(matA); });
it("should not modify matB", function() { expect(matB).toBeVec(5, 6, 7, 8); });
});
describe("when matB is the output matrix", function() {
beforeEach(function() { result = Mat2.multiplyScalarAndAdd(matB, matA, matB, 0.5); });
it("should place values into matB", function() { expect(matB).toBeVec(3.5, 5, 6.5, 8); });
it("should return matB", function() { expect(result).toBe(matB); });
it("should not modify matA", function() { expect(matA).toBeVec(1, 2, 3, 4); });
});
});
describe("exactEquals", function() {
let matC, r0, r1, r2;
beforeEach(function() {
matA = [0, 1, 2, 3];
matB = [0, 1, 2, 3];
matC = [1, 2, 3, 4];
r0 = Mat2.exactEquals(matA, matB);
r1 = Mat2.exactEquals(matA, matC);
});
it("should return true for identical matrices", function() { expect(r0).toBe(true); });
it("should return false for different matrices", function() { expect(r1).toBe(false); });
it("should not modify matA", function() { expect(matA).toBeVec(0, 1, 2, 3); });
it("should not modify matB", function() { expect(matB).toBeVec(0, 1, 2, 3); });
});
describe("equals", function() {
let matC, matD, r0, r1, r2;
beforeEach(function() {
matA = [0, 1, 2, 3];
matB = [0, 1, 2, 3];
matC = [1, 2, 3, 4];
matD = [1e-16, 1, 2, 3];
r0 = Mat2.equals(matA, matB);
r1 = Mat2.equals(matA, matC);
r2 = Mat2.equals(matA, matD);
});
it("should return true for identical matrices", function() { expect(r0).toBe(true); });
it("should return false for different matrices", function() { expect(r1).toBe(false); });
it("should return true for close but not identical matrices", function() { expect(r2).toBe(true); });
it("should not modify matA", function() { expect(matA).toBeVec(0, 1, 2, 3); });
it("should not modify matB", function() { expect(matB).toBeVec(0, 1, 2, 3); });
});
});
});

View File

@ -42,15 +42,15 @@ describe("Mat3", function() {
7, 8, 9);
});
it("should return Mat3(m0, m1, ...m8) if called with (Mat4(m0, m1, ...m9))", () => {
let v = new Mat4(
it("should return Mat3(m0, m1, ...m8) if called with (Mat3(m0, m1, ...m9))", () => {
let v = new Mat3(
1, 2, 3,
4, 5, 6,
7, 8, 9);
expect(new Mat4(v)).toBeVec(v);
expect(new Mat3(v)).toBeVec(v);
});
it("should return Mat4(m0, m1, ...m8) if called with (Float32Array([m0, m1, ...m8]))", () => {
it("should return Mat3(m0, m1, ...m8) if called with (Float32Array([m0, m1, ...m8]))", () => {
let arr = new Float32Array([
1, 2, 3,
4, 5, 6,