1/* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.
2
3Permission is hereby granted, free of charge, to any person obtaining a copy
4of this software and associated documentation files (the "Software"), to deal
5in the Software without restriction, including without limitation the rights
6to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7copies of the Software, and to permit persons to whom the Software is
8furnished to do so, subject to the following conditions:
9
10The above copyright notice and this permission notice shall be included in
11all copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19THE SOFTWARE. */
20
21var glMatrix = require("./common.js");
22var mat3 = require("./mat3.js");
23var vec3 = require("./vec3.js");
24var vec4 = require("./vec4.js");
25
26/**
27 * @class Quaternion
28 * @name quat
29 */
30var quat = {};
31
32/**
33 * Creates a new identity quat
34 *
35 * @returns {quat} a new quaternion
36 */
37quat.create = function() {
38    var out = new glMatrix.ARRAY_TYPE(4);
39    out[0] = 0;
40    out[1] = 0;
41    out[2] = 0;
42    out[3] = 1;
43    return out;
44};
45
46/**
47 * Sets a quaternion to represent the shortest rotation from one
48 * vector to another.
49 *
50 * Both vectors are assumed to be unit length.
51 *
52 * @param {quat} out the receiving quaternion.
53 * @param {vec3} a the initial vector
54 * @param {vec3} b the destination vector
55 * @returns {quat} out
56 */
57quat.rotationTo = (function() {
58    var tmpvec3 = vec3.create();
59    var xUnitVec3 = vec3.fromValues(1,0,0);
60    var yUnitVec3 = vec3.fromValues(0,1,0);
61
62    return function(out, a, b) {
63        var dot = vec3.dot(a, b);
64        if (dot < -0.999999) {
65            vec3.cross(tmpvec3, xUnitVec3, a);
66            if (vec3.length(tmpvec3) < 0.000001)
67                vec3.cross(tmpvec3, yUnitVec3, a);
68            vec3.normalize(tmpvec3, tmpvec3);
69            quat.setAxisAngle(out, tmpvec3, Math.PI);
70            return out;
71        } else if (dot > 0.999999) {
72            out[0] = 0;
73            out[1] = 0;
74            out[2] = 0;
75            out[3] = 1;
76            return out;
77        } else {
78            vec3.cross(tmpvec3, a, b);
79            out[0] = tmpvec3[0];
80            out[1] = tmpvec3[1];
81            out[2] = tmpvec3[2];
82            out[3] = 1 + dot;
83            return quat.normalize(out, out);
84        }
85    };
86})();
87
88/**
89 * Sets the specified quaternion with values corresponding to the given
90 * axes. Each axis is a vec3 and is expected to be unit length and
91 * perpendicular to all other specified axes.
92 *
93 * @param {vec3} view  the vector representing the viewing direction
94 * @param {vec3} right the vector representing the local "right" direction
95 * @param {vec3} up    the vector representing the local "up" direction
96 * @returns {quat} out
97 */
98quat.setAxes = (function() {
99    var matr = mat3.create();
100
101    return function(out, view, right, up) {
102        matr[0] = right[0];
103        matr[3] = right[1];
104        matr[6] = right[2];
105
106        matr[1] = up[0];
107        matr[4] = up[1];
108        matr[7] = up[2];
109
110        matr[2] = -view[0];
111        matr[5] = -view[1];
112        matr[8] = -view[2];
113
114        return quat.normalize(out, quat.fromMat3(out, matr));
115    };
116})();
117
118/**
119 * Creates a new quat initialized with values from an existing quaternion
120 *
121 * @param {quat} a quaternion to clone
122 * @returns {quat} a new quaternion
123 * @function
124 */
125quat.clone = vec4.clone;
126
127/**
128 * Creates a new quat initialized with the given values
129 *
130 * @param {Number} x X component
131 * @param {Number} y Y component
132 * @param {Number} z Z component
133 * @param {Number} w W component
134 * @returns {quat} a new quaternion
135 * @function
136 */
137quat.fromValues = vec4.fromValues;
138
139/**
140 * Copy the values from one quat to another
141 *
142 * @param {quat} out the receiving quaternion
143 * @param {quat} a the source quaternion
144 * @returns {quat} out
145 * @function
146 */
147quat.copy = vec4.copy;
148
149/**
150 * Set the components of a quat to the given values
151 *
152 * @param {quat} out the receiving quaternion
153 * @param {Number} x X component
154 * @param {Number} y Y component
155 * @param {Number} z Z component
156 * @param {Number} w W component
157 * @returns {quat} out
158 * @function
159 */
160quat.set = vec4.set;
161
162/**
163 * Set a quat to the identity quaternion
164 *
165 * @param {quat} out the receiving quaternion
166 * @returns {quat} out
167 */
168quat.identity = function(out) {
169    out[0] = 0;
170    out[1] = 0;
171    out[2] = 0;
172    out[3] = 1;
173    return out;
174};
175
176/**
177 * Sets a quat from the given angle and rotation axis,
178 * then returns it.
179 *
180 * @param {quat} out the receiving quaternion
181 * @param {vec3} axis the axis around which to rotate
182 * @param {Number} rad the angle in radians
183 * @returns {quat} out
184 **/
185quat.setAxisAngle = function(out, axis, rad) {
186    rad = rad * 0.5;
187    var s = Math.sin(rad);
188    out[0] = s * axis[0];
189    out[1] = s * axis[1];
190    out[2] = s * axis[2];
191    out[3] = Math.cos(rad);
192    return out;
193};
194
195/**
196 * Adds two quat's
197 *
198 * @param {quat} out the receiving quaternion
199 * @param {quat} a the first operand
200 * @param {quat} b the second operand
201 * @returns {quat} out
202 * @function
203 */
204quat.add = vec4.add;
205
206/**
207 * Multiplies two quat's
208 *
209 * @param {quat} out the receiving quaternion
210 * @param {quat} a the first operand
211 * @param {quat} b the second operand
212 * @returns {quat} out
213 */
214quat.multiply = function(out, a, b) {
215    var ax = a[0], ay = a[1], az = a[2], aw = a[3],
216        bx = b[0], by = b[1], bz = b[2], bw = b[3];
217
218    out[0] = ax * bw + aw * bx + ay * bz - az * by;
219    out[1] = ay * bw + aw * by + az * bx - ax * bz;
220    out[2] = az * bw + aw * bz + ax * by - ay * bx;
221    out[3] = aw * bw - ax * bx - ay * by - az * bz;
222    return out;
223};
224
225/**
226 * Alias for {@link quat.multiply}
227 * @function
228 */
229quat.mul = quat.multiply;
230
231/**
232 * Scales a quat by a scalar number
233 *
234 * @param {quat} out the receiving vector
235 * @param {quat} a the vector to scale
236 * @param {Number} b amount to scale the vector by
237 * @returns {quat} out
238 * @function
239 */
240quat.scale = vec4.scale;
241
242/**
243 * Rotates a quaternion by the given angle about the X axis
244 *
245 * @param {quat} out quat receiving operation result
246 * @param {quat} a quat to rotate
247 * @param {number} rad angle (in radians) to rotate
248 * @returns {quat} out
249 */
250quat.rotateX = function (out, a, rad) {
251    rad *= 0.5;
252
253    var ax = a[0], ay = a[1], az = a[2], aw = a[3],
254        bx = Math.sin(rad), bw = Math.cos(rad);
255
256    out[0] = ax * bw + aw * bx;
257    out[1] = ay * bw + az * bx;
258    out[2] = az * bw - ay * bx;
259    out[3] = aw * bw - ax * bx;
260    return out;
261};
262
263/**
264 * Rotates a quaternion by the given angle about the Y axis
265 *
266 * @param {quat} out quat receiving operation result
267 * @param {quat} a quat to rotate
268 * @param {number} rad angle (in radians) to rotate
269 * @returns {quat} out
270 */
271quat.rotateY = function (out, a, rad) {
272    rad *= 0.5;
273
274    var ax = a[0], ay = a[1], az = a[2], aw = a[3],
275        by = Math.sin(rad), bw = Math.cos(rad);
276
277    out[0] = ax * bw - az * by;
278    out[1] = ay * bw + aw * by;
279    out[2] = az * bw + ax * by;
280    out[3] = aw * bw - ay * by;
281    return out;
282};
283
284/**
285 * Rotates a quaternion by the given angle about the Z axis
286 *
287 * @param {quat} out quat receiving operation result
288 * @param {quat} a quat to rotate
289 * @param {number} rad angle (in radians) to rotate
290 * @returns {quat} out
291 */
292quat.rotateZ = function (out, a, rad) {
293    rad *= 0.5;
294
295    var ax = a[0], ay = a[1], az = a[2], aw = a[3],
296        bz = Math.sin(rad), bw = Math.cos(rad);
297
298    out[0] = ax * bw + ay * bz;
299    out[1] = ay * bw - ax * bz;
300    out[2] = az * bw + aw * bz;
301    out[3] = aw * bw - az * bz;
302    return out;
303};
304
305/**
306 * Calculates the W component of a quat from the X, Y, and Z components.
307 * Assumes that quaternion is 1 unit in length.
308 * Any existing W component will be ignored.
309 *
310 * @param {quat} out the receiving quaternion
311 * @param {quat} a quat to calculate W component of
312 * @returns {quat} out
313 */
314quat.calculateW = function (out, a) {
315    var x = a[0], y = a[1], z = a[2];
316
317    out[0] = x;
318    out[1] = y;
319    out[2] = z;
320    out[3] = Math.sqrt(Math.abs(1.0 - x * x - y * y - z * z));
321    return out;
322};
323
324/**
325 * Calculates the dot product of two quat's
326 *
327 * @param {quat} a the first operand
328 * @param {quat} b the second operand
329 * @returns {Number} dot product of a and b
330 * @function
331 */
332quat.dot = vec4.dot;
333
334/**
335 * Performs a linear interpolation between two quat's
336 *
337 * @param {quat} out the receiving quaternion
338 * @param {quat} a the first operand
339 * @param {quat} b the second operand
340 * @param {Number} t interpolation amount between the two inputs
341 * @returns {quat} out
342 * @function
343 */
344quat.lerp = vec4.lerp;
345
346/**
347 * Performs a spherical linear interpolation between two quat
348 *
349 * @param {quat} out the receiving quaternion
350 * @param {quat} a the first operand
351 * @param {quat} b the second operand
352 * @param {Number} t interpolation amount between the two inputs
353 * @returns {quat} out
354 */
355quat.slerp = function (out, a, b, t) {
356    // benchmarks:
357    //    http://jsperf.com/quaternion-slerp-implementations
358
359    var ax = a[0], ay = a[1], az = a[2], aw = a[3],
360        bx = b[0], by = b[1], bz = b[2], bw = b[3];
361
362    var        omega, cosom, sinom, scale0, scale1;
363
364    // calc cosine
365    cosom = ax * bx + ay * by + az * bz + aw * bw;
366    // adjust signs (if necessary)
367    if ( cosom < 0.0 ) {
368        cosom = -cosom;
369        bx = - bx;
370        by = - by;
371        bz = - bz;
372        bw = - bw;
373    }
374    // calculate coefficients
375    if ( (1.0 - cosom) > 0.000001 ) {
376        // standard case (slerp)
377        omega  = Math.acos(cosom);
378        sinom  = Math.sin(omega);
379        scale0 = Math.sin((1.0 - t) * omega) / sinom;
380        scale1 = Math.sin(t * omega) / sinom;
381    } else {
382        // "from" and "to" quaternions are very close
383        //  ... so we can do a linear interpolation
384        scale0 = 1.0 - t;
385        scale1 = t;
386    }
387    // calculate final values
388    out[0] = scale0 * ax + scale1 * bx;
389    out[1] = scale0 * ay + scale1 * by;
390    out[2] = scale0 * az + scale1 * bz;
391    out[3] = scale0 * aw + scale1 * bw;
392
393    return out;
394};
395
396/**
397 * Performs a spherical linear interpolation with two control points
398 *
399 * @param {quat} out the receiving quaternion
400 * @param {quat} a the first operand
401 * @param {quat} b the second operand
402 * @param {quat} c the third operand
403 * @param {quat} d the fourth operand
404 * @param {Number} t interpolation amount
405 * @returns {quat} out
406 */
407quat.sqlerp = (function () {
408  var temp1 = quat.create();
409  var temp2 = quat.create();
410
411  return function (out, a, b, c, d, t) {
412    quat.slerp(temp1, a, d, t);
413    quat.slerp(temp2, b, c, t);
414    quat.slerp(out, temp1, temp2, 2 * t * (1 - t));
415
416    return out;
417  };
418}());
419
420/**
421 * Calculates the inverse of a quat
422 *
423 * @param {quat} out the receiving quaternion
424 * @param {quat} a quat to calculate inverse of
425 * @returns {quat} out
426 */
427quat.invert = function(out, a) {
428    var a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3],
429        dot = a0*a0 + a1*a1 + a2*a2 + a3*a3,
430        invDot = dot ? 1.0/dot : 0;
431
432    // TODO: Would be faster to return [0,0,0,0] immediately if dot == 0
433
434    out[0] = -a0*invDot;
435    out[1] = -a1*invDot;
436    out[2] = -a2*invDot;
437    out[3] = a3*invDot;
438    return out;
439};
440
441/**
442 * Calculates the conjugate of a quat
443 * If the quaternion is normalized, this function is faster than quat.inverse and produces the same result.
444 *
445 * @param {quat} out the receiving quaternion
446 * @param {quat} a quat to calculate conjugate of
447 * @returns {quat} out
448 */
449quat.conjugate = function (out, a) {
450    out[0] = -a[0];
451    out[1] = -a[1];
452    out[2] = -a[2];
453    out[3] = a[3];
454    return out;
455};
456
457/**
458 * Calculates the length of a quat
459 *
460 * @param {quat} a vector to calculate length of
461 * @returns {Number} length of a
462 * @function
463 */
464quat.length = vec4.length;
465
466/**
467 * Alias for {@link quat.length}
468 * @function
469 */
470quat.len = quat.length;
471
472/**
473 * Calculates the squared length of a quat
474 *
475 * @param {quat} a vector to calculate squared length of
476 * @returns {Number} squared length of a
477 * @function
478 */
479quat.squaredLength = vec4.squaredLength;
480
481/**
482 * Alias for {@link quat.squaredLength}
483 * @function
484 */
485quat.sqrLen = quat.squaredLength;
486
487/**
488 * Normalize a quat
489 *
490 * @param {quat} out the receiving quaternion
491 * @param {quat} a quaternion to normalize
492 * @returns {quat} out
493 * @function
494 */
495quat.normalize = vec4.normalize;
496
497/**
498 * Creates a quaternion from the given 3x3 rotation matrix.
499 *
500 * NOTE: The resultant quaternion is not normalized, so you should be sure
501 * to renormalize the quaternion yourself where necessary.
502 *
503 * @param {quat} out the receiving quaternion
504 * @param {mat3} m rotation matrix
505 * @returns {quat} out
506 * @function
507 */
508quat.fromMat3 = function(out, m) {
509    // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
510    // article "Quaternion Calculus and Fast Animation".
511    var fTrace = m[0] + m[4] + m[8];
512    var fRoot;
513
514    if ( fTrace > 0.0 ) {
515        // |w| > 1/2, may as well choose w > 1/2
516        fRoot = Math.sqrt(fTrace + 1.0);  // 2w
517        out[3] = 0.5 * fRoot;
518        fRoot = 0.5/fRoot;  // 1/(4w)
519        out[0] = (m[5]-m[7])*fRoot;
520        out[1] = (m[6]-m[2])*fRoot;
521        out[2] = (m[1]-m[3])*fRoot;
522    } else {
523        // |w| <= 1/2
524        var i = 0;
525        if ( m[4] > m[0] )
526          i = 1;
527        if ( m[8] > m[i*3+i] )
528          i = 2;
529        var j = (i+1)%3;
530        var k = (i+2)%3;
531
532        fRoot = Math.sqrt(m[i*3+i]-m[j*3+j]-m[k*3+k] + 1.0);
533        out[i] = 0.5 * fRoot;
534        fRoot = 0.5 / fRoot;
535        out[3] = (m[j*3+k] - m[k*3+j]) * fRoot;
536        out[j] = (m[j*3+i] + m[i*3+j]) * fRoot;
537        out[k] = (m[k*3+i] + m[i*3+k]) * fRoot;
538    }
539
540    return out;
541};
542
543/**
544 * Returns a string representation of a quatenion
545 *
546 * @param {quat} vec vector to represent as a string
547 * @returns {String} string representation of the vector
548 */
549quat.str = function (a) {
550    return 'quat(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';
551};
552
553module.exports = quat;
554