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