Source: math/Quaternion.js

``````/**
*  @class Quaternion
*  @memberof SQR
*
*  @description Represents a quaternion with optionally setting the values directly.
*
*  Just as a reminder, given an angle `a` and an axis `x,y,z`
*  this is what the quaternion values are:
*  @example
var q = new SQR.Quaternion();
var s = Math.sin(a / 2);
q.x = x * s;
q.y = y * s;
q.z = z * s;
q.w = Math.cos(a / 2);
*/
SQR.Quaternion = function(x, y, z, w) {
this.set(w, x, y, z);
}

/**
*  Set value of the Quaternion directly.
*/
SQR.Quaternion.prototype.set = function(x, y, z, w) {
this.w = w || 1;
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
return this;
}

SQR.Quaternion.prototype.copyTo = function(p) {
p.x = this.x;
p.y = this.y;
p.z = this.z;
p.w = this.w;
return this;
}

/**
*  Copy the values from another quaternion.
*  @param q the quaternion to copy values from
*/
SQR.Quaternion.prototype.copyFrom = function(q) {
if(q instanceof Array || q instanceof Float32Array) {
this.x = q[0];
this.y = q[1];
this.z = q[2];
this.w = q[3];
} else {
this.x = q.x;
this.y = q.y;
this.z = q.z;
this.w = q.w;
}
return this;
}

/**
*  Resets the quaternion values to identity.
*/
SQR.Quaternion.prototype.identity = function() {
this.set();
return this;
}

/**
* Multiplies rq (or this if no rq) by q
* @param q
* @param rq if not defined this is multiplied by q
*/
SQR.Quaternion.prototype.mul = function(q, rq) {
rq = rq || this;

var w = (rq.w * q.w - rq.x * q.x - rq.y * q.y - rq.z * q.z);
var x = (rq.w * q.x + rq.x * q.w + rq.y * q.z - rq.z * q.y);
var y = (rq.w * q.y - rq.x * q.z + rq.y * q.w + rq.z * q.x);
var z = (rq.w * q.z + rq.x * q.y - rq.y * q.x + rq.z * q.w);

rq.set(x, y, z, w);

rq.normalize();

return rq;
}

SQR.Quaternion.prototype.dot = function(q) {
return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;
}

/**
*  Sets the quaternion to point in the given direction.
*  @param _dir the direction to look at
*  @param _up the up vector
*/
SQR.Quaternion.prototype.lookAt = function(_dir, _up) {

var dir = SQR.Quaternion.__tv1;
var right = SQR.Quaternion.__tv2;
var up = SQR.Quaternion.__tv3;

_dir.copyTo(dir);
_up.copyTo(up);

dir.norm();

// If direction is back, the returned quaternion is flipped. Not sure why, but that fixes it.
if(dir.z == -1) {
dir.x = 0.0001;
dir.norm();
}

// Probably should do the orthonormalization but not sure how that works :)
// tangent.sub(up, forward.mul(SQR.V3.dot(forward, up))).norm();
right.cross(up, dir);
up.cross(dir, right);

this.w = Math.sqrt(1 + right.x + up.y + dir.z) * 0.5;
var rc = 4 * this.w;
this.x = (dir.y - up.z) / rc;
this.y = (right.z - dir.x) / rc;
this.z = (up.x - right.y) / rc;

this.normalize();

return this;
}

/**
*  Creates a quaternion out of an angle axis representation.
*  @param a angle in radians
*  @param x x component of the axis
*  @param y y component of the axis
*  @param z z component of the axis
*/
SQR.Quaternion.prototype.fromAngleAxis = function(a, x, y, z) {
var s = Math.sin(a / 2);
this.x = x * s;
this.y = y * s;
this.z = z * s;
this.w = Math.cos(a / 2);
return this;
}

/**
*  Returns the magniture of the quaternion.
*/
SQR.Quaternion.prototype.mag = function() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
}

/**
*  Normalizes the quaternion.
*/
SQR.Quaternion.prototype.normalize = function() {
var n = this.mag();
this.x /= n;
this.y /= n;
this.z /= n;
this.w /= n;
return this;
}

/**
*  Negates the quaternion.
*/
SQR.Quaternion.prototype.neg = function() {
this.x *= -1;
this.y *= -1;
this.z *= -1;
this.w *= -1;
return this;
}

/**
*  Inverses the quaternion.
*/
SQR.Quaternion.prototype.inverse = function() {
var d = this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
this.x = -this.x / d;
this.y = -this.y / d;
this.z = -this.z / d;
this.w = this.w / d;
}

