quaternion_test.js 9.9 KB

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