MeshLib
 
Loading...
Searching...
No Matches
MRQuaternion.h
Go to the documentation of this file.
1#pragma once
2
3#include "MRAffineXf3.h"
4
5namespace MR
6{
7
11template <typename T>
13{
14 T a = 1;
15 T b = 0, c = 0, d = 0;
16
17 constexpr Quaternion() noexcept = default;
18 constexpr Quaternion( T a, T b, T c, T d ) noexcept : a( a ), b( b ), c( c ), d( d ) { }
19 constexpr Quaternion( const Vector3<T> & axis, T angle ) noexcept;
20 constexpr Quaternion( T real, const Vector3<T> & im ) noexcept : a( real ), b( im.x ), c( im.y ), d( im.z ) { }
21 constexpr Quaternion( const Matrix3<T> & m );
23 constexpr Quaternion( const Vector3<T>& from, const Vector3<T>& to ) noexcept;
24
26 [[nodiscard]] constexpr Vector3<T> im() const noexcept { return Vector3<T>{ b, c, d }; }
27
29 [[nodiscard]] constexpr T angle() const noexcept { return 2 * std::acos( std::clamp( a, T(-1), T(1) ) ); }
31 [[nodiscard]] constexpr Vector3<T> axis() const noexcept { return im().normalized(); }
32
33 [[nodiscard]] constexpr T normSq() const { return a * a + b * b + c * c + d * d; }
34 [[nodiscard]] constexpr T norm() const { return std::sqrt( normSq() ); }
36 [[nodiscard]] constexpr Quaternion operator-() const { return {-a, -b, -c, -d}; }
37
39 void normalize() { if ( T n = norm(); n > 0 ) *this /= n; }
40 [[nodiscard]] Quaternion normalized() const { Quaternion res( *this ); res.normalize(); return res; }
41
43 [[nodiscard]] constexpr Quaternion conjugate() const noexcept { return {a, -b, -c, -d}; }
45 [[nodiscard]] constexpr Quaternion inverse() const noexcept { return conjugate() / normSq(); }
48 [[nodiscard]] constexpr Vector3<T> operator()( const Vector3<T> & p ) const noexcept;
49
51 [[nodiscard]] operator Matrix3<T>() const;
52
54 [[nodiscard]] static Quaternion lerp( const Quaternion & q0, const Quaternion & q1, T t ) { return ( 1 - t ) * q0 + t * q1; }
56 [[nodiscard]] static Quaternion slerp( Quaternion q0, Quaternion q1, T t );
58 [[nodiscard]] static Matrix3<T> slerp( const Matrix3<T> & m0, const Matrix3<T> & m1, T t ) { return slerp( Quaternion<T>{ m0 }, Quaternion<T>{ m1 }, t ); }
61 [[nodiscard]] static AffineXf3<T> slerp( const AffineXf3<T> & xf0, const AffineXf3<T> & xf1, T t, const Vector3<T> & p = {} )
62 {
63 auto xfA = slerp( xf0.A, xf1.A, t );
64 return { xfA, ( 1 - t ) * xf0( p ) + t * xf1( p ) - xfA * p };
65 }
66
67 Quaternion & operator *=( T s ) { a *= s; b *= s; c *= s; d *= s; return * this; }
68 Quaternion & operator /=( T s ) { return *this *= ( 1 / s ); }
69};
70
73
74template <typename T>
75constexpr Quaternion<T>::Quaternion( const Vector3<T> & axis, T angle ) noexcept
76{
77 a = std::cos( angle / 2 );
78 Vector3<T> im = std::sin( angle / 2 ) * axis.normalized();
79 b = im.x;
80 c = im.y;
81 d = im.z;
82}
83
84template <typename T>
85constexpr Quaternion<T>::Quaternion( const Matrix3<T> & m )
86{
87 // https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
88 const auto tr = m.trace();
89 if ( tr > 0 )
90 {
91 auto S = std::sqrt( tr + 1 ) * 2;
92 a = T( 0.25 ) * S;
93 b = ( m.z.y - m.y.z ) / S;
94 c = ( m.x.z - m.z.x ) / S;
95 d = ( m.y.x - m.x.y ) / S;
96 }
97 else if ( m.x.x > m.y.y && m.x.x > m.z.z )
98 {
99 auto S = std::sqrt( 1 + m.x.x - m.y.y - m.z.z ) * 2;
100 a = ( m.z.y - m.y.z ) / S;
101 b = T( 0.25 ) * S;
102 c = ( m.x.y + m.y.x ) / S;
103 d = ( m.x.z + m.z.x ) / S;
104 }
105 else if ( m.y.y > m.z.z )
106 {
107 auto S = std::sqrt( 1 + m.y.y - m.x.x - m.z.z ) * 2;
108 a = ( m.x.z - m.z.x ) / S;
109 b = ( m.x.y + m.y.x ) / S;
110 c = T( 0.25 ) * S;
111 d = ( m.y.z + m.z.y ) / S;
112 }
113 else
114 {
115 auto S = std::sqrt( 1 + m.z.z - m.x.x - m.y.y ) * 2;
116 a = ( m.y.x - m.x.y ) / S;
117 b = ( m.x.z + m.z.x ) / S;
118 c = ( m.y.z + m.z.y ) / S;
119 d = T( 0.25 ) * S;
120 }
121}
122
123template <typename T>
124constexpr Quaternion<T>::Quaternion( const Vector3<T>& from, const Vector3<T>& to) noexcept
125{
126 // https://stackoverflow.com/questions/1171849/finding-quaternion-representing-the-rotation-from-one-vector-to-another
127 a = dot( from, to );
128 auto cr = cross( from, to );
129 if( cr.x == 0 && cr.y == 0 && cr.z == 0 )
130 {
131 if( a >= 0 )
132 {
133 // parallel co-directional vectors
134 // zero rotation
135 a = 1; b = 0; c = 0; d = 0;
136 }
137 else
138 {
139 // parallel opposing vectors
140 // any perpendicular axis Pi rotation
141 auto perp = cross( from, from.furthestBasisVector() );
142 a = 0; b = perp.x; c = perp.y; d = perp.z;
143 normalize();
144 }
145 }
146 else
147 {
148 a += std::sqrt( from.lengthSq() * to.lengthSq() );
149 b = cr.x; c = cr.y; d = cr.z;
150 normalize();
151 }
152}
153
154template <typename T>
155Quaternion<T> Quaternion<T>::slerp( Quaternion q0, Quaternion q1, T t )
156{
157 // https://en.wikipedia.org/wiki/Slerp
158 q0.normalize();
159 q1.normalize();
160
161 T cosTheta = std::clamp( dot( q0, q1 ), T(-1), T(1) );
162 if ( cosTheta < 0 )
163 {
164 q0 = -q0;
165 cosTheta = -cosTheta;
166 }
167 T theta = std::acos( cosTheta );
168 T sinTheta = std::sin( theta );
169 if ( sinTheta <= 0 )
170 return lerp( q0, q1, t ).normalized();
171
172 return std::sin( theta * ( 1 - t ) ) / sinTheta * q0 + std::sin( theta * t ) / sinTheta * q1;
173}
174
175template <typename T>
176Quaternion<T>::operator Matrix3<T>() const
177{
178 Matrix3<T> res;
179 res.x = Vector3<T>{ a * a + b * b - c * c - d * d, 2*(b * c - a * d), 2*(b * d + a * c) };
180 res.y = Vector3<T>{ 2*(b * c + a * d), a * a + c * c - b * b - d * d, 2*(c * d - a * b) };
181 res.z = Vector3<T>{ 2*(b * d - a * c), 2*(c * d + a * b), a * a + d * d - b * b - c * c };
182 return res;
183}
184
185template <typename T>
186[[nodiscard]] inline bool operator ==( const Quaternion<T> & a, const Quaternion<T> & b )
187{
188 return a.a == b.a && a.b == b.b && a.c == b.c && a.d == b.d;
189}
190
191template <typename T>
192[[nodiscard]] inline bool operator !=( const Quaternion<T> & a, const Quaternion<T> & b )
193{
194 return !( a == b );
195}
196
197template <typename T>
198[[nodiscard]] inline Quaternion<T> operator +( const Quaternion<T> & a, const Quaternion<T> & b )
199{
200 return {a.a + b.a, a.b + b.b, a.c + b.c, a.d + b.d};
201}
202
203template <typename T>
204[[nodiscard]] inline Quaternion<T> operator -( const Quaternion<T> & a, const Quaternion<T> & b )
205{
206 return {a.a - b.a, a.b - b.b, a.c - b.c, a.d - b.d};
207}
208
209template <typename T>
210[[nodiscard]] inline Quaternion<T> operator *( T a, const Quaternion<T> & b )
211{
212 return {a * b.a, a * b.b, a * b.c, a * b.d};
213}
214
215template <typename T>
216[[nodiscard]] inline Quaternion<T> operator *( const Quaternion<T> & b, T a )
217{
218 return {a * b.a, a * b.b, a * b.c, a * b.d};
219}
220
221template <typename T>
222[[nodiscard]] inline Quaternion<T> operator /( const Quaternion<T> & b, T a )
223{
224 return b * ( 1 / a );
225}
226
228template <typename T>
229[[nodiscard]] inline T dot( const Quaternion<T> & a, const Quaternion<T> & b )
230{
231 return a.a * b.a + a.b * b.b + a.c * b.c + a.d * b.d;
232}
233
235template <typename T>
236[[nodiscard]] inline Quaternion<T> operator *( const Quaternion<T> & q1, const Quaternion<T> & q2 )
237{
238 return Quaternion<T>
239 {
240 q1.a * q2.a - q1.b * q2.b - q1.c * q2.c - q1.d * q2.d,
241 q1.a * q2.b + q1.b * q2.a + q1.c * q2.d - q1.d * q2.c,
242 q1.a * q2.c - q1.b * q2.d + q1.c * q2.a + q1.d * q2.b,
243 q1.a * q2.d + q1.b * q2.c - q1.c * q2.b + q1.d * q2.a
244 };
245}
246
247template <typename T>
248inline constexpr Vector3<T> Quaternion<T>::operator()( const Vector3<T> & p ) const noexcept
249{
250 // https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Using_quaternion_as_rotations
251 return ( *this * Quaternion( T(0), p ) * this->conjugate() ).im();
252}
253
254template<typename T>
255[[nodiscard]] const Quaternion<T>* getCanonicalQuaternions() noexcept
256{
257 static Quaternion<T> canonQuats[24] =
258 {
259 Quaternion<T>(),
260
261 Quaternion<T>( Vector3<T>::plusX(), T( PI2 ) ),
262 Quaternion<T>( Vector3<T>::plusX(), T( PI ) ),
263 Quaternion<T>( Vector3<T>::plusX(), T( 3 * PI2 ) ),
264 Quaternion<T>( Vector3<T>::plusY(), T( PI2 ) ),
265 Quaternion<T>( Vector3<T>::plusY(), T( PI ) ),
266 Quaternion<T>( Vector3<T>::plusY(), T( 3 * PI2 ) ),
267 Quaternion<T>( Vector3<T>::plusZ(), T( PI2 ) ),
268 Quaternion<T>( Vector3<T>::plusZ(), T( PI ) ),
269 Quaternion<T>( Vector3<T>::plusZ(), T( 3 * PI2 ) ),
270
271 Quaternion<T>( Vector3<T>( T( 1 ),T( 1 ),T( 0 ) ), T( PI ) ),
272 Quaternion<T>( Vector3<T>( T( 1 ),T( -1 ),T( 0 ) ), T( PI ) ),
273 Quaternion<T>( Vector3<T>( T( 1 ),T( 0 ),T( 1 ) ), T( PI ) ),
274 Quaternion<T>( Vector3<T>( T( 1 ),T( 0 ),T( -1 ) ), T( PI ) ),
275 Quaternion<T>( Vector3<T>( T( 0 ),T( 1 ),T( 1 ) ), T( PI ) ),
276 Quaternion<T>( Vector3<T>( T( 0 ),T( 1 ),T( -1 ) ), T( PI ) ),
277
278 Quaternion<T>( Vector3<T>( T( 1 ),T( 1 ),T( 1 ) ), T( 2 * PI / 3 ) ),
279 Quaternion<T>( Vector3<T>( T( 1 ),T( 1 ),T( -1 ) ), T( 2 * PI / 3 ) ),
280 Quaternion<T>( Vector3<T>( T( 1 ),T( -1 ),T( 1 ) ), T( 2 * PI / 3 ) ),
281 Quaternion<T>( Vector3<T>( T( 1 ),T( -1 ),T( -1 ) ), T( 2 * PI / 3 ) ),
282 Quaternion<T>( Vector3<T>( T( -1 ),T( 1 ),T( 1 ) ), T( 2 * PI / 3 ) ),
283 Quaternion<T>( Vector3<T>( T( -1 ),T( 1 ),T( -1 ) ), T( 2 * PI / 3 ) ),
284 Quaternion<T>( Vector3<T>( T( -1 ),T( -1 ),T( 1 ) ), T( 2 * PI / 3 ) ),
285 Quaternion<T>( Vector3<T>( T( -1 ),T( -1 ),T( -1 ) ), T( 2 * PI / 3 ) )
286 };
287 return canonQuats;
288}
289
291template<typename T>
292[[nodiscard]] Quaternion<T> getClosestCanonicalQuaternion( const Quaternion<T>& base ) noexcept
293{
294 Quaternion<T> baseInverse = base.normalized().inverse();
295 int closestIndex{0};
296 T maxCos = T(-2);
297 const Quaternion<T>* canonQuats = getCanonicalQuaternions<T>();
298 for ( int i = 0; i < 24; ++i )
299 {
300 const Quaternion<T>& canonQuat = canonQuats[i];
301 Quaternion<T> relativeQuat = canonQuat * baseInverse;
302 relativeQuat.normalize();
303 T cos = std::abs( relativeQuat.a );
304 if ( cos > maxCos )
305 {
306 maxCos = cos;
307 closestIndex = i;
308 }
309 }
310 return canonQuats[closestIndex];
311}
312
313template <typename T>
314[[nodiscard]] Matrix3<T> getClosestCanonicalMatrix( const Matrix3<T>& matrix ) noexcept
315{
316 Quaternion<T> closestQuat = getClosestCanonicalQuaternion( Quaternion<T>( matrix ) );
317 return Matrix3<T>( closestQuat );
318}
319
321template <typename T>
322[[nodiscard]] inline Matrix3<T> slerp( const Matrix3<T> & m0, const Matrix3<T> & m1, T t )
323{
324 return Quaternion<T>::slerp( m0, m1, t );
325}
326
329template <typename T>
330[[nodiscard]] inline AffineXf3<T> slerp( const AffineXf3<T> & xf0, const AffineXf3<T> & xf1, T t, const Vector3<T> & p = {} )
331{
332 return Quaternion<T>::slerp( xf0, xf1, t, p );
333}
334
336template <typename T>
337[[nodiscard]] inline Matrix3<T> orthonormalized( const Matrix3<T> & m )
338{
339 return Matrix3<T>{ Quaternion<T>{ m }.normalized() };
340}
341
344template <typename T>
345[[nodiscard]] inline AffineXf3<T> orthonormalized( const AffineXf3<T> & xf, const Vector3<T> & center = {} )
346{
347 AffineXf3<T> res;
348 res.A = orthonormalized( xf.A );
349 res.b = xf( center ) - res.A * center;
350 return res;
351}
352
354
355} // namespace MR
BitSet operator-(const BitSet &a, const BitSet &b)
Definition MRMesh/MRBitSet.h:325
MRMESH_API bool operator==(const BitSet &a, const BitSet &b)
compare that two bit sets have the same set bits (they can be equal even if sizes are distinct but la...
bool operator!=(const SetBitIteratorT< T > &a, const SetBitIteratorT< T > &b)
Definition MRMesh/MRBitSet.h:259
constexpr A normalize(A a)
Definition MRImGuiVectorOperators.h:134
constexpr auto dot(A a, A b)
Definition MRImGuiVectorOperators.h:129
Definition MRCameraOrientationPlugin.h:7
Color operator/(const Color &b, float a)
Definition MRColor.h:128
Color operator*(float a, const Color &b)
Definition MRColor.h:118
MRMESH_CLASS Vector3
Definition MRMesh/MRMeshFwd.h:136
Color operator+(const Color &a, const Color &b)
Definition MRColor.h:108
Definition MRMesh/MRAffineXf.h:14
V b
Definition MRMesh/MRAffineXf.h:19
M A
Definition MRMesh/MRAffineXf.h:18
uint8_t a
Definition MRColor.h:10
Definition MRMesh/MRMatrix3.h:13
Definition MRQuaternion.h:13
static Quaternion slerp(Quaternion q0, Quaternion q1, T t)
given t in [0,1] and two unit quaternions, interpolates them spherically and produces another unit qu...
constexpr Quaternion(const Vector3< T > &from, const Vector3< T > &to) noexcept
finds shorter arc rotation quaternion from one vector to another
T d
imaginary part: b*i + c*j + d*k
Definition MRQuaternion.h:15
constexpr T norm() const
Definition MRQuaternion.h:34
constexpr Quaternion inverse() const noexcept
computes reciprocal quaternion
Definition MRQuaternion.h:45
T dot(const Quaternion< T > &a, const Quaternion< T > &b)
dot product
Definition MRQuaternion.h:229
static Matrix3< T > slerp(const Matrix3< T > &m0, const Matrix3< T > &m1, T t)
given t in [0,1] and two rotation matrices, interpolates them spherically and produces another rotati...
Definition MRQuaternion.h:58
Matrix3< T > orthonormalized(const Matrix3< T > &m)
given any matrix, returns a close rotation matrix
Definition MRQuaternion.h:337
constexpr Quaternion(const Vector3< T > &axis, T angle) noexcept
T c
Definition MRQuaternion.h:15
AffineXf3< T > orthonormalized(const AffineXf3< T > &xf, const Vector3< T > &center={})
Definition MRQuaternion.h:345
static Quaternion lerp(const Quaternion &q0, const Quaternion &q1, T t)
given t in [0,1], interpolates linearly two quaternions giving in general not-unit quaternion
Definition MRQuaternion.h:54
constexpr Vector3< T > axis() const noexcept
returns axis of rotation encoded in this quaternion
Definition MRQuaternion.h:31
constexpr Quaternion(const Matrix3< T > &m)
constexpr Quaternion(T real, const Vector3< T > &im) noexcept
Definition MRQuaternion.h:20
Matrix3< T > slerp(const Matrix3< T > &m0, const Matrix3< T > &m1, T t)
given t in [0,1] and two rotation matrices, interpolates them spherically and produces another rotati...
Definition MRQuaternion.h:322
constexpr Quaternion operator-() const
returns quaternion representing the same rotation, using the opposite rotation direction and opposite...
Definition MRQuaternion.h:36
Quaternion & operator*=(T s)
Definition MRQuaternion.h:67
Quaternion & operator/=(T s)
Definition MRQuaternion.h:68
constexpr Quaternion conjugate() const noexcept
computes conjugate quaternion, which for unit quaternions encodes the opposite rotation
Definition MRQuaternion.h:43
constexpr Quaternion() noexcept=default
Quaternion normalized() const
Definition MRQuaternion.h:40
T a
real part of the quaternion
Definition MRQuaternion.h:14
T b
Definition MRQuaternion.h:15
constexpr Vector3< T > im() const noexcept
returns imaginary part of the quaternion as a vector
Definition MRQuaternion.h:26
Quaternion< T > getClosestCanonicalQuaternion(const Quaternion< T > &base) noexcept
returns closest to base canonical quaternion
Definition MRQuaternion.h:292
static AffineXf3< T > slerp(const AffineXf3< T > &xf0, const AffineXf3< T > &xf1, T t, const Vector3< T > &p={})
Definition MRQuaternion.h:61
constexpr T angle() const noexcept
returns angle of rotation encoded in this quaternion
Definition MRQuaternion.h:29
void normalize()
scales this quaternion to make its norm unit
Definition MRQuaternion.h:39
constexpr T normSq() const
Definition MRQuaternion.h:33
AffineXf3< T > slerp(const AffineXf3< T > &xf0, const AffineXf3< T > &xf1, T t, const Vector3< T > &p={})
Definition MRQuaternion.h:330
constexpr Vector3< T > operator()(const Vector3< T > &p) const noexcept
Definition MRMesh/MRVector3.h:19
static constexpr Vector3 plusX() noexcept
Definition MRMesh/MRVector3.h:33
static constexpr Vector3 plusZ() noexcept
Definition MRMesh/MRVector3.h:35
static constexpr Vector3 plusY() noexcept
Definition MRMesh/MRVector3.h:34