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