Sen API
Sen Libraries
Loading...
Searching...
No Matches
quat.h
Go to the documentation of this file.
1// === quat.h ==========================================================================================================
2// Sen Infrastructure
3// Released under the Apache License v2.0 (SPDX-License-Identifier Apache-2.0).
4// See the LICENSE.txt file for more information.
5// © Airbus SAS, Airbus Helicopters, and Airbus Defence and Space SAU/GmbH/SAS.
6// =====================================================================================================================
7
8#ifndef SEN_LIBS_UTIL_SRC_DR_QUAT_H
9#define SEN_LIBS_UTIL_SRC_DR_QUAT_H
10
11#include "constants.h"
12#include "vec3.h"
13
14// sen
17
18namespace sen::util
19{
20
22template <typename T>
23class Quat
24{
25public:
26 SEN_COPY_MOVE(Quat)
27
28public: // set of builders
29 Quat() noexcept = default;
30
31 Quat(T x, T y, T z, T w) noexcept;
32
33 Quat(T angle, const Vec3<T>& axis) noexcept;
34
35 Quat(T yaw, T pitch, T bank) noexcept;
36
37 explicit Quat(const Vec3<T>& eulerAngles) noexcept;
38
39 ~Quat() = default;
40
41public: // custom operators
42 bool operator==(const Quat& v) const noexcept;
43
44 bool operator!=(const Quat& v) const noexcept;
45
46 bool operator<(const Quat& v) const noexcept;
47
48 Quat operator*(T rhs) const;
49
50 Quat& operator*=(T rhs) noexcept;
51
52 Quat operator*(const Quat& rhs) const noexcept;
53
54 Vec3<T> operator*(const Vec3<T>& v) const noexcept;
55
56 Quat& operator*=(const Quat& rhs) noexcept;
57
58 Quat operator/(const Quat& denom) const noexcept;
59
60 Quat& operator/=(const Quat& denom) noexcept;
61
62 Quat operator+(const Quat& rhs) const noexcept;
63
64 Quat operator/(const T rhs) const noexcept;
65
66 Quat& operator/=(T rhs) noexcept;
67
68 Quat& operator+=(const Quat& rhs) noexcept;
69
70 Quat operator-(const Quat& rhs) const noexcept;
71
72 Quat& operator-=(const Quat& rhs) noexcept;
73
74 Quat operator-() const noexcept;
75
76 // Dot product operator
77 [[nodiscard]] T dot(const Quat<T>& other) const noexcept;
78
79public: // operations to get stored values
80 [[nodiscard]] Vec3<T> asVec3() const noexcept;
81
82 void set(T x, T y, T z, T w) noexcept;
83
84 [[nodiscard]] T getX() const noexcept;
85 [[nodiscard]] T getY() const noexcept;
86 [[nodiscard]] T getZ() const noexcept;
87 [[nodiscard]] T getW() const noexcept;
88
89 void setX(T x) noexcept;
90
91 void setY(T y) noexcept;
92
93 void setZ(T z) noexcept;
94
95 void setW(T w) noexcept;
96
97 [[nodiscard]] bool zeroRotation() const noexcept;
98
99public: // quaternion information
100 [[nodiscard]] T length() const noexcept;
101
102 [[nodiscard]] T length2() const noexcept;
103
106 [[nodiscard]] Quat conj() const;
107
110 [[nodiscard]] Quat inverse() const;
111
112public: // quaternion rotations
113 void makeRotate(T angle, T x, T y, T z) noexcept;
114
115 void makeRotate(T angle, const Vec3<T>& vec) noexcept;
116
117 void makeRotate(const Vec3<T>& from, const Vec3<T>& to) noexcept;
118
119 void makeRotate(T angle1,
120 const Vec3<T>& axis1,
121 T angle2,
122 const Vec3<T>& axis2,
123 T angle3,
124 const Vec3<T>& axis3) noexcept;
125
126 void makeRotateFromEulerYPB(T yaw, T pitch, T bank) noexcept;
127
128 void makeRotateFromEulerYPB(const Vec3<T>& eulerAngles) noexcept;
129
130 void getRotate(T& angle, T& x, T& y, T& z) const noexcept;
131
132 void getRotate(T& angle, Vec3<T>& vec) const noexcept;
133
134 [[nodiscard]] Vec3<T> getRotateInEulerYPB() const noexcept;
135
136public: // interpolations
142 void slerp(T t, const Quat& from, const Quat& to) noexcept;
143
144private:
145 T v_[4] {0, 0, 0, 1};
146};
147
150
151//-------------------------------------------------------------------------------------------------------------------
152// Inline implementation
153//-------------------------------------------------------------------------------------------------------------------
154
155template <typename T>
156inline Quat<T>::Quat(T x, T y, T z, T w) noexcept
157{
158 v_[0] = x;
159 v_[1] = y;
160 v_[2] = z;
161 v_[3] = w;
162}
163
164template <typename T>
165inline Quat<T>::Quat(T angle, const Vec3<T>& axis) noexcept
166{
167 v_[0] = static_cast<T>(0.0);
168 v_[1] = static_cast<T>(0.0);
169 v_[2] = static_cast<T>(0.0);
170 v_[3] = static_cast<T>(1.0);
171
172 makeRotate(angle, axis);
173}
174
175template <typename T>
176inline Quat<T>::Quat(T yaw, T pitch, T bank) noexcept
177{
178 makeRotateFromEulerYPB(yaw, pitch, bank);
179}
180
181template <typename T>
182inline Quat<T>::Quat(const Vec3<T>& eulerAngles) noexcept
183{
184 makeRotateFromEulerYPB(eulerAngles.getX(), eulerAngles.getY(), eulerAngles.getZ());
185}
186
187template <typename T>
188inline bool Quat<T>::operator==(const Quat& v) const noexcept
189{
190 return v_[0] == v.v_[0] && v_[1] == v.v_[1] && v_[2] == v.v_[2] && v_[3] == v.v_[3];
191}
192
193template <typename T>
194inline bool Quat<T>::operator!=(const Quat& v) const noexcept
195{
196 return v_[0] != v.v_[0] || v_[1] != v.v_[1] || v_[2] != v.v_[2] || v_[3] != v.v_[3];
197}
198
199template <typename T>
200inline bool Quat<T>::operator<(const Quat& v) const noexcept
201{
202 if (v_[0] < v.v_[0])
203 {
204 return true;
205 }
206
207 if (v_[0] > v.v_[0])
208 {
209 return false;
210 }
211
212 if (v_[1] < v.v_[1])
213 {
214 return true;
215 }
216
217 if (v_[1] > v.v_[1])
218 {
219 return false;
220 }
221
222 if (v_[2] < v.v_[2])
223 {
224 return true;
225 }
226
227 if (v_[2] > v.v_[2])
228 {
229 return false;
230 }
231
232 return (v_[3] < v.v_[3]);
233}
234
235template <typename T>
236inline Vec3<T> Quat<T>::asVec3() const noexcept
237{
238 return Vec3<T> {v_[0], v_[1], v_[2]};
239}
240
241template <typename T>
242inline void Quat<T>::set(T x, T y, T z, T w) noexcept
243{
244 v_[0] = x;
245 v_[1] = y;
246 v_[2] = z;
247 v_[3] = w;
248}
249
250template <typename T>
251inline T Quat<T>::getX() const noexcept
252{
253 return v_[0];
254}
255
256template <typename T>
257inline T Quat<T>::getY() const noexcept
258{
259 return v_[1];
260}
261
262template <typename T>
263inline T Quat<T>::getZ() const noexcept
264{
265 return v_[2];
266}
267
268template <typename T>
269inline T Quat<T>::getW() const noexcept
270{
271 return v_[3];
272}
273
274template <typename T>
275inline void Quat<T>::setX(T x) noexcept
276{
277 v_[0] = x;
278}
279
280template <typename T>
281inline void Quat<T>::setY(T y) noexcept
282{
283 v_[1] = y;
284}
285
286template <typename T>
287inline void Quat<T>::setZ(T z) noexcept
288{
289 v_[2] = z;
290}
291
292template <typename T>
293inline void Quat<T>::setW(T w) noexcept
294{
295 v_[3] = w;
296}
297
298template <typename T>
299inline bool Quat<T>::zeroRotation() const noexcept
300{
301 return v_[0] == 0.0 && v_[1] == 0.0 && v_[2] == 0.0 && v_[3] == 1.0;
302}
303
304template <typename T>
305inline Quat<T> Quat<T>::operator*(T rhs) const
306{
307 return {v_[0] * rhs, v_[1] * rhs, v_[2] * rhs, v_[3] * rhs};
308}
309
310template <typename T>
311inline Quat<T>& Quat<T>::operator*=(T rhs) noexcept
312{
313 v_[0] *= rhs;
314 v_[1] *= rhs;
315 v_[2] *= rhs;
316 v_[3] *= rhs;
317 return *this;
318}
319
320template <typename T>
321inline Quat<T> Quat<T>::operator*(const Quat& rhs) const noexcept
322{
323 return {rhs.v_[3] * v_[0] + rhs.v_[0] * v_[3] + rhs.v_[1] * v_[2] - rhs.v_[2] * v_[1],
324 rhs.v_[3] * v_[1] - rhs.v_[0] * v_[2] + rhs.v_[1] * v_[3] + rhs.v_[2] * v_[0],
325 rhs.v_[3] * v_[2] + rhs.v_[0] * v_[1] - rhs.v_[1] * v_[0] + rhs.v_[2] * v_[3],
326 rhs.v_[3] * v_[3] - rhs.v_[0] * v_[0] - rhs.v_[1] * v_[1] - rhs.v_[2] * v_[2]};
327}
328
329template <typename T>
330inline Quat<T>& Quat<T>::operator*=(const Quat& rhs) noexcept
331{
332 T x = rhs.v_[3] * v_[0] + rhs.v_[0] * v_[3] + rhs.v_[1] * v_[2] - rhs.v_[2] * v_[1];
333 T y = rhs.v_[3] * v_[1] - rhs.v_[0] * v_[2] + rhs.v_[1] * v_[3] + rhs.v_[2] * v_[0];
334 T z = rhs.v_[3] * v_[2] + rhs.v_[0] * v_[1] - rhs.v_[1] * v_[0] + rhs.v_[2] * v_[3];
335 v_[3] = rhs.v_[3] * v_[3] - rhs.v_[0] * v_[0] - rhs.v_[1] * v_[1] - rhs.v_[2] * v_[2];
336
337 v_[2] = z;
338 v_[1] = y;
339 v_[0] = x;
340
341 return (*this);
342}
343
344template <typename T>
345inline Quat<T> Quat<T>::operator/(T rhs) const noexcept
346{
347 T div = 1.0 / rhs;
348 return {v_[0] * div, v_[1] * div, v_[2] * div, v_[3] * div};
349}
350
351template <typename T>
352inline Quat<T>& Quat<T>::operator/=(T rhs) noexcept
353{
354 T div = 1.0 / rhs;
355 v_[0] *= div;
356 v_[1] *= div;
357 v_[2] *= div;
358 v_[3] *= div;
359 return *this;
360}
361
362template <typename T>
363inline Quat<T> Quat<T>::operator/(const Quat& denom) const noexcept
364{
365 return ((*this) * denom.inverse());
366}
367
368template <typename T>
369inline Quat<T>& Quat<T>::operator/=(const Quat& denom) noexcept
370{
371 (*this) = (*this) * denom.inverse();
372 return (*this);
373}
374
375template <typename T>
376inline Quat<T> Quat<T>::operator+(const Quat& rhs) const noexcept
377{
378 return {v_[0] + rhs.v_[0], v_[1] + rhs.v_[1], v_[2] + rhs.v_[2], v_[3] + rhs.v_[3]};
379}
380
381template <typename T>
382inline Quat<T>& Quat<T>::operator+=(const Quat& rhs) noexcept
383{
384 v_[0] += rhs.v_[0];
385 v_[1] += rhs.v_[1];
386 v_[2] += rhs.v_[2];
387 v_[3] += rhs.v_[3];
388 return *this;
389}
390
391template <typename T>
392inline Quat<T> Quat<T>::operator-(const Quat& rhs) const noexcept
393{
394 return {v_[0] - rhs.v_[0], v_[1] - rhs.v_[1], v_[2] - rhs.v_[2], v_[3] - rhs.v_[3]};
395}
396
397template <typename T>
398inline Quat<T>& Quat<T>::operator-=(const Quat& rhs) noexcept
399{
400 v_[0] -= rhs.v_[0];
401 v_[1] -= rhs.v_[1];
402 v_[2] -= rhs.v_[2];
403 v_[3] -= rhs.v_[3];
404 return *this;
405}
406
407template <typename T>
408inline Quat<T> Quat<T>::operator-() const noexcept
409{
410 return {-v_[0], -v_[1], -v_[2], -v_[3]};
411}
412
413template <typename T>
414T Quat<T>::dot(const Quat<T>& other) const noexcept
415{
416 return v_[0] * other.v_[0] + v_[1] * other.v_[1] + v_[2] * other.v_[2] + v_[3] * other.v_[3];
417}
418
419template <typename T>
420inline T Quat<T>::length() const noexcept
421{
422 return sqrt(length2());
423}
424
425template <typename T>
426inline T Quat<T>::length2() const noexcept
427{
428 return v_[0] * v_[0] + v_[1] * v_[1] + v_[2] * v_[2] + v_[3] * v_[3];
429}
430
431template <typename T>
432inline Quat<T> Quat<T>::conj() const
433{
434 return {-v_[0], -v_[1], -v_[2], v_[3]};
435}
436
437template <typename T>
439{
440 return conj() / length2();
441}
442
443template <typename T>
444inline void Quat<T>::makeRotate(T angle, T x, T y, T z) noexcept
445{
446 constexpr T epsilon = 0.0000001;
447
448 T length = std::sqrt(x * x + y * y + z * z);
449 if (length < epsilon)
450 {
451 return;
452 }
453
454 T cosHalfAngle = cos(0.5 * angle);
455 T sinHalfAngle = sin(0.5 * angle);
456
457 T quatX = x * sinHalfAngle / length;
458 T quatY = y * sinHalfAngle / length;
459 T quatZ = z * sinHalfAngle / length;
460 T quatW = cosHalfAngle;
461
462 Quat rotationQuat {quatX, quatY, quatZ, quatW};
463
464 *this = *this * rotationQuat;
465}
466
467template <typename T>
468inline void Quat<T>::makeRotate(T angle, const Vec3<T>& vec) noexcept
469{
470 makeRotate(angle, vec.getX(), vec.getY(), vec.getZ());
471}
472
473template <typename T>
474inline void Quat<T>::makeRotate(const Vec3<T>& from, const Vec3<T>& to) noexcept
475{
476 auto sourceVector = from;
477 auto targetVector = to;
478
479 T fromLen2 = from.length2();
480 T fromLen;
481
482 if ((fromLen2 < 1.0 - 1e-7) || (fromLen2 > 1.0 + 1e-7))
483 {
484 fromLen = sqrt(fromLen2);
485 sourceVector /= fromLen;
486 }
487 else
488 {
489 fromLen = 1.0;
490 }
491
492 T toLen2 = to.length2();
493 if ((toLen2 < 1.0 - 1e-7) || (toLen2 > 1.0 + 1e-7))
494 {
495 T toLen;
496 if ((toLen2 > fromLen2 - 1e-7) && (toLen2 < fromLen2 + 1e-7))
497 {
498 toLen = fromLen;
499 }
500 else
501 {
502 toLen = sqrt(toLen2);
503 }
504 targetVector /= toLen;
505 }
506
507 T dotProdPlus1 = 1.0 + sourceVector * targetVector;
508
509 if (dotProdPlus1 < 1e-7)
510 {
511 if (fabs(sourceVector.getX()) < 0.6)
512 {
513 const auto norm = sqrt(1.0 - sourceVector.getX() * sourceVector.getX());
514 v_[0] = 0.0;
515 v_[1] = sourceVector.getZ() / norm;
516 v_[2] = -sourceVector.getY() / norm;
517 v_[3] = 0.0;
518 }
519 else if (fabs(sourceVector.getY()) < 0.6)
520 {
521 const auto norm = sqrt(1.0 - sourceVector.getY() * sourceVector.getY());
522 v_[0] = -sourceVector.getZ() / norm;
523 v_[1] = 0.0;
524 v_[2] = sourceVector.getX() / norm;
525 v_[3] = 0.0;
526 }
527 else
528 {
529 const auto norm = sqrt(1.0 - sourceVector.getZ() * sourceVector.getZ());
530 v_[0] = sourceVector.getY() / norm;
531 v_[1] = -sourceVector.getX() / norm;
532 v_[2] = 0.0;
533 v_[3] = 0.0;
534 }
535 }
536 else
537 {
538 const auto s = sqrt(0.5 * dotProdPlus1);
539 const auto tmp = sourceVector ^ targetVector / (2.0 * s);
540 v_[0] = tmp.getX();
541 v_[1] = tmp.getY();
542 v_[2] = tmp.getZ();
543 v_[3] = s;
544 }
545}
546
547template <typename T>
548inline void Quat<T>::makeRotate(T angle1,
549 const Vec3<T>& axis1,
550 T angle2,
551 const Vec3<T>& axis2,
552 T angle3,
553 const Vec3<T>& axis3) noexcept
554{
555 Quat q1;
556 q1.makeRotate(angle1, axis1);
557 Quat q2;
558 q2.makeRotate(angle2, axis2);
559 Quat q3;
560 q3.makeRotate(angle3, axis3);
561
562 *this = q1 * q2 * q3;
563}
564
565template <typename T>
566inline void Quat<T>::makeRotateFromEulerYPB(T yaw, T pitch, T bank) noexcept
567{
568 makeRotate(bank, {1.0, 0.0, 0.0}, pitch, {0.0, 1.0, 0.0}, yaw, {0.0, 0.0, 1.0});
569}
570
571template <typename T>
572inline void Quat<T>::makeRotateFromEulerYPB(const Vec3<T>& eulerAngles) noexcept
573{
574 makeRotate(eulerAngles.z(), {1.0, 0.0, 0.0}, eulerAngles.y(), {0.0, 1.0, 0.0}, eulerAngles.x(), {0.0, 0.0, 1.0});
575}
576
577template <typename T>
578inline void Quat<T>::getRotate(T& angle, Vec3<T>& vec) const noexcept
579{
580 T x;
581 T y;
582 T z;
583 getRotate(angle, x, y, z);
584 vec.setX(x);
585 vec.setY(y);
586 vec.setZ(z);
587}
588
589template <typename T>
590inline void Quat<T>::getRotate(T& angle, T& x, T& y, T& z) const noexcept
591{
592 T sinhalfangle = std::sqrt(v_[0] * v_[0] + v_[1] * v_[1] + v_[2] * v_[2]);
593
594 angle = 2.0 * std::atan2(sinhalfangle, v_[3]);
595
596 if constexpr (std::is_same_v<f32, T>)
597 {
598 angle = angle > pif ? angle - 2.0f * pif : angle < -pif ? angle + 2.0f * pif : angle;
599 }
600 else
601 {
602 angle = angle > pi ? angle - 2.0 * pi : angle < -pi ? angle + 2.0 * pi : angle;
603 }
604
605 if (sinhalfangle != 0.0)
606 {
607 x = v_[0] / sinhalfangle;
608 y = v_[1] / sinhalfangle;
609 z = v_[2] / sinhalfangle;
610 }
611 else
612 {
613 x = 0.0;
614 y = 0.0;
615 z = 1.0;
616 }
617}
618
619template <typename T>
621{
622 T sqw = v_[3] * v_[3];
623 T sqx = v_[0] * v_[0];
624 T sqy = v_[1] * v_[1];
625 T sqz = v_[2] * v_[2];
626
627 T yaw = atan2(2.0 * (v_[0] * v_[1] + v_[2] * v_[3]), (sqx - sqy - sqz + sqw));
628 T pitch = asin(-2.0 * (v_[0] * v_[2] - v_[1] * v_[3]) / (sqx + sqy + sqz + sqw));
629 T bank = atan2(2.0 * (v_[1] * v_[2] + v_[0] * v_[3]), (-sqx - sqy + sqz + sqw));
630
631 return {yaw, pitch, bank};
632}
633
634template <typename T>
635inline Vec3<T> Quat<T>::operator*(const Vec3<T>& v) const noexcept
636{
637 Vec3<T> uv;
638 Vec3<T> uuv;
639 Vec3<T> qvec(v_[0], v_[1], v_[2]);
640 uv = qvec ^ v;
641 uuv = qvec ^ uv;
642 uv *= 2.0 * v_[3];
643 uuv *= 2.0;
644 return v + uv + uuv;
645}
646
647template <typename T>
648inline void Quat<T>::slerp(T t, const Quat& from, const Quat& to) noexcept
649{
650 const double epsilon = 0.00001;
651 double omega;
652 double cosOmega;
653 double sinOmega;
654 double scaleFrom;
655 double scaleTo;
656
657 Quat quatTo(to);
658
659 cosOmega = from.v_[0] * to.v_[0] + from.v_[1] * to.v_[1] + from.v_[2] * to.v_[2] + from.v_[3] * to.v_[3];
660
661 if ((1.0 - cosOmega) > epsilon)
662 {
663 omega = acos(cosOmega);
664 sinOmega = sin(omega);
665 scaleFrom = sin((1.0 - t) * omega) / sinOmega;
666 scaleTo = sin(t * omega) / sinOmega;
667 }
668 else
669 {
670 scaleFrom = 1.0 - t;
671 scaleTo = t;
672 }
673
674 *this = (from * scaleFrom) + (quatTo * scaleTo);
675}
676
677} // namespace sen::util
678
679#endif // SEN_LIBS_UTIL_SRC_DR_QUAT_H
Quaternion. Represents the orientation of an object in space.
Definition quat.h:24
Quat & operator*=(T rhs) noexcept
Definition quat.h:311
f32 length() const noexcept
void slerp(f32 t, const Quat &from, const Quat &to) noexcept
bool operator==(const Quat &v) const noexcept
Definition quat.h:188
Quat operator/(const Quat &denom) const noexcept
Definition quat.h:363
f32 getX() const noexcept
void setX(f32 x) noexcept
Quat & operator-=(const Quat &rhs) noexcept
Definition quat.h:398
Vec3< f32 > asVec3() const noexcept
Quat operator+(const Quat &rhs) const noexcept
Definition quat.h:376
bool operator<(const Quat &v) const noexcept
Definition quat.h:200
void setW(f32 w) noexcept
Quat & operator/=(const Quat &denom) noexcept
Definition quat.h:369
bool operator!=(const Quat &v) const noexcept
Definition quat.h:194
Vec3< f32 > getRotateInEulerYPB() const noexcept
void getRotate(f32 &angle, f32 &x, f32 &y, f32 &z) const noexcept
f32 length2() const noexcept
f32 getZ() const noexcept
Quat operator-() const noexcept
Definition quat.h:408
void set(f32 x, f32 y, f32 z, f32 w) noexcept
void makeRotate(f32 angle, f32 x, f32 y, f32 z) noexcept
Quat() noexcept=default
void makeRotateFromEulerYPB(f32 yaw, f32 pitch, f32 bank) noexcept
f32 dot(const Quat< f32 > &other) const noexcept
void setY(f32 y) noexcept
f32 getY() const noexcept
void setZ(f32 z) noexcept
f32 getW() const noexcept
Quat & operator+=(const Quat &rhs) noexcept
Definition quat.h:382
Quat operator*(T rhs) const
Definition quat.h:305
bool zeroRotation() const noexcept
Handles all mathematical ops involving 3D Vectors.
Definition vec3.h:24
@ angle
Definition unit.h:35
Definition iterator_adapters.h:16
Quat< f64 > Quatd
Definition quat.h:149
constexpr f32 epsilon
Min value used to determine that an entity is not moving/accelerating.
Definition util/src/dr/constants.h:37
constexpr f32 pif
Definition util/src/dr/constants.h:19
Quat< f32 > Quatf
Definition quat.h:148
constexpr f64 pi
PI constant.
Definition util/src/dr/constants.h:18