MathLib
Loading...
Searching...
No Matches
Quaternion.hpp
Go to the documentation of this file.
1#pragma once
2
5#include <Math/math/Angle.hpp>
6#include <cassert>
7#include <cmath>
8#include <concepts>
9#include <type_traits>
10
19namespace Math
20{
21
30template <class T>
32{
33public:
37 using value_type = T;
39
43 constexpr BasicQuaternion() = default;
44 constexpr BasicQuaternion(T real_number) : _w(real_number) { }
45 constexpr BasicQuaternion(T w, T i, T j, T k) : _w(w), _i(i), _j(j), _k(k) { }
47
52 constexpr static BasicQuaternion<T> identity() { return BasicQuaternion{ T{1}, T{}, T{}, T{} }; }
53
55 constexpr static BasicQuaternion<T> zero() { return BasicQuaternion{}; }
56 constexpr static BasicQuaternion<T> unit_real() { return identity(); }
57 constexpr static BasicQuaternion<T> unit_i() { return BasicQuaternion{ T{}, T{1}, T{}, T{} }; }
58 constexpr static BasicQuaternion<T> unit_j() { return BasicQuaternion{ T{}, T{}, T{1}, T{} }; }
59 constexpr static BasicQuaternion<T> unit_k() { return BasicQuaternion{ T{}, T{}, T{}, T{1} }; }
61
62 constexpr BasicQuaternion<T> conjugate() const
63 {
64 if constexpr (std::is_floating_point_v<T>)
65 return BasicQuaternion<T>{ _w, -_i, -_j, -_k };
66 else
67 return BasicQuaternion<T>{ _w, conjugate(_i), conjugate(_j), conjugate(_k) };
68 }
69
71 BasicQuaternion<T> pow(T exponent) const
72 {
73 assert( isUnit() );
74
75 T magnitude{ imaginary().magnitude() };
76
77 // Are we a purely real number?
78 if ( approximately_equal_to(magnitude, T{0.0}) )
79 return BasicQuaternion{ std::pow( real(), exponent ) }; // Yes, so only compute the real part
80
81 // Calculate the angle
82 T theta{ std::atan2(magnitude, w()) };
83 T new_theta{ exponent * theta };
84 T coefficient{ std::sin(new_theta) / magnitude };
85
86 // NOW we can calculate to the power of "exponent"...
87 T temp{ w() * w() + magnitude * magnitude };
88
89 return BasicQuaternion<T>{ std::cos(new_theta),
90 coefficient * i(),
91 coefficient * j(),
92 coefficient * k() } * std::pow(temp, exponent);
93 }
94
103 {
104 T e_to_the_w{ std::exp( w() ) };
105 T vector_part_magnitude{ imaginary().magnitude() };
106 T cos_v{ std::cos(vector_part_magnitude) };
107 T sin_v{ (vector_part_magnitude > T{0.0}) ? std::sin(vector_part_magnitude) / vector_part_magnitude : T{0.0} };
108
109 return BasicQuaternion{ cos_v,
110 sin_v * i(),
111 sin_v * j(),
112 sin_v * k() } * e_to_the_w;
113 }
114
121 {
122 T magnitude_of_imaginary_part{ imaginary().magnitude() };
123
124 // Are we purely a real number?
125 if ( approximately_equal_to(magnitude_of_imaginary_part, T{0.0}) )
126 return BasicQuaternion{ std::log( w() ) }; // YES, so just set the w() component (the others will be zero)
127
128 T this_norm{ norm() };
129 T theta{ std::acos( w() / this_norm ) };
130 T coefficient{ theta / magnitude_of_imaginary_part };
131
132 return BasicQuaternion{ std::log(this_norm),
133 coefficient * i(),
134 coefficient * j(),
135 coefficient * k() };
136 }
137
138 T normSquared() const { return accumulate(*this * conjugate()); }
139 T norm() const { return std::sqrt( normSquared() ); }
140
141 T magnitudeSquared() const { return normSquared(); }
142 T magnitude() const { return norm(); }
143
145 {
146 assert( BasicQuaternion<T>{*this / magnitude()}.isUnit() ); // Verify that we actually return a unit-length BasicQuaternion
147
148 return *this / this->magnitude();
149 }
150
152
154 {
155 return T{2.0} * std::atan2( imaginary().magnitude(), w() );
156 }
157
158 constexpr BasicVector3D<T> axis() const
159 {
160 return imaginary().normalized();
161 }
162
166 constexpr const T &w() const { return _w; }
167 constexpr const T &real() const { return _w; }
168
169 constexpr const T &i() const { return _i; }
170 constexpr const T &j() const { return _j; }
171 constexpr const T &k() const { return _k; }
172
174 constexpr BasicVector3D<T> imaginary() const { return { _i, _j, _k }; }
176
177 bool isUnit() const { return approximately_equal_to( magnitude(), T{1} ); }
178 bool isUnit(T tolerance) const { return approximately_equal_to( magnitude(), T{1}, tolerance ); }
179
180 bool isZero() const { return approximately_equal_to( magnitude(), T{} ); }
181 bool isZero(T tolerance) const { return approximately_equal_to( magnitude(), T{}, tolerance ); }
182
183 // Checks if the real() part is 0
184 bool isPure() const { return approximately_equal_to(real(), T{}); }
185
189 bool isNaN() const
190 {
191 if constexpr ( std::is_floating_point_v<T> )
192 return std::isnan(_w) || std::isnan(_i) || std::isnan(_j) || std::isnan(_k);
193 else
194 return _w.isNaN() || _i.isNaN() || _j.isNaN() || _k.isNaN();
195 }
196 bool isInf() const
197 {
198 if constexpr ( std::is_floating_point_v<T> )
199 return std::isinf(_w) || std::isinf(_i) || std::isinf(_j) || std::isinf(_k);
200 else
201 return _w.isInf() || _i.isInf() || _j.isInf() || _k.isInf();
202 }
204
216 constexpr static BasicQuaternion<T> make_pure(T x, T y, T z) { return BasicQuaternion<T>{ T{}, x, y, z }; }
217
226 constexpr static BasicQuaternion<T> make_pure(const BasicVector3D<T> &t) { return BasicQuaternion<T>{ T{}, t.x, t.y, t.z }; }
227
238 constexpr static BasicQuaternion<T> encode_point(T x, T y, T z) { return make_pure(x, y, z); }
239
250 constexpr static BasicQuaternion<T> encode_point(const BasicVector3D<T> &point) { return make_pure(point); }
251
261 constexpr static BasicQuaternion<T> make_rotation(const BasicRadian<T> &radians, T axis_x, T axis_y, T axis_z)
262 {
263 return make_rotation(radians, { axis_x, axis_y, axis_z });
264 }
265
273 constexpr static BasicQuaternion<T> make_rotation(const BasicRadian<T> &radians, const BasicVector3D<T> &axis)
274 {
275 T half_angle = radians.value() * T{0.5};
276 T cos_theta = cos( half_angle );
277 T sin_theta = sin( half_angle );
279
280 return BasicQuaternion<T>{ cos_theta,
281 sin_theta * n.x,
282 sin_theta * n.y,
283 sin_theta * n.z };
284 }
286
287private:
288 T _w{};
289 T _i{};
290 T _j{};
291 T _k{};
292
307 friend constexpr bool operator ==(const BasicQuaternion<T> &left, const BasicQuaternion<T> &right)
308 {
309 return approximately_equal_to(left, right);
310 }
311
322 template <std::floating_point OT = T>
323 requires std::is_floating_point_v<T>
324 friend constexpr bool approximately_equal_to(const BasicQuaternion<T> &value_to_test,
325 const BasicQuaternion<T> &value_it_should_be,
326 OT tolerance = OT{0.0002})
327 {
328 return approximately_equal_to(value_to_test.w(), value_it_should_be.w(), tolerance) &&
329 approximately_equal_to(value_to_test.i(), value_it_should_be.i(), tolerance) &&
330 approximately_equal_to(value_to_test.j(), value_it_should_be.j(), tolerance) &&
331 approximately_equal_to(value_to_test.k(), value_it_should_be.k(), tolerance);
332 }
334
345 friend constexpr BasicQuaternion<T> operator *(const BasicQuaternion<T> &left, const BasicQuaternion<T> &right)
346 {
347 return { left.w() * right.w() - (left.i() * right.i() +
348 left.j() * right.j() +
349 left.k() * right.k()),
350 left.w() * right.i() +
351 left.i() * right.w() +
352 left.j() * right.k() -
353 left.k() * right.j(),
354
355 left.w() * right.j() -
356 left.i() * right.k() +
357 left.j() * right.w() +
358 left.k() * right.i(),
359
360 left.w() * right.k() +
361 left.i() * right.j() -
362 left.j() * right.i() +
363 left.k() * right.w()
364 };
365 }
366
367 template <std::floating_point OT>
368 friend constexpr BasicQuaternion<T> operator *(const BasicQuaternion<T> &quaternion, OT scalar)
369 {
370 return { quaternion.w() * scalar,
371 quaternion.i() * scalar,
372 quaternion.j() * scalar,
373 quaternion.k() * scalar };
374 }
375
376 friend constexpr BasicQuaternion<T> operator /(const BasicQuaternion<T> &left, const BasicQuaternion<T> &right)
377 {
378 return left * right.inverse();
379 }
380
381 template <std::floating_point OT>
382 friend constexpr BasicQuaternion<T> operator /(const BasicQuaternion<T> &quaternion, OT scalar)
383 {
384 return { quaternion.w() / T(scalar),
385 quaternion.i() / T(scalar),
386 quaternion.j() / T(scalar),
387 quaternion.k() / T(scalar) };
388 }
389
390 friend constexpr BasicQuaternion<T> operator +(const BasicQuaternion<T> &left, const BasicQuaternion<T> &right)
391 {
392 return { left.w() + right.w(),
393 left.i() + right.i(),
394 left.j() + right.j(),
395 left.k() + right.k() };
396 }
397
398 friend constexpr BasicQuaternion<T> operator -(const BasicQuaternion<T> &left, const BasicQuaternion<T> &right)
399 {
400 return { left.w() - right.w(),
401 left.i() - right.i(),
402 left.j() - right.j(),
403 left.k() - right.k() };
404 }
405
407 {
408 return { -q.w(), -q.i(), -q.j(), -q.k() };
409 }
412
430 template <std::floating_point OT = T>
431 requires std::is_floating_point_v<T>
432 friend bool check_if_equal(const BasicQuaternion<T> &input,
433 const BasicQuaternion<T> &near_to,
434 OT tolerance = OT{0.0002})
435 {
436 if (!approximately_equal_to(input, near_to, tolerance))
437 {
438 auto diff{ near_to - input };
439
440 std::cout << std::format("input: {} is not equal to near_to: {} within tolerance: {}. Difference is {} .",
441 format(input),
442 format(near_to),
443 tolerance,
444 format(near_to - input))
445 << std::endl;
446 return false;
447 }
448 return true;
449 }
450
459 template <std::floating_point OT = T>
460 requires std::is_floating_point_v<T>
461 friend bool check_if_not_equal(const BasicQuaternion<T> &input,
462 const BasicQuaternion<T> &near_to,
463 OT tolerance = OT{0.0002})
464 {
465 if (approximately_equal_to(input, near_to, tolerance))
466 {
467 auto diff{ near_to - input };
468
469 std::cout << std::format("input: {} is equal to near_to: {} within tolerance: {}. Difference is {} .",
470 format(input),
471 format(near_to),
472 tolerance,
473 format(near_to - input))
474 << std::endl;
475 return false;
476 }
477 return true;
478 }
481
499 template <std::floating_point OT = T>
500 requires std::is_floating_point_v<T>
501 friend void CHECK_IF_EQUAL(const BasicQuaternion<T> &input,
502 const BasicQuaternion<T> &near_to,
503 OT tolerance = OT{0.0002})
504 {
505 assert( check_if_equal(input, near_to, tolerance) );
506 }
507
516 template <std::floating_point OT = T>
517 requires std::is_floating_point_v<T>
518 friend void CHECK_IF_NOT_EQUAL(const BasicQuaternion<T> &input,
519 const BasicQuaternion<T> &near_to,
520 OT tolerance = OT{0.0002})
521 {
522 assert( check_if_not_equal(input, near_to, tolerance) );
523 }
524
532 template <std::floating_point OT = T>
533 requires std::is_floating_point_v<T>
534 friend void CHECK_IF_ZERO(const BasicQuaternion<T> &input, OT tolerance = OT{0.0002})
535 {
536 assert( check_if_equal(input, BasicQuaternion<T>::zero(), tolerance));
537 }
540
553 friend constexpr T dot(const BasicQuaternion<T> &left, const BasicQuaternion<T> &right)
554 {
555 return left.w() * right.w() +
556 left.i() * right.i() +
557 left.j() * right.j() +
558 left.k() * right.k();
559 }
560
561 friend constexpr T dot_normalized(const BasicQuaternion<T> &left, const BasicQuaternion<T> &right)
562 {
563 return ( left.w() * right.w() +
564 left.i() * right.i() +
565 left.j() * right.j() +
566 left.k() * right.k()) / (left.magnitude() * right.magnitude()
567 );
568 }
569
586 const BasicQuaternion<T> &encoded_point)
587 {
588 assert( rotation.isUnit() );
589 assert( encoded_point.isPure() );
590
591 return rotation * encoded_point * rotation.conjugate();
592 }
593
599 const BasicQuaternion<T> &encoded_point)
600 {
601 return passively_rotate_encoded_point(rotation, encoded_point);
602 }
603
620 const BasicQuaternion<T> &encoded_point)
621 {
622 assert( rotation.isUnit() );
623 assert( encoded_point.isPure() );
624
625 return rotation.conjugate() * encoded_point * rotation;
626 }
627
633 const BasicQuaternion<T> &encoded_point)
634 {
635 return actively_rotate_encoded_point(rotation, encoded_point);
636 }
637
643 friend constexpr BasicQuaternion<T> compose_rotations(const BasicQuaternion<T> &rotation_1,
644 const BasicQuaternion<T> &rotation_2)
645 {
646 return rotation_2 * rotation_1;
647 }
648
655 friend constexpr BasicQuaternion<T> normalized(const BasicQuaternion<T> &input)
656 {
657 return input.normalized();
658 }
659
664 friend constexpr T arg(const BasicQuaternion<T> &input)
665 {
666 return input.angle().value();
667 }
668
675 {
676 assert( axis.magnitude() == T{1} );
677
679 }
680
687 friend constexpr T accumulate(const BasicQuaternion<T> &input)
688 {
689 return T{input.real() + input.i() + input.j() + input.k()};
690 }
691
698 friend constexpr BasicQuaternion<T> slerp(const BasicQuaternion<T> &begin,
699 const BasicQuaternion<T> &end,
700 T percent)
701 {
702 BasicQuaternion<T> combined{ begin.conjugate() * end };
703
704 return begin * combined.pow(percent);
705 }
706
707 friend std::string format(const BasicQuaternion<T> &input)
708 {
709 return std::format("[w: {}, i: {}, j: {}, k: {}]", input.w(), input.i(), input.j(), input.k());
710 }
711
716 friend constexpr BasicQuaternion<T> conjugate(const BasicQuaternion<T> &input)
717 {
718 return input.conjugate();
719 }
720
725 friend constexpr BasicQuaternion<T> log(const BasicQuaternion<T> &input)
726 {
727 return input.log();
728 }
729
735 {
736 return input.exp();
737 }
739};
740
741
760
761}
friend BasicQuaternion< T > exp(const BasicQuaternion< T > &input)
constexpr BasicQuaternion()=default
constexpr const T & i() const
friend constexpr BasicQuaternion< T > locally_rotate_encoded_point(const BasicQuaternion< T > &rotation, const BasicQuaternion< T > &encoded_point)
static constexpr BasicQuaternion< T > encode_point(T x, T y, T z)
BasicRadian< T > angle() const
BasicQuaternion< T > exp() const
friend constexpr BasicQuaternion< T > conjugate(const BasicQuaternion< T > &input)
friend constexpr bool operator==(const BasicQuaternion< T > &left, const BasicQuaternion< T > &right)
bool isZero(T tolerance) const
constexpr const T & j() const
constexpr BasicVector3D< T > axis() const
constexpr BasicVector3D< T > imaginary() const
Extracts the imaginary part of a BasicQuaternion as a BasicVector3D.
constexpr BasicQuaternion(T real_number)
Constructs a BasicQuaternion equivalent to the given real number.
friend constexpr T dot(const BasicQuaternion< T > &left, const BasicQuaternion< T > &right)
friend constexpr BasicQuaternion< T > log(const BasicQuaternion< T > &input)
static constexpr BasicQuaternion< T > unit_j()
friend constexpr BasicQuaternion< T > actively_rotate_encoded_point(const BasicQuaternion< T > &rotation, const BasicQuaternion< T > &encoded_point)
static constexpr BasicQuaternion< T > make_rotation(const BasicRadian< T > &radians, const BasicVector3D< T > &axis)
static constexpr BasicQuaternion< T > unit_i()
static constexpr BasicQuaternion< T > unit_k()
friend constexpr BasicQuaternion< T > globally_rotate_encoded_point(const BasicQuaternion< T > &rotation, const BasicQuaternion< T > &encoded_point)
constexpr const T & k() const
static constexpr BasicQuaternion< T > unit_real()
constexpr BasicQuaternion< T > conjugate() const
static constexpr BasicQuaternion< T > identity()
BasicQuaternion representation of the real number 1.
constexpr BasicQuaternion< T > normalized() const
friend constexpr T dot_normalized(const BasicQuaternion< T > &left, const BasicQuaternion< T > &right)
friend constexpr BasicQuaternion< T > passively_rotate_encoded_point(const BasicQuaternion< T > &rotation, const BasicQuaternion< T > &encoded_point)
friend constexpr BasicQuaternion< T > normalized(const BasicQuaternion< T > &input)
BasicQuaternion< T > pow(T exponent) const
Computes this BasicQuaternion raised to a real power.
constexpr const T & w() const
static constexpr BasicQuaternion< T > make_pure(T x, T y, T z)
static constexpr BasicQuaternion< T > encode_point(const BasicVector3D< T > &point)
static constexpr BasicQuaternion< T > make_rotation(const BasicRadian< T > &radians, T axis_x, T axis_y, T axis_z)
constexpr const T & real() const
friend constexpr bool approximately_equal_to(const BasicQuaternion< T > &value_to_test, const BasicQuaternion< T > &value_it_should_be, OT tolerance=OT{0.0002})
static constexpr BasicQuaternion< T > zero()
BasicQuaternion representation of the real number 0.
friend constexpr BasicQuaternion< T > slerp(const BasicQuaternion< T > &begin, const BasicQuaternion< T > &end, T percent)
friend constexpr BasicQuaternion< T > compose_rotations(const BasicQuaternion< T > &rotation_1, const BasicQuaternion< T > &rotation_2)
BasicQuaternion< T > inverse() const
friend std::string format(const BasicQuaternion< T > &input)
BasicQuaternion< T > log() const
bool isUnit(T tolerance) const
friend constexpr BasicQuaternion< T > polar(const BasicVector3D< T > &axis, const BasicRadian< T > angle=BasicRadian< T >{})
static constexpr BasicQuaternion< T > make_pure(const BasicVector3D< T > &t)
constexpr BasicQuaternion(T w, T i, T j, T k)
friend constexpr T arg(const BasicQuaternion< T > &input)
friend constexpr T accumulate(const BasicQuaternion< T > &input)
T value_type
The underlying implementation type.
T value() const
Definition Angle.hpp:53
friend void CHECK_IF_NOT_EQUAL(const BasicQuaternion< T > &input, const BasicQuaternion< T > &near_to, OT tolerance=OT{0.0002})
friend void CHECK_IF_ZERO(const BasicQuaternion< T > &input, OT tolerance=OT{0.0002})
friend void CHECK_IF_EQUAL(const BasicQuaternion< T > &input, const BasicQuaternion< T > &near_to, OT tolerance=OT{0.0002})
constexpr BasicVector3D< Type > normalized() const
Definition Vector3D.hpp:494
friend bool check_if_equal(const BasicQuaternion< T > &input, const BasicQuaternion< T > &near_to, OT tolerance=OT{0.0002})
friend bool check_if_not_equal(const BasicQuaternion< T > &input, const BasicQuaternion< T > &near_to, OT tolerance=OT{0.0002})
friend constexpr BasicQuaternion< T > operator*(const BasicQuaternion< T > &left, const BasicQuaternion< T > &right)
friend constexpr BasicQuaternion< T > operator/(const BasicQuaternion< T > &left, const BasicQuaternion< T > &right)
friend constexpr BasicQuaternion< T > operator+(const BasicQuaternion< T > &left, const BasicQuaternion< T > &right)
friend constexpr BasicQuaternion< T > operator-(const BasicQuaternion< T > &left, const BasicQuaternion< T > &right)
Definition Angle.hpp:16