Move Vectormath main
C++20 game and graphics mathematics
Loading...
Searching...
No Matches
Quat.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cmath>
5#include <optional>
6#include <type_traits>
7
9
10namespace mv::math
11{
12 template <typename T>
13 requires std::is_floating_point_v<T>
14 class Mat3;
15
16 template <typename T>
17 requires std::is_floating_point_v<T>
18 class alignas(sizeof(T) * 4U) Quat
19 {
20 public:
21 constexpr Quat() noexcept : Components_{T(0), T(0), T(0), T(1)}
22 {
23 }
24
25 constexpr Quat(T x, T y, T z, T w) noexcept : Components_{x, y, z, w}
26 {
27 }
28
29 [[nodiscard]] constexpr T X() const noexcept
30 {
31 return Components_[0];
32 }
33 [[nodiscard]] constexpr T Y() const noexcept
34 {
35 return Components_[1];
36 }
37 [[nodiscard]] constexpr T Z() const noexcept
38 {
39 return Components_[2];
40 }
41 [[nodiscard]] constexpr T W() const noexcept
42 {
43 return Components_[3];
44 }
45
46 [[nodiscard]] constexpr Quat operator+(const Quat& other) const noexcept
47 {
48 return Quat(X() + other.X(), Y() + other.Y(), Z() + other.Z(),
49 W() + other.W());
50 }
51
52 [[nodiscard]] constexpr Quat operator-(const Quat& other) const noexcept
53 {
54 return Quat(X() - other.X(), Y() - other.Y(), Z() - other.Z(),
55 W() - other.W());
56 }
57
58 [[nodiscard]] constexpr Quat operator*(T scalar) const noexcept
59 {
60 return Quat(X() * scalar, Y() * scalar, Z() * scalar, W() * scalar);
61 }
62
63 [[nodiscard]] constexpr Quat operator/(T scalar) const noexcept
64 {
65 return Quat(X() / scalar, Y() / scalar, Z() / scalar, W() / scalar);
66 }
67
69 {
70 return Quat(-X(), -Y(), -Z(), -W());
71 }
72
73 // Hamilton product; cross-checked against RTM 2.3.1 quat_mul (MIT,
74 // commit 745bd25673d93b46941eda55e0993327dbc12b53b).
75 [[nodiscard]] constexpr Quat operator*(const Quat& right) const noexcept
76 {
77 return Quat(W() * right.X() + X() * right.W() + Y() * right.Z() -
78 Z() * right.Y(),
79 W() * right.Y() - X() * right.Z() + Y() * right.W() +
80 Z() * right.X(),
81 W() * right.Z() + X() * right.Y() - Y() * right.X() +
82 Z() * right.W(),
83 W() * right.W() - X() * right.X() - Y() * right.Y() -
84 Z() * right.Z());
85 }
86
87 [[nodiscard]] friend constexpr bool operator==(
88 const Quat&, const Quat&) noexcept = default;
89
90 private:
91 T Components_[4];
92
93 template <typename U>
94 requires std::is_floating_point_v<U>
95 friend class Mat3;
96 };
97
100
101 template <typename T>
102 [[nodiscard]] constexpr T Dot(const Quat<T>& left,
103 const Quat<T>& right) noexcept
104 {
105 return left.X() * right.X() + left.Y() * right.Y() +
106 left.Z() * right.Z() + left.W() * right.W();
107 }
108
109 template <typename T>
110 [[nodiscard]] constexpr T LengthSquared(const Quat<T>& value) noexcept
111 {
112 return Dot(value, value);
113 }
114
115 template <typename T>
116 [[nodiscard]] inline T Length(const Quat<T>& value) noexcept
117 {
118 return std::sqrt(LengthSquared(value));
119 }
120
121 template <typename T>
122 [[nodiscard]] inline std::optional<Quat<T>> TryNormalize(
123 const Quat<T>& value) noexcept
124 {
125 const T maximum = std::max({std::abs(value.X()), std::abs(value.Y()),
126 std::abs(value.Z()), std::abs(value.W())});
127 if (!(maximum > T(0)) || !std::isfinite(maximum))
128 {
129 return std::nullopt;
130 }
131 const Quat<T> scaled = value / maximum;
133 if (!(lengthSquared > T(0)) || !std::isfinite(lengthSquared))
134 {
135 return std::nullopt;
136 }
137 const T reciprocalLength = T(1) / std::sqrt(lengthSquared);
139 return std::isfinite(result.X()) && std::isfinite(result.Y()) &&
140 std::isfinite(result.Z()) && std::isfinite(result.W())
141 ? std::optional<Quat<T>>(result)
142 : std::nullopt;
143 }
144
145 template <typename T>
146 [[nodiscard]] inline std::optional<Quat<T>> TryInverse(
147 const Quat<T>& value) noexcept
148 {
149 const T maximum = std::max({std::abs(value.X()), std::abs(value.Y()),
150 std::abs(value.Z()), std::abs(value.W())});
151 if (!(maximum > T(0)) || !std::isfinite(maximum))
152 {
153 return std::nullopt;
154 }
155
156 // Scaling before the squared norm follows LAPACK's overflow-avoiding
157 // xLASSQ pattern; dividing by maximum last also preserves invertible
158 // subnormal and near-maximum finite quaternions.
159 const Quat<T> scaled = value / maximum;
161 if (!(scaledLengthSquared > T(0)) ||
162 !std::isfinite(scaledLengthSquared))
163 {
164 return std::nullopt;
165 }
166 const Quat<T> result =
167 Quat<T>(-scaled.X(), -scaled.Y(), -scaled.Z(), scaled.W()) /
169 return std::isfinite(result.X()) && std::isfinite(result.Y()) &&
170 std::isfinite(result.Z()) && std::isfinite(result.W())
171 ? std::optional<Quat<T>>(result)
172 : std::nullopt;
173 }
174
175 template <typename T>
176 [[nodiscard]] inline bool IsNearlyEqual(
177 const Quat<T>& left,
178 const Quat<T>& right,
180 {
181 return IsNearlyEqual(left.X(), right.X(), tolerance) &&
182 IsNearlyEqual(left.Y(), right.Y(), tolerance) &&
183 IsNearlyEqual(left.Z(), right.Z(), tolerance) &&
185 }
186} // namespace mv::math
187
188static_assert(sizeof(mv::math::Quatf) == 16);
189static_assert(alignof(mv::math::Quatf) == 16);
190static_assert(std::is_trivially_copyable_v<mv::math::Quatf>);
191static_assert(std::is_standard_layout_v<mv::math::Quatf>);
constexpr T Z() const noexcept
Definition Quat.hpp:37
constexpr Quat operator-() const noexcept
Definition Quat.hpp:68
constexpr Quat() noexcept
Definition Quat.hpp:21
constexpr T X() const noexcept
Definition Quat.hpp:29
constexpr Quat operator*(const Quat &right) const noexcept
Definition Quat.hpp:75
friend constexpr bool operator==(const Quat &, const Quat &) noexcept=default
constexpr Quat(T x, T y, T z, T w) noexcept
Definition Quat.hpp:25
constexpr T Y() const noexcept
Definition Quat.hpp:33
constexpr Quat operator/(T scalar) const noexcept
Definition Quat.hpp:63
constexpr Quat operator*(T scalar) const noexcept
Definition Quat.hpp:58
constexpr T W() const noexcept
Definition Quat.hpp:41
constexpr Quat operator+(const Quat &other) const noexcept
Definition Quat.hpp:46
constexpr Quat operator-(const Quat &other) const noexcept
Definition Quat.hpp:52
constexpr T Dot(const Quat< T > &left, const Quat< T > &right) noexcept
Definition Quat.hpp:102
std::optional< Quat< T > > TryInverse(const Quat< T > &value) noexcept
Definition Quat.hpp:146
bool IsNearlyEqual(const Quat< T > &left, const Quat< T > &right, Tolerance< T > tolerance=Tolerance< T >::Default()) noexcept
Definition Quat.hpp:176
constexpr T LengthSquared(const Quat< T > &value) noexcept
Definition Quat.hpp:110
T Length(const Quat< T > &value) noexcept
Definition Quat.hpp:116
Quat< float > Quatf
Definition Quat.hpp:98
std::optional< Quat< T > > TryNormalize(const Quat< T > &value) noexcept
Definition Quat.hpp:122