SuperDex Physics C++ API
Loading...
Searching...
No Matches
quaternion_utils_inl.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17#pragma once
18
19#include "quaternion_utils.h" // For IntelliSense
20
21namespace superdex {
22
23/************************************************************************************
24 Quaternion Utilities
25*/
26
27MOCHI_FORCE_INLINE bool NearEqual(Quaternion const& a, Quaternion const& b, Vec4r epsilon) {
28 return NearEqual(a.data, b.data, epsilon);
29}
30
31MOCHI_FORCE_INLINE bool NearEqual(Quaternion const& a, Quaternion const& b, real epsilon) {
32 return NearEqual(a, b, Vec4r{epsilon});
33}
34
36EquivalentRotation(Quaternion const& a, Quaternion const& b, Vec4r epsilon) {
37 return NearEqual(a, b, epsilon) || NearEqual(a, -b, epsilon);
38}
39
41 return EquivalentRotation(a, b, Vec4r{epsilon});
42}
43
47
49 constexpr real kDotThreshold = 1e-6_r;
50 real dot = Dot<4>(a.data, b.data);
51
52 // If the dot product is negative, then they have opposite handedness
53 // Invert it so it gives the shortest rotation direction
54 if (dot < 0_r) {
55 dot = -dot;
56 b = -b;
57 }
58
59 // Use linear interpolation and normalize if the angle is small enough
60 if (dot < 1_r - kDotThreshold)
62 dot = Clamp(dot, -1.0_r, 1.0_r);
63 real theta = ACos(dot) * (real)t;
64 b.data = Normalize<4>(b.data - a.data * dot);
65 return Quaternion{a.data * std::cos(theta) + b.data * std::sin(theta)};
66 }
67 else {
68 return Lerp(a, b, t);
69 }
70}
71
78
82
86
87inline Quaternion QuaternionFromMatrix(VMatrix3x3r const& matrix, real eps) {
88 return QuaternionFromMatrix(ToNdArray3x3(matrix), eps);
89}
90
91namespace details {
92inline Real4 MatrixToAxisAngleImpl(Matrix3x3r const& matrix, real eps) {
93 // Find axis and angle of rotation. Adapted from:
94 // https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/
95 real t = 0.5_r * (matrix[0][0] + matrix[1][1] + matrix[2][2] - 1_r);
96 real theta = ACos(Clamp(t, -1_r, 1_r));
97 Real3 axis{};
98 if (NearEqual(theta, 0_r))
100 // Singularity at zero degree rotation. Any axis will do.
101 theta = 0_r;
102 axis = {1_r, 0_r, 0_r};
103 }
104 else if (NearEqual(Abs(theta), kPI, eps))
106 // Singularity at +/- 180 degree rotation (sign does not matter). Compute the axis.
107 theta = kPI;
108 real xx = (matrix[0][0] + 1_r) * 0.5_r;
109 real yy = (matrix[1][1] + 1_r) * 0.5_r;
110 real zz = (matrix[2][2] + 1_r) * 0.5_r;
111 real xy = (matrix[0][1] + matrix[1][0]) * 0.25_r;
112 real xz = (matrix[0][2] + matrix[2][0]) * 0.25_r;
113 real yz = (matrix[1][2] + matrix[2][1]) * 0.25_r;
114 real constexpr kSqrt2Over2 = kSqrt2 * 0.5_r;
115 if ((xx > yy) && (xx > zz)) { // matrix[0][0] is the largest diagonal term
116 if (xx < eps) {
117 axis = Real3{0_r, kSqrt2Over2, kSqrt2Over2};
118 } else {
119 real x = Sqrt(Clamp(xx, 0_r, 1_r));
120 axis = Real3{x, xy / x, xz / x};
121 }
122 } else if (yy > zz) { // matrix[1][1] is the largest diagonal term
123 if (yy < eps) {
124 axis = Real3{kSqrt2Over2, 0_r, kSqrt2Over2};
125 } else {
126 real y = Sqrt(Clamp(yy, 0_r, 1_r));
127 axis = Real3{xy / y, y, yz / y};
128 }
129 } else { // matrix[2][2] is the largest diagonal term so base result on this
130 if (zz < eps) {
131 axis = Real3{kSqrt2Over2, kSqrt2Over2, 0_r};
132 } else {
133 real z = Sqrt(Clamp(zz, 0_r, 1_r));
134 axis = Real3{xz / z, yz / z, z};
135 }
136 }
137 }
138 else {
139 // No singularity. This is the normal case.
140 real scale = 0.5_r / Sin(theta);
141 axis = {
142 (matrix[2][1] - matrix[1][2]) * scale,
143 (matrix[0][2] - matrix[2][0]) * scale,
144 (matrix[1][0] - matrix[0][1]) * scale};
145 }
146
147 // store axis+angle as Real4 instead of as rotation vector for smaller numerical errors
148 return Real4{axis[0], axis[1], axis[2], theta};
149}
150} // namespace details
151
152inline Quaternion QuaternionFromMatrix(Matrix3x3r const& matrix, real eps) {
153 Real4 const axisAngle = superdex::details::MatrixToAxisAngleImpl(matrix, eps);
154 Real3 const axis{axisAngle[0], axisAngle[1], axisAngle[2]};
155 real const angle = axisAngle[3];
156 return Quaternion::FromAxisAngle(axis, angle);
157}
158
160 return VMatrix3x3r{q * Vec4r(1_r, 0_r, 0_r), q * Vec4r(0_r, 1_r, 0_r), q * Vec4r(0_r, 0_r, 1_r)};
161}
162
166
170
171MOCHI_FORCE_INLINE std::pair<VMatrix3x3r, VMatrix3x3r> ToVMatrix3x3_WithTranspose(
172 Quaternion const& q) {
173 auto matT = ToVMatrix3x3Transpose(q);
174 return std::make_pair(Transpose3x3(matT), matT);
175}
176
180
184
188
192
194 return AllTrue(VIsFinite(q));
195}
196
197} // namespace superdex
Quaternion GetConjugate() const
static Quaternion FromAxisAngle(Vec4r axis, real angleRadians)
#define MOCHI_UNLIKELY
#define MOCHI_FORCE_INLINE
#define MOCHI_LIKELY
T Dot(Simd< T, N > a, Simd< T, N > b)
Definition simd.h:666
Quaternion Slerp(Quaternion a, Quaternion b, real t)
constexpr T ACos(T a)
Simd< T, N > VIsFinite(Simd< T, N > a)
Definition simd_inl.h:755
T NormSqr(Simd< T, N > a)
Definition simd_inl.h:849
constexpr T Sin(T a)
Matrix3x3r ToMatrix3x3(Quaternion const &q)
T Norm(Simd< T, N > a)
Definition simd_inl.h:854
bool AllTrue(T const &a)
Definition basic_utils.h:60
NdArray< Simd< real, 4 >, 3 > VMatrix3x3r
Definition vmatrix.h:52
NdArray< T, 3, 3 > ToNdArray3x3(NdArray< Simd< T, 4 >, 3 > m)
Definition vmatrix.h:375
NdArray< Simd< T, 4 >, 3 > Transpose3x3(NdArray< Simd< T, 4 >, D0 > const &m)
Simd< real, 4 > Vec4r
Definition simd.h:206
constexpr T Abs(T a)
Definition basic_utils.h:50
constexpr real kPI
Definition constants.h:27
Simd< T, N > Normalize(Simd< T, N > a)
Definition simd_inl.h:859
constexpr NdArray< T, D0, DIMS... > operator/(NdArray< T, D0, DIMS... > const &lhs, NdArray< T, D0, DIMS... > const &rhs)
Definition nd_array.h:287
constexpr T Sqrt(T a)
Quaternion Conjugate(Quaternion const &a)
NdArray< real, 3 > Real3
Definition nd_array.h:106
VMatrix3x3r ToVMatrix3x3Transpose(Quaternion const &q)
constexpr NdArray< T, D0, DIMS... > operator*(NdArray< T, D0, DIMS... > const &lhs, NdArray< T, D0, DIMS... > const &rhs)
Definition nd_array.h:286
NdArray< real, 4 > Real4
Definition nd_array.h:107
constexpr ValT Clamp(ValT value, MinT min, MaxT max)
VMatrix3x3r ToVMatrix3x3(Quaternion const &q)
constexpr T Lerp(T a, T b, Frac t)
NdArray< real, 3, 3 > Matrix3x3r
Definition nd_array.h:125
bool NearEqual(TransformRT const &a, TransformRT const &b, real epsilon=kDefaultNearEqualEpsilon< real >)
constexpr real kSqrt2
Definition constants.h:30
std::pair< VMatrix3x3r, VMatrix3x3r > ToVMatrix3x3_WithTranspose(Quaternion const &q)
Quaternion QuaternionFromMatrix(VMatrix3x3r const &matrix, real eps=1e-3_r)
bool IsFinite(TransformRT const &a)
bool EquivalentRotation(Quaternion const &a, Quaternion const &b, Vec4r epsilon)