quaternion_unittest.cc 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ui/gfx/geometry/quaternion.h"
  5. #include <cmath>
  6. #include "base/numerics/math_constants.h"
  7. #include "testing/gtest/include/gtest/gtest.h"
  8. #include "ui/gfx/geometry/vector3d_f.h"
  9. namespace gfx {
  10. namespace {
  11. const double kEpsilon = 1e-7;
  12. #define EXPECT_QUATERNION(expected, actual) \
  13. do { \
  14. EXPECT_NEAR(expected.x(), actual.x(), kEpsilon); \
  15. EXPECT_NEAR(expected.y(), actual.y(), kEpsilon); \
  16. EXPECT_NEAR(expected.z(), actual.z(), kEpsilon); \
  17. EXPECT_NEAR(expected.w(), actual.w(), kEpsilon); \
  18. } while (false)
  19. void CompareQuaternions(const Quaternion& a, const Quaternion& b) {
  20. EXPECT_FLOAT_EQ(a.x(), b.x());
  21. EXPECT_FLOAT_EQ(a.y(), b.y());
  22. EXPECT_FLOAT_EQ(a.z(), b.z());
  23. EXPECT_FLOAT_EQ(a.w(), b.w());
  24. }
  25. } // namespace
  26. TEST(QuatTest, DefaultConstruction) {
  27. CompareQuaternions(Quaternion(0, 0, 0, 1), Quaternion());
  28. }
  29. TEST(QuatTest, AxisAngleCommon) {
  30. double radians = 0.5;
  31. Quaternion q(Vector3dF(1, 0, 0), radians);
  32. CompareQuaternions(
  33. Quaternion(std::sin(radians / 2), 0, 0, std::cos(radians / 2)), q);
  34. }
  35. TEST(QuatTest, VectorToVectorRotation) {
  36. Quaternion q(Vector3dF(1.0f, 0.0f, 0.0f), Vector3dF(0.0f, 1.0f, 0.0f));
  37. Quaternion r(Vector3dF(0.0f, 0.0f, 1.0f), base::kPiFloat / 2);
  38. EXPECT_FLOAT_EQ(r.x(), q.x());
  39. EXPECT_FLOAT_EQ(r.y(), q.y());
  40. EXPECT_FLOAT_EQ(r.z(), q.z());
  41. EXPECT_FLOAT_EQ(r.w(), q.w());
  42. }
  43. TEST(QuatTest, AxisAngleWithZeroLengthAxis) {
  44. Quaternion q(Vector3dF(0, 0, 0), 0.5);
  45. // If the axis of zero length, we should assume the default values.
  46. CompareQuaternions(q, Quaternion());
  47. }
  48. TEST(QuatTest, Addition) {
  49. double values[] = {0, 1, 100};
  50. for (size_t i = 0; i < std::size(values); ++i) {
  51. float t = values[i];
  52. Quaternion a(t, 2 * t, 3 * t, 4 * t);
  53. Quaternion b(5 * t, 4 * t, 3 * t, 2 * t);
  54. Quaternion sum = a + b;
  55. CompareQuaternions(Quaternion(t, t, t, t) * 6, sum);
  56. }
  57. }
  58. TEST(QuatTest, Multiplication) {
  59. struct {
  60. Quaternion a;
  61. Quaternion b;
  62. Quaternion expected;
  63. } cases[] = {
  64. {Quaternion(1, 0, 0, 0), Quaternion(1, 0, 0, 0), Quaternion(0, 0, 0, -1)},
  65. {Quaternion(0, 1, 0, 0), Quaternion(0, 1, 0, 0), Quaternion(0, 0, 0, -1)},
  66. {Quaternion(0, 0, 1, 0), Quaternion(0, 0, 1, 0), Quaternion(0, 0, 0, -1)},
  67. {Quaternion(0, 0, 0, 1), Quaternion(0, 0, 0, 1), Quaternion(0, 0, 0, 1)},
  68. {Quaternion(1, 2, 3, 4), Quaternion(5, 6, 7, 8),
  69. Quaternion(24, 48, 48, -6)},
  70. {Quaternion(5, 6, 7, 8), Quaternion(1, 2, 3, 4),
  71. Quaternion(32, 32, 56, -6)},
  72. };
  73. for (size_t i = 0; i < std::size(cases); ++i) {
  74. Quaternion product = cases[i].a * cases[i].b;
  75. CompareQuaternions(cases[i].expected, product);
  76. }
  77. }
  78. TEST(QuatTest, Scaling) {
  79. double values[] = {0, 10, 100};
  80. for (size_t i = 0; i < std::size(values); ++i) {
  81. double s = values[i];
  82. Quaternion q(1, 2, 3, 4);
  83. Quaternion expected(s, 2 * s, 3 * s, 4 * s);
  84. CompareQuaternions(expected, q * s);
  85. CompareQuaternions(expected, s * q);
  86. if (s > 0)
  87. CompareQuaternions(expected, q / (1 / s));
  88. }
  89. }
  90. TEST(QuatTest, Normalization) {
  91. Quaternion q(1, -1, 1, -1);
  92. EXPECT_NEAR(q.Length(), 4, kEpsilon);
  93. q = q.Normalized();
  94. EXPECT_NEAR(q.Length(), 1, kEpsilon);
  95. EXPECT_NEAR(q.x(), 0.5, kEpsilon);
  96. EXPECT_NEAR(q.y(), -0.5, kEpsilon);
  97. EXPECT_NEAR(q.z(), 0.5, kEpsilon);
  98. EXPECT_NEAR(q.w(), -0.5, kEpsilon);
  99. }
  100. TEST(QuatTest, Lerp) {
  101. for (size_t i = 1; i < 100; ++i) {
  102. Quaternion a(0, 0, 0, 0);
  103. Quaternion b(1, 2, 3, 4);
  104. float t = static_cast<float>(i) / 100.0f;
  105. Quaternion interpolated = a.Lerp(b, t);
  106. double s = 1.0 / sqrt(30.0);
  107. CompareQuaternions(Quaternion(1, 2, 3, 4) * s, interpolated);
  108. }
  109. Quaternion a(4, 3, 2, 1);
  110. Quaternion b(1, 2, 3, 4);
  111. CompareQuaternions(a.Normalized(), a.Lerp(b, 0));
  112. CompareQuaternions(b.Normalized(), a.Lerp(b, 1));
  113. CompareQuaternions(Quaternion(1, 1, 1, 1).Normalized(), a.Lerp(b, 0.5));
  114. }
  115. TEST(QuatTest, Slerp) {
  116. Vector3dF axis(1, 1, 1);
  117. double start_radians = -0.5;
  118. double stop_radians = 0.5;
  119. Quaternion start(axis, start_radians);
  120. Quaternion stop(axis, stop_radians);
  121. for (size_t i = 0; i < 100; ++i) {
  122. float t = static_cast<float>(i) / 100.0f;
  123. double radians = (1.0 - t) * start_radians + t * stop_radians;
  124. Quaternion expected(axis, radians);
  125. Quaternion interpolated = start.Slerp(stop, t);
  126. EXPECT_QUATERNION(expected, interpolated);
  127. }
  128. }
  129. TEST(QuatTest, SlerpOppositeAngles) {
  130. Vector3dF axis(1, 1, 1);
  131. double start_radians = -base::kPiDouble / 2;
  132. double stop_radians = base::kPiDouble / 2;
  133. Quaternion start(axis, start_radians);
  134. Quaternion stop(axis, stop_radians);
  135. // When quaternions are pointed in the fully opposite direction, this is
  136. // ambiguous, so we rotate as per https://www.w3.org/TR/css-transforms-1/
  137. Quaternion expected(axis, 0);
  138. Quaternion interpolated = start.Slerp(stop, 0.5f);
  139. EXPECT_QUATERNION(expected, interpolated);
  140. }
  141. TEST(QuatTest, SlerpRotateXRotateY) {
  142. Quaternion start(Vector3dF(1, 0, 0), base::kPiDouble / 2);
  143. Quaternion stop(Vector3dF(0, 1, 0), base::kPiDouble / 2);
  144. Quaternion interpolated = start.Slerp(stop, 0.5f);
  145. double expected_angle = std::acos(1.0 / 3.0);
  146. double xy = std::sin(0.5 * expected_angle) / std::sqrt(2);
  147. Quaternion expected(xy, xy, 0, std::cos(0.5 * expected_angle));
  148. EXPECT_QUATERNION(expected, interpolated);
  149. }
  150. TEST(QuatTest, Slerp360) {
  151. Quaternion start(0, 0, 0, -1); // 360 degree rotation.
  152. Quaternion stop(Vector3dF(0, 0, 1), base::kPiDouble / 2);
  153. Quaternion interpolated = start.Slerp(stop, 0.5f);
  154. double expected_half_angle = base::kPiDouble / 8;
  155. Quaternion expected(0, 0, std::sin(expected_half_angle),
  156. std::cos(expected_half_angle));
  157. EXPECT_QUATERNION(expected, interpolated);
  158. }
  159. TEST(QuatTest, SlerpEquivalentQuaternions) {
  160. Quaternion start(Vector3dF(1, 0, 0), base::kPiDouble / 3);
  161. Quaternion stop = start.flip();
  162. Quaternion interpolated = start.Slerp(stop, 0.5f);
  163. EXPECT_QUATERNION(start, interpolated);
  164. }
  165. TEST(QuatTest, SlerpQuaternionWithInverse) {
  166. Quaternion start(Vector3dF(1, 0, 0), base::kPiDouble / 3);
  167. Quaternion stop = start.inverse();
  168. Quaternion interpolated = start.Slerp(stop, 0.5f);
  169. Quaternion expected(0, 0, 0, 1);
  170. EXPECT_QUATERNION(expected, interpolated);
  171. }
  172. TEST(QuatTest, SlerpObtuseAngle) {
  173. Quaternion start(Vector3dF(1, 1, 0), base::kPiDouble / 2);
  174. Quaternion stop(Vector3dF(0, 1, -1), 3 * base::kPiDouble / 2);
  175. Quaternion interpolated = start.Slerp(stop, 0.5f);
  176. double expected_half_angle = -std::atan(0.5);
  177. double xz = std::sin(expected_half_angle) / std::sqrt(2);
  178. Quaternion expected(xz, 0, xz, -std::cos(expected_half_angle));
  179. EXPECT_QUATERNION(expected, interpolated);
  180. }
  181. TEST(QuatTest, Equals) {
  182. EXPECT_TRUE(Quaternion() == Quaternion());
  183. EXPECT_TRUE(Quaternion() == Quaternion(0, 0, 0, 1));
  184. EXPECT_TRUE(Quaternion(1, 5.2, -8.5, 222.2) ==
  185. Quaternion(1, 5.2, -8.5, 222.2));
  186. EXPECT_FALSE(Quaternion() == Quaternion(1, 0, 0, 0));
  187. EXPECT_FALSE(Quaternion() == Quaternion(0, 1, 0, 0));
  188. EXPECT_FALSE(Quaternion() == Quaternion(0, 0, 1, 0));
  189. EXPECT_FALSE(Quaternion() == Quaternion(1, 0, 0, 1));
  190. }
  191. TEST(QuatTest, NotEquals) {
  192. EXPECT_FALSE(Quaternion() != Quaternion());
  193. EXPECT_FALSE(Quaternion(1, 5.2, -8.5, 222.2) !=
  194. Quaternion(1, 5.2, -8.5, 222.2));
  195. EXPECT_TRUE(Quaternion() != Quaternion(1, 0, 0, 0));
  196. EXPECT_TRUE(Quaternion() != Quaternion(0, 1, 0, 0));
  197. EXPECT_TRUE(Quaternion() != Quaternion(0, 0, 1, 0));
  198. EXPECT_TRUE(Quaternion() != Quaternion(1, 0, 0, 1));
  199. }
  200. } // namespace gfx