xref: /aosp_15_r20/external/libchrome/ui/gfx/geometry/quaternion_unittest.cc (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
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 
5 #define _USE_MATH_DEFINES  // For VC++ to get M_PI. This has to be first.
6 
7 #include <cmath>
8 
9 #include "base/macros.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "ui/gfx/geometry/quaternion.h"
12 #include "ui/gfx/geometry/vector3d_f.h"
13 
14 namespace gfx {
15 
16 namespace {
17 
18 const double kEpsilon = 1e-7;
19 
CompareQuaternions(const Quaternion & a,const Quaternion & b)20 void CompareQuaternions(const Quaternion& a, const Quaternion& b) {
21   EXPECT_FLOAT_EQ(a.x(), b.x());
22   EXPECT_FLOAT_EQ(a.y(), b.y());
23   EXPECT_FLOAT_EQ(a.z(), b.z());
24   EXPECT_FLOAT_EQ(a.w(), b.w());
25 }
26 
27 }  // namespace
28 
TEST(QuatTest,DefaultConstruction)29 TEST(QuatTest, DefaultConstruction) {
30   CompareQuaternions(Quaternion(0, 0, 0, 1), Quaternion());
31 }
32 
TEST(QuatTest,AxisAngleCommon)33 TEST(QuatTest, AxisAngleCommon) {
34   double radians = 0.5;
35   Quaternion q(Vector3dF(1, 0, 0), radians);
36   CompareQuaternions(
37       Quaternion(std::sin(radians / 2), 0, 0, std::cos(radians / 2)), q);
38 }
39 
TEST(QuatTest,VectorToVectorRotation)40 TEST(QuatTest, VectorToVectorRotation) {
41   Quaternion q(Vector3dF(1.0f, 0.0f, 0.0f), Vector3dF(0.0f, 1.0f, 0.0f));
42   Quaternion r(Vector3dF(0.0f, 0.0f, 1.0f), M_PI_2);
43 
44   EXPECT_FLOAT_EQ(r.x(), q.x());
45   EXPECT_FLOAT_EQ(r.y(), q.y());
46   EXPECT_FLOAT_EQ(r.z(), q.z());
47   EXPECT_FLOAT_EQ(r.w(), q.w());
48 }
49 
TEST(QuatTest,AxisAngleWithZeroLengthAxis)50 TEST(QuatTest, AxisAngleWithZeroLengthAxis) {
51   Quaternion q(Vector3dF(0, 0, 0), 0.5);
52   // If the axis of zero length, we should assume the default values.
53   CompareQuaternions(q, Quaternion());
54 }
55 
TEST(QuatTest,Addition)56 TEST(QuatTest, Addition) {
57   double values[] = {0, 1, 100};
58   for (size_t i = 0; i < arraysize(values); ++i) {
59     float t = values[i];
60     Quaternion a(t, 2 * t, 3 * t, 4 * t);
61     Quaternion b(5 * t, 4 * t, 3 * t, 2 * t);
62     Quaternion sum = a + b;
63     CompareQuaternions(Quaternion(t, t, t, t) * 6, sum);
64   }
65 }
66 
TEST(QuatTest,Multiplication)67 TEST(QuatTest, Multiplication) {
68   struct {
69     Quaternion a;
70     Quaternion b;
71     Quaternion expected;
72   } cases[] = {
73       {Quaternion(1, 0, 0, 0), Quaternion(1, 0, 0, 0), Quaternion(0, 0, 0, -1)},
74       {Quaternion(0, 1, 0, 0), Quaternion(0, 1, 0, 0), Quaternion(0, 0, 0, -1)},
75       {Quaternion(0, 0, 1, 0), Quaternion(0, 0, 1, 0), Quaternion(0, 0, 0, -1)},
76       {Quaternion(0, 0, 0, 1), Quaternion(0, 0, 0, 1), Quaternion(0, 0, 0, 1)},
77       {Quaternion(1, 2, 3, 4), Quaternion(5, 6, 7, 8),
78        Quaternion(24, 48, 48, -6)},
79       {Quaternion(5, 6, 7, 8), Quaternion(1, 2, 3, 4),
80        Quaternion(32, 32, 56, -6)},
81   };
82 
83   for (size_t i = 0; i < arraysize(cases); ++i) {
84     Quaternion product = cases[i].a * cases[i].b;
85     CompareQuaternions(cases[i].expected, product);
86   }
87 }
88 
TEST(QuatTest,Scaling)89 TEST(QuatTest, Scaling) {
90   double values[] = {0, 10, 100};
91   for (size_t i = 0; i < arraysize(values); ++i) {
92     double s = values[i];
93     Quaternion q(1, 2, 3, 4);
94     Quaternion expected(s, 2 * s, 3 * s, 4 * s);
95     CompareQuaternions(expected, q * s);
96     CompareQuaternions(expected, s * q);
97     if (s > 0)
98       CompareQuaternions(expected, q / (1 / s));
99   }
100 }
101 
TEST(QuatTest,Normalization)102 TEST(QuatTest, Normalization) {
103   Quaternion q(1, -1, 1, -1);
104   EXPECT_NEAR(q.Length(), 4, kEpsilon);
105 
106   q = q.Normalized();
107 
108   EXPECT_NEAR(q.Length(), 1, kEpsilon);
109   EXPECT_NEAR(q.x(), 0.5, kEpsilon);
110   EXPECT_NEAR(q.y(), -0.5, kEpsilon);
111   EXPECT_NEAR(q.z(), 0.5, kEpsilon);
112   EXPECT_NEAR(q.w(), -0.5, kEpsilon);
113 }
114 
TEST(QuatTest,Lerp)115 TEST(QuatTest, Lerp) {
116   for (size_t i = 1; i < 100; ++i) {
117     Quaternion a(0, 0, 0, 0);
118     Quaternion b(1, 2, 3, 4);
119     float t = static_cast<float>(i) / 100.0f;
120     Quaternion interpolated = a.Lerp(b, t);
121     double s = 1.0 / sqrt(30.0);
122     CompareQuaternions(Quaternion(1, 2, 3, 4) * s, interpolated);
123   }
124 
125   Quaternion a(4, 3, 2, 1);
126   Quaternion b(1, 2, 3, 4);
127   CompareQuaternions(a.Normalized(), a.Lerp(b, 0));
128   CompareQuaternions(b.Normalized(), a.Lerp(b, 1));
129   CompareQuaternions(Quaternion(1, 1, 1, 1).Normalized(), a.Lerp(b, 0.5));
130 }
131 
TEST(QuatTest,Slerp)132 TEST(QuatTest, Slerp) {
133   Vector3dF axis(1, 1, 1);
134   double start_radians = -0.5;
135   double stop_radians = 0.5;
136   Quaternion start(axis, start_radians);
137   Quaternion stop(axis, stop_radians);
138 
139   for (size_t i = 0; i < 100; ++i) {
140     float t = static_cast<float>(i) / 100.0f;
141     double radians = (1.0 - t) * start_radians + t * stop_radians;
142     Quaternion expected(axis, radians);
143     Quaternion interpolated = start.Slerp(stop, t);
144     EXPECT_NEAR(expected.x(), interpolated.x(), kEpsilon);
145     EXPECT_NEAR(expected.y(), interpolated.y(), kEpsilon);
146     EXPECT_NEAR(expected.z(), interpolated.z(), kEpsilon);
147     EXPECT_NEAR(expected.w(), interpolated.w(), kEpsilon);
148   }
149 }
150 
TEST(QuatTest,SlerpOppositeAngles)151 TEST(QuatTest, SlerpOppositeAngles) {
152   Vector3dF axis(1, 1, 1);
153   double start_radians = -M_PI_2;
154   double stop_radians = M_PI_2;
155   Quaternion start(axis, start_radians);
156   Quaternion stop(axis, stop_radians);
157 
158   // When quaternions are pointed in the fully opposite direction, this is
159   // ambiguous, so we rotate as per https://www.w3.org/TR/css-transforms-1/
160   Quaternion expected(axis, 0);
161 
162   Quaternion interpolated = start.Slerp(stop, 0.5f);
163   EXPECT_NEAR(expected.x(), interpolated.x(), kEpsilon);
164   EXPECT_NEAR(expected.y(), interpolated.y(), kEpsilon);
165   EXPECT_NEAR(expected.z(), interpolated.z(), kEpsilon);
166   EXPECT_NEAR(expected.w(), interpolated.w(), kEpsilon);
167 }
168 
169 }  // namespace gfx
170