quaternion_test.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. // Copyright 2011 The Closure Library Authors. All Rights Reserved.
  2. // Use of this source code is governed by the Apache License, Version 2.0.
  3. goog.provide('goog.vec.QuaternionTest');
  4. goog.setTestOnly('goog.vec.QuaternionTest');
  5. goog.require('goog.testing.jsunit');
  6. goog.require('goog.vec.Mat3');
  7. goog.require('goog.vec.Mat4');
  8. goog.require('goog.vec.Quaternion');
  9. goog.require('goog.vec.Vec3');
  10. goog.require('goog.vec.vec3f');
  11. function testCreateIdentityFloat32() {
  12. var q = goog.vec.Quaternion.createIdentityFloat32();
  13. assertElementsEquals([0, 0, 0, 1], q);
  14. }
  15. function testInvert() {
  16. var q0 = goog.vec.Quaternion.createFloat32FromValues(1, 2, 3, 4);
  17. var q1 = goog.vec.Quaternion.createFloat32();
  18. goog.vec.Quaternion.invert(q0, q1);
  19. assertElementsRoughlyEqual([1, 2, 3, 4], q0, goog.vec.EPSILON);
  20. assertElementsRoughlyEqual([-0.033333, -0.066666, -0.1, 0.133333], q1,
  21. goog.vec.EPSILON);
  22. }
  23. function testConjugate() {
  24. var q0 = goog.vec.Quaternion.createFloat32FromValues(1, 2, 3, 4);
  25. var q1 = goog.vec.Quaternion.createFloat32();
  26. goog.vec.Quaternion.conjugate(q0, q1);
  27. assertElementsEquals([1, 2, 3, 4], q0);
  28. assertElementsEquals([-1, -2, -3, 4], q1);
  29. goog.vec.Quaternion.conjugate(q1, q1);
  30. assertElementsEquals([1, 2, 3, 4], q1);
  31. // Conjugate and inverse of a normalized quaternion should be equal.
  32. var q2 = goog.vec.Quaternion.createFloat32();
  33. var q3 = goog.vec.Quaternion.createFloat32();
  34. goog.vec.Quaternion.normalize(q0, q2);
  35. goog.vec.Quaternion.conjugate(q2, q2);
  36. goog.vec.Quaternion.normalize(q0, q3);
  37. goog.vec.Quaternion.invert(q3, q3);
  38. assertElementsRoughlyEqual(q2, q3, goog.vec.EPSILON);
  39. }
  40. function testConcat() {
  41. var q0 = goog.vec.Quaternion.createFloat32FromValues(1, 2, 3, 4);
  42. var q1 = goog.vec.Quaternion.createFloat32FromValues(2, 3, 4, 5);
  43. var q2 = goog.vec.Quaternion.createFloat32();
  44. goog.vec.Quaternion.concat(q0, q1, q2);
  45. assertElementsEquals([12, 24, 30, 0], q2);
  46. goog.vec.Quaternion.concat(q0, q1, q0);
  47. assertElementsEquals([12, 24, 30, 0], q0);
  48. }
  49. function testMakeIdentity() {
  50. var q = goog.vec.Quaternion.createFloat32FromValues(1, 2, 3, 4);
  51. goog.vec.Quaternion.makeIdentity(q);
  52. assertElementsEquals([0, 0, 0, 1], q);
  53. }
  54. function testRotateX() {
  55. var q = goog.vec.Quaternion.createIdentityFloat32();
  56. goog.vec.Quaternion.rotateX(q, Math.PI / 2, q);
  57. var axis = goog.vec.Vec3.createFloat32();
  58. var angle = goog.vec.Quaternion.toAngleAxis(q, axis);
  59. assertElementsRoughlyEqual([1, 0, 0], axis, goog.vec.EPSILON);
  60. assertRoughlyEquals(Math.PI / 2, angle, goog.vec.EPSILON);
  61. }
  62. function testRotateY() {
  63. var q = goog.vec.Quaternion.createIdentityFloat32();
  64. goog.vec.Quaternion.rotateY(q, Math.PI / 2, q);
  65. var axis = goog.vec.Vec3.createFloat32();
  66. var angle = goog.vec.Quaternion.toAngleAxis(q, axis);
  67. assertElementsRoughlyEqual([0, 1, 0], axis, goog.vec.EPSILON);
  68. assertRoughlyEquals(Math.PI / 2, angle, goog.vec.EPSILON);
  69. }
  70. function testRotateZ() {
  71. var q = goog.vec.Quaternion.createIdentityFloat32();
  72. goog.vec.Quaternion.rotateZ(q, Math.PI / 2, q);
  73. var axis = goog.vec.Vec3.createFloat32();
  74. var angle = goog.vec.Quaternion.toAngleAxis(q, axis);
  75. assertElementsRoughlyEqual([0, 0, 1], axis, goog.vec.EPSILON);
  76. assertRoughlyEquals(Math.PI / 2, angle, goog.vec.EPSILON);
  77. }
  78. function testTransformVec() {
  79. var q = goog.vec.Quaternion.createIdentityFloat32();
  80. goog.vec.Quaternion.rotateX(q, Math.PI / 2, q);
  81. var v0 = goog.vec.vec3f.setFromArray(goog.vec.vec3f.create(), [0, 0, 1]);
  82. var v1 = goog.vec.vec3f.create();
  83. goog.vec.Quaternion.transformVec(v0, q, v1);
  84. assertElementsRoughlyEqual([0, -1, 0], v1, goog.vec.EPSILON);
  85. }
  86. function testSlerp() {
  87. var q0 = goog.vec.Quaternion.createFloat32FromValues(1, 2, 3, 4);
  88. var q1 = goog.vec.Quaternion.createFloat32FromValues(5, -6, 7, -8);
  89. var q2 = goog.vec.Quaternion.createFloat32();
  90. goog.vec.Quaternion.slerp(q0, q1, 0, q2);
  91. assertElementsEquals([5, -6, 7, -8], q2);
  92. goog.vec.Quaternion.normalize(q0, q0);
  93. goog.vec.Quaternion.normalize(q1, q1);
  94. goog.vec.Quaternion.slerp(q0, q0, .5, q2);
  95. assertElementsEquals(q0, q2);
  96. goog.vec.Quaternion.slerp(q0, q1, 0, q2);
  97. assertElementsEquals(q0, q2);
  98. goog.vec.Quaternion.slerp(q0, q1, 1, q2);
  99. if (q1[3] * q2[3] < 0) {
  100. goog.vec.Quaternion.negate(q2, q2);
  101. }
  102. assertElementsEquals(q1, q2);
  103. goog.vec.Quaternion.slerp(q0, q1, .3, q2);
  104. assertElementsRoughlyEqual(
  105. [-0.000501537327541, 0.4817612034640, 0.2398775270769, 0.842831337398],
  106. q2, goog.vec.EPSILON);
  107. goog.vec.Quaternion.slerp(q0, q1, .5, q2);
  108. assertElementsRoughlyEqual(
  109. [-0.1243045421171, 0.51879732466, 0.0107895780990, 0.845743047108],
  110. q2, goog.vec.EPSILON);
  111. goog.vec.Quaternion.slerp(q0, q1, .8, q0);
  112. assertElementsRoughlyEqual(
  113. [-0.291353561485, 0.506925588797, -0.3292443285721, 0.741442999653],
  114. q0, goog.vec.EPSILON);
  115. }
  116. function testFromRotMatrix() {
  117. var m0 = goog.vec.Mat3.createFloat32FromValues(
  118. -0.408248, 0.8796528, -0.244016935,
  119. -0.4082482, 0.06315623, 0.9106836,
  120. 0.8164965, 0.47140452, 0.3333333);
  121. var q0 = goog.vec.Quaternion.createFloat32();
  122. goog.vec.Quaternion.fromRotationMatrix3(m0, q0);
  123. assertElementsRoughlyEqual(
  124. [0.22094256606638, 0.53340203646030, 0.64777022739548, 0.497051689967954],
  125. q0, goog.vec.EPSILON);
  126. var m1 = goog.vec.Mat3.createFloat32FromValues(
  127. -0.544310, 0, 0.838884, 0, 1, 0, -0.838884, 0, -0.544310);
  128. var q1 = goog.vec.Quaternion.createFloat32();
  129. goog.vec.Quaternion.fromRotationMatrix3(m1, q1);
  130. assertElementsRoughlyEqual(
  131. [0, -0.87872350215912, 0, 0.477331042289734], q1, goog.vec.EPSILON);
  132. var m2 = goog.vec.Mat4.createFloat32FromValues(
  133. -0.408248, 0.8796528, -0.244016935, 0, -0.4082482, 0.06315623, 0.9106836,
  134. 0, 0.8164965, 0.47140452, 0.3333333, 0, 0, 0, 0, 1);
  135. var q2 = goog.vec.Quaternion.createFloat32();
  136. goog.vec.Quaternion.fromRotationMatrix4(m2, q2);
  137. assertElementsRoughlyEqual(
  138. [0.22094256606638, 0.53340203646030, 0.64777022739548, 0.497051689967954],
  139. q2, goog.vec.EPSILON);
  140. var m3 = goog.vec.Mat4.createFloat32FromValues(
  141. -0.544310, 0, 0.838884, 0, 0, 1, 0, 0, -0.838884, 0, -0.544310, 0, 0, 0,
  142. 0, 1);
  143. var q3 = goog.vec.Quaternion.createFloat32();
  144. goog.vec.Quaternion.fromRotationMatrix4(m3, q3);
  145. assertElementsRoughlyEqual(
  146. [0, -0.87872350215912, 0, 0.477331042289734], q3, goog.vec.EPSILON);
  147. assertElementsRoughlyEqual(q0, q2, goog.vec.EPSILON);
  148. assertElementsRoughlyEqual(q1, q3, goog.vec.EPSILON);
  149. }
  150. function testToRotMatrix() {
  151. var q0 = goog.vec.Quaternion.createFloat32FromValues(
  152. 0.22094256606638, 0.53340203646030,
  153. 0.64777022739548, 0.497051689967954);
  154. var m0 = goog.vec.Mat3.createFloat32();
  155. goog.vec.Quaternion.toRotationMatrix3(q0, m0);
  156. assertElementsRoughlyEqual(
  157. [-0.408248, 0.8796528, -0.244016935,
  158. -0.4082482, 0.06315623, 0.9106836,
  159. 0.8164965, 0.47140452, 0.3333333],
  160. m0, goog.vec.EPSILON);
  161. var m1 = goog.vec.Mat4.createFloat32();
  162. goog.vec.Quaternion.toRotationMatrix4(q0, m1);
  163. assertElementsRoughlyEqual(
  164. [-0.408248, 0.8796528, -0.244016935, 0,
  165. -0.4082482, 0.06315623, 0.9106836, 0,
  166. 0.8164965, 0.47140452, 0.3333333, 0,
  167. 0, 0, 0, 1],
  168. m1, goog.vec.EPSILON);
  169. }
  170. function testToAngleAxis() {
  171. // Test the identity rotation.
  172. var q0 = goog.vec.Quaternion.createFloat32FromValues(0, 0, 0, 1);
  173. var axis = goog.vec.Vec3.createFloat32();
  174. var angle = goog.vec.Quaternion.toAngleAxis(q0, axis);
  175. assertRoughlyEquals(0.0, angle, goog.vec.EPSILON);
  176. assertElementsRoughlyEqual([1, 0, 0], axis, goog.vec.EPSILON);
  177. // Check equivalent representations of the same rotation.
  178. goog.vec.Quaternion.setFromValues(
  179. q0, -0.288675032, 0.622008682, -0.17254543, 0.70710678);
  180. angle = goog.vec.Quaternion.toAngleAxis(q0, axis);
  181. assertRoughlyEquals(Math.PI / 2, angle, goog.vec.EPSILON);
  182. assertElementsRoughlyEqual([-0.408248, 0.8796528, -0.244016],
  183. axis, goog.vec.EPSILON);
  184. // The polar opposite unit quaternion is the same rotation, so we
  185. // check that the negated quaternion yields the negated angle and axis.
  186. goog.vec.Quaternion.negate(q0, q0);
  187. angle = goog.vec.Quaternion.toAngleAxis(q0, axis);
  188. assertRoughlyEquals(-Math.PI / 2, angle, goog.vec.EPSILON);
  189. assertElementsRoughlyEqual([0.408248, -0.8796528, 0.244016],
  190. axis, goog.vec.EPSILON);
  191. // Verify that the inverse rotation yields the inverse axis.
  192. goog.vec.Quaternion.conjugate(q0, q0);
  193. angle = goog.vec.Quaternion.toAngleAxis(q0, axis);
  194. assertRoughlyEquals(-Math.PI / 2, angle, goog.vec.EPSILON);
  195. assertElementsRoughlyEqual([-0.408248, 0.8796528, -0.244016],
  196. axis, goog.vec.EPSILON);
  197. }
  198. function testFromAngleAxis() {
  199. // Test identity rotation (zero angle or multiples of TWO_PI).
  200. var angle = 0.0;
  201. var axis = goog.vec.Vec3.createFloat32FromValues(-0.408248, 0.8796528,
  202. -0.244016);
  203. var q0 = goog.vec.Quaternion.createFloat32();
  204. goog.vec.Quaternion.fromAngleAxis(angle, axis, q0);
  205. assertElementsRoughlyEqual([0, 0, 0, 1], q0, goog.vec.EPSILON);
  206. angle = 4 * Math.PI;
  207. goog.vec.Quaternion.fromAngleAxis(angle, axis, q0);
  208. assertElementsRoughlyEqual([0, 0, 0, 1], q0, goog.vec.EPSILON);
  209. // General test of various rotations around axes of different lengths.
  210. angle = Math.PI / 2;
  211. goog.vec.Quaternion.fromAngleAxis(angle, axis, q0);
  212. assertElementsRoughlyEqual(
  213. [-0.288675032, 0.622008682, -0.17254543, 0.70710678],
  214. q0, goog.vec.EPSILON);
  215. // Angle multiples of TWO_PI with a scaled axis should be the same.
  216. angle += 4 * Math.PI;
  217. goog.vec.Vec3.scale(axis, 7.0, axis);
  218. goog.vec.Quaternion.fromAngleAxis(angle, axis, q0);
  219. assertElementsRoughlyEqual(
  220. [-0.288675032, 0.622008682, -0.17254543, 0.70710678],
  221. q0, goog.vec.EPSILON);
  222. goog.vec.Vec3.setFromValues(axis, 1, 5, 8);
  223. goog.vec.Quaternion.fromAngleAxis(angle, axis, q0);
  224. assertElementsRoughlyEqual(
  225. [0.074535599, 0.372677996, 0.596284794, 0.70710678],
  226. q0, goog.vec.EPSILON);
  227. // Check equivalent representations of the same rotation.
  228. angle = Math.PI / 5;
  229. goog.vec.Vec3.setFromValues(axis, 5, -2, -10);
  230. goog.vec.Quaternion.fromAngleAxis(angle, axis, q0);
  231. assertElementsRoughlyEqual(
  232. [0.136037146, -0.0544148586, -0.27207429, 0.951056516],
  233. q0, goog.vec.EPSILON);
  234. // The negated angle and axis should yield the same rotation.
  235. angle = -Math.PI / 5;
  236. goog.vec.Vec3.negate(axis, axis);
  237. goog.vec.Quaternion.fromAngleAxis(angle, axis, q0);
  238. assertElementsRoughlyEqual(
  239. [0.136037146, -0.0544148586, -0.27207429, 0.951056516],
  240. q0, goog.vec.EPSILON);
  241. }