Move Vectormath main
C++20 game and graphics mathematics
Loading...
Searching...
No Matches
Mat3.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cassert>
4#include <cmath>
5#include <cstddef>
6#include <optional>
7#include <type_traits>
8
10#include <mv/math/Vec3.hpp>
11#include <mv/math/detail/Matrix3Ops.hpp>
12
13namespace mv::math
14{
15 template <typename T>
16 requires std::is_floating_point_v<T>
17 class Mat3
18 {
19 public:
20 using Component = T;
22
24 Rows_{RowVector(T(1), T(0), T(0)), RowVector(T(0), T(1), T(0)),
25 RowVector(T(0), T(0), T(1))}
26 {
27 }
28
33
35 T m12,
36 T m13,
37 T m21,
38 T m22,
39 T m23,
40 T m31,
41 T m32,
42 T m33) noexcept :
46 {
47 }
48
50 {
51 return Mat3();
52 }
53
55 {
56 return Mat3(RowVector(), RowVector(), RowVector());
57 }
58
59 [[nodiscard]] static Mat3 Scale(RowVector scale) noexcept
60 {
61 return Mat3(scale.X(), T(0), T(0), T(0), scale.Y(), T(0), T(0),
62 T(0), scale.Z());
63 }
64
66 const Rotation3<T>& rotation) noexcept
67 {
68 const Quat<T>& quaternion = rotation.Quaternion();
69 using Ops = detail::Matrix3Ops<T>;
70 if constexpr (Ops::HasNativeFromQuaternion)
71 {
72 const typename Ops::Rows rows =
73 Ops::FromQuaternion(quaternion.Components_);
74 return Mat3(RowVector(rows.Row0), RowVector(rows.Row1),
75 RowVector(rows.Row2));
76 }
77
78 const T x = quaternion.X();
79 const T y = quaternion.Y();
80 const T z = quaternion.Z();
81 const T w = quaternion.W();
82 const T xx = x * x;
83 const T yy = y * y;
84 const T zz = z * z;
85 const T xy = x * y;
86 const T xz = x * z;
87 const T yz = y * z;
88 const T xw = x * w;
89 const T yw = y * w;
90 const T zw = z * w;
91
92 // Quaternion-to-matrix equation follows Szeliski,
93 // MSR-TR-2004-92 eq. 22, transposed for Move's row vectors;
94 // cross-checked against RTM 2.3.1 matrix_from_quat (MIT).
95 return Mat3(
96 T(1) - T(2) * (yy + zz), T(2) * (xy + zw), T(2) * (xz - yw),
97 T(2) * (xy - zw), T(1) - T(2) * (xx + zz), T(2) * (yz + xw),
98 T(2) * (xz + yw), T(2) * (yz - xw), T(1) - T(2) * (xx + yy));
99 }
100
101 [[nodiscard]] const RowVector& Row(std::size_t index) const noexcept
102 {
103 assert(index < 3U);
104 return Rows_[index];
105 }
106
107 [[nodiscard]] RowVector Column(std::size_t index) const noexcept
108 {
109 assert(index < 3U);
110 if (index == 0U)
111 {
112 return RowVector(Rows_[0].X(), Rows_[1].X(), Rows_[2].X());
113 }
114 if (index == 1U)
115 {
116 return RowVector(Rows_[0].Y(), Rows_[1].Y(), Rows_[2].Y());
117 }
118 return RowVector(Rows_[0].Z(), Rows_[1].Z(), Rows_[2].Z());
119 }
120
121 [[nodiscard]] T Element(std::size_t row,
122 std::size_t column) const noexcept
123 {
124 const RowVector& value = Row(row);
125 assert(column < 3U);
126 if (column == 0U)
127 {
128 return value.X();
129 }
130 if (column == 1U)
131 {
132 return value.Y();
133 }
134 return value.Z();
135 }
136
138 {
139 return Mat3(Column(0U), Column(1U), Column(2U));
140 }
141
143 {
144 const T a = Rows_[0].X();
145 const T b = Rows_[0].Y();
146 const T c = Rows_[0].Z();
147 const T d = Rows_[1].X();
148 const T e = Rows_[1].Y();
149 const T f = Rows_[1].Z();
150 const T g = Rows_[2].X();
151 const T h = Rows_[2].Y();
152 const T i = Rows_[2].Z();
153 return a * (e * i - f * h) - b * (d * i - f * g) +
154 c * (d * h - e * g);
155 }
156
157 [[nodiscard]] std::optional<Mat3> TryInverse() const noexcept
158 {
159 using Ops = detail::Matrix3Ops<T>;
160 if constexpr (Ops::HasNativeInverse)
161 {
162 // RTM's unchecked inverse produces non-finite lanes for an
163 // exact singular input; validating its result preserves this
164 // API's failure contract without computing the determinant a
165 // second time in the facade.
166 const typename Ops::Rows inverse = Ops::InverseUnchecked(
167 Rows_[0].Native_, Rows_[1].Native_, Rows_[2].Native_);
168 if (!Ops::IsFinite(inverse))
169 {
170 return std::nullopt;
171 }
172 return Mat3(RowVector(inverse.Row0), RowVector(inverse.Row1),
173 RowVector(inverse.Row2));
174 }
175
176 const T a = Rows_[0].X();
177 const T b = Rows_[0].Y();
178 const T c = Rows_[0].Z();
179 const T d = Rows_[1].X();
180 const T e = Rows_[1].Y();
181 const T f = Rows_[1].Z();
182 const T g = Rows_[2].X();
183 const T h = Rows_[2].Y();
184 const T i = Rows_[2].Z();
185
186 const T cofactor00 = e * i - f * h;
187 const T cofactor01 = f * g - d * i;
188 const T cofactor02 = d * h - e * g;
189 const T determinant =
191 if (determinant == T(0) || !std::isfinite(determinant))
192 {
193 return std::nullopt;
194 }
195
196 // Adjugate/cofactor formula independently derived and
197 // cross-checked against RTM 2.3.1 matrix_inverse (MIT, commit
198 // 745bd25673d93b46941eda55e0993327dbc12b53b).
201 const T inverse01 = (c * h - b * i) * reciprocalDeterminant;
202 const T inverse02 = (b * f - c * e) * reciprocalDeterminant;
204 const T inverse11 = (a * i - c * g) * reciprocalDeterminant;
205 const T inverse12 = (c * d - a * f) * reciprocalDeterminant;
207 const T inverse21 = (b * g - a * h) * reciprocalDeterminant;
208 const T inverse22 = (a * e - b * d) * reciprocalDeterminant;
209
210 const bool finite =
211 std::isfinite(inverse00) && std::isfinite(inverse01) &&
212 std::isfinite(inverse02) && std::isfinite(inverse10) &&
213 std::isfinite(inverse11) && std::isfinite(inverse12) &&
214 std::isfinite(inverse20) && std::isfinite(inverse21) &&
215 std::isfinite(inverse22);
216 if (!finite)
217 {
218 return std::nullopt;
219 }
220
223 }
224
226 {
227 // Bitwise conjunction keeps this validation branchless on common
228 // optimizing compilers while still checking every component.
229 return std::isfinite(Rows_[0].X()) && std::isfinite(Rows_[0].Y()) &&
230 std::isfinite(Rows_[0].Z()) && std::isfinite(Rows_[1].X()) &&
231 std::isfinite(Rows_[1].Y()) && std::isfinite(Rows_[1].Z()) &&
232 std::isfinite(Rows_[2].X()) && std::isfinite(Rows_[2].Y()) &&
233 std::isfinite(Rows_[2].Z());
234 }
235
237 const RowVector& vector) const noexcept
238 {
239 return Rows_[0] * vector.X() + Rows_[1] * vector.Y() +
240 Rows_[2] * vector.Z();
241 }
242
243 [[nodiscard]] Mat3 operator*(const Mat3& right) const noexcept
244 {
245 // Row-vector composition matches RTM 2.3.1 matrix_mul (MIT,
246 // commit 745bd25673d93b46941eda55e0993327dbc12b53b).
247 return Mat3(right.TransformVector(Rows_[0]),
248 right.TransformVector(Rows_[1]),
249 right.TransformVector(Rows_[2]));
250 }
251
252 [[nodiscard]] friend bool operator==(const Mat3& left,
253 const Mat3& right) noexcept
254 {
255 return left.Rows_[0] == right.Rows_[0] &&
256 left.Rows_[1] == right.Rows_[1] &&
257 left.Rows_[2] == right.Rows_[2];
258 }
259
260 private:
261 RowVector Rows_[3];
262 };
263
264 template <typename T>
265 requires std::is_floating_point_v<T>
267 const Mat3<T>& matrix) noexcept
268 {
269 // Public value * matrix spelling intentionally preserves Move's
270 // row-vector convention and RTM's matrix_mul_vector3 semantics.
271 return matrix.TransformVector(vector);
272 }
273
276} // namespace mv::math
277
278static_assert(sizeof(mv::math::Mat3f) == 48);
279static_assert(alignof(mv::math::Mat3f) == 16);
280static_assert(std::is_trivially_copyable_v<mv::math::Mat3f>);
281static_assert(std::is_standard_layout_v<mv::math::Mat3f>);
Mat3(RowVector row0, RowVector row1, RowVector row2) noexcept
Definition Mat3.hpp:29
bool IsFinite() const noexcept
Definition Mat3.hpp:225
static Mat3 Identity() noexcept
Definition Mat3.hpp:49
Mat3 Transposed() const noexcept
Definition Mat3.hpp:137
Vec3< T > RowVector
Definition Mat3.hpp:21
Mat3(T m11, T m12, T m13, T m21, T m22, T m23, T m31, T m32, T m33) noexcept
Definition Mat3.hpp:34
std::optional< Mat3 > TryInverse() const noexcept
Definition Mat3.hpp:157
friend bool operator==(const Mat3 &left, const Mat3 &right) noexcept
Definition Mat3.hpp:252
RowVector TransformVector(const RowVector &vector) const noexcept
Definition Mat3.hpp:236
static Mat3 Scale(RowVector scale) noexcept
Definition Mat3.hpp:59
static Mat3 Zero() noexcept
Definition Mat3.hpp:54
RowVector Column(std::size_t index) const noexcept
Definition Mat3.hpp:107
Mat3() noexcept
Definition Mat3.hpp:23
T Element(std::size_t row, std::size_t column) const noexcept
Definition Mat3.hpp:121
T Determinant() const noexcept
Definition Mat3.hpp:142
Mat3 operator*(const Mat3 &right) const noexcept
Definition Mat3.hpp:243
const RowVector & Row(std::size_t index) const noexcept
Definition Mat3.hpp:101
static Mat3 FromRotation(const Rotation3< T > &rotation) noexcept
Definition Mat3.hpp:65
Vec3< T > operator*(const Vec3< T > &vector, const Mat3< T > &matrix) noexcept
Definition Mat3.hpp:266
Mat3< float > Mat3f
Definition Mat3.hpp:274