OpenHPS/openhps-core

View on GitHub
src/utils/math/Quaternion.ts

Summary

Maintainability
A
1 hr
Test Coverage
import { SerializableObject, SerializableMember, NumberType } from '../../data/decorators';
import * as THREE from './_internal';
import { Euler, EulerOrder } from './Euler';
import { Matrix4 } from './Matrix4';
import { AxisAngle } from './AxisAngle';
import { Vector3 } from './Vector3';
import { AngleUnit } from '../unit';

/**
 * Serializable THREE.js Quaternion
 */
@SerializableObject()
export class Quaternion extends THREE.Quaternion {
    @SerializableMember({
        numberType: NumberType.DECIMAL,
    })
    x: number;

    @SerializableMember({
        numberType: NumberType.DECIMAL,
    })
    y: number;

    @SerializableMember({
        numberType: NumberType.DECIMAL,
    })
    z: number;

    @SerializableMember({
        numberType: NumberType.DECIMAL,
    })
    w: number;

    /**
     * Convert a threejs quaternion to serializable quaternion
     * @param {THREE.Quaternion} threeQuaternion ThreeJS created quaternion
     * @returns {Quaternion} Serializable quaternion
     */
    static fromThreeJS<T extends typeof Quaternion>(threeQuaternion: THREE.Quaternion): InstanceType<T> {
        const quaternion = new this();
        quaternion.x = threeQuaternion.x;
        quaternion.y = threeQuaternion.y;
        quaternion.z = threeQuaternion.z;
        quaternion.w = threeQuaternion.w;
        return quaternion as InstanceType<T>;
    }

    /**
     * Convert euler angles to quaternion
     * @param {Vector3 | Euler} euler Euler vector
     * @returns {Quaternion} Serializable quaternion
     */
    static fromEuler<T extends typeof Quaternion>(this: T, euler: Vector3): InstanceType<T>;
    static fromEuler<T extends typeof Quaternion>(this: T, euler: Euler): InstanceType<T>;
    static fromEuler<T extends typeof Quaternion>(
        this: T,
        euler: {
            yaw: number;
            pitch: number;
            roll: number;
            unit?: AngleUnit;
        },
    ): InstanceType<T>;
    static fromEuler<T extends typeof Quaternion>(
        this: T,
        euler: {
            x: number;
            y: number;
            z: number;
            order?: EulerOrder;
            unit?: AngleUnit;
        },
    ): InstanceType<T>;
    static fromEuler<T extends typeof Quaternion>(this: T, euler: number[]): InstanceType<T>;
    static fromEuler<T extends typeof Quaternion>(this: T, euler: any): InstanceType<T> {
        const quaternion = new this();
        if (euler instanceof Euler) {
            quaternion.setFromEuler(euler);
        } else if (euler instanceof Vector3) {
            quaternion.setFromEuler(new Euler(euler.x, euler.y, euler.z));
        } else if (euler instanceof Array) {
            quaternion.setFromEuler(new Euler(euler[0], euler[1], euler[2]));
        } else if (euler['yaw'] === undefined) {
            quaternion.setFromEuler(new Euler(euler.x, euler.y, euler.z, euler.order, euler.unit));
        } else {
            quaternion.setFromEuler(new Euler(euler.roll, euler.pitch, euler.yaw, 'ZYX', euler.unit));
        }
        return quaternion as InstanceType<T>;
    }

    /**
     * Convert axis angle rotation to quaternion
     * @param {any} axis Axis-angle rotation
     * @returns {Quaternion} Serializable quaternion
     */
    static fromAxisAngle<T extends typeof Quaternion>(
        this: T,
        axis: {
            x: number;
            y: number;
            z: number;
            angle?: number;
            unit?: AngleUnit;
        },
    ): InstanceType<T>;
    static fromAxisAngle<T extends typeof Quaternion>(this: T, axis: number[]): InstanceType<T>;
    static fromAxisAngle<T extends typeof Quaternion>(this: T, axis: AxisAngle): InstanceType<T>;
    static fromAxisAngle<T extends typeof Quaternion>(this: T, axis: any): InstanceType<T> {
        const quaternion = new this();
        if (axis instanceof AxisAngle) {
            quaternion.setFromAxisAngle(new Vector3(axis.x, axis.y, axis.z), axis.angle);
        } else if (axis instanceof Array) {
            const axisAngle = new AxisAngle(axis[0], axis[1], axis[2], axis.length === 4 ? axis[3] : null);
            quaternion.setFromAxisAngle(axisAngle, axisAngle.angle);
        } else {
            const axisAngle = new AxisAngle(axis.x, axis.y, axis.z, axis.angle ? axis.angle : null, axis.unit);
            quaternion.setFromAxisAngle(axisAngle, axisAngle.angle);
        }
        return quaternion as InstanceType<T>;
    }

    /**
     * Convert rotation matrix to quaternion
     * @param {Quaternion} this This type
     * @param {Matrix4} matrix Rotation matrix
     * @returns {Quaternion} Serializable quaternion
     */
    static fromRotationMatrix<T extends typeof Quaternion>(this: T, matrix: Matrix4): InstanceType<T> {
        const quaternion = new this();
        quaternion.setFromRotationMatrix(matrix);
        return quaternion as InstanceType<T>;
    }

    /**
     * Convert the quaternion to euler angles
     * @param {EulerOrder} order Euler order
     * @returns {Euler} Converted euler
     */
    toEuler(order?: EulerOrder): Euler {
        return Euler.fromQuaternion(this, order);
    }

    /**
     * Convert the quaternion to axis angles
     * @returns {AxisAngle} Converted axis angle
     */
    toAxisAngle(): AxisAngle {
        return AxisAngle.fromQuaternion(this);
    }

    /**
     * Convert quaternion to rotation matrix
     * @returns {Matrix4} Rotation matrix
     */
    toRotationMatrix(): Matrix4 {
        return Matrix4.rotationFromQuaternion(this);
    }

    clone(): this {
        return new (this.constructor as new () => this)().copy(this) as this;
    }
}