Skip to content

Commit

Permalink
Add zyx2R function
Browse files Browse the repository at this point in the history
  • Loading branch information
ezzatron committed Apr 29, 2024
1 parent 9c7aadb commit a1b7ce8
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { n_EA_E_and_p_AB_E2n_EB_E } from "./n_EA_E_and_p_AB_E2n_EB_E.js";
export { n_EB_E2p_EB_E } from "./n_EB_E2p_EB_E.js";
export { p_EB_E2n_EB_E } from "./p_EB_E2n_EB_E.js";
export { rotateVector3, unrotateVector3 } from "./rotation.js";
export { zyx2R } from "./zyx2R.js";
34 changes: 34 additions & 0 deletions src/zyx2R.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Matrix3x3 } from "./matrix.js";

/**
* Creates a rotation matrix from Euler angles in the zyx-order.
*
* @param z - The angle in radians of rotation about the new z-axis.
* @param y - The angle in radians of rotation about the new y-axis.
* @param x - The angle in radians of rotation about the new x-axis.
*
* @returns The rotation matrix.
*/
export function zyx2R(z: number, y: number, x: number): Matrix3x3 {
// Based on https://github.com/pbrod/nvector/blob/b8afd89a860a4958d499789607aacb4168dcef87/src/nvector/rotation.py#L329
const sinX = Math.sin(x);
const sinY = Math.sin(y);
const sinZ = Math.sin(z);
const cosX = Math.cos(x);
const cosY = Math.cos(y);
const cosZ = Math.cos(z);

return [
[
cosZ * cosY,
-sinZ * cosX + cosZ * sinY * sinX,
sinZ * sinX + cosZ * sinY * cosX,
],
[
sinZ * cosY,
cosZ * cosX + sinZ * sinY * sinX,
-cosZ * sinX + sinZ * sinY * cosX,
],
[-sinY, cosY * sinX, cosY * cosX],
];
}
4 changes: 4 additions & 0 deletions test/arbitrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,7 @@ function arbitraryQuaternion(): fc.Arbitrary<Vector4> {
return [x, y, z, w];
});
}

export function arbitraryRadians(): fc.Arbitrary<number> {
return fc.double({ min: -Math.PI, max: Math.PI, noNaN: true });
}
6 changes: 6 additions & 0 deletions test/nvector-test-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
n_EA_E_and_p_AB_E2n_EB_E,
n_EB_E2p_EB_E,
p_EB_E2n_EB_E,
zyx2R,
} from "../src/index.js";
import type { Matrix3x3 } from "../src/matrix.js";
import type { Vector3 } from "../src/vector.js";
Expand All @@ -19,6 +20,7 @@ export type NvectorTestClient = {
n_EA_E_and_p_AB_E2n_EB_E: Async<typeof n_EA_E_and_p_AB_E2n_EB_E>;
n_EB_E2p_EB_E: Async<typeof n_EB_E2p_EB_E>;
p_EB_E2n_EB_E: Async<typeof p_EB_E2n_EB_E>;
zyx2R: Async<typeof zyx2R>;

close: () => void;
};
Expand Down Expand Up @@ -118,6 +120,10 @@ export async function createNvectorTestClient(): Promise<NvectorTestClient> {
return [unwrapVector3(n_EB_E), depth];
},

async zyx2R(z, y, x) {
return await call<Matrix3x3>("zyx2R", { z, y, x });
},

close: () => ws.close(),
};

Expand Down
56 changes: 56 additions & 0 deletions test/vitest/zyx2R.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { it } from "@fast-check/vitest";
import { afterAll, beforeAll, describe, expect } from "vitest";
import { zyx2R } from "../../src/index.js";
import { arbitraryRadians } from "../arbitrary.js";
import {
NvectorTestClient,
createNvectorTestClient,
} from "../nvector-test-api.js";

const TEST_DURATION = 5000;

describe("zyx2R()", () => {
let nvectorTestClient: NvectorTestClient;

beforeAll(async () => {
nvectorTestClient = await createNvectorTestClient();
});

afterAll(() => {
nvectorTestClient?.close();
});

it.prop([arbitraryRadians(), arbitraryRadians(), arbitraryRadians()], {
interruptAfterTimeLimit: TEST_DURATION,
numRuns: Infinity,
})(
"matches the Python implementation",
async (x, y, z) => {
const expected = await nvectorTestClient.zyx2R(x, y, z);

expect(expected).toMatchObject([
[expect.any(Number), expect.any(Number), expect.any(Number)],
[expect.any(Number), expect.any(Number), expect.any(Number)],
[expect.any(Number), expect.any(Number), expect.any(Number)],
]);

const actual = zyx2R(x, y, z);

expect(actual).toMatchObject([
[expect.any(Number), expect.any(Number), expect.any(Number)],
[expect.any(Number), expect.any(Number), expect.any(Number)],
[expect.any(Number), expect.any(Number), expect.any(Number)],
]);
expect(actual[0][0]).toBeCloseTo(expected[0][0], 15);
expect(actual[0][1]).toBeCloseTo(expected[0][1], 15);
expect(actual[0][2]).toBeCloseTo(expected[0][2], 15);
expect(actual[1][0]).toBeCloseTo(expected[1][0], 15);
expect(actual[1][1]).toBeCloseTo(expected[1][1], 15);
expect(actual[1][2]).toBeCloseTo(expected[1][2], 15);
expect(actual[2][0]).toBeCloseTo(expected[2][0], 15);
expect(actual[2][1]).toBeCloseTo(expected[2][1], 15);
expect(actual[2][2]).toBeCloseTo(expected[2][2], 15);
},
TEST_DURATION + 1000,
);
});

0 comments on commit a1b7ce8

Please sign in to comment.