function Quaternion() { this.loadIdentity(); } Quaternion.prototype.copy = function() { var result = new Quaternion(); result.x = this.x; result.y = this.y; result.z = this.z; result.w = this.w; return result; } Quaternion.prototype.loadIdentity = function() { this.x = 0.0; this.y = 0.0; this.z = 0.0; this.w = 1.0; } Quaternion.withValues = function(x, y, z, w) { var quaternion = new Quaternion(); quaternion.x = x; quaternion.y = y; quaternion.z = z; quaternion.w = w; return quaternion; } Quaternion.fromVector = function(vector) { var quaternion = new Quaternion(); quaternion.x = vector.x; quaternion.y = vector.y; quaternion.z = vector.z; quaternion.w = 0.0; return quaternion; } Quaternion.prototype.toVector3 = function() { var vector = new Vector3(); vector.x = this.x; vector.y = this.y; vector.z = this.z; return vector; } Quaternion.fromAxisAngle = function(axis, angle) { var quaternion = new Quaternion(); var sinAngle; angle *= 0.5; axis.normalize(); sinAngle = Math.sin(angle); quaternion.x = (axis.x * sinAngle); quaternion.y = (axis.y * sinAngle); quaternion.z = (axis.z * sinAngle); quaternion.w = Math.cos(angle); return quaternion; } Quaternion.prototype.toAxisAngle = function() { var axis = new Vector3(); var angle; var sinAngle; var quaternion = this.copy(); quaternion.normalize(); sinAngle = Math.sqrt(1.0 - (quaternion.w * quaternion.w)); if (Math.abs(sinAngle) < 0.0005) { sinAngle = 1.0; } axis.x = quaternion.x / sinAngle; axis.y = quaternion.y / sinAngle; axis.z = quaternion.z / sinAngle; angle = Math.acos(quaternion.w) * 2.0; return {"axis": axis, "angle": angle}; } Quaternion.prototype.toMatrix = function() { var matrix = new Matrix(); matrix.m[0] = (1.0 - (2.0 * ((this.y * this.y) + (this.z * this.z)))); matrix.m[1] = (2.0 * ((this.x * this.y) + (this.z * this.w))); matrix.m[2] = (2.0 * ((this.x * this.z) - (this.y * this.w))); matrix.m[3] = 0.0; matrix.m[4] = (2.0 * ((this.x * this.y) - (this.z * this.w))); matrix.m[5] = (1.0 - (2.0 * ((this.x * this.x) + (this.z * this.z)))); matrix.m[6] = (2.0 * ((this.y * this.z) + (this.x * this.w))); matrix.m[7] = 0.0; matrix.m[8] = (2.0 * ((this.x * this.z) + (this.y * this.w))); matrix.m[9] = (2.0 * ((this.y * this.z) - (this.x * this.w))); matrix.m[10] = (1.0 - (2.0 * ((this.x * this.x) + (this.y * this.y)))); matrix.m[11] = 0.0; matrix.m[12] = 0.0; matrix.m[13] = 0.0; matrix.m[14] = 0.0; matrix.m[15] = 1.0; return matrix; } Quaternion.prototype.normalize = function() { var magnitude; magnitude = Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z) + (this.w * this.w)); this.x /= magnitude; this.y /= magnitude; this.z /= magnitude; this.w /= magnitude; } Quaternion.prototype.normalized = function() { var result = this.copy(); result.normalize(); return result; } Quaternion.prototype.multiply = function(quaternion) { var vector1, vector2, cross; var angle; vector1 = this.toVector3(); vector2 = quaternion.toVector3(); angle = this.w * quaternion.w - vector1.dot(vector2); cross = vector1.cross(vector2); vector1.x *= quaternion.w; vector1.y *= quaternion.w; vector1.z *= quaternion.w; vector2.x *= this.w; vector2.y *= this.w; vector2.z *= this.w; this.x = vector1.x + vector2.x + cross.x; this.y = vector1.y + vector2.y + cross.y; this.z = vector1.z + vector2.z + cross.z; this.w = angle; } Quaternion.prototype.multiplied = function(quaternion) { var result = this.copy(); result.multiply(quaternion); return result; } Quaternion.prototype.slerp = function(dest, alpha) { const SLERP_TO_LERP_SWITCH_THRESHOLD = 0.01; var startWeight, endWeight, difference; var result = new Quaternion; difference = (this.x * dest.x) + (this.y * dest.y) + (this.z * dest.z) + (this.w * dest.w); if ((1.0 - Math.abs(difference)) > SLERP_TO_LERP_SWITCH_THRESHOLD) { var theta, oneOverSinTheta; theta = Math.acos(Math.abs(difference)); oneOverSinTheta = 1.0 / Math.sin(theta); startWeight = Math.sin(theta * (1.0 - alpha)) * oneOverSinTheta; endWeight = Math.sin(theta * alpha) * oneOverSinTheta; if (difference < 0.0) { startWeight = -startWeight; } } else { startWeight = 1.0 - alpha; endWeight = alpha; } result.x = (this.x * startWeight) + (dest.x * endWeight); result.y = (this.y * startWeight) + (dest.y * endWeight); result.z = (this.z * startWeight) + (dest.z * endWeight); result.w = (this.w * startWeight) + (dest.w * endWeight); result.normalize(); this.x = result.x; this.y = result.y; this.z = result.z; this.w = result.w; } Quaternion.prototype.slerped = function(dest, alpha) { var result = this.copy(); result.slerp(dest, alpha); return result; } Quaternion.prototype.rotate = function(axis, angle) { var rotationQuaternion; rotationQuaternion = Quaternion.fromAxisAngle(axis, angle); this.multiply(rotationQuaternion); } Quaternion.prototype.rotated = function(axis, angle) { var result = this.copy(); result.rotate(axis, angle); return result; } Quaternion.prototype.invert = function() { var length; length = 1.0 / ((this.x * this.x) + (this.y * this.y) + (this.z * this.z) + (this.w * this.w)); this.x *= -length; this.y *= -length; this.z *= -length; this.w *= length; } Quaternion.prototype.inverted = function() { var result = this.copy(); result.invert(); return result; } Quaternion.prototype.multiplyVector3 = function(vector) { var vectorQuaternion, inverseQuaternion, result; vectorQuaternion = Quaternion.fromVector(vector); inverseQuaternion = quaternion.inverted(); result = quaternion.multiplied(vectorQuaternion.multiplied(inverseQuaternion)); return result.toVector3(); } Quaternion.prototype.multiplyVector4 = function(vector) { var vector3; vector3 = quaternion.multiplyVector3(Vector3.withValues(vector.x, vector.y, vector.z)); return Vector4.withValues(vector3.x, vector3.y, vector3.z, vector.w); }