sandbox
Loading...
Searching...
No Matches
quaternion.hpp
1#ifndef LIBSBX_MATH_QUATERNION_HPP_
2#define LIBSBX_MATH_QUATERNION_HPP_
3
4#include <cstddef>
5#include <concepts>
6#include <cmath>
7#include <type_traits>
8
9#include <yaml-cpp/yaml.h>
10
11#include <fmt/format.h>
12
13#include <libsbx/math/concepts.hpp>
14#include <libsbx/math/constants.hpp>
15#include <libsbx/math/algorithm.hpp>
16#include <libsbx/math/vector3.hpp>
17#include <libsbx/math/vector4.hpp>
18#include <libsbx/math/matrix4x4.hpp>
19#include <libsbx/math/angle.hpp>
20
21namespace sbx::math {
22
23template<floating_point Type>
25
26 template<floating_point Other>
28
29 template<floating_point Other>
31
32 template<floating_point Other>
34
35 template<floating_point Other>
37
38public:
39
40 using value_type = Type;
41 using reference = value_type&;
42 using const_reference = const value_type&;
43 using size_type = std::size_t;
44 using length_type = std::float_t;
48
49 inline static constexpr basic_quaternion identity{vector_type::zero, value_type{1}};
50
51 template<floating_point Other = value_type>
52 constexpr basic_quaternion(Other value = Other{0}) noexcept;
53
54 template<floating_point Complex = value_type, floating_point Scalar = value_type>
55 constexpr basic_quaternion(const vector_type_for<Complex>& complex, Scalar scalar) noexcept;
56
57 template<floating_point Other = value_type>
58 constexpr basic_quaternion(const vector_type_for<Other>& euler_angles) noexcept;
59
60 template<floating_point Other = value_type>
61 constexpr basic_quaternion(Other x, Other y, Other z, Other w) noexcept;
62
63 template<floating_point Complex = value_type, floating_point Scalar = value_type>
64 constexpr basic_quaternion(const vector_type_for<Complex>& axis, const basic_angle<Scalar>& angle) noexcept;
65
66 template<floating_point Other = value_type>
67 constexpr basic_quaternion(const basic_matrix4x4<Other>& matrix) noexcept;
68
69 template<floating_point Other = value_type>
70 constexpr basic_quaternion(const basic_matrix3x3<Other>& matrix) noexcept;
71
72 template<floating_point Other = value_type>
73 [[nodiscard]] static constexpr auto wxyz(Other w, Other x, Other y, Other z) noexcept -> basic_quaternion {
74 return basic_quaternion{x, y, z, w};
75 }
76
77 [[nodiscard]] static constexpr auto conjugate(const basic_quaternion& quaternion) noexcept -> basic_quaternion {
78 return basic_quaternion{-quaternion.complex(), quaternion.scalar()};
79 }
80
81 [[nodiscard]] static constexpr auto normalized(const basic_quaternion& quaternion) noexcept -> basic_quaternion {
82 const auto length = quaternion.length();
83
84 if(length <= static_cast<value_type>(0)) {
85 return basic_quaternion{static_cast<value_type>(0), static_cast<value_type>(0), static_cast<value_type>(0), static_cast<value_type>(1)};
86 }
87
88 const auto one_over_length = static_cast<value_type>(1) / length;
89 return basic_quaternion{quaternion.x() * one_over_length, quaternion.y() * one_over_length, quaternion.z() * one_over_length, quaternion.w() * one_over_length};
90 }
91
92 [[nodiscard]] static constexpr auto dot(const basic_quaternion& lhs, const basic_quaternion& rhs) noexcept -> value_type {
93 return lhs.x() * rhs.x() + lhs.y() * rhs.y() + lhs.z() * rhs.z() + lhs.w() * rhs.w();
94 }
95
96 [[nodiscard]] static constexpr auto lerp(const basic_quaternion& start, const basic_quaternion& end, const value_type t) noexcept -> basic_quaternion {
97 return start * (1.0f - t) + end * t;
98 }
99
109 [[nodiscard]] static constexpr auto slerp(const basic_quaternion& x, basic_quaternion y, const value_type a) noexcept -> basic_quaternion {
110 utility::assert_that(a >= 0.0f && a <= 1.0f, "Interpolation factor out of bounds in quaternion slerp");
111
112 auto z = y;
113
114 auto cos_theta = dot(x, y);
115
116 // If cos_theta < 0, the interpolation will take the long way around the sphere.
117 // To fix this, one quat must be negated.
118 if(cos_theta < static_cast<value_type>(0)) {
119 z = -y;
120 cos_theta = -cos_theta;
121 }
122
123 // Perform a linear interpolation when cos_theta is close to 1 to avoid side effect of sin(angle) becoming a zero denominator
124 if(cos_theta > static_cast<value_type>(1) - math::epsilon_v<value_type>) {
125 // Linear interpolation
126 return basic_quaternion::wxyz(
127 math::mix(x.w(), z.w(), a),
128 math::mix(x.x(), z.x(), a),
129 math::mix(x.y(), z.y(), a),
130 math::mix(x.z(), z.z(), a)
131 );
132 } else {
133 // Essential Mathematics, page 467
134 const auto angle = std::acos(cos_theta);
135 return (std::sin((static_cast<value_type>(1) - a) * angle) * x + std::sin(a * angle) * z) / std::sin(angle);
136 }
137 }
138
139 template<floating_point Other = value_type>
140 constexpr auto operator+=(const basic_quaternion<Other>& other) noexcept -> basic_quaternion&;
141
142 template<floating_point Other = value_type>
143 constexpr auto operator-=(const basic_quaternion<Other>& other) noexcept -> basic_quaternion&;
144
145 template<floating_point Other = value_type>
146 constexpr auto operator*=(Other value) noexcept -> basic_quaternion&;
147
148 template<floating_point Other = value_type>
149 constexpr auto operator*=(const basic_quaternion<Other>& other) noexcept -> basic_quaternion&;
150
151 template<floating_point Other = value_type>
152 constexpr auto operator/=(Other value) noexcept -> basic_quaternion&;
153
154 [[nodiscard]] constexpr auto x() noexcept -> reference;
155
156 [[nodiscard]] constexpr auto x() const noexcept -> const_reference;
157
158 [[nodiscard]] constexpr auto y() noexcept -> reference;
159
160 [[nodiscard]] constexpr auto y() const noexcept -> const_reference;
161
162 [[nodiscard]] constexpr auto z() noexcept -> reference;
163
164 [[nodiscard]] constexpr auto z() const noexcept -> const_reference;
165
166 [[nodiscard]] constexpr auto w() noexcept -> reference;
167
168 [[nodiscard]] constexpr auto w() const noexcept -> const_reference;
169
170 [[nodiscard]] constexpr auto complex() noexcept -> vector_type&;
171
172 [[nodiscard]] constexpr auto complex() const noexcept -> const vector_type&;
173
174 [[nodiscard]] constexpr auto scalar() noexcept -> reference;
175
176 [[nodiscard]] constexpr auto scalar() const noexcept -> const_reference;
177
178 [[nodiscard]] constexpr auto length_squared() const noexcept -> length_type;
179
180 [[nodiscard]] constexpr auto length() const noexcept -> length_type;
181
182 constexpr auto normalize() noexcept -> basic_quaternion&;
183
184private:
185
186 vector_type _complex;
187 value_type _scalar;
188
189}; // class basic_quaternion
190
191template<floating_point Lhs, floating_point Rhs>
192[[nodiscard]] constexpr auto operator==(const basic_quaternion<Lhs>& lhs, const basic_quaternion<Rhs>& rhs) noexcept -> bool;
193
194template<floating_point Lhs, floating_point Rhs>
195[[nodiscard]] constexpr auto operator+(basic_quaternion<Lhs> lhs, const basic_quaternion<Rhs>& rhs) noexcept -> basic_quaternion<Lhs>;
196
197template<floating_point Lhs, floating_point Rhs>
198[[nodiscard]] constexpr auto operator-(basic_quaternion<Lhs> lhs, const basic_quaternion<Rhs>& rhs) noexcept -> basic_quaternion<Lhs>;
199
200template<floating_point Type>
201[[nodiscard]] constexpr auto operator-(basic_quaternion<Type> quaternion) noexcept -> basic_quaternion<Type>;
202
203template<floating_point Lhs, floating_point Rhs>
204[[nodiscard]] constexpr auto operator*(basic_quaternion<Lhs> lhs, Rhs rhs) noexcept -> basic_quaternion<Lhs>;
205
206template<floating_point Lhs, floating_point Rhs>
207[[nodiscard]] constexpr auto operator*(Lhs lhs, basic_quaternion<Rhs> rhs) noexcept -> basic_quaternion<Rhs>;
208
209template<floating_point Lhs, floating_point Rhs>
210[[nodiscard]] constexpr auto operator*(basic_quaternion<Lhs> lhs, const basic_quaternion<Rhs>& rhs) noexcept -> basic_quaternion<Lhs>;
211
212template<floating_point Lhs, floating_point Rhs>
213[[nodiscard]] constexpr auto operator/(basic_quaternion<Lhs> lhs, Rhs rhs) noexcept -> basic_quaternion<Lhs>;
214
216using quaternionf = basic_quaternion<std::float_t>;
217
219using quaternion = quaternionf;
220
221} // namespace sbx::math
222
223template<sbx::math::floating_point Type>
224struct std::hash<sbx::math::basic_quaternion<Type>> {
225
226 auto operator()(const sbx::math::basic_quaternion<Type>& quaternion) const noexcept -> std::size_t;
227
228}; // struct std::hash
229
230template<sbx::math::floating_point Type>
231struct YAML::convert<sbx::math::basic_quaternion<Type>> {
232
233 static auto encode(const sbx::math::basic_quaternion<Type>& quaternion) -> YAML::Node;
234
235 static auto decode(const YAML::Node& node, sbx::math::basic_quaternion<Type>& quaternion) -> bool;
236
237}; // struct YAML::convert
238
239template<sbx::math::floating_point Type>
240auto operator<<(YAML::Emitter& out, const sbx::math::basic_quaternion<Type>& quaternion) -> YAML::Emitter& {
241 return out << YAML::convert<sbx::math::basic_quaternion<Type>>::encode(quaternion);
242}
243
244template<sbx::math::floating_point Type>
245struct fmt::formatter<sbx::math::basic_quaternion<Type>> {
246
247 template<typename ParseContext>
248 constexpr auto parse(ParseContext& context) -> decltype(context.begin());
249
250 template<typename FormatContext>
251 auto format(const sbx::math::basic_quaternion<Type>& quaternion, FormatContext& context) -> decltype(context.out());
252
253}; // struct fmt::formatter
254
255#include <libsbx/math/quaternion.ipp>
256
257#endif // LIBSBX_MATH_QUATERNION_HPP_
Definition: tests.cpp:5
Definition: angle.hpp:279
Definition: matrix3x3.hpp:25
Definition: matrix4x4.hpp:26
Definition: quaternion.hpp:24
static constexpr auto slerp(const basic_quaternion &x, basic_quaternion y, const value_type a) noexcept -> basic_quaternion
Spherical linear interpolation between two quaternions.
Definition: quaternion.hpp:109
Definition: vector3.hpp:22