Repository

js/Mobilizing/renderer/3D/three/types/Quaternion.js

import * as THREE from 'three';
import Vector3 from "./Vector3";
import Euler from "./Euler";
import * as _Math from "../../../../core/util/Math";
/**
* Represents a Quaternion, a way to describe a spatial rotation that is less susceptible to gimbal lock.
*
* This class extends the one from Three.js, API available here : https://threejs.org/docs/index.html#api/en/math/Quaternion
*/
export default class Quaternion extends THREE.Quaternion {
    /**
    * @returns {Object} {upVector, forwardVector, leftVector}
    */
    getDirections() {
        const upVector = new Vector3();
        upVector.x = 2 * (this.x * this.y - this.w * this.z);
        upVector.y = 1 - 2 * (this.x * this.x + this.z * this.z);
        upVector.z = 2 * (this.y * this.z + this.w * this.x);

        const forwardVector = new Vector3();
        forwardVector.x = 2 * (this.x * this.z + this.w * this.y);
        forwardVector.y = 2 * (this.y * this.z - this.w * this.x);
        forwardVector.z = 1 - 2 * (this.x * this.x + this.y * this.y);

        const leftVector = new Vector3();
        leftVector.x = 1 - 2 * (this.y * this.y + this.z * this.z);
        leftVector.y = 2 * (this.x * this.y + this.w * this.z);
        leftVector.z = 2 * (this.x * this.z - this.w * this.y);

        const result = {
            upVector,
            forwardVector,
            leftVector
        };

        return result;
    }

    /**
    * Creates a Quaternion that matches the device's current orientation. Calculated in radians.
    * @private
    * @param {Number} alpha the angle to use for the quaternion creation
    * @param {Number} beta  the angle to use for the quaternion creation
    * @param {Number} gamma the angle to use for the quaternion creation
    * @param {Object} screenOrientation orientation of the screen in degree! Can be very different depending on the plateform...
    * @author Rich Tibbett, https://github.com/richtr/threeVR
    */
    createGyroQuaternion(alpha, beta, gamma, screenOrientation) {
        if (alpha !== 0 && beta !== 0 && gamma !== 0) {
            //const finalQuaternion = new Quaternion();
            const deviceEuler = new Euler();
            const screenTransform = new Quaternion();
            const worldTransform = new Quaternion(- Math.sqrt(0.5), 0, 0, Math.sqrt(0.5)); // - PI/2 around the x-axis
            let minusHalfAngle = 0;

            deviceEuler.set(beta, alpha, -gamma, 'YXZ');
            this.setFromEuler(deviceEuler);
            minusHalfAngle = -screenOrientation / 2;

            screenTransform.set(0, Math.sin(minusHalfAngle), 0, Math.cos(minusHalfAngle));

            this.multiply(screenTransform);
            this.multiply(worldTransform);
        }
    }

    /**
    * Can be used in the transform of a perspective camera to produce a "blind camera" effect : move the screen around to frame a part of the current scene like if you were using a camera.
    */
    setFromGyro(compass) {
        if (compass.alpha) {
            this.createGyroQuaternion(_Math.degToRad(compass.alpha), _Math.degToRad(compass.beta), _Math.degToRad(compass.gamma), _Math.degToRad(window.orientation || 0));
        }
    }

}