import { assert } from 'chai'; import { Matrix } from '../../../lib/graphics/Matrix'; import { Vector2D } from '../../../lib/graphics/Vector2D'; import { DOUBLE_EPSILON, fuzzyEqual } from '../../../lib/math/float'; const matrix = Matrix.create; const vector2d = Vector2D.create; function mutable(op: Op, m: Matrix, ...args: any[]) { const clone = m.clone(); assert.ok((m[op])(...args) === m); assert.ok(!m.fuzzyEquals(clone)); } function immutable(op: Op, m: Matrix, ...args: any[]) { const clone = m.clone(); assert.ok((m[op])(...args) !== m); assert.ok(m.fuzzyEquals(clone)); } describe('graphics.Matrix', () => { it('create', () => { const m1 = matrix(); assert.ok(m1 instanceof Matrix); assert.strictEqual(m1.a, 1); assert.strictEqual(m1.b, 0); assert.strictEqual(m1.c, 0); assert.strictEqual(m1.d, 1); assert.strictEqual(m1.e, 0); assert.strictEqual(m1.f, 0); const m2 = matrix(1, 2, 3, 4, 5, 6); assert.ok(m2 instanceof Matrix); assert.strictEqual(m2.a, 1); assert.strictEqual(m2.b, 2); assert.strictEqual(m2.c, 3); assert.strictEqual(m2.d, 4); assert.strictEqual(m2.e, 5); assert.strictEqual(m2.f, 6); }); it('getters/setters', () => { const m = matrix(1, 2, 3, 4, 5, 6); assert.strictEqual(m.m11, 1); assert.strictEqual(m.m12, 2); assert.strictEqual(m.m21, 3); assert.strictEqual(m.m22, 4); assert.strictEqual(m.dx, 5); assert.strictEqual(m.dy, 6); m.m11 = -1; m.m12 = -2; m.m21 = -3; m.m22 = -4; m.dx = -5; m.dy = -6; assert.strictEqual(m.m11, -1); assert.strictEqual(m.m12, -2); assert.strictEqual(m.m21, -3); assert.strictEqual(m.m22, -4); assert.strictEqual(m.dx, -5); assert.strictEqual(m.dy, -6); }); it('clone', () => { const m1 = matrix(1, 2, 3, 4, 5, 6); const m2 = m1.clone(); assert.ok(m2 instanceof Matrix); assert.ok(m2.equals(m1)); m1.a = -1; m1.b = -2; m1.c = -3; m1.d = -4; m1.e = -5; m1.f = -6; assert.ok(m2.equals(matrix(1, 2, 3, 4, 5, 6))); }); it('reset', () => { assert.ok(matrix(1, 2, 3, 4, 5, 6).reset(-1, -2, -3, -4, -5, -6).equals(matrix(-1, -2, -3, -4, -5, -6))); }); it('determinant', () => { assert.strictEqual(matrix().determinant(), 1); assert.strictEqual(matrix(1, 1, 1, 1, 1, 1).determinant(), 0); assert.strictEqual(matrix(1, 2, 3, 4, 5, 6).determinant(), -2); }); it('isIdentity', () => { assert.ok(matrix().isIdentity()); assert.ok(matrix(1, 0, 0, 1, 0, 0).isIdentity()); assert.ok(!matrix(0, 0, 0, 1, 0, 0).isIdentity()); assert.ok(!matrix(1, 1, 0, 1, 0, 0).isIdentity()); assert.ok(!matrix(1, 0, 1, 1, 0, 0).isIdentity()); assert.ok(!matrix(1, 0, 0, 0, 0, 0).isIdentity()); assert.ok(!matrix(1, 0, 0, 1, 1, 0).isIdentity()); assert.ok(!matrix(1, 0, 0, 1, 0, 1).isIdentity()); }); it('fuzzyIsIdentity', () => { assert.ok(!matrix(1.00001, 0.00001, 0.00001, 1.00001, 0.00001, 0.00001).isIdentity()); assert.ok(matrix(1.00001, 0.00001, 0.00001, 1.00001, 0.00001, 0.00001).fuzzyIsIdentity()); assert.ok(!matrix(1.00001, 0.00001, 0.00001, 1.00001, 0.00001, 0.00001).fuzzyIsIdentity(DOUBLE_EPSILON)); }); it('equals', () => { assert.ok(matrix().equals(matrix())); assert.ok(matrix(1, 1, 1, 1, 1, 1).equals(matrix(1, 1, 1, 1, 1, 1))); assert.ok(!matrix(2, 1, 1, 1, 1, 1).equals(matrix(1, 1, 1, 1, 1, 1))); assert.ok(!matrix(1, 2, 1, 1, 1, 1).equals(matrix(1, 1, 1, 1, 1, 1))); assert.ok(!matrix(1, 1, 2, 1, 1, 1).equals(matrix(1, 1, 1, 1, 1, 1))); assert.ok(!matrix(1, 1, 1, 2, 1, 1).equals(matrix(1, 1, 1, 1, 1, 1))); assert.ok(!matrix(1, 1, 1, 1, 2, 1).equals(matrix(1, 1, 1, 1, 1, 1))); assert.ok(!matrix(1, 1, 1, 1, 1, 2).equals(matrix(1, 1, 1, 1, 1, 1))); }); it('fuzzyEquals', () => { const m0 = matrix(1, 2, 3, 4, 5, 6); const m1 = matrix(1.00001, 2.00001, 3.00001, 4.00001, 5.00001, 6.00001); assert.ok(!m0.equals(m1)); assert.ok(m0.fuzzyEquals(m1)); assert.ok(!m0.fuzzyEquals(m1, DOUBLE_EPSILON)); }); it('isInvertible', () => { assert.ok(matrix(1, 2, 3, 4, 5, 6).isInvertible()); assert.ok(!matrix(1, 1, 1, 1, 1, 1).isInvertible()); }); it('fuzzyIsInvertible', () => { assert.ok(matrix(1.00001, 2.00001, 3.00001, 4.00001, 5.00001, 6.00001).fuzzyIsInvertible()); assert.ok(!matrix(1.00001, 1.00001, 1.00001, 1.00001, 1.00001, 1.00001).fuzzyIsInvertible()); }); it('xScale/yScale', () => { const m = matrix().scale(2, 3); assert.strictEqual(m.xScale(), 2); assert.strictEqual(m.yScale(), 3); }); it('xSkew/ySkew', () => { const m = matrix().skew(Math.PI / 2, Math.PI / 4); assert.ok(fuzzyEqual(m.xSkew(), Math.PI / 2)); assert.ok(fuzzyEqual(m.ySkew(), Math.PI / 4)); }); it('mul', () => { assert.ok(matrix(1, 2, 3, 4, 5, 6).mul(matrix(6, 5, 4, 3, 2, 1)).equals(matrix(14, 11, 34, 27, 56, 44))); assert.ok(matrix(1, 2, 3, 4, 5, 6).mul(2).equals(matrix(2, 4, 6, 8, 10, 12))); assert.ok(matrix(1, 2, 3, 4, 5, 6).mul({}).equals(matrix())); immutable('mul', matrix(1, 2, 3, 4, 5, 6), matrix(1, 2, 3, 4, 5, 6)); immutable('mul', matrix(1, 2, 3, 4, 5, 6), 2); }); it('div', () => { assert.ok(matrix(2, 4, 6, 8, 10, 12).div(2).equals(matrix(1, 2, 3, 4, 5, 6))); immutable('div', matrix(1, 2, 3, 4, 5, 6), 2); }); it('inverted', () => { assert.ok(matrix(1, 2, 3, 4, 5, 6).inverted().equals(matrix(-2, 1, 1.5, -0.5, 1, -2))); assert.ok(matrix(1, 1, 1, 1, 1, 1).inverted().equals(matrix())); // non-invertable assert.ok(matrix(1, 2, 3, 4, 5, 6).inverted().mul(matrix(1, 2, 3, 4, 5, 6)).isIdentity()); immutable('inverted', matrix(1, 2, 3, 4, 5, 6)); }); it('invert', () => { assert.ok(matrix(1, 2, 3, 4, 5, 6).invert().equals(matrix(-2, 1, 1.5, -0.5, 1, -2))); assert.ok(matrix(1, 1, 1, 1, 1, 1).invert().equals(matrix())); // non-invertable assert.ok(matrix(1, 2, 3, 4, 5, 6).invert().mul(matrix(1, 2, 3, 4, 5, 6)).isIdentity()); mutable('invert', matrix(1, 2, 3, 4, 5, 6)); }); it('translated', () => { assert.ok(matrix().translated(2, 2).equals(matrix(1, 0, 0, 1, 2, 2))); assert.ok(matrix(1, 2, 3, 4, 5, 6).translated(2, 2).equals(matrix(1, 2, 3, 4, 13, 18))); assert.ok(matrix(1, 2, 3, 4, 5, 6).translated(0, 0).equals(matrix(1, 2, 3, 4, 5, 6))); immutable('translated', matrix(1, 2, 3, 4, 5, 6), 2, 2); }); it('translate', () => { assert.ok(matrix().translate(2, 2).equals(matrix(1, 0, 0, 1, 2, 2))); assert.ok(matrix(1, 2, 3, 4, 5, 6).translate(2, 2).equals(matrix(1, 2, 3, 4, 13, 18))); assert.ok(matrix(1, 2, 3, 4, 5, 6).translate(0, 0).equals(matrix(1, 2, 3, 4, 5, 6))); mutable('translate', matrix(1, 2, 3, 4, 5, 6), 2, 2); }); it('scaled', () => { assert.ok(matrix(1, 2, 3, 4, 5, 6).scaled(2, 3).equals(matrix(2, 4, 9, 12, 5, 6))); assert.ok(matrix(1, 2, 3, 4, 5, 6).scaled(2, 2).equals(matrix(2, 4, 6, 8, 5, 6))); assert.ok(matrix(1, 2, 3, 4, 5, 6).scaled(2).equals(matrix(2, 4, 6, 8, 5, 6))); assert.ok(matrix(1, 2, 3, 4, 5, 6).scaled(1).equals(matrix(1, 2, 3, 4, 5, 6))); immutable('scaled', matrix(1, 2, 3, 4, 5, 6), 2); }); it('scale', () => { assert.ok(matrix(1, 2, 3, 4, 5, 6).scale(2, 3).equals(matrix(2, 4, 9, 12, 5, 6))); assert.ok(matrix(1, 2, 3, 4, 5, 6).scale(2, 2).equals(matrix(2, 4, 6, 8, 5, 6))); assert.ok(matrix(1, 2, 3, 4, 5, 6).scale(2).equals(matrix(2, 4, 6, 8, 5, 6))); assert.ok(matrix(1, 2, 3, 4, 5, 6).scale(1).equals(matrix(1, 2, 3, 4, 5, 6))); mutable('scale', matrix(1, 2, 3, 4, 5, 6), 2); }); it('rotated', () => { assert.ok(matrix(1, 2, 3, 4, 5, 6).rotated(0).fuzzyEquals(matrix(1, 2, 3, 4, 5, 6), DOUBLE_EPSILON)); assert.ok(matrix().rotated(Math.PI / 2).fuzzyEquals(matrix(0, 1, -1, 0, 0, 0), DOUBLE_EPSILON)); immutable('rotated', matrix(1, 2, 3, 4, 5, 6), Math.PI / 2); }); it('rotate', () => { assert.ok(matrix(1, 2, 3, 4, 5, 6).rotate(0).fuzzyEquals(matrix(1, 2, 3, 4, 5, 6), DOUBLE_EPSILON)); assert.ok(matrix().rotate(Math.PI / 2).fuzzyEquals(matrix(0, 1, -1, 0, 0, 0), DOUBLE_EPSILON)); mutable('rotate', matrix(1, 2, 3, 4, 5, 6), Math.PI / 2); }); it('flippedX/flippedY', () => { assert.ok(vector2d(1, 2).mul(matrix().flippedX()).equals(vector2d(-1, 2))); assert.ok(vector2d(1, 2).mul(matrix().flippedY()).equals(vector2d(1, -2))); immutable('flippedX', matrix(1, 2, 3, 4, 5, 6), 2); immutable('flippedY', matrix(1, 2, 3, 4, 5, 6), 2); }); it('flipX/flipY', () => { assert.ok(vector2d(1, 2).mul(matrix().flipX()).equals(vector2d(-1, 2))); assert.ok(vector2d(1, 2).mul(matrix().flipY()).equals(vector2d(1, -2))); mutable('flipX', matrix(1, 2, 3, 4, 5, 6), 2); mutable('flipY', matrix(1, 2, 3, 4, 5, 6), 2); }); it('sheared', () => { assert.ok(matrix(1, 2, 3, 4, 5, 6).sheared(0, 0).equals(matrix(1, 2, 3, 4, 5, 6))); assert.ok(matrix(1, 2, 3, 4, 5, 6).sheared(1, 1).equals(matrix(4, 6, 4, 6, 5, 6))); assert.ok(matrix(1, 2, 3, 4, 5, 6).sheared(2, 2).equals(matrix(7, 10, 5, 8, 5, 6))); immutable('sheared', matrix(1, 2, 3, 4, 5, 6), 1, 2); }); it('shear', () => { assert.ok(matrix(1, 2, 3, 4, 5, 6).shear(0, 0).equals(matrix(1, 2, 3, 4, 5, 6))); assert.ok(matrix(1, 2, 3, 4, 5, 6).shear(1, 1).equals(matrix(4, 6, 4, 6, 5, 6))); assert.ok(matrix(1, 2, 3, 4, 5, 6).shear(2, 2).equals(matrix(7, 10, 5, 8, 5, 6))); mutable('shear', matrix(1, 2, 3, 4, 5, 6), 1, 2); }); it('skewed', () => { assert.ok(matrix(1, 2, 3, 4, 5, 6).skewed(Math.PI / 4, -Math.PI / 4).fuzzyEquals(matrix(-2, -2, 4, 6, 5, 6))); assert.ok(matrix(1, 2, 3, 4, 5, 6).skewed(0, Math.PI).fuzzyEquals(matrix(1, 2, 3, 4, 5, 6))); immutable('skewed', matrix(1, 2, 3, 4, 5, 6), Math.PI / 4, -Math.PI / 4); }); it('skew', () => { assert.ok(matrix(1, 2, 3, 4, 5, 6).skew(Math.PI / 4, -Math.PI / 4).fuzzyEquals(matrix(-2, -2, 4, 6, 5, 6))); assert.ok(matrix(1, 2, 3, 4, 5, 6).skew(0, Math.PI).fuzzyEquals(matrix(1, 2, 3, 4, 5, 6))); mutable('skew', matrix(1, 2, 3, 4, 5, 6), Math.PI / 4, -Math.PI / 4); }); describe('lerp', () => { const ident = matrix(); test.each([ [ident, ident.translated(4, 4), 0, ident], [ident, ident.translated(4, 4), .5, ident.translated(2, 2)], [ident, ident.translated(4, 4), 1, ident.translated(4, 4)], [ident, ident.rotated(Math.PI / 2), 0, ident], [ident, ident.rotated(Math.PI / 2), .5, ident.rotated(Math.PI / 4)], [ident, ident.rotated(Math.PI / 2), 1, ident.rotated(Math.PI / 2)], [ident, ident.scaled(2), 0, ident], [ident, ident.scaled(2), .5, ident.scaled(1.5)], [ident, ident.scaled(2), 1, ident.scaled(2)], [matrix(1, 2, 3, 4, 5, 6), matrix(2, 4, 6, 8, 10, 12), .5, matrix(1.5, 3, 4.5, 6, 7.5, 9)], [matrix(6, 5, 4, 3, 2, 1), matrix(12, 10, 8, 6, 4, 2), .5, matrix(9, 7.5, 6, 4.5, 3, 1.5)], ])('lerp#%#', (from: Matrix, to: Matrix, progress: number, result: Matrix) => { assert.ok(from.interpolated(to, progress).fuzzyEquals(result, DOUBLE_EPSILON)); assert.ok(Matrix.interpolate(from, to, progress).fuzzyEquals(result, DOUBLE_EPSILON)); }); }); it('map', () => { assert.ok(matrix(1, 2, 3, 4, 5, 6).map(n => -n).equals(matrix(-1, -2, -3, -4, -5, -6))); const bind2nd = (fn: (arg1: T1, arg2: T2) => R, boundArg: T2) => (arg: T1) => fn(arg, boundArg); assert.ok(matrix(1, 2, 3, 4, 5, 6).map(bind2nd(Math.pow, 2)).equals(matrix(1, 4, 9, 16, 25, 36))); }); it('Matrix.equal', () => { assert.ok(Matrix.equal(matrix(1, 2, 3, 4, 5, 6), matrix(1, 2, 3, 4, 5, 6))); assert.ok(!Matrix.equal(matrix(6, 5, 4, 3, 2, 1), matrix(1, 2, 3, 4, 5, 6))); }); it('Matrix.fuzzyEqual', () => { const m0 = matrix(1, 2, 3, 4, 5, 6); const m1 = matrix(1.00001, 2.00001, 3.00001, 4.00001, 5.00001, 6.00001); assert.ok(Matrix.fuzzyEqual(m0, m1)); assert.ok(!Matrix.fuzzyEqual(m0, m1, DOUBLE_EPSILON)); }); });