xref: /aosp_15_r20/external/libchrome/ui/gfx/geometry/quaternion.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 #include "ui/gfx/geometry/quaternion.h"
6 
7 #include <algorithm>
8 #include <cmath>
9 
10 #include "base/numerics/math_constants.h"
11 #include "base/strings/stringprintf.h"
12 #include "ui/gfx/geometry/vector3d_f.h"
13 
14 namespace gfx {
15 
16 namespace {
17 
18 const double kEpsilon = 1e-5;
19 
20 }  // namespace
21 
Quaternion(const Vector3dF & axis,double theta)22 Quaternion::Quaternion(const Vector3dF& axis, double theta) {
23   // Rotation angle is the product of |angle| and the magnitude of |axis|.
24   double length = axis.Length();
25   if (std::abs(length) < kEpsilon)
26     return;
27 
28   Vector3dF normalized = axis;
29   normalized.Scale(1.0 / length);
30 
31   theta *= 0.5;
32   double s = sin(theta);
33   x_ = normalized.x() * s;
34   y_ = normalized.y() * s;
35   z_ = normalized.z() * s;
36   w_ = cos(theta);
37 }
38 
Quaternion(const Vector3dF & from,const Vector3dF & to)39 Quaternion::Quaternion(const Vector3dF& from, const Vector3dF& to) {
40   double dot = gfx::DotProduct(from, to);
41   double norm = sqrt(from.LengthSquared() * to.LengthSquared());
42   double real = norm + dot;
43   gfx::Vector3dF axis;
44   if (real < kEpsilon * norm) {
45     real = 0.0f;
46     axis = std::abs(from.x()) > std::abs(from.z())
47                ? gfx::Vector3dF{-from.y(), from.x(), 0.0}
48                : gfx::Vector3dF{0.0, -from.z(), from.y()};
49   } else {
50     axis = gfx::CrossProduct(from, to);
51   }
52   x_ = axis.x();
53   y_ = axis.y();
54   z_ = axis.z();
55   w_ = real;
56   *this = this->Normalized();
57 }
58 
59 // Taken from http://www.w3.org/TR/css3-transforms/.
Slerp(const Quaternion & q,double t) const60 Quaternion Quaternion::Slerp(const Quaternion& q, double t) const {
61   double dot = x_ * q.x_ + y_ * q.y_ + z_ * q.z_ + w_ * q.w_;
62 
63   // Clamp dot to -1.0 <= dot <= 1.0.
64   dot = std::min(std::max(dot, -1.0), 1.0);
65 
66   // Quaternions are facing the same direction.
67   if (std::abs(dot - 1.0) < kEpsilon || std::abs(dot + 1.0) < kEpsilon)
68     return *this;
69 
70   double denom = std::sqrt(1.0 - dot * dot);
71   double theta = std::acos(dot);
72   double w = std::sin(t * theta) * (1.0 / denom);
73 
74   double s1 = std::cos(t * theta) - dot * w;
75   double s2 = w;
76 
77   return (s1 * *this) + (s2 * q);
78 }
79 
Lerp(const Quaternion & q,double t) const80 Quaternion Quaternion::Lerp(const Quaternion& q, double t) const {
81   return (((1.0 - t) * *this) + (t * q)).Normalized();
82 }
83 
Length() const84 double Quaternion::Length() const {
85   return x_ * x_ + y_ * y_ + z_ * z_ + w_ * w_;
86 }
87 
Normalized() const88 Quaternion Quaternion::Normalized() const {
89   double length = Length();
90   if (length < kEpsilon)
91     return *this;
92   return *this / sqrt(length);
93 }
94 
ToString() const95 std::string Quaternion::ToString() const {
96   // q = (con(abs(v_theta)/2), v_theta/abs(v_theta) * sin(abs(v_theta)/2))
97   float abs_theta = acos(w_) * 2;
98   float scale = 1. / sin(abs_theta * .5);
99   gfx::Vector3dF v(x_, y_, z_);
100   v.Scale(scale);
101   return base::StringPrintf("[%f %f %f %f], v:", x_, y_, z_, w_) +
102          v.ToString() +
103          base::StringPrintf(", θ:%fπ", abs_theta / base::kPiFloat);
104 }
105 
106 }  // namespace gfx
107