MeshLib C++ Docs
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{
9
10
14template <typename T>
16{
17 T a = 1;
18 T b = 0, c = 0, d = 0;
19
20 constexpr Quaternion() noexcept = default;
21 constexpr Quaternion( T a, T b, T c, T d ) noexcept : a( a ), b( b ), c( c ), d( d ) { }
22 constexpr Quaternion( const Vector3<T> & axis, T angle ) noexcept;
23 constexpr Quaternion( T real, const Vector3<T> & im ) noexcept : a( real ), b( im.x ), c( im.y ), d( im.z ) { }
24 constexpr Quaternion( const Matrix3<T> & m );
26 constexpr Quaternion( const Vector3<T>& from, const Vector3<T>& to ) noexcept;
27
29 [[nodiscard]] constexpr Vector3<T> im() const noexcept { return Vector3<T>{ b, c, d }; }
30
32 [[nodiscard]] constexpr T angle() const noexcept { return 2 * std::acos( std::clamp( a, T(-1), T(1) ) ); }
34 [[nodiscard]] constexpr Vector3<T> axis() const noexcept { return im().normalized(); }
35
36 [[nodiscard]] constexpr T normSq() const { return a * a + b * b + c * c + d * d; }
37 [[nodiscard]] constexpr T norm() const { return std::sqrt( normSq() ); }
39 [[nodiscard]] constexpr Quaternion operator-() const { return {-a, -b, -c, -d}; }
40
42 void normalize() { if ( T n = norm(); n > 0 ) *this /= n; }
43 [[nodiscard]] Quaternion normalized() const { Quaternion res( *this ); res.normalize(); return res; }
44
46 [[nodiscard]] constexpr Quaternion conjugate() const noexcept { return {a, -b, -c, -d}; }
48 [[nodiscard]] constexpr Quaternion inverse() const noexcept { return conjugate() / normSq(); }
51 [[nodiscard]] constexpr Vector3<T> operator()( const Vector3<T> & p ) const noexcept;
52
54 [[nodiscard]] operator Matrix3<T>() const;
55
57 [[nodiscard]] static Quaternion lerp( const Quaternion & q0, const Quaternion & q1, T t ) { return ( 1 - t ) * q0 + t * q1; }
59 [[nodiscard]] static Quaternion slerp( Quaternion q0, Quaternion q1, T t );
61 [[nodiscard]] static Matrix3<T> slerp( const Matrix3<T> & m0, const Matrix3<T> & m1, T t ) { return slerp( Quaternion<T>{ m0 }, Quaternion<T>{ m1 }, t ); }
64 [[nodiscard]] static AffineXf3<T> slerp( const AffineXf3<T> & xf0, const AffineXf3<T> & xf1, T t, const Vector3<T> & p = {} )
65 {
66 auto xfA = slerp( xf0.A, xf1.A, t );
67 return { xfA, ( 1 - t ) * xf0( p ) + t * xf1( p ) - xfA * p };
68 }
69
70 Quaternion & operator *=( T s ) { a *= s; b *= s; c *= s; d *= s; return * this; }
71 Quaternion & operator /=( T s ) { return *this *= ( 1 / s ); }
72};
73
76
77template <typename T>
78constexpr Quaternion<T>::Quaternion( const Vector3<T> & axis, T angle ) noexcept
79{
80 a = std::cos( angle / 2 );
81 Vector3<T> im = std::sin( angle / 2 ) * axis.normalized();
82 b = im.x;
83 c = im.y;
84 d = im.z;
85}
86
87template <typename T>
88constexpr Quaternion<T>::Quaternion( const Matrix3<T> & m )
89{
91 const auto tr = m.trace();
92 if ( tr > 0 )
93 {
94 auto S = std::sqrt( tr + 1 ) * 2;
95 a = T( 0.25 ) * S;
96 b = ( m.z.y - m.y.z ) / S;
97 c = ( m.x.z - m.z.x ) / S;
98 d = ( m.y.x - m.x.y ) / S;
99 }
100 else if ( m.x.x > m.y.y && m.x.x > m.z.z )
101 {
102 auto S = std::sqrt( 1 + m.x.x - m.y.y - m.z.z ) * 2;
103 a = ( m.z.y - m.y.z ) / S;
104 b = T( 0.25 ) * S;
105 c = ( m.x.y + m.y.x ) / S;
106 d = ( m.x.z + m.z.x ) / S;
107 }
108 else if ( m.y.y > m.z.z )
109 {
110 auto S = std::sqrt( 1 + m.y.y - m.x.x - m.z.z ) * 2;
111 a = ( m.x.z - m.z.x ) / S;
112 b = ( m.x.y + m.y.x ) / S;
113 c = T( 0.25 ) * S;
114 d = ( m.y.z + m.z.y ) / S;
115 }
116 else
117 {
118 auto S = std::sqrt( 1 + m.z.z - m.x.x - m.y.y ) * 2;
119 a = ( m.y.x - m.x.y ) / S;
120 b = ( m.x.z + m.z.x ) / S;
121 c = ( m.y.z + m.z.y ) / S;
122 d = T( 0.25 ) * S;
123 }
124}
125
126template <typename T>
127constexpr Quaternion<T>::Quaternion( const Vector3<T>& from, const Vector3<T>& to) noexcept
128{
130 a = dot( from, to );
131 auto cr = cross( from, to );
132 if( cr.x == 0 && cr.y == 0 && cr.z == 0 )
133 {
134 if( a >= 0 )
135 {
138 a = 1; b = 0; c = 0; d = 0;
139 }
140 else
141 {
144 auto perp = cross( from, from.furthestBasisVector() );
145 a = 0; b = perp.x; c = perp.y; d = perp.z;
146 normalize();
147 }
148 }
149 else
150 {
151 a += std::sqrt( from.lengthSq() * to.lengthSq() );
152 b = cr.x; c = cr.y; d = cr.z;
153 normalize();
154 }
155}
156
157template <typename T>
158Quaternion<T> Quaternion<T>::slerp( Quaternion q0, Quaternion q1, T t )
159{
161 q0.normalize();
162 q1.normalize();
163
164 T cosTheta = std::clamp( dot( q0, q1 ), T(-1), T(1) );
165 if ( cosTheta < 0 )
166 {
167 q0 = -q0;
168 cosTheta = -cosTheta;
169 }
170 T theta = std::acos( cosTheta );
171 T sinTheta = std::sin( theta );
172 if ( sinTheta <= 0 )
173 return lerp( q0, q1, t ).normalized();
174
175 return std::sin( theta * ( 1 - t ) ) / sinTheta * q0 + std::sin( theta * t ) / sinTheta * q1;
176}
177
178template <typename T>
179Quaternion<T>::operator Matrix3<T>() const
180{
181 Matrix3<T> res;
182 res.x = Vector3<T>{ a * a + b * b - c * c - d * d, 2*(b * c - a * d), 2*(b * d + a * c) };
183 res.y = Vector3<T>{ 2*(b * c + a * d), a * a + c * c - b * b - d * d, 2*(c * d - a * b) };
184 res.z = Vector3<T>{ 2*(b * d - a * c), 2*(c * d + a * b), a * a + d * d - b * b - c * c };
185 return res;
186}
187
188template <typename T>
189[[nodiscard]] inline bool operator ==( const Quaternion<T> & a, const Quaternion<T> & b )
190{
191 return a.a == b.a && a.b == b.b && a.c == b.c && a.d == b.d;
192}
193
194template <typename T>
195[[nodiscard]] inline bool operator !=( const Quaternion<T> & a, const Quaternion<T> & b )
196{
197 return !( a == b );
198}
199
200template <typename T>
201[[nodiscard]] inline Quaternion<T> operator +( const Quaternion<T> & a, const Quaternion<T> & b )
202{
203 return {a.a + b.a, a.b + b.b, a.c + b.c, a.d + b.d};
204}
205
206template <typename T>
207[[nodiscard]] inline Quaternion<T> operator -( const Quaternion<T> & a, const Quaternion<T> & b )
208{
209 return {a.a - b.a, a.b - b.b, a.c - b.c, a.d - b.d};
210}
211
212template <typename T>
213[[nodiscard]] inline Quaternion<T> operator *( T a, const Quaternion<T> & b )
214{
215 return {a * b.a, a * b.b, a * b.c, a * b.d};
216}
217
218template <typename T>
219[[nodiscard]] inline Quaternion<T> operator *( const Quaternion<T> & b, T a )
220{
221 return {a * b.a, a * b.b, a * b.c, a * b.d};
222}
223
224template <typename T>
225[[nodiscard]] inline Quaternion<T> operator /( const Quaternion<T> & b, T a )
226{
227 return b * ( 1 / a );
228}
229
231template <typename T>
232[[nodiscard]] inline T dot( const Quaternion<T> & a, const Quaternion<T> & b )
233{
234 return a.a * b.a + a.b * b.b + a.c * b.c + a.d * b.d;
235}
236
238template <typename T>
239[[nodiscard]] inline Quaternion<T> operator *( const Quaternion<T> & q1, const Quaternion<T> & q2 )
240{
241 return Quaternion<T>
242 {
243 q1.a * q2.a - q1.b * q2.b - q1.c * q2.c - q1.d * q2.d,
244 q1.a * q2.b + q1.b * q2.a + q1.c * q2.d - q1.d * q2.c,
245 q1.a * q2.c - q1.b * q2.d + q1.c * q2.a + q1.d * q2.b,
246 q1.a * q2.d + q1.b * q2.c - q1.c * q2.b + q1.d * q2.a
247 };
248}
249
250template <typename T>
251inline constexpr Vector3<T> Quaternion<T>::operator()( const Vector3<T> & p ) const noexcept
252{
254 return ( *this * Quaternion( T(0), p ) * this->conjugate() ).im();
255}
256
257template<typename T>
258[[nodiscard]] const Quaternion<T>* getCanonicalQuaternions() noexcept
259{
260 static Quaternion<T> canonQuats[24] =
261 {
263
264 Quaternion<T>( Vector3<T>::plusX(), T( PI2 ) ),
265 Quaternion<T>( Vector3<T>::plusX(), T( PI ) ),
266 Quaternion<T>( Vector3<T>::plusX(), T( 3 * PI2 ) ),
267 Quaternion<T>( Vector3<T>::plusY(), T( PI2 ) ),
268 Quaternion<T>( Vector3<T>::plusY(), T( PI ) ),
269 Quaternion<T>( Vector3<T>::plusY(), T( 3 * PI2 ) ),
270 Quaternion<T>( Vector3<T>::plusZ(), T( PI2 ) ),
271 Quaternion<T>( Vector3<T>::plusZ(), T( PI ) ),
272 Quaternion<T>( Vector3<T>::plusZ(), T( 3 * PI2 ) ),
273
274 Quaternion<T>( Vector3<T>( T( 1 ),T( 1 ),T( 0 ) ), T( PI ) ),
275 Quaternion<T>( Vector3<T>( T( 1 ),T( -1 ),T( 0 ) ), T( PI ) ),
276 Quaternion<T>( Vector3<T>( T( 1 ),T( 0 ),T( 1 ) ), T( PI ) ),
277 Quaternion<T>( Vector3<T>( T( 1 ),T( 0 ),T( -1 ) ), T( PI ) ),
278 Quaternion<T>( Vector3<T>( T( 0 ),T( 1 ),T( 1 ) ), T( PI ) ),
279 Quaternion<T>( Vector3<T>( T( 0 ),T( 1 ),T( -1 ) ), T( PI ) ),
280
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 Quaternion<T>( Vector3<T>( T( -1 ),T( 1 ),T( -1 ) ), T( 2 * PI / 3 ) ),
287 Quaternion<T>( Vector3<T>( T( -1 ),T( -1 ),T( 1 ) ), T( 2 * PI / 3 ) ),
288 Quaternion<T>( Vector3<T>( T( -1 ),T( -1 ),T( -1 ) ), T( 2 * PI / 3 ) )
289 };
290 return canonQuats;
291}
292
294template<typename T>
295[[nodiscard]] Quaternion<T> getClosestCanonicalQuaternion( const Quaternion<T>& base ) noexcept
296{
297 Quaternion<T> baseInverse = base.normalized().inverse();
298 int closestIndex{0};
299 T maxCos = T(-2);
300 const Quaternion<T>* canonQuats = getCanonicalQuaternions<T>();
301 for ( int i = 0; i < 24; ++i )
302 {
303 const Quaternion<T>& canonQuat = canonQuats[i];
304 Quaternion<T> relativeQuat = canonQuat * baseInverse;
305 relativeQuat.normalize();
306 T cos = std::abs( relativeQuat.a );
307 if ( cos > maxCos )
308 {
309 maxCos = cos;
310 closestIndex = i;
311 }
312 }
313 return canonQuats[closestIndex];
314}
315
316template <typename T>
317[[nodiscard]] Matrix3<T> getClosestCanonicalMatrix( const Matrix3<T>& matrix ) noexcept
318{
319 Quaternion<T> closestQuat = getClosestCanonicalQuaternion( Quaternion<T>( matrix ) );
320 return Matrix3<T>( closestQuat );
321}
322
324template <typename T>
325[[nodiscard]] inline Matrix3<T> slerp( const Matrix3<T> & m0, const Matrix3<T> & m1, T t )
326{
327 return Quaternion<T>::slerp( m0, m1, t );
328}
329
332template <typename T>
333[[nodiscard]] inline AffineXf3<T> slerp( const AffineXf3<T> & xf0, const AffineXf3<T> & xf1, T t, const Vector3<T> & p = {} )
334{
335 return Quaternion<T>::slerp( xf0, xf1, t, p );
336}
337
339template <typename T>
340[[nodiscard]] inline Matrix3<T> orthonormalized( const Matrix3<T> & m )
341{
342 return Matrix3<T>{ Quaternion<T>{ m }.normalized() };
343}
344
347template <typename T>
348[[nodiscard]] inline AffineXf3<T> orthonormalized( const AffineXf3<T> & xf, const Vector3<T> & center = {} )
349{
350 AffineXf3<T> res;
351 res.A = orthonormalized( xf.A );
352 res.b = xf( center ) - res.A * center;
353 return res;
354}
355
357
358}
BitSet operator-(const BitSet &a, const BitSet &b)
Definition MRBitSet.h:457
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...
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:18
uint8_t a
Definition MRColor.h:13
constexpr T norm() const
Definition MRQuaternion.h:37
constexpr Quaternion inverse() const noexcept
computes reciprocal quaternion
Definition MRQuaternion.h:48
T dot(const Quaternion< T > &a, const Quaternion< T > &b)
dot product
Definition MRQuaternion.h:232
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:61
Matrix3< T > orthonormalized(const Matrix3< T > &m)
given any matrix, returns a close rotation matrix
Definition MRQuaternion.h:340
constexpr Quaternion(const Vector3< T > &axis, T angle) noexcept
T c
Definition MRQuaternion.h:18
AffineXf3< T > orthonormalized(const AffineXf3< T > &xf, const Vector3< T > &center={})
Definition MRQuaternion.h:348
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:57
constexpr Vector3< T > axis() const noexcept
returns axis of rotation encoded in this quaternion
Definition MRQuaternion.h:34
constexpr Quaternion(const Matrix3< T > &m)
bool operator!=(const Color &a, const Color &b)
Definition MRColor.h:104
constexpr Quaternion(T real, const Vector3< T > &im) noexcept
Definition MRQuaternion.h:23
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:325
Color operator/(const Color &b, float a)
Definition MRColor.h:129
constexpr Quaternion operator-() const
returns quaternion representing the same rotation, using the opposite rotation direction and opposite...
Definition MRQuaternion.h:39
Quaternion & operator*=(T s)
Definition MRQuaternion.h:70
Color operator*(float a, const Color &b)
Definition MRColor.h:119
Quaternion & operator/=(T s)
Definition MRQuaternion.h:71
const Quaternion< T > * getCanonicalQuaternions() noexcept
Definition MRQuaternion.h:258
constexpr Quaternion conjugate() const noexcept
computes conjugate quaternion, which for unit quaternions encodes the opposite rotation
Definition MRQuaternion.h:46
constexpr Quaternion() noexcept=default
Quaternion normalized() const
Definition MRQuaternion.h:43
T a
real part of the quaternion
Definition MRQuaternion.h:17
T b
Definition MRQuaternion.h:18
constexpr Vector3< T > im() const noexcept
returns imaginary part of the quaternion as a vector
Definition MRQuaternion.h:29
Quaternion< T > getClosestCanonicalQuaternion(const Quaternion< T > &base) noexcept
returns closest to base canonical quaternion
Definition MRQuaternion.h:295
static AffineXf3< T > slerp(const AffineXf3< T > &xf0, const AffineXf3< T > &xf1, T t, const Vector3< T > &p={})
Definition MRQuaternion.h:64
constexpr T angle() const noexcept
returns angle of rotation encoded in this quaternion
Definition MRQuaternion.h:32
void normalize()
scales this quaternion to make its norm unit
Definition MRQuaternion.h:42
constexpr T normSq() const
Definition MRQuaternion.h:36
AffineXf3< T > slerp(const AffineXf3< T > &xf0, const AffineXf3< T > &xf1, T t, const Vector3< T > &p={})
Definition MRQuaternion.h:333
Color operator+(const Color &a, const Color &b)
Definition MRColor.h:109
constexpr Vector3< T > operator()(const Vector3< T > &p) const noexcept
@ angle
Direction, normally Vector3f.
constexpr A normalize(A a)
Definition MRImGuiVectorOperators.h:134
constexpr auto dot(A a, A b)
Definition MRImGuiVectorOperators.h:129
only for bindings generation
Definition MRCameraOrientationPlugin.h:8
Definition MRMatrix3.h:24
Definition MRQuaternion.h:16
Definition MRVector3.h:33