2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/public-api/math/quaternion.h>
25 #include <dali/internal/render/common/performance-monitor.h>
26 #include <dali/public-api/common/constants.h>
27 #include <dali/public-api/math/degree.h>
28 #include <dali/public-api/math/math-utils.h>
29 #include <dali/public-api/math/matrix.h>
30 #include <dali/public-api/math/radian.h>
34 using Internal::PerformanceMonitor;
36 const Quaternion Quaternion::IDENTITY;
41 Quaternion::Quaternion()
42 : mVector(0.0f, 0.0f, 0.0f, 1.0f)
46 Quaternion::Quaternion(float cosThetaBy2, float iBySineTheta, float jBySineTheta, float kBySineTheta)
47 : mVector(iBySineTheta, jBySineTheta, kBySineTheta, cosThetaBy2)
51 Quaternion::Quaternion(const Vector4& vector)
56 Quaternion::Quaternion(Radian angle, const Vector3& axis)
58 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 4);
60 Vector3 tmpAxis = axis;
62 const float halfAngle = angle.radian * 0.5f;
63 const float sinThetaByTwo = sinf(halfAngle);
64 const float cosThetaByTwo = cosf(halfAngle);
65 mVector.x = tmpAxis.x * sinThetaByTwo;
66 mVector.y = tmpAxis.y * sinThetaByTwo;
67 mVector.z = tmpAxis.z * sinThetaByTwo;
68 mVector.w = cosThetaByTwo;
71 Quaternion::Quaternion(Radian pitch, Radian yaw, Radian roll)
73 SetEuler(pitch, yaw, roll);
76 Quaternion::Quaternion(const Matrix& matrix)
78 Vector3 xAxis(matrix.GetXAxis());
79 Vector3 yAxis(matrix.GetYAxis());
80 Vector3 zAxis(matrix.GetZAxis());
82 SetFromAxes(xAxis, yAxis, zAxis);
85 Quaternion::Quaternion(const Vector3& xAxis, const Vector3& yAxis, const Vector3& zAxis)
87 SetFromAxes(xAxis, yAxis, zAxis);
90 Quaternion::Quaternion(const Vector3& v0, const Vector3& v1)
92 float dot = v0.Dot(v1);
93 if(dot > 1.0f - Math::MACHINE_EPSILON_1)
96 mVector.x = mVector.y = mVector.z = 0.0f;
99 else if(dot < -1.0f + Math::MACHINE_EPSILON_1)
101 //180 degree rotation across the Z axis
102 mVector.x = mVector.y = mVector.w = 0.0f;
107 Vector3 w = v0.Cross(v1);
108 mVector.w = 1.0f + dot;
116 Quaternion::~Quaternion()
120 bool Quaternion::IsIdentity() const
122 // start from w as its unlikely that any real rotation has w == 1
123 // Uses a relaxed epsilon, as composition of rotation introduces error
124 return ((fabsf(mVector.w - 1.0f) < Math::MACHINE_EPSILON_10) &&
125 (fabsf(mVector.x) < Math::MACHINE_EPSILON_10) &&
126 (fabsf(mVector.y) < Math::MACHINE_EPSILON_10) &&
127 (fabsf(mVector.z) < Math::MACHINE_EPSILON_10));
130 bool Quaternion::ToAxisAngle(Vector3& axis, Radian& angle) const
132 angle = acosf(mVector.w);
133 bool converted = false;
134 // pre-compute to save time
135 const float sine = sinf(angle.radian);
137 // If sine(angle) is zero, conversion is not possible
139 if(!EqualsZero(sine))
141 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 3);
143 float sinf_theta_inv = 1.0f / sine;
145 axis.x = mVector.x * sinf_theta_inv;
146 axis.y = mVector.y * sinf_theta_inv;
147 axis.z = mVector.z * sinf_theta_inv;
148 angle.radian *= 2.0f;
154 const Vector4& Quaternion::AsVector() const
159 void Quaternion::SetEuler(Radian pitch, Radian yaw, Radian roll)
161 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 19);
163 const float halfX = 0.5f * pitch.radian;
164 const float halfY = 0.5f * yaw.radian;
165 const float halfZ = 0.5f * roll.radian;
167 float cosX2 = cosf(halfX);
168 float cosY2 = cosf(halfY);
169 float cosZ2 = cosf(halfZ);
171 float sinX2 = sinf(halfX);
172 float sinY2 = sinf(halfY);
173 float sinZ2 = sinf(halfZ);
175 mVector.w = cosZ2 * cosY2 * cosX2 + sinZ2 * sinY2 * sinX2;
176 mVector.x = cosZ2 * cosY2 * sinX2 - sinZ2 * sinY2 * cosX2;
177 mVector.y = cosZ2 * sinY2 * cosX2 + sinZ2 * cosY2 * sinX2;
178 mVector.z = sinZ2 * cosY2 * cosX2 - cosZ2 * sinY2 * sinX2;
181 Vector4 Quaternion::EulerAngles() const
183 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 13);
185 float sqw = mVector.w * mVector.w;
186 float sqx = mVector.x * mVector.x;
187 float sqy = mVector.y * mVector.y;
188 float sqz = mVector.z * mVector.z;
191 euler.x = atan2f(2.0f * (mVector.y * mVector.z + mVector.x * mVector.w), -sqx - sqy + sqz + sqw);
192 euler.y = asinf(-2.0f * (mVector.x * mVector.z - mVector.y * mVector.w));
193 euler.z = atan2f(2.0f * (mVector.x * mVector.y + mVector.z * mVector.w), sqx - sqy - sqz + sqw);
197 const Quaternion Quaternion::operator+(const Quaternion& other) const
199 return Quaternion(mVector + other.mVector);
202 const Quaternion Quaternion::operator-(const Quaternion& other) const
204 return Quaternion(mVector - other.mVector);
207 const Quaternion Quaternion::operator*(const Quaternion& other) const
209 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 12);
211 return Quaternion(mVector.w * other.mVector.w - mVector.Dot(other.mVector),
212 mVector.y * other.mVector.z - mVector.z * other.mVector.y + mVector.w * other.mVector.x + mVector.x * other.mVector.w,
213 mVector.z * other.mVector.x - mVector.x * other.mVector.z + mVector.w * other.mVector.y + mVector.y * other.mVector.w,
214 mVector.x * other.mVector.y - mVector.y * other.mVector.x + mVector.w * other.mVector.z + mVector.z * other.mVector.w);
217 Vector3 Quaternion::operator*(const Vector3& other) const
219 Vector3 qvec(mVector.x, mVector.y, mVector.z);
220 Vector3 uv = qvec.Cross(other);
221 Vector3 uuv = qvec.Cross(uv);
222 uv *= (2.0f * mVector.w);
225 return other + uv + uuv;
228 const Quaternion Quaternion::operator/(const Quaternion& q) const
235 const Quaternion Quaternion::operator*(float scale) const
237 return Quaternion(mVector * scale);
240 const Quaternion Quaternion::operator/(float scale) const
242 return Quaternion(mVector / scale);
245 Quaternion Quaternion::operator-() const
247 return Quaternion(-mVector.w, -mVector.x, -mVector.y, -mVector.z);
250 const Quaternion& Quaternion::operator+=(const Quaternion& q)
252 mVector += q.mVector;
256 const Quaternion& Quaternion::operator-=(const Quaternion& q)
258 mVector -= q.mVector;
262 const Quaternion& Quaternion::operator*=(const Quaternion& q)
264 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 12);
266 float x = mVector.x, y = mVector.y, z = mVector.z, w = mVector.w;
268 mVector.w = mVector.w * q.mVector.w - mVector.Dot(q.mVector);
269 mVector.x = y * q.mVector.z - z * q.mVector.y + w * q.mVector.x + x * q.mVector.w;
270 mVector.y = z * q.mVector.x - x * q.mVector.z + w * q.mVector.y + y * q.mVector.w;
271 mVector.z = x * q.mVector.y - y * q.mVector.x + w * q.mVector.z + z * q.mVector.w;
275 const Quaternion& Quaternion::operator*=(float scale)
281 const Quaternion& Quaternion::operator/=(float scale)
287 bool Quaternion::operator==(const Quaternion& rhs) const
289 return ((fabsf(mVector.x - rhs.mVector.x) < Math::MACHINE_EPSILON_1 &&
290 fabsf(mVector.y - rhs.mVector.y) < Math::MACHINE_EPSILON_1 &&
291 fabsf(mVector.z - rhs.mVector.z) < Math::MACHINE_EPSILON_1 &&
292 fabsf(mVector.w - rhs.mVector.w) < Math::MACHINE_EPSILON_1) ||
293 // Or equal to negation of rhs
294 (fabsf(mVector.x + rhs.mVector.x) < Math::MACHINE_EPSILON_1 &&
295 fabsf(mVector.y + rhs.mVector.y) < Math::MACHINE_EPSILON_1 &&
296 fabsf(mVector.z + rhs.mVector.z) < Math::MACHINE_EPSILON_1 &&
297 fabsf(mVector.w + rhs.mVector.w) < Math::MACHINE_EPSILON_1));
300 bool Quaternion::operator!=(const Quaternion& rhs) const
302 return !operator==(rhs);
305 float Quaternion::Length() const
307 return static_cast<float>(sqrt(mVector.w * mVector.w + mVector.Dot(mVector)));
310 float Quaternion::LengthSquared() const
312 return static_cast<float>(mVector.w * mVector.w + mVector.Dot(mVector));
315 void Quaternion::Normalize()
320 Quaternion Quaternion::Normalized() const
322 return *this / Length();
325 void Quaternion::Conjugate()
327 mVector.x = -mVector.x;
328 mVector.y = -mVector.y;
329 mVector.z = -mVector.z;
332 void Quaternion::Invert()
335 *this /= LengthSquared();
338 Quaternion Quaternion::Log() const
340 float a = acosf(mVector.w);
341 float sina = sinf(a);
345 if(fabsf(sina) >= Math::MACHINE_EPSILON_1)
347 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 4);
349 float angleBySinAngle = a * (1.0f / sina);
350 ret.mVector.x = mVector.x * angleBySinAngle;
351 ret.mVector.y = mVector.y * angleBySinAngle;
352 ret.mVector.z = mVector.z * angleBySinAngle;
356 ret.mVector.x = ret.mVector.y = ret.mVector.z = 0;
361 Quaternion Quaternion::Exp() const
363 DALI_ASSERT_ALWAYS(EqualsZero(mVector.w) && "Cannot perform Exponent");
365 float a = mVector.Length();
366 float sina = sinf(a);
369 ret.mVector.w = cosf(a);
371 if(a >= Math::MACHINE_EPSILON_1)
373 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 4);
375 float sinAOverA = sina * (1.0f / a);
376 ret.mVector.x = mVector.x * sinAOverA;
377 ret.mVector.y = mVector.y * sinAOverA;
378 ret.mVector.z = mVector.z * sinAOverA;
382 ret.mVector.x = ret.mVector.y = ret.mVector.z = 0.0f;
387 float Quaternion::Dot(const Quaternion& q1, const Quaternion& q2)
389 return q1.mVector.Dot4(q2.mVector);
392 Quaternion Quaternion::Lerp(const Quaternion& q1, const Quaternion& q2, float t)
394 return (q1 * (1.0f - t) + q2 * t).Normalized();
397 Quaternion Quaternion::Slerp(const Quaternion& q1, const Quaternion& q2, float progress)
400 float cosTheta = Quaternion::Dot(q1, q2);
403 * If cos(theta) < 0, q1 and q2 are more than 90 degrees apart,
404 * so invert one to reduce spinning.
408 cosTheta = -cosTheta;
416 if(fabsf(cosTheta) < 0.95f)
418 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 5);
421 float sine = sqrtf(1.0f - cosTheta * cosTheta);
422 float angle = atan2f(sine, cosTheta);
423 float invSine = 1.0f / sine;
424 float coeff0 = sinf((1.0f - progress) * angle) * invSine;
425 float coeff1 = sinf(progress * angle) * invSine;
427 return q1 * coeff0 + q3 * coeff1;
431 // If the angle is small, use linear interpolation
432 Quaternion result = q1 * (1.0f - progress) + q3 * progress;
434 return result.Normalized();
438 Quaternion Quaternion::SlerpNoInvert(const Quaternion& q1, const Quaternion& q2, float t)
440 float cosTheta = Quaternion::Dot(q1, q2);
442 if(cosTheta > -0.95f && cosTheta < 0.95f)
444 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 2);
446 float theta = acosf(cosTheta);
447 return (q1 * sinf(theta * (1.0f - t)) + q2 * sinf(theta * t)) / sinf(theta);
451 return Lerp(q1, q2, t);
455 Quaternion Quaternion::Squad(const Quaternion& start, const Quaternion& end, const Quaternion& ctrl1, const Quaternion& ctrl2, float t)
457 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 2);
459 Quaternion c = SlerpNoInvert(start, end, t);
460 Quaternion d = SlerpNoInvert(ctrl1, ctrl2, t);
461 return SlerpNoInvert(c, d, 2 * t * (1 - t));
464 float Quaternion::AngleBetween(const Quaternion& q1, const Quaternion& q2)
472 //Formula for angle θ between two quaternion is:
473 //θ = cos^−1 (2⟨q1,q2⟩^2 − 1), Where (q1,q2) is inner product of the quaternions.
474 float X = from.mVector.Dot4(to.mVector);
475 float theta = acosf((2 * X * X) - 1); // float arc cosine
480 Vector4 Quaternion::Rotate(const Vector4& vector) const
482 Quaternion V(0.0f, vector.x, vector.y, vector.z);
483 Quaternion conjugate(*this);
484 conjugate.Conjugate();
485 return (*this * V * conjugate).mVector;
488 Vector3 Quaternion::Rotate(const Vector3& vector) const
490 Quaternion V(0.0f, vector.x, vector.y, vector.z);
491 Quaternion conjugate(*this);
492 conjugate.Conjugate();
493 return Vector3((*this * V * conjugate).mVector);
496 void Quaternion::SetFromAxes(const Vector3& xAxis, const Vector3& yAxis, const Vector3& zAxis)
498 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 4);
500 float t = xAxis.x + yAxis.y + zAxis.z;
501 if(t > 0.0f) // w is largest
503 float root = sqrtf(t + 1.0f);
504 float one_over_4w = 0.5f / root;
505 mVector.x = (yAxis.z - zAxis.y) * one_over_4w;
506 mVector.y = (zAxis.x - xAxis.z) * one_over_4w;
507 mVector.z = (xAxis.y - yAxis.x) * one_over_4w;
508 mVector.w = root * 0.5f;
510 else if(zAxis.z > xAxis.x && zAxis.z > yAxis.y) // z is largest
512 float root = sqrtf(zAxis.z - xAxis.x - yAxis.y + 1.0f);
513 float one_over_4w = 0.5f / root;
514 mVector.x = (xAxis.z + zAxis.x) * one_over_4w;
515 mVector.y = (yAxis.z + zAxis.y) * one_over_4w;
516 mVector.z = root * 0.5f;
517 mVector.w = (xAxis.y - yAxis.x) * one_over_4w;
519 else if(yAxis.y > xAxis.x) // y is largest
521 float root = sqrtf(yAxis.y - zAxis.z - xAxis.x + 1.0f);
522 float one_over_4w = 0.5f / root;
524 mVector.x = (xAxis.y + yAxis.x) * one_over_4w;
525 mVector.y = root * 0.5f;
526 mVector.z = (zAxis.y + yAxis.z) * one_over_4w;
527 mVector.w = (zAxis.x - xAxis.z) * one_over_4w;
531 float root = sqrtf(xAxis.x - yAxis.y - zAxis.z + 1.0f);
532 float one_over_4w = 0.5f / root;
533 mVector.x = root * 0.5f;
534 mVector.y = (yAxis.x + xAxis.y) * one_over_4w;
535 mVector.z = (zAxis.x + xAxis.z) * one_over_4w;
536 mVector.w = (yAxis.z - zAxis.y) * one_over_4w;
542 std::ostream& operator<<(std::ostream& o, const Quaternion& quaternion)
547 quaternion.ToAxisAngle(axis, angleRadians);
548 Degree degrees(angleRadians);
550 return o << "[ Axis: [" << axis.x << ", " << axis.y << ", " << axis.z << "], Angle: " << degrees.degree << " degrees ]";