1 /*
2  * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
3  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  */
21 
22 #include "third_party/blink/renderer/platform/transforms/rotation.h"
23 
24 #include "third_party/blink/renderer/platform/geometry/blend.h"
25 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
26 
27 namespace blink {
28 
29 namespace {
30 
31 const double kAngleEpsilon = 1e-4;
32 
ExtractFromMatrix(const TransformationMatrix & matrix,const Rotation & fallback_value)33 Rotation ExtractFromMatrix(const TransformationMatrix& matrix,
34                            const Rotation& fallback_value) {
35   TransformationMatrix::DecomposedType decomp;
36   if (!matrix.Decompose(decomp))
37     return fallback_value;
38   double x = -decomp.quaternion_x;
39   double y = -decomp.quaternion_y;
40   double z = -decomp.quaternion_z;
41   double length = std::sqrt(x * x + y * y + z * z);
42   double angle = 0;
43   if (length > 0.00001) {
44     x /= length;
45     y /= length;
46     z /= length;
47     angle = rad2deg(std::acos(decomp.quaternion_w) * 2);
48   } else {
49     x = 0;
50     y = 0;
51     z = 1;
52   }
53   return Rotation(FloatPoint3D(x, y, z), angle);
54 }
55 
56 }  // namespace
57 
GetCommonAxis(const Rotation & a,const Rotation & b,FloatPoint3D & result_axis,double & result_angle_a,double & result_angle_b)58 bool Rotation::GetCommonAxis(const Rotation& a,
59                              const Rotation& b,
60                              FloatPoint3D& result_axis,
61                              double& result_angle_a,
62                              double& result_angle_b) {
63   result_axis = FloatPoint3D(0, 0, 1);
64   result_angle_a = 0;
65   result_angle_b = 0;
66 
67   bool is_zero_a = a.axis.IsZero() || fabs(a.angle) < kAngleEpsilon;
68   bool is_zero_b = b.axis.IsZero() || fabs(b.angle) < kAngleEpsilon;
69 
70   if (is_zero_a && is_zero_b)
71     return true;
72 
73   if (is_zero_a) {
74     result_axis = b.axis;
75     result_angle_b = b.angle;
76     return true;
77   }
78 
79   if (is_zero_b) {
80     result_axis = a.axis;
81     result_angle_a = a.angle;
82     return true;
83   }
84 
85   double dot = a.axis.Dot(b.axis);
86   if (dot < 0)
87     return false;
88 
89   double a_squared = a.axis.LengthSquared();
90   double b_squared = b.axis.LengthSquared();
91   double error = std::abs(1 - (dot * dot) / (a_squared * b_squared));
92   if (error > kAngleEpsilon)
93     return false;
94 
95   result_axis = a.axis;
96   result_angle_a = a.angle;
97   result_angle_b = b.angle;
98   return true;
99 }
100 
Slerp(const Rotation & from,const Rotation & to,double progress)101 Rotation Rotation::Slerp(const Rotation& from,
102                          const Rotation& to,
103                          double progress) {
104   double from_angle;
105   double to_angle;
106   FloatPoint3D axis;
107   if (GetCommonAxis(from, to, axis, from_angle, to_angle))
108     return Rotation(axis, blink::Blend(from_angle, to_angle, progress));
109 
110   TransformationMatrix from_matrix;
111   TransformationMatrix to_matrix;
112   from_matrix.Rotate3d(from);
113   to_matrix.Rotate3d(to);
114   to_matrix.Blend(from_matrix, progress);
115   return ExtractFromMatrix(to_matrix, progress < 0.5 ? from : to);
116 }
117 
Add(const Rotation & a,const Rotation & b)118 Rotation Rotation::Add(const Rotation& a, const Rotation& b) {
119   double angle_a;
120   double angle_b;
121   FloatPoint3D axis;
122   if (GetCommonAxis(a, b, axis, angle_a, angle_b))
123     return Rotation(axis, angle_a + angle_b);
124 
125   TransformationMatrix matrix;
126   matrix.Rotate3d(a);
127   matrix.Rotate3d(b);
128   return ExtractFromMatrix(matrix, b);
129 }
130 
131 }  // namespace blink
132