/**
*  That method doesn't do anything.
*  Check {SQR.Matrix44.TQS()} to see how to turn a
*  Quanternion into a matrix representation.
*
*  @todo Implement (or not... not sure how much this is needed)
*/
SQR.Quaternion.prototype.toMatrix = function(m) {
throw "SQR.Quaternion.toMatrix() is not implemented. Check SQR.Matrix44.TQS()";
}

// This one is from three.js (used for reference )
// SQR.Quaternion.slerp2 = function(qa, qb, t, qr) {

//     qr = qr || new SQR.Quaternion();

//     if ( t === 0 ) return qr.copyFrom(qa);
//     if ( t === 1 ) return qr.copyFrom(qb);

//     var x = qa.x, y = qa.y, z = qa.z, w = qa.w;

//     // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
//     var cosHalfTheta = w * qb.w + x * qb.x + y * qb.y + z * qb.z;

//     if (cosHalfTheta < 0) {
//         qr.w = - qb.w;
//         qr.x = - qb.x;
//         qr.y = - qb.y;
//         qr.z = - qb.z;
//         cosHalfTheta = - cosHalfTheta;
//     } else {
//         qr.copyFrom( qb );
//     }

//     if (cosHalfTheta >= 1.0) {
//         qr.w = w;
//         qr.x = x;
//         qr.y = y;
//         qr.z = z;
//         return qr;
//     }

//     var halfTheta = Math.acos( cosHalfTheta );
//     var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );

//     if (Math.abs( sinHalfTheta ) < 0.001) {
//         qr.w = 0.5 * (w + qr.w);
//         qr.x = 0.5 * (x + qr.x);
//         qr.y = 0.5 * (y + qr.y);
//         qr.z = 0.5 * (z + qr.z);
//         return qr;
//     }

//     var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
//     ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;

//     qr.w = (w * ratioA + qr.w * ratioB);
//     qr.x = (x * ratioA + qr.x * ratioB);
//     qr.y = (y * ratioA + qr.y * ratioB);
//     qr.z = (z * ratioA + qr.z * ratioB);

//     return qr;

// }

/**
*  Returns a spherical linear interpolation between two quaternions.
*  @param qa first quaternion
*  @param qb second quaternion
*  @param t interpolation value [0-1]
*  @param qr the quaterion to store the results in and return. If omitted results are returned in a new quaternion object.
*/
SQR.Quaternion.slerp = function(qa, qb, t, qr) {
qr = qr || new SQR.Quaternion();

if (t === 0) return qr.copyFrom(qa);
if (t === 1) return qr.copyFrom(qb);

// Try taking the dot product of your two quaternions (i.e., the 4-D dot product),
// and if the dot product is negative, replace your quaterions q1 and q2 with -q1 and q2 before performing Slerp.
// http://stackoverflow.com/questions/2886606/flipping-issue-when-interpolating-rotations-using-quaternions

// (This is not working for me)

var cha = SQR.Quaternion.dot(qa, qb);

if(cha < 0) {
qa.neg();
cha = SQR.Quaternion.dot(qa, qb);
}

var ha = Math.acos(cha);
var sha = Math.sqrt(1 - cha * cha);
var ra = Math.sin((1 - t) * ha) / sha;
var rb = Math.sin(t * ha) / sha;

if (Math.abs(cha) >= 1) {
// If angle is 0 (i.e cos(a) = 1) just
// return the first quaternion
ra = 1;
rb = 0;
} else if (Math.abs(sha) < 0.001) {
// If angle is 180 deg (i.e. sin(a) = 0) there is
// an infinite amount of possible rotations between those 2
ra = 0.5;
rb = 0.5;
}

qr.w = (qa.w * ra + qb.w * rb);
qr.x = (qa.x * ra + qb.x * rb);
qr.y = (qa.y * ra + qb.y * rb);
qr.z = (qa.z * ra + qb.z * rb);
return qr;
}

SQR.Quaternion.dot = function(qa, qb) {
return qa.x * qb.x + qa.y * qb.y + qa.z * qb.z + qa.w * qb.w;
}

SQR.Quaternion.prototype.slerp = function(qa, qb, t) {
SQR.Quaternion.slerp(qa, qb, t, this);
return this;
}

SQR.Quaternion.__tv1 = new SQR.Quaternion();
SQR.Quaternion.__tv2 = new SQR.Quaternion();
SQR.Quaternion.__tv3 = new SQR.Quaternion();

``````