"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@vived/core");
const Transform3D_1 = require("./Transform3D");
function makeTestRig() {
    const appObjects = (0, core_1.makeAppObjectRepo)();
    const appObj = (0, core_1.makeAppObject)("appObj", appObjects);
    const transform3D = (0, Transform3D_1.makeTransform3D)(appObj);
    const observer = jest.fn();
    transform3D.addChangeObserver(observer);
    const mockDateNow = jest.fn();
    mockDateNow.mockReturnValue(0);
    Date.now = mockDateNow;
    return { transform3D, observer, appObj, appObjects, mockDateNow };
}
const linearEase = (p) => p;
describe("Transform node entity", () => {
    it("Sets the App Object", () => {
        const { transform3D, appObj } = makeTestRig();
        expect(transform3D.appObject).toEqual(appObj);
    });
    it("Adds itself to the App Object", () => {
        const { transform3D, appObj } = makeTestRig();
        expect(appObj.hasComponent(Transform3D_1.Transform3D.type)).toEqual(true);
        expect(appObj.getComponent(Transform3D_1.Transform3D.type)).toEqual(transform3D);
    });
    it("Removes itself from the App Object when disposed", () => {
        const { transform3D, appObj } = makeTestRig();
        transform3D.dispose();
        expect(appObj.hasComponent(Transform3D_1.Transform3D.type)).toEqual(false);
    });
    it("Notifies when the position changes", () => {
        const { transform3D, observer } = makeTestRig();
        expect(transform3D.localPosition).toEqual(core_1.Vector3.Zero());
        transform3D.localPosition = new core_1.Vector3(1, 2, 3);
        expect(observer).toHaveBeenCalled();
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(1, 2, 3));
        observer.mockClear();
        transform3D.localPosition = new core_1.Vector3(1, 2, 3);
        transform3D.localPosition = new core_1.Vector3(1, 2, 3);
        transform3D.localPosition = new core_1.Vector3(1, 2, 3);
        expect(observer).not.toHaveBeenCalled();
    });
    it("Notifies when the rotation changes", () => {
        const { transform3D, observer } = makeTestRig();
        expect(transform3D.localRotation).toEqual(core_1.Quaternion.Identity());
        const quat = core_1.Quaternion.FromEuler(core_1.Angle.FromDegrees(10), core_1.Angle.FromDegrees(20), core_1.Angle.FromDegrees(30));
        transform3D.localRotation = quat;
        expect(observer).toHaveBeenCalled();
        expect(core_1.Quaternion.Close(transform3D.localRotation, quat)).toEqual(true);
        observer.mockClear();
        transform3D.localRotation = quat;
        transform3D.localRotation = quat;
        transform3D.localRotation = quat;
        expect(observer).not.toHaveBeenCalled();
    });
    it("Notifies when the scale changes", () => {
        const { transform3D, observer } = makeTestRig();
        expect(transform3D.localScale).toEqual(core_1.Vector3.One());
        transform3D.localScale = new core_1.Vector3(1, 2, 3);
        expect(observer).toHaveBeenCalled();
        expect(transform3D.localScale).toEqual(new core_1.Vector3(1, 2, 3));
        observer.mockClear();
        transform3D.localScale = new core_1.Vector3(1, 2, 3);
        transform3D.localScale = new core_1.Vector3(1, 2, 3);
        transform3D.localScale = new core_1.Vector3(1, 2, 3);
        expect(observer).not.toHaveBeenCalled();
    });
    it("Notifies when the transformation matrix changes", () => {
        const { transform3D, observer } = makeTestRig();
        expect(transform3D.localMatrix).toEqual(core_1.Matrix.Identity());
        transform3D.localMatrix = core_1.Matrix.Zero();
        expect(observer).toHaveBeenCalled();
        expect(transform3D.localMatrix).toEqual(core_1.Matrix.Zero());
        observer.mockClear();
        transform3D.localMatrix = core_1.Matrix.Zero();
        transform3D.localMatrix = core_1.Matrix.Zero();
        transform3D.localMatrix = core_1.Matrix.Zero();
        expect(observer).not.toHaveBeenCalled();
    });
    it("Removes an observer", () => {
        const { transform3D, observer } = makeTestRig();
        transform3D.removeChangeObserver(observer);
        transform3D.localScale = new core_1.Vector3(1, 2, 3);
        transform3D.localRotation = core_1.Quaternion.FromEuler(core_1.Angle.FromDegrees(10), core_1.Angle.FromDegrees(20), core_1.Angle.FromDegrees(30));
        transform3D.localPosition = new core_1.Vector3(1, 2, 3);
        expect(observer).not.toHaveBeenCalled();
    });
    it("Lerps to a new position", () => {
        jest.useFakeTimers();
        const { transform3D, mockDateNow } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(1, 2, 3);
        const lerpData = {
            durationMS: 1000,
            ease: linearEase,
            desiredPosition: new core_1.Vector3(2, 3, 4),
        };
        transform3D.lerp(lerpData);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(1, 2, 3));
        mockDateNow.mockReturnValue(500);
        jest.advanceTimersByTime(500);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(1.5, 2.5, 3.5));
        mockDateNow.mockReturnValue(1000);
        jest.advanceTimersByTime(500);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(2, 3, 4));
        mockDateNow.mockReturnValue(1500);
        jest.advanceTimersByTime(500);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(2, 3, 4));
    });
    it("Lerp to a new position does not change the scale or rotation", () => {
        jest.useFakeTimers();
        const { transform3D, mockDateNow } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(1, 2, 3);
        const lerpData = {
            durationMS: 1000,
            ease: linearEase,
            desiredPosition: new core_1.Vector3(2, 3, 4),
        };
        transform3D.lerp(lerpData);
        mockDateNow.mockReturnValue(1000);
        jest.advanceTimersByTime(1000);
        expect(core_1.Quaternion.Close(transform3D.localRotation, core_1.Quaternion.Identity())).toEqual(true);
        expect(core_1.Vector3.Close(transform3D.localScale, core_1.Vector3.One())).toEqual(true);
    });
    it("Sets position directly if you set a duration of 0", () => {
        const { transform3D } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(0, 0, 0);
        const lerpData = {
            durationMS: 0,
            ease: linearEase,
            desiredPosition: new core_1.Vector3(2, 3, 4),
        };
        transform3D.lerp(lerpData);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(2, 3, 4));
    });
    it("Lerps to a new scale", () => {
        jest.useFakeTimers();
        const { transform3D, mockDateNow } = makeTestRig();
        transform3D.localScale = new core_1.Vector3(1, 2, 3);
        const lerpData = {
            durationMS: 1000,
            ease: linearEase,
            desiredScale: new core_1.Vector3(2, 3, 4),
        };
        transform3D.lerp(lerpData);
        expect(transform3D.localScale).toEqual(new core_1.Vector3(1, 2, 3));
        mockDateNow.mockReturnValue(500);
        jest.advanceTimersByTime(500);
        expect(transform3D.localScale).toEqual(new core_1.Vector3(1.5, 2.5, 3.5));
        mockDateNow.mockReturnValue(1000);
        jest.advanceTimersByTime(500);
        expect(transform3D.localScale).toEqual(new core_1.Vector3(2, 3, 4));
        mockDateNow.mockReturnValue(1500);
        jest.advanceTimersByTime(500);
        expect(transform3D.localScale).toEqual(new core_1.Vector3(2, 3, 4));
    });
    it("Lerp to a new scale does not change the position or rotation", () => {
        jest.useFakeTimers();
        const { transform3D, mockDateNow } = makeTestRig();
        transform3D.localScale = new core_1.Vector3(1, 2, 3);
        const lerpData = {
            durationMS: 1000,
            ease: linearEase,
            desiredScale: new core_1.Vector3(2, 3, 4),
        };
        transform3D.lerp(lerpData);
        mockDateNow.mockReturnValue(1000);
        jest.advanceTimersByTime(1000);
        expect(core_1.Quaternion.Close(transform3D.localRotation, core_1.Quaternion.Identity())).toEqual(true);
        expect(core_1.Vector3.Close(transform3D.localPosition, core_1.Vector3.Zero())).toEqual(true);
    });
    it("Sets scale directly if you set a duration of 0", () => {
        const { transform3D } = makeTestRig();
        transform3D.localScale = new core_1.Vector3(1, 2, 3);
        const lerpData = {
            durationMS: 0,
            ease: linearEase,
            desiredScale: new core_1.Vector3(2, 3, 4),
        };
        transform3D.lerp(lerpData);
        expect(transform3D.localScale).toEqual(new core_1.Vector3(2, 3, 4));
    });
    it("Slerps to a new quaternion", () => {
        jest.useFakeTimers();
        const { transform3D, mockDateNow } = makeTestRig();
        transform3D.localRotation = core_1.Quaternion.Identity();
        // Final is about 0.1893, 0.0381, 0.2392, 0.9515
        const final = core_1.Quaternion.FromYawPitchRoll(core_1.Angle.FromDegrees(10), core_1.Angle.FromDegrees(20), core_1.Angle.FromDegrees(30));
        const lerpData = {
            durationMS: 1000,
            ease: linearEase,
            desiredRotation: final,
        };
        transform3D.lerp(lerpData);
        expect(transform3D.localRotation).toEqual(core_1.Quaternion.Identity());
        mockDateNow.mockReturnValue(500);
        jest.advanceTimersByTime(500);
        // From https://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/index.htm
        expect(core_1.Quaternion.Close(transform3D.localRotation, new core_1.Quaternion(0.09582, 0.0193025, 0.1211253, 0.987812))).toEqual(true);
        mockDateNow.mockReturnValue(1000);
        jest.advanceTimersByTime(500);
        expect(core_1.Quaternion.Close(transform3D.localRotation, final)).toEqual(true);
        mockDateNow.mockReturnValue(1500);
        jest.advanceTimersByTime(500);
        expect(core_1.Quaternion.Close(transform3D.localRotation, final)).toEqual(true);
    });
    it("Lerp to a new rotation does not change the position or scale", () => {
        jest.useFakeTimers();
        const { transform3D, mockDateNow } = makeTestRig();
        const final = core_1.Quaternion.FromYawPitchRoll(core_1.Angle.FromDegrees(10), core_1.Angle.FromDegrees(20), core_1.Angle.FromDegrees(30));
        const lerpData = {
            durationMS: 1000,
            ease: linearEase,
            desiredRotation: final,
        };
        transform3D.lerp(lerpData);
        mockDateNow.mockReturnValue(1000);
        jest.advanceTimersByTime(1000);
        expect(core_1.Vector3.Close(transform3D.localScale, core_1.Vector3.One())).toEqual(true);
        expect(core_1.Vector3.Close(transform3D.localPosition, core_1.Vector3.Zero())).toEqual(true);
    });
    it("Sets rotation directly if you set a duration of 0", () => {
        const { transform3D } = makeTestRig();
        const final = core_1.Quaternion.FromYawPitchRoll(core_1.Angle.FromDegrees(10), core_1.Angle.FromDegrees(20), core_1.Angle.FromDegrees(30));
        const lerpData = {
            durationMS: 0,
            ease: linearEase,
            desiredRotation: final,
        };
        transform3D.lerp(lerpData);
        expect(core_1.Quaternion.Close(transform3D.localRotation, final)).toEqual(true);
    });
    it("Cancels an existing lerp if a position is set", () => {
        jest.useFakeTimers();
        const { transform3D, mockDateNow } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(1, 2, 3);
        const lerpData = {
            durationMS: 1000,
            ease: linearEase,
            desiredPosition: new core_1.Vector3(2, 3, 4),
        };
        transform3D.lerp(lerpData);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(1, 2, 3));
        mockDateNow.mockReturnValue(500);
        jest.advanceTimersByTime(500);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(1.5, 2.5, 3.5));
        transform3D.localPosition = new core_1.Vector3(-1, -2, -3);
        mockDateNow.mockReturnValue(1000);
        jest.advanceTimersByTime(1500);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(-1, -2, -3));
    });
    it("Cancels an existing lerp if a rotation is set", () => {
        jest.useFakeTimers();
        const { transform3D, mockDateNow } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(1, 2, 3);
        const lerpData = {
            durationMS: 1000,
            ease: linearEase,
            desiredPosition: new core_1.Vector3(2, 3, 4),
        };
        transform3D.lerp(lerpData);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(1, 2, 3));
        mockDateNow.mockReturnValue(500);
        jest.advanceTimersByTime(500);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(1.5, 2.5, 3.5));
        transform3D.localRotation = new core_1.Quaternion(1, 2, 3, 4);
        mockDateNow.mockReturnValue(1000);
        jest.advanceTimersByTime(1500);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(1.5, 2.5, 3.5));
    });
    it("Cancels an existing lerp if a scale is set", () => {
        jest.useFakeTimers();
        const { transform3D, mockDateNow } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(1, 2, 3);
        const lerpData = {
            durationMS: 1000,
            ease: linearEase,
            desiredPosition: new core_1.Vector3(2, 3, 4),
        };
        transform3D.lerp(lerpData);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(1, 2, 3));
        mockDateNow.mockReturnValue(500);
        jest.advanceTimersByTime(500);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(1.5, 2.5, 3.5));
        transform3D.localScale = new core_1.Vector3(2, 2, 2);
        mockDateNow.mockReturnValue(1500);
        jest.advanceTimersByTime(1000);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(1.5, 2.5, 3.5));
    });
    it("Cancels an existing lerp it the matrix is set", () => {
        jest.useFakeTimers();
        const { transform3D, mockDateNow } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(1, 2, 3);
        const lerpData = {
            durationMS: 1000,
            ease: linearEase,
            desiredPosition: new core_1.Vector3(2, 3, 4),
        };
        transform3D.lerp(lerpData);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(1, 2, 3));
        mockDateNow.mockReturnValue(500);
        jest.advanceTimersByTime(500);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(1.5, 2.5, 3.5));
        transform3D.localMatrix = core_1.Matrix.Identity();
        mockDateNow.mockReturnValue(1500);
        jest.advanceTimersByTime(1000);
        expect(transform3D.localPosition).toEqual(core_1.Vector3.Zero());
    });
    it("Cancels an existing lerp if a new lerp is passed in", () => {
        jest.useFakeTimers();
        const { transform3D, mockDateNow } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(0, 0, 0);
        const lerpData = {
            durationMS: 1000,
            ease: linearEase,
            desiredPosition: new core_1.Vector3(10, 20, 30),
        };
        transform3D.lerp(lerpData);
        mockDateNow.mockReturnValue(500);
        jest.advanceTimersByTime(500);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(5, 10, 15));
        const lerpData2 = {
            durationMS: 1000,
            ease: linearEase,
            desiredPosition: new core_1.Vector3(0, 0, 0),
        };
        transform3D.lerp(lerpData2);
        mockDateNow.mockReturnValue(1000);
        jest.advanceTimersByTime(500);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(2.5, 5, 7.5));
    });
    it("Cancels a lerp manually", () => {
        jest.useFakeTimers();
        const { transform3D, mockDateNow } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(0, 0, 0);
        const lerpData = {
            durationMS: 1000,
            ease: linearEase,
            desiredPosition: new core_1.Vector3(10, 20, 30),
        };
        transform3D.lerp(lerpData);
        mockDateNow.mockReturnValue(500);
        jest.advanceTimersByTime(500);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(5, 10, 15));
        transform3D.cancelLerp();
        mockDateNow.mockReturnValue(1000);
        jest.advanceTimersByTime(500);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(5, 10, 15));
    });
    it("Transforms a point from local to world coordinates when there is no parent", () => {
        const { transform3D } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(5, 0, 0);
        transform3D.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Up(), core_1.Angle.FromDegrees(-90));
        const transformedPoint = transform3D.localPointToWorld(new core_1.Vector3(2, 1, -1));
        const expected = new core_1.Vector3(6, 1, 2);
        expect(core_1.Vector3.Close(transformedPoint, expected)).toEqual(true);
    });
    it("Transforms a point from world to local coordinates when there is no parent", () => {
        const { transform3D } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(5, 0, 0);
        transform3D.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Up(), core_1.Angle.FromDegrees(-90));
        const transformedPoint = transform3D.worldPointToLocal(new core_1.Vector3(6, 1, 2));
        const expected = new core_1.Vector3(2, 1, -1);
        expect(core_1.Vector3.Close(transformedPoint, expected)).toEqual(true);
    });
    it("Transforms a point from local to world coordinates when there is a parent", () => {
        const { transform3D, appObjects } = makeTestRig();
        const parnetObj = (0, core_1.makeAppObject)("parent", appObjects);
        const parent = (0, Transform3D_1.makeTransform3D)(parnetObj);
        parent.localPosition = new core_1.Vector3(4, 0, -3);
        parent.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Up(), core_1.Angle.FromDegrees(-90));
        transform3D.parent = parent;
        transform3D.localPosition = new core_1.Vector3(0, 2, -2.5);
        transform3D.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Forward(), core_1.Angle.FromDegrees(90));
        const transformedPoint = transform3D.localPointToWorld(new core_1.Vector3(0, 3, 0));
        const expected = new core_1.Vector3(6.5, 2, -6);
        expect(core_1.Vector3.Close(transformedPoint, expected)).toEqual(true);
    });
    it("Transforms a point from world to local coordinates when there is a parent", () => {
        const { transform3D, appObjects } = makeTestRig();
        const parnetObj = (0, core_1.makeAppObject)("parent", appObjects);
        const parent = (0, Transform3D_1.makeTransform3D)(parnetObj);
        parent.localPosition = new core_1.Vector3(4, 0, -3);
        parent.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Up(), core_1.Angle.FromDegrees(-90));
        transform3D.parent = parent;
        transform3D.localPosition = new core_1.Vector3(0, 2, -2.5);
        transform3D.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Forward(), core_1.Angle.FromDegrees(90));
        const transformedPoint = transform3D.worldPointToLocal(new core_1.Vector3(6.5, 2, -6));
        const expected = new core_1.Vector3(0, 3, 0);
        expect(core_1.Vector3.Close(transformedPoint, expected)).toEqual(true);
    });
    it("Returns the local position as the world position if there is no parent", () => {
        const { transform3D } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(5, 4, 3);
        expect(transform3D.worldPosition).toEqual(new core_1.Vector3(5, 4, 3));
    });
    it("Returns the world position if the transform has a parent", () => {
        const { transform3D, appObjects } = makeTestRig();
        const parnetObj = (0, core_1.makeAppObject)("parent", appObjects);
        const parent = (0, Transform3D_1.makeTransform3D)(parnetObj);
        parent.localPosition = new core_1.Vector3(4, 0, -3);
        parent.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Up(), core_1.Angle.FromDegrees(-90));
        transform3D.parent = parent;
        transform3D.localPosition = new core_1.Vector3(0, 2, -2.5);
        transform3D.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Forward(), core_1.Angle.FromDegrees(90));
        const expected = new core_1.Vector3(6.5, 2, -3);
        expect(core_1.Vector3.Close(transform3D.worldPosition, expected)).toEqual(true);
    });
    it("Sets the world position as the local position if there is no parent", () => {
        const { transform3D } = makeTestRig();
        transform3D.worldPosition = new core_1.Vector3(5, 4, 3);
        expect(transform3D.localPosition).toEqual(new core_1.Vector3(5, 4, 3));
    });
    it("Sets the local position from the world position if the transform has a parent", () => {
        const { transform3D, appObjects } = makeTestRig();
        const parnetObj = (0, core_1.makeAppObject)("parent", appObjects);
        const parent = (0, Transform3D_1.makeTransform3D)(parnetObj);
        parent.localPosition = new core_1.Vector3(4, 0, -3);
        parent.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Up(), core_1.Angle.FromDegrees(-90));
        transform3D.parent = parent;
        transform3D.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Forward(), core_1.Angle.FromDegrees(90));
        transform3D.worldPosition = new core_1.Vector3(6.5, 2, -3);
        const expected = new core_1.Vector3(0, 2, -2.5);
        expect(core_1.Vector3.Close(transform3D.localPosition, expected)).toEqual(true);
    });
    it("Returns the local rotation as the world rotation if there is no parent", () => {
        const { transform3D } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(5, 4, 3);
        transform3D.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Left(), core_1.Angle.FromDegrees(25));
        const expected = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Left(), core_1.Angle.FromDegrees(25));
        expect(core_1.Quaternion.Close(transform3D.localRotation, expected)).toEqual(true);
    });
    it("Returns the world rotation if there is a parent", () => {
        const { transform3D, appObjects } = makeTestRig();
        const parnetObj = (0, core_1.makeAppObject)("parent", appObjects);
        const parent = (0, Transform3D_1.makeTransform3D)(parnetObj);
        parent.localPosition = new core_1.Vector3(4, 0, -3);
        parent.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Up(), core_1.Angle.FromDegrees(-30));
        transform3D.parent = parent;
        transform3D.localPosition = new core_1.Vector3(0, 2, -2.5);
        transform3D.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Forward(), core_1.Angle.FromDegrees(25));
        const expected = core_1.Quaternion.FromYawPitchRoll(core_1.Angle.FromDegrees(-30), core_1.Angle.FromDegrees(0), core_1.Angle.FromDegrees(25));
        expect(core_1.Quaternion.Close(transform3D.worldRotation, expected)).toEqual(true);
    });
    it("Sets the world rotation as the local rotation if there is no parent", () => {
        const { transform3D } = makeTestRig();
        const quaterion = core_1.Quaternion.FromYawPitchRoll(core_1.Angle.FromDegrees(-30), core_1.Angle.FromDegrees(0), core_1.Angle.FromDegrees(25));
        transform3D.worldRotation = quaterion;
        expect(core_1.Quaternion.Close(transform3D.localRotation, quaterion)).toEqual(true);
    });
    it("Sets the local quaterion from the world quaternion if there is a parent", () => {
        const { transform3D, appObjects } = makeTestRig();
        const parnetObj = (0, core_1.makeAppObject)("parent", appObjects);
        const parent = (0, Transform3D_1.makeTransform3D)(parnetObj);
        parent.localPosition = new core_1.Vector3(4, 0, -3);
        parent.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Up(), core_1.Angle.FromDegrees(-30));
        transform3D.parent = parent;
        transform3D.localPosition = new core_1.Vector3(0, 2, -2.5);
        transform3D.worldRotation = core_1.Quaternion.FromYawPitchRoll(core_1.Angle.FromDegrees(-30), core_1.Angle.FromDegrees(0), core_1.Angle.FromDegrees(25));
        const expected = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Forward(), core_1.Angle.FromDegrees(25));
        expect(core_1.Quaternion.Close(transform3D.localRotation, expected)).toEqual(true);
    });
    it("Returns the world scale if there is no parent", () => {
        const { transform3D } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(5, 4, 3);
        transform3D.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Left(), core_1.Angle.FromDegrees(25));
        transform3D.localScale = new core_1.Vector3(4, 5, 6);
        expect(transform3D.worldScale).toEqual(new core_1.Vector3(4, 5, 6));
    });
    it("Returns the world scale if there is a parent", () => {
        const { transform3D, appObjects } = makeTestRig();
        const parnetObj = (0, core_1.makeAppObject)("parent", appObjects);
        const parent = (0, Transform3D_1.makeTransform3D)(parnetObj);
        parent.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Left(), core_1.Angle.FromDegrees(90));
        parent.localScale = new core_1.Vector3(1, 2, 3);
        transform3D.parent = parent;
        transform3D.localScale = new core_1.Vector3(4, 5, 6);
        expect(transform3D.worldScale).toEqual(new core_1.Vector3(4, 15, 12));
    });
    it("Sets the world scale if there is no parent", () => {
        const { transform3D } = makeTestRig();
        transform3D.localPosition = new core_1.Vector3(5, 4, 3);
        transform3D.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Left(), core_1.Angle.FromDegrees(25));
        transform3D.worldScale = new core_1.Vector3(4, 5, 6);
        expect(transform3D.localScale).toEqual(new core_1.Vector3(4, 5, 6));
    });
    it("Sets the local scale from the world scale if there is a parent", () => {
        const { transform3D, appObjects } = makeTestRig();
        const parnetObj = (0, core_1.makeAppObject)("parent", appObjects);
        const parent = (0, Transform3D_1.makeTransform3D)(parnetObj);
        parent.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Left(), core_1.Angle.FromDegrees(90));
        parent.localScale = new core_1.Vector3(1, 2, 3);
        transform3D.parent = parent;
        transform3D.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Left(), core_1.Angle.FromDegrees(25));
        transform3D.worldScale = new core_1.Vector3(4, 10, 18);
        const expected = new core_1.Vector3(4, 5, 6);
        expect(core_1.Vector3.Close(transform3D.localScale, expected)).toEqual(true);
    });
    it("Notifes when the parent is set", () => {
        const { transform3D, appObjects, observer } = makeTestRig();
        const parnetObj = (0, core_1.makeAppObject)("parent", appObjects);
        const parent = (0, Transform3D_1.makeTransform3D)(parnetObj);
        observer.mockClear();
        transform3D.parent = parent;
        expect(observer).toHaveBeenCalled();
        observer.mockClear();
        transform3D.parent = parent;
        transform3D.parent = parent;
        transform3D.parent = parent;
        expect(observer).not.toHaveBeenCalled();
        transform3D.parent = undefined;
        expect(observer).toHaveBeenCalled();
    });
    it("Maintains world position when the parent is changed", () => {
        const { transform3D, appObjects } = makeTestRig();
        const parnetObj = (0, core_1.makeAppObject)("parent", appObjects);
        const parent = (0, Transform3D_1.makeTransform3D)(parnetObj);
        const wPos = new core_1.Vector3(10, 20, 30);
        transform3D.worldPosition = wPos;
        parent.worldPosition = new core_1.Vector3(1, 2, 3);
        transform3D.parent = parent;
        expect(core_1.Vector3.Close(transform3D.worldPosition, wPos)).toEqual(true);
        transform3D.parent = undefined;
        expect(core_1.Vector3.Close(transform3D.worldPosition, wPos)).toEqual(true);
    });
    it("Maintains world rotation when the parent is changed", () => {
        const { transform3D, appObjects } = makeTestRig();
        const parnetObj = (0, core_1.makeAppObject)("parent", appObjects);
        const parent = (0, Transform3D_1.makeTransform3D)(parnetObj);
        const wRotation = core_1.Quaternion.FromYawPitchRoll(core_1.Angle.FromDegrees(-30), core_1.Angle.FromDegrees(0), core_1.Angle.FromDegrees(25));
        parent.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Up(), core_1.Angle.FromDegrees(-30));
        transform3D.worldRotation = wRotation;
        transform3D.parent = parent;
        expect(core_1.Quaternion.Close(transform3D.worldRotation, wRotation)).toEqual(true);
        transform3D.parent = undefined;
        expect(core_1.Quaternion.Close(transform3D.worldRotation, wRotation)).toEqual(true);
    });
    it("Maintains world scale when the parent is changed", () => {
        const { transform3D, appObjects } = makeTestRig();
        const parnetObj = (0, core_1.makeAppObject)("parent", appObjects);
        const parent = (0, Transform3D_1.makeTransform3D)(parnetObj);
        const wScale = new core_1.Vector3(10, 20, 30);
        transform3D.worldScale = wScale;
        parent.worldScale = new core_1.Vector3(1, 2, 3);
        transform3D.parent = parent;
        expect(core_1.Vector3.Close(transform3D.worldScale, wScale)).toEqual(true);
        transform3D.parent = undefined;
        expect(core_1.Vector3.Close(transform3D.worldScale, wScale)).toEqual(true);
    });
    it("Transforms a local direction to world coordinates", () => {
        const { transform3D, appObjects } = makeTestRig();
        const parnetObj = (0, core_1.makeAppObject)("parent", appObjects);
        const parent = (0, Transform3D_1.makeTransform3D)(parnetObj);
        parent.localPosition = new core_1.Vector3(4, 0, -3);
        parent.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Up(), core_1.Angle.FromDegrees(-90));
        transform3D.parent = parent;
        transform3D.localPosition = new core_1.Vector3(0, 2, -2.5);
        transform3D.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Forward(), core_1.Angle.FromDegrees(90));
        const localDir = core_1.Vector3.Up();
        const expected = core_1.Vector3.Backward();
        const transformed = transform3D.localDirectionToWorld(localDir);
        expect(core_1.Vector3.Close(transformed, expected)).toEqual(true);
    });
    it("Transforms a world direction to local coordinates", () => {
        const { transform3D, appObjects } = makeTestRig();
        const parnetObj = (0, core_1.makeAppObject)("parent", appObjects);
        const parent = (0, Transform3D_1.makeTransform3D)(parnetObj);
        parent.localPosition = new core_1.Vector3(4, 0, -3);
        parent.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Up(), core_1.Angle.FromDegrees(-90));
        transform3D.parent = parent;
        transform3D.localPosition = new core_1.Vector3(0, 2, -2.5);
        transform3D.localRotation = core_1.Quaternion.FromAngleAxis(core_1.Vector3.Forward(), core_1.Angle.FromDegrees(90));
        const worldDir = core_1.Vector3.Backward();
        const expected = core_1.Vector3.Up();
        const transformed = transform3D.worldDirectionToLocal(worldDir);
        expect(core_1.Vector3.Close(transformed, expected)).toEqual(true);
    });
    it("Call the on lerp completed callback when the lerp ends", () => {
        jest.useFakeTimers();
        const { transform3D, mockDateNow } = makeTestRig();
        const finishCallback = jest.fn();
        const lerpData = {
            durationMS: 1000,
            ease: linearEase,
            desiredPosition: new core_1.Vector3(2, 3, 4),
            onCompletedCallback: finishCallback,
        };
        transform3D.lerp(lerpData);
        mockDateNow.mockReturnValue(900);
        jest.advanceTimersByTime(900);
        expect(finishCallback).not.toHaveBeenCalled();
        mockDateNow.mockReturnValue(1000);
        jest.advanceTimersByTime(100);
        expect(finishCallback).toHaveBeenCalled();
    });
    it("Gets the entity from an AO", () => {
        const { transform3D, appObj } = makeTestRig();
        expect(Transform3D_1.Transform3D.get(appObj)).toEqual(transform3D);
    });
    it("Returns the existing when calling add if missing ", () => {
        const { transform3D, appObj } = makeTestRig();
        expect(Transform3D_1.Transform3D.addIfMissing(appObj)).toEqual(transform3D);
    });
    it("Creates a new one if addIfMissing is called", () => {
        const { appObjects } = makeTestRig();
        const newAO = appObjects.getOrCreate("anotherAO");
        expect(newAO.hasComponent(Transform3D_1.Transform3D.type)).toEqual(false);
        Transform3D_1.Transform3D.addIfMissing(newAO);
        expect(newAO.hasComponent(Transform3D_1.Transform3D.type)).toEqual(true);
    });
    it("Gets by ID", () => {
        const { appObjects, transform3D } = makeTestRig();
        expect(Transform3D_1.Transform3D.getByID("appObj", appObjects)).toEqual(transform3D);
    });
});
//# sourceMappingURL=Transform3D.test.js.map