Object.assign(pc, (function () {
'use strict';
/**
* @constructor
* @name pc.Quat
* @classdesc A quaternion.
* @description Create a new Quat object.
* @param {Number} [x] The quaternion's x component. Default value 0. If x is an array of length 4, the array will be used to populate all components.
* @param {Number} [y] The quaternion's y component. Default value 0.
* @param {Number} [z] The quaternion's z component. Default value 0.
* @param {Number} [w] The quaternion's w component. Default value 1.
*/
var Quat = function (x, y, z, w) {
if (x && x.length === 4) {
this.x = x[0];
this.y = x[1];
this.z = x[2];
this.w = x[3];
} else {
this.x = (x === undefined) ? 0 : x;
this.y = (y === undefined) ? 0 : y;
this.z = (z === undefined) ? 0 : z;
this.w = (w === undefined) ? 1 : w;
}
};
/**
* @field
* @type Number
* @name pc.Quat#x
* @description The x component of the quaternion.
* @example
* var quat = new pc.Quat();
*
* // Get x
* var x = quat.x;
*
* // Set x
* quat.x = 0;
*/
/**
* @field
* @type Number
* @name pc.Quat#y
* @description The y component of the quaternion.
* @example
* var quat = new pc.Quat();
*
* // Get y
* var y = quat.y;
*
* // Set y
* quat.y = 0;
*/
/**
* @field
* @type Number
* @name pc.Quat#z
* @description The z component of the quaternion.
* @example
* var quat = new pc.Quat();
*
* // Get z
* var z = quat.z;
*
* // Set z
* quat.z = 0;
*/
/**
* @field
* @type Number
* @name pc.Quat#w
* @description The w component of the quaternion.
* @example
* var quat = new pc.Quat();
*
* // Get w
* var w = quat.w;
*
* // Set w
* quat.w = 0;
*/
Object.assign(Quat.prototype, {
/**
* @function
* @name pc.Quat#clone
* @description Returns an identical copy of the specified quaternion.
* @returns {pc.Quat} A quaternion containing the result of the cloning.
* @example
* var q = new pc.Quat(-0.11, -0.15, -0.46, 0.87);
* var qclone = q.clone();
*
* console.log("The result of the cloning is: " + q.toString());
*/
clone: function () {
return new pc.Quat(this.x, this.y, this.z, this.w);
},
conjugate: function () {
this.x *= -1;
this.y *= -1;
this.z *= -1;
return this;
},
/**
* @function
* @name pc.Quat#copy
* @description Copies the contents of a source quaternion to a destination quaternion.
* @param {pc.Quat} rhs The quaternion to be copied.
* @returns {pc.Quat} Self for chaining.
* @example
* var src = new pc.Quat();
* var dst = new pc.Quat();
* dst.copy(src, src);
* console.log("The two quaternions are " + (src.equals(dst) ? "equal" : "different"));
*/
copy: function (rhs) {
this.x = rhs.x;
this.y = rhs.y;
this.z = rhs.z;
this.w = rhs.w;
return this;
},
/**
* @function
* @name pc.Quat#equals
* @description Reports whether two quaternions are equal.
* @param {pc.Quat} rhs The quaternion to be compared against.
* @returns {Boolean} true if the quaternions are equal and false otherwise.
* @example
* var a = new pc.Quat();
* var b = new pc.Quat();
* console.log("The two quaternions are " + (a.equals(b) ? "equal" : "different"));
*/
equals: function (rhs) {
return ((this.x === rhs.x) && (this.y === rhs.y) && (this.z === rhs.z) && (this.w === rhs.w));
},
/**
* @function
* @name pc.Quat#getAxisAngle
* @description Gets the rotation axis and angle for a given
* quaternion. If a quaternion is created with
* setFromAxisAngle, this method will return the same
* values as provided in the original parameter list
* OR functionally equivalent values.
* @param {pc.Vec3} axis The 3-dimensional vector to receive the axis of rotation.
* @returns {Number} Angle, in degrees, of the rotation
* @example
* var q = new pc.Quat();
* q.setFromAxisAngle(new pc.Vec3(0, 1, 0), 90);
* var v = new pc.Vec3();
* var angle = q.getAxisAngle(v);
* // Should output 90
* console.log(angle)
* // Should output [0, 1, 0]
* console.log(v.toString());
*/
getAxisAngle: function (axis) {
var rad = Math.acos(this.w) * 2;
var s = Math.sin(rad / 2);
if (s !== 0) {
axis.x = this.x / s;
axis.y = this.y / s;
axis.z = this.z / s;
if (axis.x < 0 || axis.y < 0 || axis.z < 0) {
// Flip the sign
axis.x *= -1;
axis.y *= -1;
axis.z *= -1;
rad *= -1;
}
} else {
// If s is zero, return any axis (no rotation - axis does not matter)
axis.x = 1;
axis.y = 0;
axis.z = 0;
}
return rad * pc.math.RAD_TO_DEG;
},
/**
* @function
* @name pc.Quat#getEulerAngles
* @description Converts the supplied quaternion to Euler angles.
* @param {pc.Vec3} [eulers] The 3-dimensional vector to receive the Euler angles.
* @returns {pc.Vec3} The 3-dimensional vector holding the Euler angles that
* correspond to the supplied quaternion.
*/
getEulerAngles: function (eulers) {
var x, y, z, qx, qy, qz, qw, a2;
eulers = (eulers === undefined) ? new pc.Vec3() : eulers;
qx = this.x;
qy = this.y;
qz = this.z;
qw = this.w;
a2 = 2 * (qw * qy - qx * qz);
if (a2 <= -0.99999) {
x = 2 * Math.atan2(qx, qw);
y = -Math.PI / 2;
z = 0;
} else if (a2 >= 0.99999) {
x = 2 * Math.atan2(qx, qw);
y = Math.PI / 2;
z = 0;
} else {
x = Math.atan2(2 * (qw * qx + qy * qz), 1 - 2 * (qx * qx + qy * qy));
y = Math.asin(a2);
z = Math.atan2(2 * (qw * qz + qx * qy), 1 - 2 * (qy * qy + qz * qz));
}
return eulers.set(x, y, z).scale(pc.math.RAD_TO_DEG);
},
/**
* @function
* @name pc.Quat#invert
* @description Generates the inverse of the specified quaternion.
* @returns {pc.Quat} Self for chaining.
* @example
* // Create a quaternion rotated 180 degrees around the y-axis
* var rot = new pc.Quat().setFromEulerAngles(0, 180, 0);
*
* // Invert in place
* rot.invert();
*/
invert: function () {
return this.conjugate().normalize();
},
/**
* @function
* @name pc.Quat#length
* @description Returns the magnitude of the specified quaternion.
* @returns {Number} The magnitude of the specified quaternion.
* @example
* var q = new pc.Quat(0, 0, 0, 5);
* var len = q.length();
* // Should output 5
* console.log("The length of the quaternion is: " + len);
*/
length: function () {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w);
},
/**
* @function
* @name pc.Quat#lengthSq
* @description Returns the magnitude squared of the specified quaternion.
* @returns {Number} The magnitude of the specified quaternion.
* @example
* var q = new pc.Quat(3, 4, 0);
* var lenSq = q.lengthSq();
* // Should output 25
* console.log("The length squared of the quaternion is: " + lenSq);
*/
lengthSq: function () {
return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
},
/**
* @function
* @name pc.Quat#mul
* @description Returns the result of multiplying the specified quaternions together.
* @param {pc.Quat} rhs The quaternion used as the second multiplicand of the operation.
* @returns {pc.Quat} Self for chaining.
* @example
* var a = new pc.Quat().setFromEulerAngles(0, 30, 0);
* var b = new pc.Quat().setFromEulerAngles(0, 60, 0);
*
* // a becomes a 90 degree rotation around the Y axis
* // In other words, a = a * b
* a.mul(b);
*
* console.log("The result of the multiplication is: " a.toString());
*/
mul: function (rhs) {
var q1x, q1y, q1z, q1w, q2x, q2y, q2z, q2w;
q1x = this.x;
q1y = this.y;
q1z = this.z;
q1w = this.w;
q2x = rhs.x;
q2y = rhs.y;
q2z = rhs.z;
q2w = rhs.w;
this.x = q1w * q2x + q1x * q2w + q1y * q2z - q1z * q2y;
this.y = q1w * q2y + q1y * q2w + q1z * q2x - q1x * q2z;
this.z = q1w * q2z + q1z * q2w + q1x * q2y - q1y * q2x;
this.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z;
return this;
},
/**
* @function
* @name pc.Quat#mul2
* @description Returns the result of multiplying the specified quaternions together.
* @param {pc.Quat} lhs The quaternion used as the first multiplicand of the operation.
* @param {pc.Quat} rhs The quaternion used as the second multiplicand of the operation.
* @returns {pc.Quat} Self for chaining.
* @example
* var a = new pc.Quat().setFromEulerAngles(0, 30, 0);
* var b = new pc.Quat().setFromEulerAngles(0, 60, 0);
* var r = new pc.Quat();
*
* // r is set to a 90 degree rotation around the Y axis
* // In other words, r = a * b
* r.mul2(a, b);
*
* console.log("The result of the multiplication is: " r.toString());
*/
mul2: function (lhs, rhs) {
var q1x, q1y, q1z, q1w, q2x, q2y, q2z, q2w;
q1x = lhs.x;
q1y = lhs.y;
q1z = lhs.z;
q1w = lhs.w;
q2x = rhs.x;
q2y = rhs.y;
q2z = rhs.z;
q2w = rhs.w;
this.x = q1w * q2x + q1x * q2w + q1y * q2z - q1z * q2y;
this.y = q1w * q2y + q1y * q2w + q1z * q2x - q1x * q2z;
this.z = q1w * q2z + q1z * q2w + q1x * q2y - q1y * q2x;
this.w = q1w * q2w - q1x * q2x - q1y * q2y - q1z * q2z;
return this;
},
/**
* @function
* @name pc.Quat#normalize
* @description Returns the specified quaternion converted in place to a unit quaternion.
* @returns {pc.Quat} The result of the normalization.
* @example
* var v = new pc.Quat(0, 0, 0, 5);
*
* v.normalize();
*
* // Should output 0, 0, 0, 1
* console.log("The result of the vector normalization is: " + v.toString());
*/
normalize: function () {
var len = this.length();
if (len === 0) {
this.x = this.y = this.z = 0;
this.w = 1;
} else {
len = 1 / len;
this.x *= len;
this.y *= len;
this.z *= len;
this.w *= len;
}
return this;
},
/**
* @function
* @name pc.Quat#set
* @description Sets the specified quaternion to the supplied numerical values.
* @param {Number} x The x component of the quaternion.
* @param {Number} y The y component of the quaternion.
* @param {Number} z The z component of the quaternion.
* @param {Number} w The w component of the quaternion.
* @returns {pc.Quat} Self for chaining.
* @example
* var q = new pc.Quat();
* q.set(1, 0, 0, 0);
*
* // Should output 1, 0, 0, 0
* console.log("The result of the vector set is: " + q.toString());
*/
set: function (x, y, z, w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
return this;
},
/**
* @function
* @name pc.Quat#setFromAxisAngle
* @description Sets a quaternion from an angular rotation around an axis.
* @param {pc.Vec3} axis World space axis around which to rotate.
* @param {Number} angle Angle to rotate around the given axis in degrees.
* @returns {pc.Quat} Self for chaining.
* @example
* var q = new pc.Quat();
* q.setFromAxisAngle(pc.Vec3.UP, 90);
*/
setFromAxisAngle: function (axis, angle) {
var sa, ca;
angle *= 0.5 * pc.math.DEG_TO_RAD;
sa = Math.sin(angle);
ca = Math.cos(angle);
this.x = sa * axis.x;
this.y = sa * axis.y;
this.z = sa * axis.z;
this.w = ca;
return this;
},
/**
* @function
* @name pc.Quat#setFromEulerAngles
* @description Sets a quaternion from Euler angles specified in XYZ order.
* @param {Number} ex Angle to rotate around X axis in degrees.
* @param {Number} ey Angle to rotate around Y axis in degrees.
* @param {Number} ez Angle to rotate around Z axis in degrees.
* @returns {pc.Quat} Self for chaining.
* @example
* var q = new pc.Quat();
* q.setFromEulerAngles(45, 90, 180);
*/
setFromEulerAngles: function (ex, ey, ez) {
var sx, cx, sy, cy, sz, cz, halfToRad;
halfToRad = 0.5 * pc.math.DEG_TO_RAD;
ex *= halfToRad;
ey *= halfToRad;
ez *= halfToRad;
sx = Math.sin(ex);
cx = Math.cos(ex);
sy = Math.sin(ey);
cy = Math.cos(ey);
sz = Math.sin(ez);
cz = Math.cos(ez);
this.x = sx * cy * cz - cx * sy * sz;
this.y = cx * sy * cz + sx * cy * sz;
this.z = cx * cy * sz - sx * sy * cz;
this.w = cx * cy * cz + sx * sy * sz;
return this;
},
/**
* @function
* @name pc.Quat#setFromMat4
* @description Converts the specified 4x4 matrix to a quaternion. Note that since
* a quaternion is purely a representation for orientation, only the translational part
* of the matrix is lost.
* @param {pc.Mat4} m The 4x4 matrix to convert.
* @returns {pc.Quat} Self for chaining.
* @example
* // Create a 4x4 rotation matrix of 180 degrees around the y-axis
* var rot = new pc.Mat4().setFromAxisAngle(pc.Vec3.UP, 180);
*
* // Convert to a quaternion
* var q = new pc.Quat().setFromMat4(rot);
*/
setFromMat4: function (m) {
var m00, m01, m02, m10, m11, m12, m20, m21, m22,
tr, s, rs, lx, ly, lz;
m = m.data;
// Cache matrix values for super-speed
m00 = m[0];
m01 = m[1];
m02 = m[2];
m10 = m[4];
m11 = m[5];
m12 = m[6];
m20 = m[8];
m21 = m[9];
m22 = m[10];
// Remove the scale from the matrix
lx = m00 * m00 + m01 * m01 + m02 * m02;
if (lx === 0)
return this;
lx = 1 / Math.sqrt(lx);
ly = m10 * m10 + m11 * m11 + m12 * m12;
if (ly === 0)
return this;
ly = 1 / Math.sqrt(ly);
lz = m20 * m20 + m21 * m21 + m22 * m22;
if (lz === 0)
return this;
lz = 1 / Math.sqrt(lz);
m00 *= lx;
m01 *= lx;
m02 *= lx;
m10 *= ly;
m11 *= ly;
m12 *= ly;
m20 *= lz;
m21 *= lz;
m22 *= lz;
// http://www.cs.ucr.edu/~vbz/resources/quatut.pdf
tr = m00 + m11 + m22;
if (tr >= 0) {
s = Math.sqrt(tr + 1);
this.w = s * 0.5;
s = 0.5 / s;
this.x = (m12 - m21) * s;
this.y = (m20 - m02) * s;
this.z = (m01 - m10) * s;
} else {
if (m00 > m11) {
if (m00 > m22) {
// XDiagDomMatrix
rs = (m00 - (m11 + m22)) + 1;
rs = Math.sqrt(rs);
this.x = rs * 0.5;
rs = 0.5 / rs;
this.w = (m12 - m21) * rs;
this.y = (m01 + m10) * rs;
this.z = (m02 + m20) * rs;
} else {
// ZDiagDomMatrix
rs = (m22 - (m00 + m11)) + 1;
rs = Math.sqrt(rs);
this.z = rs * 0.5;
rs = 0.5 / rs;
this.w = (m01 - m10) * rs;
this.x = (m20 + m02) * rs;
this.y = (m21 + m12) * rs;
}
} else if (m11 > m22) {
// YDiagDomMatrix
rs = (m11 - (m22 + m00)) + 1;
rs = Math.sqrt(rs);
this.y = rs * 0.5;
rs = 0.5 / rs;
this.w = (m20 - m02) * rs;
this.z = (m12 + m21) * rs;
this.x = (m10 + m01) * rs;
} else {
// ZDiagDomMatrix
rs = (m22 - (m00 + m11)) + 1;
rs = Math.sqrt(rs);
this.z = rs * 0.5;
rs = 0.5 / rs;
this.w = (m01 - m10) * rs;
this.x = (m20 + m02) * rs;
this.y = (m21 + m12) * rs;
}
}
return this;
},
/**
* @function
* @name pc.Quat#slerp
* @description Performs a spherical interpolation between two quaternions. The result of
* the interpolation is written to the quaternion calling the function.
* @param {pc.Quat} lhs The quaternion to interpolate from.
* @param {pc.Quat} rhs The quaternion to interpolate to.
* @param {Number} alpha The value controlling the interpolation in relation to the two input
* quaternions. The value is in the range 0 to 1, 0 generating q1, 1 generating q2 and anything
* in between generating a spherical interpolation between the two.
* @returns {pc.Quat} Self for chaining.
* @example
* var q1 = new pc.Quat(-0.11,-0.15,-0.46,0.87);
* var q2 = new pc.Quat(-0.21,-0.21,-0.67,0.68);
*
* var result;
* result = new pc.Quat().slerp(q1, q2, 0); // Return q1
* result = new pc.Quat().slerp(q1, q2, 0.5); // Return the midpoint interpolant
* result = new pc.Quat().slerp(q1, q2, 1); // Return q2
*/
slerp: function (lhs, rhs, alpha) {
// Algorithm sourced from:
// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
var lx, ly, lz, lw, rx, ry, rz, rw;
lx = lhs.x;
ly = lhs.y;
lz = lhs.z;
lw = lhs.w;
rx = rhs.x;
ry = rhs.y;
rz = rhs.z;
rw = rhs.w;
// Calculate angle between them.
var cosHalfTheta = lw * rw + lx * rx + ly * ry + lz * rz;
if (cosHalfTheta < 0) {
rw = -rw;
rx = -rx;
ry = -ry;
rz = -rz;
cosHalfTheta = -cosHalfTheta;
}
// If lhs == rhs or lhs == -rhs then theta == 0 and we can return lhs
if (Math.abs(cosHalfTheta) >= 1) {
this.w = lw;
this.x = lx;
this.y = ly;
this.z = lz;
return this;
}
// Calculate temporary values.
var halfTheta = Math.acos(cosHalfTheta);
var sinHalfTheta = Math.sqrt(1 - cosHalfTheta * cosHalfTheta);
// If theta = 180 degrees then result is not fully defined
// we could rotate around any axis normal to qa or qb
if (Math.abs(sinHalfTheta) < 0.001) {
this.w = (lw * 0.5 + rw * 0.5);
this.x = (lx * 0.5 + rx * 0.5);
this.y = (ly * 0.5 + ry * 0.5);
this.z = (lz * 0.5 + rz * 0.5);
return this;
}
var ratioA = Math.sin((1 - alpha) * halfTheta) / sinHalfTheta;
var ratioB = Math.sin(alpha * halfTheta) / sinHalfTheta;
// Calculate Quaternion.
this.w = (lw * ratioA + rw * ratioB);
this.x = (lx * ratioA + rx * ratioB);
this.y = (ly * ratioA + ry * ratioB);
this.z = (lz * ratioA + rz * ratioB);
return this;
},
/**
* @function
* @name pc.Quat#transformVector
* @description Transforms a 3-dimensional vector by the specified quaternion.
* @param {pc.Vec3} vec The 3-dimensional vector to be transformed.
* @param {pc.Vec3} [res] An optional 3-dimensional vector to receive the result of the transformation.
* @returns {pc.Vec3} The input vector v transformed by the current instance.
* @example
* // Create a 3-dimensional vector
* var v = new pc.Vec3(1, 2, 3);
*
* // Create a 4x4 rotation matrix
* var q = new pc.Quat().setFromEulerAngles(10, 20, 30);
*
* var tv = q.transformVector(v);
*/
transformVector: function (vec, res) {
if (res === undefined) {
res = new pc.Vec3();
}
var x = vec.x, y = vec.y, z = vec.z;
var qx = this.x, qy = this.y, qz = this.z, qw = this.w;
// calculate quat * vec
var ix = qw * x + qy * z - qz * y;
var iy = qw * y + qz * x - qx * z;
var iz = qw * z + qx * y - qy * x;
var iw = -qx * x - qy * y - qz * z;
// calculate result * inverse quat
res.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
res.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
res.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;
return res;
},
/**
* @function
* @name pc.Quat#toString
* @description Converts the quaternion to string form.
* @returns {String} The quaternion in string form.
* @example
* var v = new pc.Quat(0, 0, 0, 1);
* // Should output '[0, 0, 0, 1]'
* console.log(v.toString());
*/
toString: function () {
return '[' + this.x + ', ' + this.y + ', ' + this.z + ', ' + this.w + ']';
}
});
/**
* @field
* @static
* @readonly
* @type pc.Quat
* @name pc.Quat.IDENTITY
* @description A constant quaternion set to [0, 0, 0, 1] (the identity).
*/
Object.defineProperty(Quat, 'IDENTITY', {
get: (function () {
var identity = new Quat();
return function () {
return identity;
};
}())
});
/**
* @field
* @static
* @readonly
* @type pc.Quat
* @name pc.Quat.ZERO
* @description A constant quaternion set to [0, 0, 0, 0].
*/
Object.defineProperty(Quat, 'ZERO', {
get: (function () {
var zero = new Quat(0, 0, 0, 0);
return function () {
return zero;
};
}())
});
return {
Quat: Quat
};
}()));