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