All files / src Quaternion.js

95.12% Statements 39/41
50% Branches 4/8
90% Functions 9/10
95.12% Lines 39/41

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76  5x   90x 90x 90x 90x         30x         15x       15x     15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x     15x   15x   15x 15x     15x 15x 15x 15x 15x   15x 15x   15x 15x   15x 15x       30x       15x           15x 15x    
import {vectorLength,multiplyVector} from './vector'
export const Quaternion = class Quaternion {
    constructor(x, y, z, w) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.w = w;
    }
 
    static fromVector(vector) {
        // Convert vector to quaternion with w = 0
        return new Quaternion(vector.x, vector.y, vector.z, 0);
    }
 
    toVector() {
        // Convert quaternion back to vector, ignoring w
        return {x:this.x, y:this.y, z:this.z};
    }
 
    static dot(q1, q2) {
        return q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
    }
    static slerpFromVectors(u,v,t) {
        const normU=vectorLength(u)
        const normV=vectorLength(v)
        const newNormW = normU+Math.pow(Math.sin(t*Math.PI/2),2)*(normV - normU)
        const U=normU!=0?multiplyVector(1/normU,u):u
        const V=normV!=0?multiplyVector(1/normV,v):V
        const qU=Quaternion.fromVector(U)
        const qV=Quaternion.fromVector(V)
        const qW = Quaternion.slerp(qU,qV,t)
        const W=qW.toVector()
        const normW = vectorLength(W)
        const w = normW!=0 ? multiplyVector(newNormW/normW,W) : W
        return w
    }
    static slerp(q1, q2, t) {
        let dot = Quaternion.dot(q1, q2)
        // Clamp dot product to stay in the domain of acos()
        dot = Math.max(-1, Math.min(1, dot))
 
        const theta_0 = Math.acos(dot)
        Iif(theta_0<0.01){
            return q1
        }
        const theta = theta_0 * t
        const thetaComp = theta_0 * (1-t)
        const sinTheta = Math.sin(theta)
        const sinThetaComp = Math.sin(thetaComp)
        const sinTheta_0 = Math.sin(theta_0)
 
        const s1 = sinThetaComp/sinTheta_0
        const s2 = sinTheta/sinTheta_0
 
        const q1Component = q1.multiply(s1)
        const q2Component = q2.multiply(s2)
 
        const result = q1Component.add(q2Component)
        return result.normalize()
    }
 
    multiply(scalar) {
        return new Quaternion(this.x * scalar, this.y * scalar, this.z * scalar, this.w * scalar);
    }
 
    add(q) {
        return new Quaternion(this.x + q.x, this.y + q.y, this.z + q.z, this.w + q.w);
    }
    subtract(q) {
        return new Quaternion(this.x - q.x, this.y - q.y, this.z - q.z, this.w - q.w);
    }
    normalize() {
        const norm = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
        return new Quaternion(this.x / norm, this.y / norm, this.z / norm, this.w / norm);
    }
}