Quaternion.js 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141
  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. let 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. const halfAngle = angle / 2.0;
  61. const s = Math.sin(halfAngle);
  62. fromAxisAngleScratch = Cartesian3.normalize(axis, fromAxisAngleScratch);
  63. const x = fromAxisAngleScratch.x * s;
  64. const y = fromAxisAngleScratch.y * s;
  65. const z = fromAxisAngleScratch.z * s;
  66. const 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. const fromRotationMatrixNext = [1, 2, 0];
  77. const 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. let root;
  92. let x;
  93. let y;
  94. let z;
  95. let w;
  96. const m00 = matrix[Matrix3.COLUMN0ROW0];
  97. const m11 = matrix[Matrix3.COLUMN1ROW1];
  98. const m22 = matrix[Matrix3.COLUMN2ROW2];
  99. const 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. const next = fromRotationMatrixNext;
  111. let i = 0;
  112. if (m11 > m00) {
  113. i = 1;
  114. }
  115. if (m22 > m00 && m22 > m11) {
  116. i = 2;
  117. }
  118. const j = next[i];
  119. const 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. const 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. const scratchHPRQuaternion = new Quaternion();
  155. let scratchHeadingQuaternion = new Quaternion();
  156. let scratchPitchQuaternion = new Quaternion();
  157. let 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. const sampledQuaternionAxis = new Cartesian3();
  194. const sampledQuaternionRotation = new Cartesian3();
  195. const sampledQuaternionTempQuaternion = new Quaternion();
  196. const sampledQuaternionQuaternion0 = new Quaternion();
  197. const 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 (let i = 0, len = lastIndex - startingIndex + 1; i < len; i++) {
  275. const 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. const 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. const 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. const inverseMagnitude = 1.0 / Quaternion.magnitude(quaternion);
  424. const x = quaternion.x * inverseMagnitude;
  425. const y = quaternion.y * inverseMagnitude;
  426. const z = quaternion.z * inverseMagnitude;
  427. const 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. const 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. const leftX = left.x;
  538. const leftY = left.y;
  539. const leftZ = left.z;
  540. const leftW = left.w;
  541. const rightX = right.x;
  542. const rightY = right.y;
  543. const rightZ = right.z;
  544. const rightW = right.w;
  545. const x = leftW * rightX + leftX * rightW + leftY * rightZ - leftZ * rightY;
  546. const y = leftW * rightY - leftX * rightZ + leftY * rightW + leftZ * rightX;
  547. const z = leftW * rightZ + leftX * rightY - leftY * rightX + leftZ * rightW;
  548. const 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. const 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. const 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. let 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. let slerpEndNegated = new Quaternion();
  655. let slerpScaledP = new Quaternion();
  656. let 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. let 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. let 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. const 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. const theta = CesiumMath.acosClamped(quaternion.w);
  715. let 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. const theta = Cartesian3.magnitude(cartesian);
  734. let 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. const squadScratchCartesian0 = new Cartesian3();
  745. const squadScratchCartesian1 = new Cartesian3();
  746. const squadScratchQuaternion0 = new Quaternion();
  747. const 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. const qInv = Quaternion.conjugate(q1, squadScratchQuaternion0);
  768. Quaternion.multiply(qInv, q2, squadScratchQuaternion1);
  769. const cart0 = Quaternion.log(squadScratchQuaternion1, squadScratchCartesian0);
  770. Quaternion.multiply(qInv, q0, squadScratchQuaternion1);
  771. const 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. * const s0 = Cesium.Quaternion.computeInnerQuadrangle(quaternions[i - 1], quaternions[i], quaternions[i + 1], new Cesium.Quaternion());
  793. * const s1 = Cesium.Quaternion.computeInnerQuadrangle(quaternions[i], quaternions[i + 1], quaternions[i + 2], new Cesium.Quaternion());
  794. * const 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. * const s1 = Cesium.Quaternion.computeInnerQuadrangle(quaternions[0], quaternions[1], quaternions[2], new Cesium.Quaternion());
  798. * const 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. const slerp0 = Quaternion.slerp(q0, q1, t, squadScratchQuaternion0);
  812. const slerp1 = Quaternion.slerp(s0, s1, t, squadScratchQuaternion1);
  813. return Quaternion.slerp(slerp0, slerp1, 2.0 * t * (1.0 - t), result);
  814. };
  815. const fastSlerpScratchQuaternion = new Quaternion();
  816. const opmu = 1.90110745351730037;
  817. const u = FeatureDetection.supportsTypedArrays() ? new Float32Array(8) : [];
  818. const v = FeatureDetection.supportsTypedArrays() ? new Float32Array(8) : [];
  819. const bT = FeatureDetection.supportsTypedArrays() ? new Float32Array(8) : [];
  820. const bD = FeatureDetection.supportsTypedArrays() ? new Float32Array(8) : [];
  821. for (let i = 0; i < 7; ++i) {
  822. const s = i + 1.0;
  823. const 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. let x = Quaternion.dot(start, end);
  849. let sign;
  850. if (x >= 0) {
  851. sign = 1.0;
  852. } else {
  853. sign = -1.0;
  854. x = -x;
  855. }
  856. const xm1 = x - 1.0;
  857. const d = 1.0 - t;
  858. const sqrT = t * t;
  859. const sqrD = d * d;
  860. for (let 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. const 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. const 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. const temp = Quaternion.multiplyByScalar(
  892. start,
  893. cD,
  894. fastSlerpScratchQuaternion
  895. );
  896. Quaternion.multiplyByScalar(end, cT, result);
  897. return Quaternion.add(temp, result, result);
  898. };
  899. /**
  900. * Computes the spherical quadrangle interpolation between quaternions.
  901. * An implementation that is faster than {@link Quaternion#squad}, but less accurate.
  902. *
  903. * @param {Quaternion} q0 The first quaternion.
  904. * @param {Quaternion} q1 The second quaternion.
  905. * @param {Quaternion} s0 The first inner quadrangle.
  906. * @param {Quaternion} s1 The second inner quadrangle.
  907. * @param {Number} t The time in [0,1] used to interpolate.
  908. * @param {Quaternion} result The object onto which to store the result.
  909. * @returns {Quaternion} The modified result parameter or a new instance if none was provided.
  910. *
  911. * @see Quaternion#squad
  912. */
  913. Quaternion.fastSquad = function (q0, q1, s0, s1, t, result) {
  914. //>>includeStart('debug', pragmas.debug);
  915. Check.typeOf.object("q0", q0);
  916. Check.typeOf.object("q1", q1);
  917. Check.typeOf.object("s0", s0);
  918. Check.typeOf.object("s1", s1);
  919. Check.typeOf.number("t", t);
  920. Check.typeOf.object("result", result);
  921. //>>includeEnd('debug');
  922. const slerp0 = Quaternion.fastSlerp(q0, q1, t, squadScratchQuaternion0);
  923. const slerp1 = Quaternion.fastSlerp(s0, s1, t, squadScratchQuaternion1);
  924. return Quaternion.fastSlerp(slerp0, slerp1, 2.0 * t * (1.0 - t), result);
  925. };
  926. /**
  927. * Compares the provided quaternions componentwise and returns
  928. * <code>true</code> if they are equal, <code>false</code> otherwise.
  929. *
  930. * @param {Quaternion} [left] The first quaternion.
  931. * @param {Quaternion} [right] The second quaternion.
  932. * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise.
  933. */
  934. Quaternion.equals = function (left, right) {
  935. return (
  936. left === right ||
  937. (defined(left) &&
  938. defined(right) &&
  939. left.x === right.x &&
  940. left.y === right.y &&
  941. left.z === right.z &&
  942. left.w === right.w)
  943. );
  944. };
  945. /**
  946. * Compares the provided quaternions componentwise and returns
  947. * <code>true</code> if they are within the provided epsilon,
  948. * <code>false</code> otherwise.
  949. *
  950. * @param {Quaternion} [left] The first quaternion.
  951. * @param {Quaternion} [right] The second quaternion.
  952. * @param {Number} [epsilon=0] The epsilon to use for equality testing.
  953. * @returns {Boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise.
  954. */
  955. Quaternion.equalsEpsilon = function (left, right, epsilon) {
  956. epsilon = defaultValue(epsilon, 0);
  957. return (
  958. left === right ||
  959. (defined(left) &&
  960. defined(right) &&
  961. Math.abs(left.x - right.x) <= epsilon &&
  962. Math.abs(left.y - right.y) <= epsilon &&
  963. Math.abs(left.z - right.z) <= epsilon &&
  964. Math.abs(left.w - right.w) <= epsilon)
  965. );
  966. };
  967. /**
  968. * An immutable Quaternion instance initialized to (0.0, 0.0, 0.0, 0.0).
  969. *
  970. * @type {Quaternion}
  971. * @constant
  972. */
  973. Quaternion.ZERO = Object.freeze(new Quaternion(0.0, 0.0, 0.0, 0.0));
  974. /**
  975. * An immutable Quaternion instance initialized to (0.0, 0.0, 0.0, 1.0).
  976. *
  977. * @type {Quaternion}
  978. * @constant
  979. */
  980. Quaternion.IDENTITY = Object.freeze(new Quaternion(0.0, 0.0, 0.0, 1.0));
  981. /**
  982. * Duplicates this Quaternion instance.
  983. *
  984. * @param {Quaternion} [result] The object onto which to store the result.
  985. * @returns {Quaternion} The modified result parameter or a new Quaternion instance if one was not provided.
  986. */
  987. Quaternion.prototype.clone = function (result) {
  988. return Quaternion.clone(this, result);
  989. };
  990. /**
  991. * Compares this and the provided quaternion componentwise and returns
  992. * <code>true</code> if they are equal, <code>false</code> otherwise.
  993. *
  994. * @param {Quaternion} [right] The right hand side quaternion.
  995. * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise.
  996. */
  997. Quaternion.prototype.equals = function (right) {
  998. return Quaternion.equals(this, right);
  999. };
  1000. /**
  1001. * Compares this and the provided quaternion componentwise and returns
  1002. * <code>true</code> if they are within the provided epsilon,
  1003. * <code>false</code> otherwise.
  1004. *
  1005. * @param {Quaternion} [right] The right hand side quaternion.
  1006. * @param {Number} [epsilon=0] The epsilon to use for equality testing.
  1007. * @returns {Boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise.
  1008. */
  1009. Quaternion.prototype.equalsEpsilon = function (right, epsilon) {
  1010. return Quaternion.equalsEpsilon(this, right, epsilon);
  1011. };
  1012. /**
  1013. * Returns a string representing this quaternion in the format (x, y, z, w).
  1014. *
  1015. * @returns {String} A string representing this Quaternion.
  1016. */
  1017. Quaternion.prototype.toString = function () {
  1018. return `(${this.x}, ${this.y}, ${this.z}, ${this.w})`;
  1019. };
  1020. export default Quaternion;