Quaternion.js 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137
  1. import Cartesian3 from "./Cartesian3.js";
  2. import Check from "./Check.js";
  3. import defaultValue from "./defaultValue.js";
  4. import defined from "./defined.js";
  5. import FeatureDetection from "./FeatureDetection.js";
  6. import CesiumMath from "./Math.js";
  7. import Matrix3 from "./Matrix3.js";
  8. /**
  9. * A set of 4-dimensional coordinates used to represent rotation in 3-dimensional space.
  10. * @alias Quaternion
  11. * @constructor
  12. *
  13. * @param {Number} [x=0.0] The X component.
  14. * @param {Number} [y=0.0] The Y component.
  15. * @param {Number} [z=0.0] The Z component.
  16. * @param {Number} [w=0.0] The W component.
  17. *
  18. * @see PackableForInterpolation
  19. */
  20. function Quaternion(x, y, z, w) {
  21. /**
  22. * The X component.
  23. * @type {Number}
  24. * @default 0.0
  25. */
  26. this.x = defaultValue(x, 0.0);
  27. /**
  28. * The Y component.
  29. * @type {Number}
  30. * @default 0.0
  31. */
  32. this.y = defaultValue(y, 0.0);
  33. /**
  34. * The Z component.
  35. * @type {Number}
  36. * @default 0.0
  37. */
  38. this.z = defaultValue(z, 0.0);
  39. /**
  40. * The W component.
  41. * @type {Number}
  42. * @default 0.0
  43. */
  44. this.w = defaultValue(w, 0.0);
  45. }
  46. var fromAxisAngleScratch = new Cartesian3();
  47. /**
  48. * Computes a quaternion representing a rotation around an axis.
  49. *
  50. * @param {Cartesian3} axis The axis of rotation.
  51. * @param {Number} angle The angle in radians to rotate around the axis.
  52. * @param {Quaternion} [result] The object onto which to store the result.
  53. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if one was not provided.
  54. */
  55. Quaternion.fromAxisAngle = function (axis, angle, result) {
  56. //>>includeStart('debug', pragmas.debug);
  57. Check.typeOf.object("axis", axis);
  58. Check.typeOf.number("angle", angle);
  59. //>>includeEnd('debug');
  60. var halfAngle = angle / 2.0;
  61. var s = Math.sin(halfAngle);
  62. fromAxisAngleScratch = Cartesian3.normalize(axis, fromAxisAngleScratch);
  63. var x = fromAxisAngleScratch.x * s;
  64. var y = fromAxisAngleScratch.y * s;
  65. var z = fromAxisAngleScratch.z * s;
  66. var w = Math.cos(halfAngle);
  67. if (!defined(result)) {
  68. return new Quaternion(x, y, z, w);
  69. }
  70. result.x = x;
  71. result.y = y;
  72. result.z = z;
  73. result.w = w;
  74. return result;
  75. };
  76. var fromRotationMatrixNext = [1, 2, 0];
  77. var fromRotationMatrixQuat = new Array(3);
  78. /**
  79. * Computes a Quaternion from the provided Matrix3 instance.
  80. *
  81. * @param {Matrix3} matrix The rotation matrix.
  82. * @param {Quaternion} [result] The object onto which to store the result.
  83. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if one was not provided.
  84. *
  85. * @see Matrix3.fromQuaternion
  86. */
  87. Quaternion.fromRotationMatrix = function (matrix, result) {
  88. //>>includeStart('debug', pragmas.debug);
  89. Check.typeOf.object("matrix", matrix);
  90. //>>includeEnd('debug');
  91. var root;
  92. var x;
  93. var y;
  94. var z;
  95. var w;
  96. var m00 = matrix[Matrix3.COLUMN0ROW0];
  97. var m11 = matrix[Matrix3.COLUMN1ROW1];
  98. var m22 = matrix[Matrix3.COLUMN2ROW2];
  99. var trace = m00 + m11 + m22;
  100. if (trace > 0.0) {
  101. // |w| > 1/2, may as well choose w > 1/2
  102. root = Math.sqrt(trace + 1.0); // 2w
  103. w = 0.5 * root;
  104. root = 0.5 / root; // 1/(4w)
  105. x = (matrix[Matrix3.COLUMN1ROW2] - matrix[Matrix3.COLUMN2ROW1]) * root;
  106. y = (matrix[Matrix3.COLUMN2ROW0] - matrix[Matrix3.COLUMN0ROW2]) * root;
  107. z = (matrix[Matrix3.COLUMN0ROW1] - matrix[Matrix3.COLUMN1ROW0]) * root;
  108. } else {
  109. // |w| <= 1/2
  110. var next = fromRotationMatrixNext;
  111. var i = 0;
  112. if (m11 > m00) {
  113. i = 1;
  114. }
  115. if (m22 > m00 && m22 > m11) {
  116. i = 2;
  117. }
  118. var j = next[i];
  119. var k = next[j];
  120. root = Math.sqrt(
  121. matrix[Matrix3.getElementIndex(i, i)] -
  122. matrix[Matrix3.getElementIndex(j, j)] -
  123. matrix[Matrix3.getElementIndex(k, k)] +
  124. 1.0
  125. );
  126. var quat = fromRotationMatrixQuat;
  127. quat[i] = 0.5 * root;
  128. root = 0.5 / root;
  129. w =
  130. (matrix[Matrix3.getElementIndex(k, j)] -
  131. matrix[Matrix3.getElementIndex(j, k)]) *
  132. root;
  133. quat[j] =
  134. (matrix[Matrix3.getElementIndex(j, i)] +
  135. matrix[Matrix3.getElementIndex(i, j)]) *
  136. root;
  137. quat[k] =
  138. (matrix[Matrix3.getElementIndex(k, i)] +
  139. matrix[Matrix3.getElementIndex(i, k)]) *
  140. root;
  141. x = -quat[0];
  142. y = -quat[1];
  143. z = -quat[2];
  144. }
  145. if (!defined(result)) {
  146. return new Quaternion(x, y, z, w);
  147. }
  148. result.x = x;
  149. result.y = y;
  150. result.z = z;
  151. result.w = w;
  152. return result;
  153. };
  154. var scratchHPRQuaternion = new Quaternion();
  155. var scratchHeadingQuaternion = new Quaternion();
  156. var scratchPitchQuaternion = new Quaternion();
  157. var scratchRollQuaternion = new Quaternion();
  158. /**
  159. * Computes a rotation from the given heading, pitch and roll angles. Heading is the rotation about the
  160. * negative z axis. Pitch is the rotation about the negative y axis. Roll is the rotation about
  161. * the positive x axis.
  162. *
  163. * @param {HeadingPitchRoll} headingPitchRoll The rotation expressed as a heading, pitch and roll.
  164. * @param {Quaternion} [result] The object onto which to store the result.
  165. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if none was provided.
  166. */
  167. Quaternion.fromHeadingPitchRoll = function (headingPitchRoll, result) {
  168. //>>includeStart('debug', pragmas.debug);
  169. Check.typeOf.object("headingPitchRoll", headingPitchRoll);
  170. //>>includeEnd('debug');
  171. scratchRollQuaternion = Quaternion.fromAxisAngle(
  172. Cartesian3.UNIT_X,
  173. headingPitchRoll.roll,
  174. scratchHPRQuaternion
  175. );
  176. scratchPitchQuaternion = Quaternion.fromAxisAngle(
  177. Cartesian3.UNIT_Y,
  178. -headingPitchRoll.pitch,
  179. result
  180. );
  181. result = Quaternion.multiply(
  182. scratchPitchQuaternion,
  183. scratchRollQuaternion,
  184. scratchPitchQuaternion
  185. );
  186. scratchHeadingQuaternion = Quaternion.fromAxisAngle(
  187. Cartesian3.UNIT_Z,
  188. -headingPitchRoll.heading,
  189. scratchHPRQuaternion
  190. );
  191. return Quaternion.multiply(scratchHeadingQuaternion, result, result);
  192. };
  193. var sampledQuaternionAxis = new Cartesian3();
  194. var sampledQuaternionRotation = new Cartesian3();
  195. var sampledQuaternionTempQuaternion = new Quaternion();
  196. var sampledQuaternionQuaternion0 = new Quaternion();
  197. var sampledQuaternionQuaternion0Conjugate = new Quaternion();
  198. /**
  199. * The number of elements used to pack the object into an array.
  200. * @type {Number}
  201. */
  202. Quaternion.packedLength = 4;
  203. /**
  204. * Stores the provided instance into the provided array.
  205. *
  206. * @param {Quaternion} value The value to pack.
  207. * @param {Number[]} array The array to pack into.
  208. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements.
  209. *
  210. * @returns {Number[]} The array that was packed into
  211. */
  212. Quaternion.pack = function (value, array, startingIndex) {
  213. //>>includeStart('debug', pragmas.debug);
  214. Check.typeOf.object("value", value);
  215. Check.defined("array", array);
  216. //>>includeEnd('debug');
  217. startingIndex = defaultValue(startingIndex, 0);
  218. array[startingIndex++] = value.x;
  219. array[startingIndex++] = value.y;
  220. array[startingIndex++] = value.z;
  221. array[startingIndex] = value.w;
  222. return array;
  223. };
  224. /**
  225. * Retrieves an instance from a packed array.
  226. *
  227. * @param {Number[]} array The packed array.
  228. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked.
  229. * @param {Quaternion} [result] The object into which to store the result.
  230. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if one was not provided.
  231. */
  232. Quaternion.unpack = function (array, startingIndex, result) {
  233. //>>includeStart('debug', pragmas.debug);
  234. Check.defined("array", array);
  235. //>>includeEnd('debug');
  236. startingIndex = defaultValue(startingIndex, 0);
  237. if (!defined(result)) {
  238. result = new Quaternion();
  239. }
  240. result.x = array[startingIndex];
  241. result.y = array[startingIndex + 1];
  242. result.z = array[startingIndex + 2];
  243. result.w = array[startingIndex + 3];
  244. return result;
  245. };
  246. /**
  247. * The number of elements used to store the object into an array in its interpolatable form.
  248. * @type {Number}
  249. */
  250. Quaternion.packedInterpolationLength = 3;
  251. /**
  252. * Converts a packed array into a form suitable for interpolation.
  253. *
  254. * @param {Number[]} packedArray The packed array.
  255. * @param {Number} [startingIndex=0] The index of the first element to be converted.
  256. * @param {Number} [lastIndex=packedArray.length] The index of the last element to be converted.
  257. * @param {Number[]} [result] The object into which to store the result.
  258. */
  259. Quaternion.convertPackedArrayForInterpolation = function (
  260. packedArray,
  261. startingIndex,
  262. lastIndex,
  263. result
  264. ) {
  265. Quaternion.unpack(
  266. packedArray,
  267. lastIndex * 4,
  268. sampledQuaternionQuaternion0Conjugate
  269. );
  270. Quaternion.conjugate(
  271. sampledQuaternionQuaternion0Conjugate,
  272. sampledQuaternionQuaternion0Conjugate
  273. );
  274. for (var i = 0, len = lastIndex - startingIndex + 1; i < len; i++) {
  275. var offset = i * 3;
  276. Quaternion.unpack(
  277. packedArray,
  278. (startingIndex + i) * 4,
  279. sampledQuaternionTempQuaternion
  280. );
  281. Quaternion.multiply(
  282. sampledQuaternionTempQuaternion,
  283. sampledQuaternionQuaternion0Conjugate,
  284. sampledQuaternionTempQuaternion
  285. );
  286. if (sampledQuaternionTempQuaternion.w < 0) {
  287. Quaternion.negate(
  288. sampledQuaternionTempQuaternion,
  289. sampledQuaternionTempQuaternion
  290. );
  291. }
  292. Quaternion.computeAxis(
  293. sampledQuaternionTempQuaternion,
  294. sampledQuaternionAxis
  295. );
  296. var angle = Quaternion.computeAngle(sampledQuaternionTempQuaternion);
  297. if (!defined(result)) {
  298. result = [];
  299. }
  300. result[offset] = sampledQuaternionAxis.x * angle;
  301. result[offset + 1] = sampledQuaternionAxis.y * angle;
  302. result[offset + 2] = sampledQuaternionAxis.z * angle;
  303. }
  304. };
  305. /**
  306. * Retrieves an instance from a packed array converted with {@link convertPackedArrayForInterpolation}.
  307. *
  308. * @param {Number[]} array The array previously packed for interpolation.
  309. * @param {Number[]} sourceArray The original packed array.
  310. * @param {Number} [firstIndex=0] The firstIndex used to convert the array.
  311. * @param {Number} [lastIndex=packedArray.length] The lastIndex used to convert the array.
  312. * @param {Quaternion} [result] The object into which to store the result.
  313. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if one was not provided.
  314. */
  315. Quaternion.unpackInterpolationResult = function (
  316. array,
  317. sourceArray,
  318. firstIndex,
  319. lastIndex,
  320. result
  321. ) {
  322. if (!defined(result)) {
  323. result = new Quaternion();
  324. }
  325. Cartesian3.fromArray(array, 0, sampledQuaternionRotation);
  326. var magnitude = Cartesian3.magnitude(sampledQuaternionRotation);
  327. Quaternion.unpack(sourceArray, lastIndex * 4, sampledQuaternionQuaternion0);
  328. if (magnitude === 0) {
  329. Quaternion.clone(Quaternion.IDENTITY, sampledQuaternionTempQuaternion);
  330. } else {
  331. Quaternion.fromAxisAngle(
  332. sampledQuaternionRotation,
  333. magnitude,
  334. sampledQuaternionTempQuaternion
  335. );
  336. }
  337. return Quaternion.multiply(
  338. sampledQuaternionTempQuaternion,
  339. sampledQuaternionQuaternion0,
  340. result
  341. );
  342. };
  343. /**
  344. * Duplicates a Quaternion instance.
  345. *
  346. * @param {Quaternion} quaternion The quaternion to duplicate.
  347. * @param {Quaternion} [result] The object onto which to store the result.
  348. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if one was not provided. (Returns undefined if quaternion is undefined)
  349. */
  350. Quaternion.clone = function (quaternion, result) {
  351. if (!defined(quaternion)) {
  352. return undefined;
  353. }
  354. if (!defined(result)) {
  355. return new Quaternion(
  356. quaternion.x,
  357. quaternion.y,
  358. quaternion.z,
  359. quaternion.w
  360. );
  361. }
  362. result.x = quaternion.x;
  363. result.y = quaternion.y;
  364. result.z = quaternion.z;
  365. result.w = quaternion.w;
  366. return result;
  367. };
  368. /**
  369. * Computes the conjugate of the provided quaternion.
  370. *
  371. * @param {Quaternion} quaternion The quaternion to conjugate.
  372. * @param {Quaternion} result The object onto which to store the result.
  373. * @returns {Quaternion} The modified result parameter.
  374. */
  375. Quaternion.conjugate = function (quaternion, result) {
  376. //>>includeStart('debug', pragmas.debug);
  377. Check.typeOf.object("quaternion", quaternion);
  378. Check.typeOf.object("result", result);
  379. //>>includeEnd('debug');
  380. result.x = -quaternion.x;
  381. result.y = -quaternion.y;
  382. result.z = -quaternion.z;
  383. result.w = quaternion.w;
  384. return result;
  385. };
  386. /**
  387. * Computes magnitude squared for the provided quaternion.
  388. *
  389. * @param {Quaternion} quaternion The quaternion to conjugate.
  390. * @returns {Number} The magnitude squared.
  391. */
  392. Quaternion.magnitudeSquared = function (quaternion) {
  393. //>>includeStart('debug', pragmas.debug);
  394. Check.typeOf.object("quaternion", quaternion);
  395. //>>includeEnd('debug');
  396. return (
  397. quaternion.x * quaternion.x +
  398. quaternion.y * quaternion.y +
  399. quaternion.z * quaternion.z +
  400. quaternion.w * quaternion.w
  401. );
  402. };
  403. /**
  404. * Computes magnitude for the provided quaternion.
  405. *
  406. * @param {Quaternion} quaternion The quaternion to conjugate.
  407. * @returns {Number} The magnitude.
  408. */
  409. Quaternion.magnitude = function (quaternion) {
  410. return Math.sqrt(Quaternion.magnitudeSquared(quaternion));
  411. };
  412. /**
  413. * Computes the normalized form of the provided quaternion.
  414. *
  415. * @param {Quaternion} quaternion The quaternion to normalize.
  416. * @param {Quaternion} result The object onto which to store the result.
  417. * @returns {Quaternion} The modified result parameter.
  418. */
  419. Quaternion.normalize = function (quaternion, result) {
  420. //>>includeStart('debug', pragmas.debug);
  421. Check.typeOf.object("result", result);
  422. //>>includeEnd('debug');
  423. var inverseMagnitude = 1.0 / Quaternion.magnitude(quaternion);
  424. var x = quaternion.x * inverseMagnitude;
  425. var y = quaternion.y * inverseMagnitude;
  426. var z = quaternion.z * inverseMagnitude;
  427. var w = quaternion.w * inverseMagnitude;
  428. result.x = x;
  429. result.y = y;
  430. result.z = z;
  431. result.w = w;
  432. return result;
  433. };
  434. /**
  435. * Computes the inverse of the provided quaternion.
  436. *
  437. * @param {Quaternion} quaternion The quaternion to normalize.
  438. * @param {Quaternion} result The object onto which to store the result.
  439. * @returns {Quaternion} The modified result parameter.
  440. */
  441. Quaternion.inverse = function (quaternion, result) {
  442. //>>includeStart('debug', pragmas.debug);
  443. Check.typeOf.object("result", result);
  444. //>>includeEnd('debug');
  445. var magnitudeSquared = Quaternion.magnitudeSquared(quaternion);
  446. result = Quaternion.conjugate(quaternion, result);
  447. return Quaternion.multiplyByScalar(result, 1.0 / magnitudeSquared, result);
  448. };
  449. /**
  450. * Computes the componentwise sum of two quaternions.
  451. *
  452. * @param {Quaternion} left The first quaternion.
  453. * @param {Quaternion} right The second quaternion.
  454. * @param {Quaternion} result The object onto which to store the result.
  455. * @returns {Quaternion} The modified result parameter.
  456. */
  457. Quaternion.add = function (left, right, result) {
  458. //>>includeStart('debug', pragmas.debug);
  459. Check.typeOf.object("left", left);
  460. Check.typeOf.object("right", right);
  461. Check.typeOf.object("result", result);
  462. //>>includeEnd('debug');
  463. result.x = left.x + right.x;
  464. result.y = left.y + right.y;
  465. result.z = left.z + right.z;
  466. result.w = left.w + right.w;
  467. return result;
  468. };
  469. /**
  470. * Computes the componentwise difference of two quaternions.
  471. *
  472. * @param {Quaternion} left The first quaternion.
  473. * @param {Quaternion} right The second quaternion.
  474. * @param {Quaternion} result The object onto which to store the result.
  475. * @returns {Quaternion} The modified result parameter.
  476. */
  477. Quaternion.subtract = function (left, right, result) {
  478. //>>includeStart('debug', pragmas.debug);
  479. Check.typeOf.object("left", left);
  480. Check.typeOf.object("right", right);
  481. Check.typeOf.object("result", result);
  482. //>>includeEnd('debug');
  483. result.x = left.x - right.x;
  484. result.y = left.y - right.y;
  485. result.z = left.z - right.z;
  486. result.w = left.w - right.w;
  487. return result;
  488. };
  489. /**
  490. * Negates the provided quaternion.
  491. *
  492. * @param {Quaternion} quaternion The quaternion to be negated.
  493. * @param {Quaternion} result The object onto which to store the result.
  494. * @returns {Quaternion} The modified result parameter.
  495. */
  496. Quaternion.negate = function (quaternion, result) {
  497. //>>includeStart('debug', pragmas.debug);
  498. Check.typeOf.object("quaternion", quaternion);
  499. Check.typeOf.object("result", result);
  500. //>>includeEnd('debug');
  501. result.x = -quaternion.x;
  502. result.y = -quaternion.y;
  503. result.z = -quaternion.z;
  504. result.w = -quaternion.w;
  505. return result;
  506. };
  507. /**
  508. * Computes the dot (scalar) product of two quaternions.
  509. *
  510. * @param {Quaternion} left The first quaternion.
  511. * @param {Quaternion} right The second quaternion.
  512. * @returns {Number} The dot product.
  513. */
  514. Quaternion.dot = function (left, right) {
  515. //>>includeStart('debug', pragmas.debug);
  516. Check.typeOf.object("left", left);
  517. Check.typeOf.object("right", right);
  518. //>>includeEnd('debug');
  519. return (
  520. left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w
  521. );
  522. };
  523. /**
  524. * Computes the product of two quaternions.
  525. *
  526. * @param {Quaternion} left The first quaternion.
  527. * @param {Quaternion} right The second quaternion.
  528. * @param {Quaternion} result The object onto which to store the result.
  529. * @returns {Quaternion} The modified result parameter.
  530. */
  531. Quaternion.multiply = function (left, right, result) {
  532. //>>includeStart('debug', pragmas.debug);
  533. Check.typeOf.object("left", left);
  534. Check.typeOf.object("right", right);
  535. Check.typeOf.object("result", result);
  536. //>>includeEnd('debug');
  537. var leftX = left.x;
  538. var leftY = left.y;
  539. var leftZ = left.z;
  540. var leftW = left.w;
  541. var rightX = right.x;
  542. var rightY = right.y;
  543. var rightZ = right.z;
  544. var rightW = right.w;
  545. var x = leftW * rightX + leftX * rightW + leftY * rightZ - leftZ * rightY;
  546. var y = leftW * rightY - leftX * rightZ + leftY * rightW + leftZ * rightX;
  547. var z = leftW * rightZ + leftX * rightY - leftY * rightX + leftZ * rightW;
  548. var w = leftW * rightW - leftX * rightX - leftY * rightY - leftZ * rightZ;
  549. result.x = x;
  550. result.y = y;
  551. result.z = z;
  552. result.w = w;
  553. return result;
  554. };
  555. /**
  556. * Multiplies the provided quaternion componentwise by the provided scalar.
  557. *
  558. * @param {Quaternion} quaternion The quaternion to be scaled.
  559. * @param {Number} scalar The scalar to multiply with.
  560. * @param {Quaternion} result The object onto which to store the result.
  561. * @returns {Quaternion} The modified result parameter.
  562. */
  563. Quaternion.multiplyByScalar = function (quaternion, scalar, result) {
  564. //>>includeStart('debug', pragmas.debug);
  565. Check.typeOf.object("quaternion", quaternion);
  566. Check.typeOf.number("scalar", scalar);
  567. Check.typeOf.object("result", result);
  568. //>>includeEnd('debug');
  569. result.x = quaternion.x * scalar;
  570. result.y = quaternion.y * scalar;
  571. result.z = quaternion.z * scalar;
  572. result.w = quaternion.w * scalar;
  573. return result;
  574. };
  575. /**
  576. * Divides the provided quaternion componentwise by the provided scalar.
  577. *
  578. * @param {Quaternion} quaternion The quaternion to be divided.
  579. * @param {Number} scalar The scalar to divide by.
  580. * @param {Quaternion} result The object onto which to store the result.
  581. * @returns {Quaternion} The modified result parameter.
  582. */
  583. Quaternion.divideByScalar = function (quaternion, scalar, result) {
  584. //>>includeStart('debug', pragmas.debug);
  585. Check.typeOf.object("quaternion", quaternion);
  586. Check.typeOf.number("scalar", scalar);
  587. Check.typeOf.object("result", result);
  588. //>>includeEnd('debug');
  589. result.x = quaternion.x / scalar;
  590. result.y = quaternion.y / scalar;
  591. result.z = quaternion.z / scalar;
  592. result.w = quaternion.w / scalar;
  593. return result;
  594. };
  595. /**
  596. * Computes the axis of rotation of the provided quaternion.
  597. *
  598. * @param {Quaternion} quaternion The quaternion to use.
  599. * @param {Cartesian3} result The object onto which to store the result.
  600. * @returns {Cartesian3} The modified result parameter.
  601. */
  602. Quaternion.computeAxis = function (quaternion, result) {
  603. //>>includeStart('debug', pragmas.debug);
  604. Check.typeOf.object("quaternion", quaternion);
  605. Check.typeOf.object("result", result);
  606. //>>includeEnd('debug');
  607. var w = quaternion.w;
  608. if (Math.abs(w - 1.0) < CesiumMath.EPSILON6) {
  609. result.x = result.y = result.z = 0;
  610. return result;
  611. }
  612. var scalar = 1.0 / Math.sqrt(1.0 - w * w);
  613. result.x = quaternion.x * scalar;
  614. result.y = quaternion.y * scalar;
  615. result.z = quaternion.z * scalar;
  616. return result;
  617. };
  618. /**
  619. * Computes the angle of rotation of the provided quaternion.
  620. *
  621. * @param {Quaternion} quaternion The quaternion to use.
  622. * @returns {Number} The angle of rotation.
  623. */
  624. Quaternion.computeAngle = function (quaternion) {
  625. //>>includeStart('debug', pragmas.debug);
  626. Check.typeOf.object("quaternion", quaternion);
  627. //>>includeEnd('debug');
  628. if (Math.abs(quaternion.w - 1.0) < CesiumMath.EPSILON6) {
  629. return 0.0;
  630. }
  631. return 2.0 * Math.acos(quaternion.w);
  632. };
  633. var lerpScratch = new Quaternion();
  634. /**
  635. * Computes the linear interpolation or extrapolation at t using the provided quaternions.
  636. *
  637. * @param {Quaternion} start The value corresponding to t at 0.0.
  638. * @param {Quaternion} end The value corresponding to t at 1.0.
  639. * @param {Number} t The point along t at which to interpolate.
  640. * @param {Quaternion} result The object onto which to store the result.
  641. * @returns {Quaternion} The modified result parameter.
  642. */
  643. Quaternion.lerp = function (start, end, t, result) {
  644. //>>includeStart('debug', pragmas.debug);
  645. Check.typeOf.object("start", start);
  646. Check.typeOf.object("end", end);
  647. Check.typeOf.number("t", t);
  648. Check.typeOf.object("result", result);
  649. //>>includeEnd('debug');
  650. lerpScratch = Quaternion.multiplyByScalar(end, t, lerpScratch);
  651. result = Quaternion.multiplyByScalar(start, 1.0 - t, result);
  652. return Quaternion.add(lerpScratch, result, result);
  653. };
  654. var slerpEndNegated = new Quaternion();
  655. var slerpScaledP = new Quaternion();
  656. var slerpScaledR = new Quaternion();
  657. /**
  658. * Computes the spherical linear interpolation or extrapolation at t using the provided quaternions.
  659. *
  660. * @param {Quaternion} start The value corresponding to t at 0.0.
  661. * @param {Quaternion} end The value corresponding to t at 1.0.
  662. * @param {Number} t The point along t at which to interpolate.
  663. * @param {Quaternion} result The object onto which to store the result.
  664. * @returns {Quaternion} The modified result parameter.
  665. *
  666. * @see Quaternion#fastSlerp
  667. */
  668. Quaternion.slerp = function (start, end, t, result) {
  669. //>>includeStart('debug', pragmas.debug);
  670. Check.typeOf.object("start", start);
  671. Check.typeOf.object("end", end);
  672. Check.typeOf.number("t", t);
  673. Check.typeOf.object("result", result);
  674. //>>includeEnd('debug');
  675. var dot = Quaternion.dot(start, end);
  676. // The angle between start must be acute. Since q and -q represent
  677. // the same rotation, negate q to get the acute angle.
  678. var r = end;
  679. if (dot < 0.0) {
  680. dot = -dot;
  681. r = slerpEndNegated = Quaternion.negate(end, slerpEndNegated);
  682. }
  683. // dot > 0, as the dot product approaches 1, the angle between the
  684. // quaternions vanishes. use linear interpolation.
  685. if (1.0 - dot < CesiumMath.EPSILON6) {
  686. return Quaternion.lerp(start, r, t, result);
  687. }
  688. var theta = Math.acos(dot);
  689. slerpScaledP = Quaternion.multiplyByScalar(
  690. start,
  691. Math.sin((1 - t) * theta),
  692. slerpScaledP
  693. );
  694. slerpScaledR = Quaternion.multiplyByScalar(
  695. r,
  696. Math.sin(t * theta),
  697. slerpScaledR
  698. );
  699. result = Quaternion.add(slerpScaledP, slerpScaledR, result);
  700. return Quaternion.multiplyByScalar(result, 1.0 / Math.sin(theta), result);
  701. };
  702. /**
  703. * The logarithmic quaternion function.
  704. *
  705. * @param {Quaternion} quaternion The unit quaternion.
  706. * @param {Cartesian3} result The object onto which to store the result.
  707. * @returns {Cartesian3} The modified result parameter.
  708. */
  709. Quaternion.log = function (quaternion, result) {
  710. //>>includeStart('debug', pragmas.debug);
  711. Check.typeOf.object("quaternion", quaternion);
  712. Check.typeOf.object("result", result);
  713. //>>includeEnd('debug');
  714. var theta = CesiumMath.acosClamped(quaternion.w);
  715. var thetaOverSinTheta = 0.0;
  716. if (theta !== 0.0) {
  717. thetaOverSinTheta = theta / Math.sin(theta);
  718. }
  719. return Cartesian3.multiplyByScalar(quaternion, thetaOverSinTheta, result);
  720. };
  721. /**
  722. * The exponential quaternion function.
  723. *
  724. * @param {Cartesian3} cartesian The cartesian.
  725. * @param {Quaternion} result The object onto which to store the result.
  726. * @returns {Quaternion} The modified result parameter.
  727. */
  728. Quaternion.exp = function (cartesian, result) {
  729. //>>includeStart('debug', pragmas.debug);
  730. Check.typeOf.object("cartesian", cartesian);
  731. Check.typeOf.object("result", result);
  732. //>>includeEnd('debug');
  733. var theta = Cartesian3.magnitude(cartesian);
  734. var sinThetaOverTheta = 0.0;
  735. if (theta !== 0.0) {
  736. sinThetaOverTheta = Math.sin(theta) / theta;
  737. }
  738. result.x = cartesian.x * sinThetaOverTheta;
  739. result.y = cartesian.y * sinThetaOverTheta;
  740. result.z = cartesian.z * sinThetaOverTheta;
  741. result.w = Math.cos(theta);
  742. return result;
  743. };
  744. var squadScratchCartesian0 = new Cartesian3();
  745. var squadScratchCartesian1 = new Cartesian3();
  746. var squadScratchQuaternion0 = new Quaternion();
  747. var squadScratchQuaternion1 = new Quaternion();
  748. /**
  749. * Computes an inner quadrangle point.
  750. * <p>This will compute quaternions that ensure a squad curve is C<sup>1</sup>.</p>
  751. *
  752. * @param {Quaternion} q0 The first quaternion.
  753. * @param {Quaternion} q1 The second quaternion.
  754. * @param {Quaternion} q2 The third quaternion.
  755. * @param {Quaternion} result The object onto which to store the result.
  756. * @returns {Quaternion} The modified result parameter.
  757. *
  758. * @see Quaternion#squad
  759. */
  760. Quaternion.computeInnerQuadrangle = function (q0, q1, q2, result) {
  761. //>>includeStart('debug', pragmas.debug);
  762. Check.typeOf.object("q0", q0);
  763. Check.typeOf.object("q1", q1);
  764. Check.typeOf.object("q2", q2);
  765. Check.typeOf.object("result", result);
  766. //>>includeEnd('debug');
  767. var qInv = Quaternion.conjugate(q1, squadScratchQuaternion0);
  768. Quaternion.multiply(qInv, q2, squadScratchQuaternion1);
  769. var cart0 = Quaternion.log(squadScratchQuaternion1, squadScratchCartesian0);
  770. Quaternion.multiply(qInv, q0, squadScratchQuaternion1);
  771. var cart1 = Quaternion.log(squadScratchQuaternion1, squadScratchCartesian1);
  772. Cartesian3.add(cart0, cart1, cart0);
  773. Cartesian3.multiplyByScalar(cart0, 0.25, cart0);
  774. Cartesian3.negate(cart0, cart0);
  775. Quaternion.exp(cart0, squadScratchQuaternion0);
  776. return Quaternion.multiply(q1, squadScratchQuaternion0, result);
  777. };
  778. /**
  779. * Computes the spherical quadrangle interpolation between quaternions.
  780. *
  781. * @param {Quaternion} q0 The first quaternion.
  782. * @param {Quaternion} q1 The second quaternion.
  783. * @param {Quaternion} s0 The first inner quadrangle.
  784. * @param {Quaternion} s1 The second inner quadrangle.
  785. * @param {Number} t The time in [0,1] used to interpolate.
  786. * @param {Quaternion} result The object onto which to store the result.
  787. * @returns {Quaternion} The modified result parameter.
  788. *
  789. *
  790. * @example
  791. * // 1. compute the squad interpolation between two quaternions on a curve
  792. * var s0 = Cesium.Quaternion.computeInnerQuadrangle(quaternions[i - 1], quaternions[i], quaternions[i + 1], new Cesium.Quaternion());
  793. * var s1 = Cesium.Quaternion.computeInnerQuadrangle(quaternions[i], quaternions[i + 1], quaternions[i + 2], new Cesium.Quaternion());
  794. * var q = Cesium.Quaternion.squad(quaternions[i], quaternions[i + 1], s0, s1, t, new Cesium.Quaternion());
  795. *
  796. * // 2. compute the squad interpolation as above but where the first quaternion is a end point.
  797. * var s1 = Cesium.Quaternion.computeInnerQuadrangle(quaternions[0], quaternions[1], quaternions[2], new Cesium.Quaternion());
  798. * var q = Cesium.Quaternion.squad(quaternions[0], quaternions[1], quaternions[0], s1, t, new Cesium.Quaternion());
  799. *
  800. * @see Quaternion#computeInnerQuadrangle
  801. */
  802. Quaternion.squad = function (q0, q1, s0, s1, t, result) {
  803. //>>includeStart('debug', pragmas.debug);
  804. Check.typeOf.object("q0", q0);
  805. Check.typeOf.object("q1", q1);
  806. Check.typeOf.object("s0", s0);
  807. Check.typeOf.object("s1", s1);
  808. Check.typeOf.number("t", t);
  809. Check.typeOf.object("result", result);
  810. //>>includeEnd('debug');
  811. var slerp0 = Quaternion.slerp(q0, q1, t, squadScratchQuaternion0);
  812. var slerp1 = Quaternion.slerp(s0, s1, t, squadScratchQuaternion1);
  813. return Quaternion.slerp(slerp0, slerp1, 2.0 * t * (1.0 - t), result);
  814. };
  815. var fastSlerpScratchQuaternion = new Quaternion();
  816. var opmu = 1.90110745351730037;
  817. var u = FeatureDetection.supportsTypedArrays() ? new Float32Array(8) : [];
  818. var v = FeatureDetection.supportsTypedArrays() ? new Float32Array(8) : [];
  819. var bT = FeatureDetection.supportsTypedArrays() ? new Float32Array(8) : [];
  820. var bD = FeatureDetection.supportsTypedArrays() ? new Float32Array(8) : [];
  821. for (var i = 0; i < 7; ++i) {
  822. var s = i + 1.0;
  823. var t = 2.0 * s + 1.0;
  824. u[i] = 1.0 / (s * t);
  825. v[i] = s / t;
  826. }
  827. u[7] = opmu / (8.0 * 17.0);
  828. v[7] = (opmu * 8.0) / 17.0;
  829. /**
  830. * Computes the spherical linear interpolation or extrapolation at t using the provided quaternions.
  831. * This implementation is faster than {@link Quaternion#slerp}, but is only accurate up to 10<sup>-6</sup>.
  832. *
  833. * @param {Quaternion} start The value corresponding to t at 0.0.
  834. * @param {Quaternion} end The value corresponding to t at 1.0.
  835. * @param {Number} t The point along t at which to interpolate.
  836. * @param {Quaternion} result The object onto which to store the result.
  837. * @returns {Quaternion} The modified result parameter.
  838. *
  839. * @see Quaternion#slerp
  840. */
  841. Quaternion.fastSlerp = function (start, end, t, result) {
  842. //>>includeStart('debug', pragmas.debug);
  843. Check.typeOf.object("start", start);
  844. Check.typeOf.object("end", end);
  845. Check.typeOf.number("t", t);
  846. Check.typeOf.object("result", result);
  847. //>>includeEnd('debug');
  848. var x = Quaternion.dot(start, end);
  849. var sign;
  850. if (x >= 0) {
  851. sign = 1.0;
  852. } else {
  853. sign = -1.0;
  854. x = -x;
  855. }
  856. var xm1 = x - 1.0;
  857. var d = 1.0 - t;
  858. var sqrT = t * t;
  859. var sqrD = d * d;
  860. for (var i = 7; i >= 0; --i) {
  861. bT[i] = (u[i] * sqrT - v[i]) * xm1;
  862. bD[i] = (u[i] * sqrD - v[i]) * xm1;
  863. }
  864. var cT =
  865. sign *
  866. t *
  867. (1.0 +
  868. bT[0] *
  869. (1.0 +
  870. bT[1] *
  871. (1.0 +
  872. bT[2] *
  873. (1.0 +
  874. bT[3] *
  875. (1.0 +
  876. bT[4] *
  877. (1.0 + bT[5] * (1.0 + bT[6] * (1.0 + bT[7]))))))));
  878. var cD =
  879. d *
  880. (1.0 +
  881. bD[0] *
  882. (1.0 +
  883. bD[1] *
  884. (1.0 +
  885. bD[2] *
  886. (1.0 +
  887. bD[3] *
  888. (1.0 +
  889. bD[4] *
  890. (1.0 + bD[5] * (1.0 + bD[6] * (1.0 + bD[7]))))))));
  891. var temp = Quaternion.multiplyByScalar(start, cD, fastSlerpScratchQuaternion);
  892. Quaternion.multiplyByScalar(end, cT, result);
  893. return Quaternion.add(temp, result, result);
  894. };
  895. /**
  896. * Computes the spherical quadrangle interpolation between quaternions.
  897. * An implementation that is faster than {@link Quaternion#squad}, but less accurate.
  898. *
  899. * @param {Quaternion} q0 The first quaternion.
  900. * @param {Quaternion} q1 The second quaternion.
  901. * @param {Quaternion} s0 The first inner quadrangle.
  902. * @param {Quaternion} s1 The second inner quadrangle.
  903. * @param {Number} t The time in [0,1] used to interpolate.
  904. * @param {Quaternion} result The object onto which to store the result.
  905. * @returns {Quaternion} The modified result parameter or a new instance if none was provided.
  906. *
  907. * @see Quaternion#squad
  908. */
  909. Quaternion.fastSquad = function (q0, q1, s0, s1, t, result) {
  910. //>>includeStart('debug', pragmas.debug);
  911. Check.typeOf.object("q0", q0);
  912. Check.typeOf.object("q1", q1);
  913. Check.typeOf.object("s0", s0);
  914. Check.typeOf.object("s1", s1);
  915. Check.typeOf.number("t", t);
  916. Check.typeOf.object("result", result);
  917. //>>includeEnd('debug');
  918. var slerp0 = Quaternion.fastSlerp(q0, q1, t, squadScratchQuaternion0);
  919. var slerp1 = Quaternion.fastSlerp(s0, s1, t, squadScratchQuaternion1);
  920. return Quaternion.fastSlerp(slerp0, slerp1, 2.0 * t * (1.0 - t), result);
  921. };
  922. /**
  923. * Compares the provided quaternions componentwise and returns
  924. * <code>true</code> if they are equal, <code>false</code> otherwise.
  925. *
  926. * @param {Quaternion} [left] The first quaternion.
  927. * @param {Quaternion} [right] The second quaternion.
  928. * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise.
  929. */
  930. Quaternion.equals = function (left, right) {
  931. return (
  932. left === right ||
  933. (defined(left) &&
  934. defined(right) &&
  935. left.x === right.x &&
  936. left.y === right.y &&
  937. left.z === right.z &&
  938. left.w === right.w)
  939. );
  940. };
  941. /**
  942. * Compares the provided quaternions componentwise and returns
  943. * <code>true</code> if they are within the provided epsilon,
  944. * <code>false</code> otherwise.
  945. *
  946. * @param {Quaternion} [left] The first quaternion.
  947. * @param {Quaternion} [right] The second quaternion.
  948. * @param {Number} [epsilon=0] The epsilon to use for equality testing.
  949. * @returns {Boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise.
  950. */
  951. Quaternion.equalsEpsilon = function (left, right, epsilon) {
  952. epsilon = defaultValue(epsilon, 0);
  953. return (
  954. left === right ||
  955. (defined(left) &&
  956. defined(right) &&
  957. Math.abs(left.x - right.x) <= epsilon &&
  958. Math.abs(left.y - right.y) <= epsilon &&
  959. Math.abs(left.z - right.z) <= epsilon &&
  960. Math.abs(left.w - right.w) <= epsilon)
  961. );
  962. };
  963. /**
  964. * An immutable Quaternion instance initialized to (0.0, 0.0, 0.0, 0.0).
  965. *
  966. * @type {Quaternion}
  967. * @constant
  968. */
  969. Quaternion.ZERO = Object.freeze(new Quaternion(0.0, 0.0, 0.0, 0.0));
  970. /**
  971. * An immutable Quaternion instance initialized to (0.0, 0.0, 0.0, 1.0).
  972. *
  973. * @type {Quaternion}
  974. * @constant
  975. */
  976. Quaternion.IDENTITY = Object.freeze(new Quaternion(0.0, 0.0, 0.0, 1.0));
  977. /**
  978. * Duplicates this Quaternion instance.
  979. *
  980. * @param {Quaternion} [result] The object onto which to store the result.
  981. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if one was not provided.
  982. */
  983. Quaternion.prototype.clone = function (result) {
  984. return Quaternion.clone(this, result);
  985. };
  986. /**
  987. * Compares this and the provided quaternion componentwise and returns
  988. * <code>true</code> if they are equal, <code>false</code> otherwise.
  989. *
  990. * @param {Quaternion} [right] The right hand side quaternion.
  991. * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise.
  992. */
  993. Quaternion.prototype.equals = function (right) {
  994. return Quaternion.equals(this, right);
  995. };
  996. /**
  997. * Compares this and the provided quaternion componentwise and returns
  998. * <code>true</code> if they are within the provided epsilon,
  999. * <code>false</code> otherwise.
  1000. *
  1001. * @param {Quaternion} [right] The right hand side quaternion.
  1002. * @param {Number} [epsilon=0] The epsilon to use for equality testing.
  1003. * @returns {Boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise.
  1004. */
  1005. Quaternion.prototype.equalsEpsilon = function (right, epsilon) {
  1006. return Quaternion.equalsEpsilon(this, right, epsilon);
  1007. };
  1008. /**
  1009. * Returns a string representing this quaternion in the format (x, y, z, w).
  1010. *
  1011. * @returns {String} A string representing this Quaternion.
  1012. */
  1013. Quaternion.prototype.toString = function () {
  1014. return "(" + this.x + ", " + this.y + ", " + this.z + ", " + this.w + ")";
  1015. };
  1016. export default Quaternion;