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 matrix_type_for<Other>& matrix) noexcept;
68
69 template<floating_point Other = value_type>
70 [[nodiscard]] static constexpr auto wxyz(Other w, Other x, Other y, Other z) noexcept -> basic_quaternion {
71 return basic_quaternion{x, y, z, w};
72 }
73
74 [[nodiscard]] static constexpr auto normalized(const basic_quaternion& quaternion) noexcept -> basic_quaternion {
75 const auto length = quaternion.length();
76
77 if(length <= static_cast<value_type>(0)) {
78 return basic_quaternion{static_cast<value_type>(0), static_cast<value_type>(0), static_cast<value_type>(0), static_cast<value_type>(1)};
79 }
80
81 const auto one_over_length = static_cast<value_type>(1) / length;
82 return basic_quaternion{quaternion.x() * one_over_length, quaternion.y() * one_over_length, quaternion.z() * one_over_length, quaternion.w() * one_over_length};
83 }
84
85 [[nodiscard]] static constexpr auto dot(const basic_quaternion& lhs, const basic_quaternion& rhs) noexcept -> value_type {
86 return lhs.x() * rhs.x() + lhs.y() * rhs.y() + lhs.z() * rhs.z() + lhs.w() * rhs.w();
87 }
88
89 [[nodiscard]] static constexpr auto lerp(const basic_quaternion& start, const basic_quaternion& end, const value_type t) noexcept -> basic_quaternion {
90 return start * (1.0f - t) + end * t;
91 }
92
102 [[nodiscard]] static constexpr auto slerp(const basic_quaternion& x, basic_quaternion y, const value_type a) noexcept -> basic_quaternion {
103 utility::assert_that(a >= 0.0f && a <= 1.0f, "Interpolation factor out of bounds in quaternion slerp");
104
105 auto z = y;
106
107 auto cos_theta = dot(x, y);
108
109 // If cos_theta < 0, the interpolation will take the long way around the sphere.
110 // To fix this, one quat must be negated.
111 if(cos_theta < static_cast<value_type>(0)) {
112 z = -y;
113 cos_theta = -cos_theta;
114 }
115
116 // Perform a linear interpolation when cos_theta is close to 1 to avoid side effect of sin(angle) becoming a zero denominator
117 if(cos_theta > static_cast<value_type>(1) - math::epsilon_v<value_type>) {
118 // Linear interpolation
119 return basic_quaternion::wxyz(
120 math::mix(x.w(), z.w(), a),
121 math::mix(x.x(), z.x(), a),
122 math::mix(x.y(), z.y(), a),
123 math::mix(x.z(), z.z(), a)
124 );
125 } else {
126 // Essential Mathematics, page 467
127 const auto angle = std::acos(cos_theta);
128 return (std::sin((static_cast<value_type>(1) - a) * angle) * x + std::sin(a * angle) * z) / std::sin(angle);
129 }
130 }
131
132 [[nodiscard]] constexpr operator matrix_type() const noexcept;
133
134 [[nodiscard]] constexpr auto to_matrix() const noexcept -> matrix_type;
135
136 template<floating_point Other = value_type>
137 constexpr auto operator+=(const basic_quaternion<Other>& other) noexcept -> basic_quaternion&;
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*=(Other value) noexcept -> basic_quaternion&;
144
145 template<floating_point Other = value_type>
146 constexpr auto operator*=(const basic_quaternion<Other>& other) noexcept -> basic_quaternion&;
147
148 template<floating_point Other = value_type>
149 constexpr auto operator/=(Other value) noexcept -> basic_quaternion&;
150
151 [[nodiscard]] constexpr auto x() noexcept -> reference;
152
153 [[nodiscard]] constexpr auto x() const noexcept -> const_reference;
154
155 [[nodiscard]] constexpr auto y() noexcept -> reference;
156
157 [[nodiscard]] constexpr auto y() const noexcept -> const_reference;
158
159 [[nodiscard]] constexpr auto z() noexcept -> reference;
160
161 [[nodiscard]] constexpr auto z() const noexcept -> const_reference;
162
163 [[nodiscard]] constexpr auto w() noexcept -> reference;
164
165 [[nodiscard]] constexpr auto w() const noexcept -> const_reference;
166
167 [[nodiscard]] constexpr auto complex() noexcept -> vector_type&;
168
169 [[nodiscard]] constexpr auto complex() const noexcept -> const vector_type&;
170
171 [[nodiscard]] constexpr auto scalar() noexcept -> reference;
172
173 [[nodiscard]] constexpr auto scalar() const noexcept -> const_reference;
174
175 [[nodiscard]] constexpr auto length_squared() const noexcept -> length_type;
176
177 [[nodiscard]] constexpr auto length() const noexcept -> length_type;
178
179 constexpr auto normalize() noexcept -> basic_quaternion&;
180
181private:
182
183 vector_type _complex;
184 value_type _scalar;
185
186}; // class basic_quaternion
187
188template<floating_point Lhs, floating_point Rhs>
189[[nodiscard]] constexpr auto operator==(const basic_quaternion<Lhs>& lhs, const basic_quaternion<Rhs>& rhs) noexcept -> bool;
190
191template<floating_point Lhs, floating_point Rhs>
192[[nodiscard]] constexpr auto operator+(basic_quaternion<Lhs> lhs, const basic_quaternion<Rhs>& rhs) noexcept -> basic_quaternion<Lhs>;
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 Type>
198[[nodiscard]] constexpr auto operator-(basic_quaternion<Type> quaternion) noexcept -> basic_quaternion<Type>;
199
200template<floating_point Lhs, floating_point Rhs>
201[[nodiscard]] constexpr auto operator*(basic_quaternion<Lhs> lhs, Rhs rhs) noexcept -> basic_quaternion<Lhs>;
202
203template<floating_point Lhs, floating_point Rhs>
204[[nodiscard]] constexpr auto operator*(Lhs lhs, basic_quaternion<Rhs> rhs) noexcept -> basic_quaternion<Rhs>;
205
206template<floating_point Lhs, floating_point Rhs>
207[[nodiscard]] constexpr auto operator*(basic_quaternion<Lhs> lhs, const basic_quaternion<Rhs>& rhs) noexcept -> basic_quaternion<Lhs>;
208
209template<floating_point Lhs, floating_point Rhs>
210[[nodiscard]] constexpr auto operator/(basic_quaternion<Lhs> lhs, Rhs rhs) noexcept -> basic_quaternion<Lhs>;
211
213using quaternionf = basic_quaternion<std::float_t>;
214
216using quaternion = quaternionf;
217
218} // namespace sbx::math
219
220template<sbx::math::floating_point Type>
221struct std::hash<sbx::math::basic_quaternion<Type>> {
222
223 auto operator()(const sbx::math::basic_quaternion<Type>& quaternion) const noexcept -> std::size_t;
224
225}; // struct std::hash
226
227template<sbx::math::floating_point Type>
228struct YAML::convert<sbx::math::basic_quaternion<Type>> {
229
230 static auto encode(const sbx::math::basic_quaternion<Type>& quaternion) -> YAML::Node;
231
232 static auto decode(const YAML::Node& node, sbx::math::basic_quaternion<Type>& quaternion) -> bool;
233
234}; // struct YAML::convert
235
236template<sbx::math::floating_point Type>
237struct fmt::formatter<sbx::math::basic_quaternion<Type>> {
238
239 template<typename ParseContext>
240 constexpr auto parse(ParseContext& context) -> decltype(context.begin());
241
242 template<typename FormatContext>
243 auto format(const sbx::math::basic_quaternion<Type>& quaternion, FormatContext& context) -> decltype(context.out());
244
245}; // struct fmt::formatter
246
247#include <libsbx/math/quaternion.ipp>
248
249#endif // LIBSBX_MATH_QUATERNION_HPP_
Definition: tests.cpp:5
Definition: angle.hpp:271
Definition: matrix4x4.hpp:25
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:102
Definition: vector3.hpp:22