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