From 7a7e4c1cd4d36bb13b623c869efcff1a88e14699 Mon Sep 17 00:00:00 2001 From: Gregg Tavares Date: Mon, 9 Sep 2024 16:32:27 -0700 Subject: [PATCH] add 3d rotation to mat3 rotateX rotationX rotateY rotationY rotateZ rotationZ --- src/mat3-impl.ts | 135 ++++++++++++++++++++++++++++++++++++++++ test/tests/mat3-test.js | 72 +++++++++++++++++++++ 2 files changed, 207 insertions(+) diff --git a/src/mat3-impl.ts b/src/mat3-impl.ts index 40c935e..0d5f77d 100644 --- a/src/mat3-impl.ts +++ b/src/mat3-impl.ts @@ -661,6 +661,135 @@ function rotate(m: Mat3Arg, angleInRadians: number, return newDst; } +/** + * Creates a 3-by-3 matrix which rotates around the x-axis by the given angle. + * @param angleInRadians - The angle by which to rotate (in radians). + * @param dst - matrix to hold result. If not passed a new one is created. + * @returns The rotation matrix. + */ +function rotationX(angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; + + const c = Math.cos(angleInRadians); + const s = Math.sin(angleInRadians); + + newDst[ 0] = 1; newDst[ 1] = 0; newDst[ 2] = 0; + newDst[ 4] = 0; newDst[ 5] = c; newDst[ 6] = s; + newDst[ 8] = 0; newDst[ 9] = -s; newDst[10] = c; + + return newDst; +} + +/** + * Rotates the given 3-by-3 matrix around the x-axis by the given + * angle. + * @param m - The matrix. + * @param angleInRadians - The angle by which to rotate (in radians). + * @param dst - matrix to hold result. If not passed a new one is created. + * @returns The rotated matrix. + */ +function rotateX(m: Mat3Arg, angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; + + const m10 = m[4]; + const m11 = m[5]; + const m12 = m[6]; + const m20 = m[8]; + const m21 = m[9]; + const m22 = m[10]; + + const c = Math.cos(angleInRadians); + const s = Math.sin(angleInRadians); + + newDst[4] = c * m10 + s * m20; + newDst[5] = c * m11 + s * m21; + newDst[6] = c * m12 + s * m22; + newDst[8] = c * m20 - s * m10; + newDst[9] = c * m21 - s * m11; + newDst[10] = c * m22 - s * m12; + + if (m !== newDst) { + newDst[ 0] = m[ 0]; + newDst[ 1] = m[ 1]; + newDst[ 2] = m[ 2]; + } + + return newDst; +} + +/** + * Creates a 3-by-3 matrix which rotates around the y-axis by the given angle. + * @param angleInRadians - The angle by which to rotate (in radians). + * @param dst - matrix to hold result. If not passed a new one is created. + * @returns The rotation matrix. + */ +function rotationY(angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; + + const c = Math.cos(angleInRadians); + const s = Math.sin(angleInRadians); + + newDst[ 0] = c; newDst[ 1] = 0; newDst[ 2] = -s; + newDst[ 4] = 0; newDst[ 5] = 1; newDst[ 6] = 0; + newDst[ 8] = s; newDst[ 9] = 0; newDst[10] = c; + + return newDst; +} + +/** + * Rotates the given 3-by-3 matrix around the y-axis by the given + * angle. + * @param m - The matrix. + * @param angleInRadians - The angle by which to rotate (in radians). + * @param dst - matrix to hold result. If not passed a new one is created. + * @returns The rotated matrix. + */ +function rotateY(m: Mat3Arg, angleInRadians: number, dst?: T) { + const newDst = (dst ?? new Ctor(12)) as T; + + const m00 = m[0 * 4 + 0]; + const m01 = m[0 * 4 + 1]; + const m02 = m[0 * 4 + 2]; + const m20 = m[2 * 4 + 0]; + const m21 = m[2 * 4 + 1]; + const m22 = m[2 * 4 + 2]; + const c = Math.cos(angleInRadians); + const s = Math.sin(angleInRadians); + + newDst[ 0] = c * m00 - s * m20; + newDst[ 1] = c * m01 - s * m21; + newDst[ 2] = c * m02 - s * m22; + newDst[ 8] = c * m20 + s * m00; + newDst[ 9] = c * m21 + s * m01; + newDst[10] = c * m22 + s * m02; + + if (m !== newDst) { + newDst[ 4] = m[ 4]; + newDst[ 5] = m[ 5]; + newDst[ 6] = m[ 6]; + } + + return newDst; +} + +/** + * Creates a 3-by-3 matrix which rotates around the z-axis by the given angle. + * @param angleInRadians - The angle by which to rotate (in radians). + * @param dst - matrix to hold result. If not passed a new one is created. + * @returns The rotation matrix. + */ +const rotationZ = rotation; + +/** + * Rotates the given 3-by-3 matrix around the z-axis by the given + * angle. + * @param m - The matrix. + * @param angleInRadians - The angle by which to rotate (in radians). + * @param dst - matrix to hold result. If not passed a new one is created. + * @returns The rotated matrix. + */ +const rotateZ = rotate; + /** * Creates a 3-by-3 matrix which scales in each dimension by an amount given by * the corresponding entry in the given vector; assumes the vector has three @@ -784,6 +913,12 @@ return { translate, rotation, rotate, + rotationX, + rotateX, + rotationY, + rotateY, + rotationZ, + rotateZ, scaling, scale, uniformScaling, diff --git a/test/tests/mat3-test.js b/test/tests/mat3-test.js index 3ccde0c..ea93dc4 100644 --- a/test/tests/mat3-test.js +++ b/test/tests/mat3-test.js @@ -448,6 +448,78 @@ function check(mat3, Type) { }, expected); }); + it('should make rotationX matrix', () => { + const angle = 1.23; + const c = Math.cos(angle); + const s = Math.sin(angle); + const expected = [ + 1, 0, 0, 0, + 0, c, s, 0, + 0, -s, c, 0, + ]; + testMat3WithAndWithoutDest((newDst) => { + return mat3.rotationX(angle, newDst); + }, expected); + }); + + it('should rotateX', () => { + const angle = 1.23; + // switch to Array type to keep precision high for expected + const expected = mat3n.multiply(m, mat3.rotationX(angle)); + + testMat3WithAndWithoutDest((newDst) => { + return mat3.rotateX(m, angle, newDst); + }, expected); + }); + + it('should make rotationY matrix', () => { + const angle = 1.23; + const c = Math.cos(angle); + const s = Math.sin(angle); + const expected = [ + c, 0, -s, 0, + 0, 1, 0, 0, + s, 0, c, 0, + ]; + testMat3WithAndWithoutDest((newDst) => { + return mat3.rotationY(angle, newDst); + }, expected); + }); + + it('should rotateY', () => { + const angle = 1.23; + // switch to Array type to keep precision high for expected + const expected = mat3n.multiply(m, mat3.rotationY(angle)); + + testMat3WithAndWithoutDest((newDst) => { + return mat3.rotateY(m, angle, newDst); + }, expected); + }); + + it('should make rotationZ matrix', () => { + const angle = 1.23; + const c = Math.cos(angle); + const s = Math.sin(angle); + const expected = [ + c, s, 0, 0, + -s, c, 0, 0, + 0, 0, 1, 0, + ]; + testMat3WithAndWithoutDest((newDst) => { + return mat3.rotationZ(angle, newDst); + }, expected); + }); + + it('should rotateZ', () => { + const angle = 1.23; + // switch to Array type to keep precision high for expected + const expected = mat3n.multiply(m, mat3.rotationZ(angle)); + + testMat3WithAndWithoutDest((newDst) => { + return mat3.rotateZ(m, angle, newDst); + }, expected); + }); + it('should make scaling matrix', () => { const expected = [ 2, 0, 0, 0,