test_quaternion.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. /**************************************************************************/
  2. /* test_quaternion.h */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #ifndef TEST_QUATERNION_H
  31. #define TEST_QUATERNION_H
  32. #include "core/math/math_defs.h"
  33. #include "core/math/math_funcs.h"
  34. #include "core/math/quaternion.h"
  35. #include "core/math/vector3.h"
  36. #include "tests/test_macros.h"
  37. namespace TestQuaternion {
  38. Quaternion quat_euler_yxz_deg(Vector3 angle) {
  39. double yaw = Math::deg_to_rad(angle[1]);
  40. double pitch = Math::deg_to_rad(angle[0]);
  41. double roll = Math::deg_to_rad(angle[2]);
  42. // Generate YXZ (Z-then-X-then-Y) Quaternion using single-axis Euler
  43. // constructor and quaternion product, both tested separately.
  44. Quaternion q_y = Quaternion::from_euler(Vector3(0.0, yaw, 0.0));
  45. Quaternion q_p = Quaternion::from_euler(Vector3(pitch, 0.0, 0.0));
  46. Quaternion q_r = Quaternion::from_euler(Vector3(0.0, 0.0, roll));
  47. // Roll-Z is followed by Pitch-X, then Yaw-Y.
  48. Quaternion q_yxz = q_y * q_p * q_r;
  49. return q_yxz;
  50. }
  51. TEST_CASE("[Quaternion] Default Construct") {
  52. Quaternion q;
  53. CHECK(q[0] == 0.0);
  54. CHECK(q[1] == 0.0);
  55. CHECK(q[2] == 0.0);
  56. CHECK(q[3] == 1.0);
  57. }
  58. TEST_CASE("[Quaternion] Construct x,y,z,w") {
  59. // Values are taken from actual use in another project & are valid (except roundoff error).
  60. Quaternion q(0.2391, 0.099, 0.3696, 0.8924);
  61. CHECK(q[0] == doctest::Approx(0.2391));
  62. CHECK(q[1] == doctest::Approx(0.099));
  63. CHECK(q[2] == doctest::Approx(0.3696));
  64. CHECK(q[3] == doctest::Approx(0.8924));
  65. }
  66. TEST_CASE("[Quaternion] Construct AxisAngle 1") {
  67. // Easy to visualize: 120 deg about X-axis.
  68. Quaternion q(Vector3(1.0, 0.0, 0.0), Math::deg_to_rad(120.0));
  69. // 0.866 isn't close enough; doctest::Approx doesn't cut much slack!
  70. CHECK(q[0] == doctest::Approx(0.866025)); // Sine of half the angle.
  71. CHECK(q[1] == doctest::Approx(0.0));
  72. CHECK(q[2] == doctest::Approx(0.0));
  73. CHECK(q[3] == doctest::Approx(0.5)); // Cosine of half the angle.
  74. }
  75. TEST_CASE("[Quaternion] Construct AxisAngle 2") {
  76. // Easy to visualize: 30 deg about Y-axis.
  77. Quaternion q(Vector3(0.0, 1.0, 0.0), Math::deg_to_rad(30.0));
  78. CHECK(q[0] == doctest::Approx(0.0));
  79. CHECK(q[1] == doctest::Approx(0.258819)); // Sine of half the angle.
  80. CHECK(q[2] == doctest::Approx(0.0));
  81. CHECK(q[3] == doctest::Approx(0.965926)); // Cosine of half the angle.
  82. }
  83. TEST_CASE("[Quaternion] Construct AxisAngle 3") {
  84. // Easy to visualize: 60 deg about Z-axis.
  85. Quaternion q(Vector3(0.0, 0.0, 1.0), Math::deg_to_rad(60.0));
  86. CHECK(q[0] == doctest::Approx(0.0));
  87. CHECK(q[1] == doctest::Approx(0.0));
  88. CHECK(q[2] == doctest::Approx(0.5)); // Sine of half the angle.
  89. CHECK(q[3] == doctest::Approx(0.866025)); // Cosine of half the angle.
  90. }
  91. TEST_CASE("[Quaternion] Construct AxisAngle 4") {
  92. // More complex & hard to visualize, so test w/ data from online calculator.
  93. Vector3 axis(1.0, 2.0, 0.5);
  94. Quaternion q(axis.normalized(), Math::deg_to_rad(35.0));
  95. CHECK(q[0] == doctest::Approx(0.131239));
  96. CHECK(q[1] == doctest::Approx(0.262478));
  97. CHECK(q[2] == doctest::Approx(0.0656194));
  98. CHECK(q[3] == doctest::Approx(0.953717));
  99. }
  100. TEST_CASE("[Quaternion] Construct from Quaternion") {
  101. Vector3 axis(1.0, 2.0, 0.5);
  102. Quaternion q_src(axis.normalized(), Math::deg_to_rad(35.0));
  103. Quaternion q(q_src);
  104. CHECK(q[0] == doctest::Approx(0.131239));
  105. CHECK(q[1] == doctest::Approx(0.262478));
  106. CHECK(q[2] == doctest::Approx(0.0656194));
  107. CHECK(q[3] == doctest::Approx(0.953717));
  108. }
  109. TEST_CASE("[Quaternion] Construct Euler SingleAxis") {
  110. double yaw = Math::deg_to_rad(45.0);
  111. double pitch = Math::deg_to_rad(30.0);
  112. double roll = Math::deg_to_rad(10.0);
  113. Vector3 euler_y(0.0, yaw, 0.0);
  114. Quaternion q_y = Quaternion::from_euler(euler_y);
  115. CHECK(q_y[0] == doctest::Approx(0.0));
  116. CHECK(q_y[1] == doctest::Approx(0.382684));
  117. CHECK(q_y[2] == doctest::Approx(0.0));
  118. CHECK(q_y[3] == doctest::Approx(0.923879));
  119. Vector3 euler_p(pitch, 0.0, 0.0);
  120. Quaternion q_p = Quaternion::from_euler(euler_p);
  121. CHECK(q_p[0] == doctest::Approx(0.258819));
  122. CHECK(q_p[1] == doctest::Approx(0.0));
  123. CHECK(q_p[2] == doctest::Approx(0.0));
  124. CHECK(q_p[3] == doctest::Approx(0.965926));
  125. Vector3 euler_r(0.0, 0.0, roll);
  126. Quaternion q_r = Quaternion::from_euler(euler_r);
  127. CHECK(q_r[0] == doctest::Approx(0.0));
  128. CHECK(q_r[1] == doctest::Approx(0.0));
  129. CHECK(q_r[2] == doctest::Approx(0.0871558));
  130. CHECK(q_r[3] == doctest::Approx(0.996195));
  131. }
  132. TEST_CASE("[Quaternion] Construct Euler YXZ dynamic axes") {
  133. double yaw = Math::deg_to_rad(45.0);
  134. double pitch = Math::deg_to_rad(30.0);
  135. double roll = Math::deg_to_rad(10.0);
  136. // Generate YXZ comparison data (Z-then-X-then-Y) using single-axis Euler
  137. // constructor and quaternion product, both tested separately.
  138. Vector3 euler_y(0.0, yaw, 0.0);
  139. Quaternion q_y = Quaternion::from_euler(euler_y);
  140. Vector3 euler_p(pitch, 0.0, 0.0);
  141. Quaternion q_p = Quaternion::from_euler(euler_p);
  142. Vector3 euler_r(0.0, 0.0, roll);
  143. Quaternion q_r = Quaternion::from_euler(euler_r);
  144. // Instrinsically, Yaw-Y then Pitch-X then Roll-Z.
  145. // Extrinsically, Roll-Z is followed by Pitch-X, then Yaw-Y.
  146. Quaternion check_yxz = q_y * q_p * q_r;
  147. // Test construction from YXZ Euler angles.
  148. Vector3 euler_yxz(pitch, yaw, roll);
  149. Quaternion q = Quaternion::from_euler(euler_yxz);
  150. CHECK(q[0] == doctest::Approx(check_yxz[0]));
  151. CHECK(q[1] == doctest::Approx(check_yxz[1]));
  152. CHECK(q[2] == doctest::Approx(check_yxz[2]));
  153. CHECK(q[3] == doctest::Approx(check_yxz[3]));
  154. CHECK(q.is_equal_approx(check_yxz));
  155. CHECK(q.get_euler().is_equal_approx(euler_yxz));
  156. CHECK(check_yxz.get_euler().is_equal_approx(euler_yxz));
  157. }
  158. TEST_CASE("[Quaternion] Construct Basis Euler") {
  159. double yaw = Math::deg_to_rad(45.0);
  160. double pitch = Math::deg_to_rad(30.0);
  161. double roll = Math::deg_to_rad(10.0);
  162. Vector3 euler_yxz(pitch, yaw, roll);
  163. Quaternion q_yxz = Quaternion::from_euler(euler_yxz);
  164. Basis basis_axes = Basis::from_euler(euler_yxz);
  165. Quaternion q(basis_axes);
  166. CHECK(q.is_equal_approx(q_yxz));
  167. }
  168. TEST_CASE("[Quaternion] Construct Basis Axes") {
  169. // Arbitrary Euler angles.
  170. Vector3 euler_yxz(Math::deg_to_rad(31.41), Math::deg_to_rad(-49.16), Math::deg_to_rad(12.34));
  171. // Basis vectors from online calculation of rotation matrix.
  172. Vector3 i_unit(0.5545787, 0.1823950, 0.8118957);
  173. Vector3 j_unit(-0.5249245, 0.8337420, 0.1712555);
  174. Vector3 k_unit(-0.6456754, -0.5211586, 0.5581192);
  175. // Quaternion from online calculation.
  176. Quaternion q_calc(0.2016913, -0.4245716, 0.206033, 0.8582598);
  177. // Quaternion from local calculation.
  178. Quaternion q_local = quat_euler_yxz_deg(Vector3(31.41, -49.16, 12.34));
  179. // Quaternion from Euler angles constructor.
  180. Quaternion q_euler = Quaternion::from_euler(euler_yxz);
  181. CHECK(q_calc.is_equal_approx(q_local));
  182. CHECK(q_local.is_equal_approx(q_euler));
  183. // Calculate Basis and construct Quaternion.
  184. // When this is written, C++ Basis class does not construct from basis vectors.
  185. // This is by design, but may be subject to change.
  186. // Workaround by constructing Basis from Euler angles.
  187. // basis_axes = Basis(i_unit, j_unit, k_unit);
  188. Basis basis_axes = Basis::from_euler(euler_yxz);
  189. Quaternion q(basis_axes);
  190. CHECK(basis_axes.get_column(0).is_equal_approx(i_unit));
  191. CHECK(basis_axes.get_column(1).is_equal_approx(j_unit));
  192. CHECK(basis_axes.get_column(2).is_equal_approx(k_unit));
  193. CHECK(q.is_equal_approx(q_calc));
  194. CHECK_FALSE(q.inverse().is_equal_approx(q_calc));
  195. CHECK(q.is_equal_approx(q_local));
  196. CHECK(q.is_equal_approx(q_euler));
  197. CHECK(q[0] == doctest::Approx(0.2016913));
  198. CHECK(q[1] == doctest::Approx(-0.4245716));
  199. CHECK(q[2] == doctest::Approx(0.206033));
  200. CHECK(q[3] == doctest::Approx(0.8582598));
  201. }
  202. TEST_CASE("[Quaternion] Construct Shortest Arc For 180 Degree Arc") {
  203. Vector3 up(0, 1, 0);
  204. Vector3 down(0, -1, 0);
  205. Vector3 left(-1, 0, 0);
  206. Vector3 right(1, 0, 0);
  207. Vector3 forward(0, 0, -1);
  208. Vector3 back(0, 0, 1);
  209. // When we have a 180 degree rotation quaternion which was defined as
  210. // A to B, logically when we transform A we expect to get B.
  211. Quaternion left_to_right(left, right);
  212. Quaternion right_to_left(right, left);
  213. CHECK(left_to_right.xform(left).is_equal_approx(right));
  214. CHECK(Quaternion(right, left).xform(right).is_equal_approx(left));
  215. CHECK(Quaternion(up, down).xform(up).is_equal_approx(down));
  216. CHECK(Quaternion(down, up).xform(down).is_equal_approx(up));
  217. CHECK(Quaternion(forward, back).xform(forward).is_equal_approx(back));
  218. CHECK(Quaternion(back, forward).xform(back).is_equal_approx(forward));
  219. // With (arbitrary) opposite vectors that are not axis-aligned as parameters.
  220. Vector3 diagonal_up = Vector3(1.2, 2.3, 4.5).normalized();
  221. Vector3 diagonal_down = -diagonal_up;
  222. Quaternion q1(diagonal_up, diagonal_down);
  223. CHECK(q1.xform(diagonal_down).is_equal_approx(diagonal_up));
  224. CHECK(q1.xform(diagonal_up).is_equal_approx(diagonal_down));
  225. // For the consistency of the rotation direction, they should be symmetrical to the plane.
  226. CHECK(left_to_right.is_equal_approx(right_to_left.inverse()));
  227. // If vectors are same, no rotation.
  228. CHECK(Quaternion(diagonal_up, diagonal_up).is_equal_approx(Quaternion()));
  229. }
  230. TEST_CASE("[Quaternion] Get Euler Orders") {
  231. double x = Math::deg_to_rad(30.0);
  232. double y = Math::deg_to_rad(45.0);
  233. double z = Math::deg_to_rad(10.0);
  234. Vector3 euler(x, y, z);
  235. for (int i = 0; i < 6; i++) {
  236. EulerOrder order = (EulerOrder)i;
  237. Basis basis = Basis::from_euler(euler, order);
  238. Quaternion q = Quaternion(basis);
  239. Vector3 check = q.get_euler(order);
  240. CHECK_MESSAGE(check.is_equal_approx(euler),
  241. "Quaternion get_euler method should return the original angles.");
  242. CHECK_MESSAGE(check.is_equal_approx(basis.get_euler(order)),
  243. "Quaternion get_euler method should behave the same as Basis get_euler.");
  244. }
  245. }
  246. TEST_CASE("[Quaternion] Product (book)") {
  247. // Example from "Quaternions and Rotation Sequences" by Jack Kuipers, p. 108.
  248. Quaternion p(1.0, -2.0, 1.0, 3.0);
  249. Quaternion q(-1.0, 2.0, 3.0, 2.0);
  250. Quaternion pq = p * q;
  251. CHECK(pq[0] == doctest::Approx(-9.0));
  252. CHECK(pq[1] == doctest::Approx(-2.0));
  253. CHECK(pq[2] == doctest::Approx(11.0));
  254. CHECK(pq[3] == doctest::Approx(8.0));
  255. }
  256. TEST_CASE("[Quaternion] Product") {
  257. double yaw = Math::deg_to_rad(45.0);
  258. double pitch = Math::deg_to_rad(30.0);
  259. double roll = Math::deg_to_rad(10.0);
  260. Vector3 euler_y(0.0, yaw, 0.0);
  261. Quaternion q_y = Quaternion::from_euler(euler_y);
  262. CHECK(q_y[0] == doctest::Approx(0.0));
  263. CHECK(q_y[1] == doctest::Approx(0.382684));
  264. CHECK(q_y[2] == doctest::Approx(0.0));
  265. CHECK(q_y[3] == doctest::Approx(0.923879));
  266. Vector3 euler_p(pitch, 0.0, 0.0);
  267. Quaternion q_p = Quaternion::from_euler(euler_p);
  268. CHECK(q_p[0] == doctest::Approx(0.258819));
  269. CHECK(q_p[1] == doctest::Approx(0.0));
  270. CHECK(q_p[2] == doctest::Approx(0.0));
  271. CHECK(q_p[3] == doctest::Approx(0.965926));
  272. Vector3 euler_r(0.0, 0.0, roll);
  273. Quaternion q_r = Quaternion::from_euler(euler_r);
  274. CHECK(q_r[0] == doctest::Approx(0.0));
  275. CHECK(q_r[1] == doctest::Approx(0.0));
  276. CHECK(q_r[2] == doctest::Approx(0.0871558));
  277. CHECK(q_r[3] == doctest::Approx(0.996195));
  278. // Test ZYX dynamic-axes since test data is available online.
  279. // Rotate first about X axis, then new Y axis, then new Z axis.
  280. // (Godot uses YXZ Yaw-Pitch-Roll order).
  281. Quaternion q_yp = q_y * q_p;
  282. CHECK(q_yp[0] == doctest::Approx(0.239118));
  283. CHECK(q_yp[1] == doctest::Approx(0.369644));
  284. CHECK(q_yp[2] == doctest::Approx(-0.099046));
  285. CHECK(q_yp[3] == doctest::Approx(0.892399));
  286. Quaternion q_ryp = q_r * q_yp;
  287. CHECK(q_ryp[0] == doctest::Approx(0.205991));
  288. CHECK(q_ryp[1] == doctest::Approx(0.389078));
  289. CHECK(q_ryp[2] == doctest::Approx(-0.0208912));
  290. CHECK(q_ryp[3] == doctest::Approx(0.897636));
  291. }
  292. TEST_CASE("[Quaternion] xform unit vectors") {
  293. // Easy to visualize: 120 deg about X-axis.
  294. // Transform the i, j, & k unit vectors.
  295. Quaternion q(Vector3(1.0, 0.0, 0.0), Math::deg_to_rad(120.0));
  296. Vector3 i_t = q.xform(Vector3(1.0, 0.0, 0.0));
  297. Vector3 j_t = q.xform(Vector3(0.0, 1.0, 0.0));
  298. Vector3 k_t = q.xform(Vector3(0.0, 0.0, 1.0));
  299. //
  300. CHECK(i_t.is_equal_approx(Vector3(1.0, 0.0, 0.0)));
  301. CHECK(j_t.is_equal_approx(Vector3(0.0, -0.5, 0.866025)));
  302. CHECK(k_t.is_equal_approx(Vector3(0.0, -0.866025, -0.5)));
  303. CHECK(i_t.length_squared() == doctest::Approx(1.0));
  304. CHECK(j_t.length_squared() == doctest::Approx(1.0));
  305. CHECK(k_t.length_squared() == doctest::Approx(1.0));
  306. // Easy to visualize: 30 deg about Y-axis.
  307. q = Quaternion(Vector3(0.0, 1.0, 0.0), Math::deg_to_rad(30.0));
  308. i_t = q.xform(Vector3(1.0, 0.0, 0.0));
  309. j_t = q.xform(Vector3(0.0, 1.0, 0.0));
  310. k_t = q.xform(Vector3(0.0, 0.0, 1.0));
  311. //
  312. CHECK(i_t.is_equal_approx(Vector3(0.866025, 0.0, -0.5)));
  313. CHECK(j_t.is_equal_approx(Vector3(0.0, 1.0, 0.0)));
  314. CHECK(k_t.is_equal_approx(Vector3(0.5, 0.0, 0.866025)));
  315. CHECK(i_t.length_squared() == doctest::Approx(1.0));
  316. CHECK(j_t.length_squared() == doctest::Approx(1.0));
  317. CHECK(k_t.length_squared() == doctest::Approx(1.0));
  318. // Easy to visualize: 60 deg about Z-axis.
  319. q = Quaternion(Vector3(0.0, 0.0, 1.0), Math::deg_to_rad(60.0));
  320. i_t = q.xform(Vector3(1.0, 0.0, 0.0));
  321. j_t = q.xform(Vector3(0.0, 1.0, 0.0));
  322. k_t = q.xform(Vector3(0.0, 0.0, 1.0));
  323. //
  324. CHECK(i_t.is_equal_approx(Vector3(0.5, 0.866025, 0.0)));
  325. CHECK(j_t.is_equal_approx(Vector3(-0.866025, 0.5, 0.0)));
  326. CHECK(k_t.is_equal_approx(Vector3(0.0, 0.0, 1.0)));
  327. CHECK(i_t.length_squared() == doctest::Approx(1.0));
  328. CHECK(j_t.length_squared() == doctest::Approx(1.0));
  329. CHECK(k_t.length_squared() == doctest::Approx(1.0));
  330. }
  331. TEST_CASE("[Quaternion] xform vector") {
  332. // Arbitrary quaternion rotates an arbitrary vector.
  333. Vector3 euler_yzx(Math::deg_to_rad(31.41), Math::deg_to_rad(-49.16), Math::deg_to_rad(12.34));
  334. Basis basis_axes = Basis::from_euler(euler_yzx);
  335. Quaternion q(basis_axes);
  336. Vector3 v_arb(3.0, 4.0, 5.0);
  337. Vector3 v_rot = q.xform(v_arb);
  338. Vector3 v_compare = basis_axes.xform(v_arb);
  339. CHECK(v_rot.length_squared() == doctest::Approx(v_arb.length_squared()));
  340. CHECK(v_rot.is_equal_approx(v_compare));
  341. }
  342. // Test vector xform for a single combination of Quaternion and Vector.
  343. void test_quat_vec_rotate(Vector3 euler_yzx, Vector3 v_in) {
  344. Basis basis_axes = Basis::from_euler(euler_yzx);
  345. Quaternion q(basis_axes);
  346. Vector3 v_rot = q.xform(v_in);
  347. Vector3 v_compare = basis_axes.xform(v_in);
  348. CHECK(v_rot.length_squared() == doctest::Approx(v_in.length_squared()));
  349. CHECK(v_rot.is_equal_approx(v_compare));
  350. }
  351. TEST_CASE("[Stress][Quaternion] Many vector xforms") {
  352. // Many arbitrary quaternions rotate many arbitrary vectors.
  353. // For each trial, check that rotation by Quaternion yields same result as
  354. // rotation by Basis.
  355. const int STEPS = 100; // Number of test steps in each dimension
  356. const double delta = 2.0 * Math_PI / STEPS; // Angle increment per step
  357. const double delta_vec = 20.0 / STEPS; // Vector increment per step
  358. Vector3 vec_arb(1.0, 1.0, 1.0);
  359. double x_angle = -Math_PI;
  360. double y_angle = -Math_PI;
  361. double z_angle = -Math_PI;
  362. for (double i = 0; i < STEPS; ++i) {
  363. vec_arb[0] = -10.0 + i * delta_vec;
  364. x_angle = i * delta - Math_PI;
  365. for (double j = 0; j < STEPS; ++j) {
  366. vec_arb[1] = -10.0 + j * delta_vec;
  367. y_angle = j * delta - Math_PI;
  368. for (double k = 0; k < STEPS; ++k) {
  369. vec_arb[2] = -10.0 + k * delta_vec;
  370. z_angle = k * delta - Math_PI;
  371. Vector3 euler_yzx(x_angle, y_angle, z_angle);
  372. test_quat_vec_rotate(euler_yzx, vec_arb);
  373. }
  374. }
  375. }
  376. }
  377. TEST_CASE("[Quaternion] Finite number checks") {
  378. const real_t x = NAN;
  379. CHECK_MESSAGE(
  380. Quaternion(0, 1, 2, 3).is_finite(),
  381. "Quaternion with all components finite should be finite");
  382. CHECK_FALSE_MESSAGE(
  383. Quaternion(x, 1, 2, 3).is_finite(),
  384. "Quaternion with one component infinite should not be finite.");
  385. CHECK_FALSE_MESSAGE(
  386. Quaternion(0, x, 2, 3).is_finite(),
  387. "Quaternion with one component infinite should not be finite.");
  388. CHECK_FALSE_MESSAGE(
  389. Quaternion(0, 1, x, 3).is_finite(),
  390. "Quaternion with one component infinite should not be finite.");
  391. CHECK_FALSE_MESSAGE(
  392. Quaternion(0, 1, 2, x).is_finite(),
  393. "Quaternion with one component infinite should not be finite.");
  394. CHECK_FALSE_MESSAGE(
  395. Quaternion(x, x, 2, 3).is_finite(),
  396. "Quaternion with two components infinite should not be finite.");
  397. CHECK_FALSE_MESSAGE(
  398. Quaternion(x, 1, x, 3).is_finite(),
  399. "Quaternion with two components infinite should not be finite.");
  400. CHECK_FALSE_MESSAGE(
  401. Quaternion(x, 1, 2, x).is_finite(),
  402. "Quaternion with two components infinite should not be finite.");
  403. CHECK_FALSE_MESSAGE(
  404. Quaternion(0, x, x, 3).is_finite(),
  405. "Quaternion with two components infinite should not be finite.");
  406. CHECK_FALSE_MESSAGE(
  407. Quaternion(0, x, 2, x).is_finite(),
  408. "Quaternion with two components infinite should not be finite.");
  409. CHECK_FALSE_MESSAGE(
  410. Quaternion(0, 1, x, x).is_finite(),
  411. "Quaternion with two components infinite should not be finite.");
  412. CHECK_FALSE_MESSAGE(
  413. Quaternion(0, x, x, x).is_finite(),
  414. "Quaternion with three components infinite should not be finite.");
  415. CHECK_FALSE_MESSAGE(
  416. Quaternion(x, 1, x, x).is_finite(),
  417. "Quaternion with three components infinite should not be finite.");
  418. CHECK_FALSE_MESSAGE(
  419. Quaternion(x, x, 2, x).is_finite(),
  420. "Quaternion with three components infinite should not be finite.");
  421. CHECK_FALSE_MESSAGE(
  422. Quaternion(x, x, x, 3).is_finite(),
  423. "Quaternion with three components infinite should not be finite.");
  424. CHECK_FALSE_MESSAGE(
  425. Quaternion(x, x, x, x).is_finite(),
  426. "Quaternion with four components infinite should not be finite.");
  427. }
  428. } // namespace TestQuaternion
  429. #endif // TEST_QUATERNION_H