test_quaternion.h 19 KB

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