41#ifndef UFO_NUMERIC_MAT_HPP
42#define UFO_NUMERIC_MAT_HPP
45#include <ufo/numeric/vec.hpp>
72template <std::
size_t Rows, std::
size_t Cols,
class T>
86concept Matrix =
requires(T
const& t) {
87 []<std::size_t R, std::size_t C,
class U>(Mat<R, C, U> const&) {}(t);
112template <std::
size_t Rows, std::
size_t Cols,
class T>
114 using value_type = T;
115 using size_type = std::size_t;
119 std::array<row_type, Rows> fields{};
122 constexpr Mat() noexcept = default;
129 template <
std::convertible_to<T>... Args>
130 requires(sizeof...(Args) == Rows * Cols)
131 explicit(sizeof...(Args) == 1) constexpr
Mat(Args... args) noexcept
133 auto elements = std::array<T, Rows * Cols>{
static_cast<T
>(args)...};
134 for (std::size_t r = 0; r < Rows; ++r) {
135 for (std::size_t c = 0; c < Cols; ++c) {
136 fields[r][c] = elements[r * Cols + c];
145 [[nodiscard]]
static consteval Mat zeros() noexcept {
return {}; }
152 requires(Rows == Cols)
155 for (std::size_t i = 0; i < Rows; ++i) {
165 [[nodiscard]]
static consteval Mat ones() noexcept
168 for (
auto&
row : m.fields) {
169 std::ranges::fill(
row.fields, T(1));
179 template <std::convertible_to<row_type>... Rs>
180 requires(
sizeof...(Rs) == Rows)
181 constexpr Mat(Rs&&... rs) noexcept
182 : fields{
static_cast<row_type>(std::forward<Rs>(rs))...}
196 std::ranges::transform(other.fields, fields.begin(),
197 [](
auto const&
row) { return row_type(row); });
206 template <std::
size_t R2, std::
size_t C2>
207 requires(R2 != Rows || C2 != Cols)
211 auto const max_r = std::min(Rows, R2);
212 auto const max_c = std::min(Cols, C2);
213 for (std::size_t r = 0; r < max_r; ++r) {
214 for (std::size_t c = 0; c < max_c; ++c) {
215 fields[r][c] = other.fields[r][c];
232 constexpr auto&
operator[](
this auto& self, size_type r, size_type c)
noexcept
234 return self.fields[r][c];
242 constexpr auto&
operator[](
this auto& self, size_type i)
noexcept
244 return self.fields[i];
254 constexpr auto&
at(
this auto& self, size_type r, size_type c)
256 if (r >= Rows || c >= Cols) [[unlikely]] {
257 throw std::out_of_range(
"Matrix index out of bounds");
259 return self.fields[r][c];
267 [[nodiscard]]
constexpr row_type const&
row(size_type r)
const noexcept
280 for (std::size_t r = 0; r < Rows; ++r) {
281 result[r] = fields[r][c];
290 [[nodiscard]]
constexpr auto begin(
this auto& self)
noexcept
292 return self.fields.begin();
299 [[nodiscard]]
constexpr auto end(
this auto& self)
noexcept {
return self.fields.end(); }
305 [[nodiscard]]
constexpr auto data(
this auto& self)
noexcept
307 return self.fields.data();
314 [[nodiscard]]
static constexpr std::size_t
rows() noexcept {
return Rows; }
320 [[nodiscard]]
static constexpr std::size_t
cols() noexcept {
return Cols; }
326 [[nodiscard]]
static constexpr std::size_t
size() noexcept {
return Rows * Cols; }
345 [[nodiscard]]
constexpr T
trace() const noexcept
346 requires(Rows == Cols)
349 for (std::size_t i = 0; i < Rows; ++i) {
350 result += fields[i][i];
362 for (
auto const&
row : fields) {
363 for (
auto const& elem :
row.fields) {
367 return std::sqrt(
sum);
375 [[nodiscard]]
constexpr bool isNearZero(T epsilon = std::numeric_limits<T>::epsilon() *
376 T(100)) const noexcept
377 requires
std::floating_point<T>
404 T epsilon = std::numeric_limits<T>::epsilon() * T(1000)) const noexcept
408 if (std::abs(det) < epsilon) {
418 [[nodiscard]]
constexpr auto flat_view(
this auto& self)
noexcept
420 return std::span{self.data()->data(), Rows * Cols};
432 template <std::
size_t R2>
433 [[nodiscard]]
constexpr auto block(std::size_t start_row,
434 std::size_t start_col)
const noexcept
436 requires(R2 <= Rows && R2 <= Cols)
439 for (std::size_t r = 0; r < R2; ++r) {
440 for (std::size_t c = 0; c < R2; ++c) {
441 result[r][c] = fields[start_row + r][start_col + c];
453 template <std::predicate<T> Pred>
454 [[nodiscard]]
constexpr bool all_of(Pred&& pred)
const noexcept
456 return std::ranges::all_of(
flat_view(), std::forward<Pred>(pred));
465 template <std::predicate<T> Pred>
466 [[nodiscard]]
constexpr bool any_of(Pred&& pred)
const noexcept
468 return std::ranges::any_of(
flat_view(), std::forward<Pred>(pred));
477 template <
class UnaryOp>
481 std::ranges::transform(
flat_view(), result.flat_view().begin(),
482 std::forward<UnaryOp>(op));
496 [[nodiscard]]
static constexpr bool is_row_major() noexcept {
return true; }
513 T epsilon = std::numeric_limits<T>::epsilon()) const noexcept
516 for (std::size_t r = 0; r < Rows; ++r) {
517 for (std::size_t c = 0; c < Cols; ++c) {
518 if (r != c && std::abs(fields[r][c]) > epsilon) {
532 T epsilon = std::numeric_limits<T>::epsilon()) const noexcept
535 for (std::size_t r = 0; r < Rows; ++r) {
536 for (std::size_t c = r + 1; c < Cols; ++c) {
537 if (std::abs(fields[r][c] - fields[c][r]) > epsilon) {
552 T epsilon = std::numeric_limits<T>::epsilon()) const noexcept
555 for (std::size_t r = 1; r < Rows; ++r) {
556 for (std::size_t c = 0; c < r; ++c) {
557 if (std::abs(fields[r][c]) > epsilon) {
572 T epsilon = std::numeric_limits<T>::epsilon()) const noexcept
575 for (std::size_t r = 0; r < Rows - 1; ++r) {
576 for (std::size_t c = r + 1; c < Cols; ++c) {
577 if (std::abs(fields[r][c]) > epsilon) {
591 [[nodiscard]]
constexpr std::size_t
rank(T epsilon = std::numeric_limits<T>::epsilon() *
592 T(1000)) const noexcept
593 requires
std::floating_point<T>
596 std::size_t
rank = 0;
598 for (std::size_t r = 0; r < std::min(Rows, Cols); ++r) {
600 std::size_t pivot_row = r;
601 for (std::size_t i = r + 1; i < Rows; ++i) {
602 if (std::abs(temp[i][r]) > std::abs(temp[pivot_row][r])) {
607 if (std::abs(temp[pivot_row][r]) <= epsilon)
continue;
609 if (pivot_row != r) {
610 std::ranges::swap(temp[r], temp[pivot_row]);
616 for (std::size_t i = r + 1; i < Rows; ++i) {
617 if (std::abs(temp[i][r]) <= epsilon)
continue;
619 T factor = temp[i][r] / temp[r][r];
620 for (std::size_t j = r; j < Cols; ++j) {
621 temp[i][j] -= factor * temp[r][j];
648 std::ranges::transform(fields, result.fields.begin(), std::negate{});
665 std::ranges::transform(fields, rhs.fields, fields.begin(), std::plus{});
676 std::ranges::transform(fields, rhs.fields, fields.begin(), std::minus{});
686 requires(Rows == Cols)
705 for (
auto&
row : fields)
row += rhs;
716 for (
auto&
row : fields)
row -= rhs;
727 for (
auto&
row : fields)
row *= rhs;
738 for (
auto&
row : fields)
row /= rhs;
750template <
class T =
float>
751using Mat2 = Mat<2, 2, T>;
752template <
class T =
float>
753using Mat3 = Mat<3, 3, T>;
754template <
class T =
float>
755using Mat4 = Mat<4, 4, T>;
758using Mat2f = Mat2<float>;
759using Mat2d = Mat2<double>;
760using Mat3f = Mat3<float>;
761using Mat3d = Mat3<double>;
762using Mat4f = Mat4<float>;
763using Mat4d = Mat4<double>;
766using Mat2x2f = Mat2f;
767using Mat2x2d = Mat2d;
768using Mat3x3f = Mat3f;
769using Mat3x3d = Mat3d;
770using Mat4x4f = Mat4f;
771using Mat4x4d = Mat4d;
788template <std::
size_t Rows, std::
size_t Cols,
class T>
805template <std::
size_t Rows, std::
size_t Cols,
class T>
828template <std::
size_t Rows, std::
size_t Cols,
class T>
845template <std::
size_t Rows, std::
size_t Cols,
class T>
862template <std::
size_t Rows, std::
size_t Cols,
class T>
879template <std::
size_t Rows, std::
size_t Cols,
class T>
896template <std::
size_t Rows, std::
size_t Cols,
class T>
913template <std::
size_t Rows, std::
size_t Cols,
class T>
918 std::ranges::transform(rhs.fields, result.fields.begin(),
919 [lhs](
auto const& row) { return lhs - row; });
932template <std::
size_t Rows, std::
size_t Cols,
class T>
949template <std::
size_t Rows, std::
size_t Cols,
class T>
954 std::ranges::transform(rhs.fields, result.fields.begin(),
955 [lhs](
auto const& row) { return lhs / row; });
974template <std::
size_t Rows, std::
size_t Cols,
class T>
979 for (std::size_t r = 0; r < Rows; ++r) {
980 result[r] =
dot(m.fields[r], v);
994template <std::
size_t Rows, std::
size_t Cols,
class T>
999 for (std::size_t c = 0; c < Cols; ++c) {
1000 for (std::size_t r = 0; r < Rows; ++r) {
1001 result[c] += v[r] * m.fields[r][c];
1026template <std::
size_t Rows, std::
size_t Shared, std::
size_t Cols,
class T>
1031 for (std::size_t r = 0; r < Rows; ++r) {
1032 for (std::size_t c = 0; c < Cols; ++c) {
1033 for (std::size_t k = 0; k < Shared; ++k) {
1034 result.fields[r][c] +=
a.fields[r][k] *
b.fields[k][c];
1055template <SquareMatrix M>
1058 using T =
typename M::value_type;
1059 constexpr auto N = M::rows();
1061 if constexpr (2 == N) {
1062 return m[0][0] * m[1][1] - m[0][1] * m[1][0];
1063 }
else if constexpr (3 == N) {
1064 return m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) -
1065 m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) +
1066 m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
1067 }
else if constexpr (4 == N) {
1069 T
const A2323 = m[2][2] * m[3][3] - m[2][3] * m[3][2];
1070 T
const A1323 = m[2][1] * m[3][3] - m[2][3] * m[3][1];
1071 T
const A1223 = m[2][1] * m[3][2] - m[2][2] * m[3][1];
1072 T
const A0323 = m[2][0] * m[3][3] - m[2][3] * m[3][0];
1073 T
const A0223 = m[2][0] * m[3][2] - m[2][2] * m[3][0];
1074 T
const A0123 = m[2][0] * m[3][1] - m[2][1] * m[3][0];
1075 return m[0][0] * (m[1][1] * A2323 - m[1][2] * A1323 + m[1][3] * A1223) -
1076 m[0][1] * (m[1][0] * A2323 - m[1][2] * A0323 + m[1][3] * A0223) +
1077 m[0][2] * (m[1][0] * A1323 - m[1][1] * A0323 + m[1][3] * A0123) -
1078 m[0][3] * (m[1][0] * A1223 - m[1][1] * A0223 + m[1][2] * A0123);
1080 []<
bool flag =
false>() {
1081 static_assert(flag,
"determinant only implemented for 2x2, 3x3, and 4x4 matrices");
1096template <InvertibleMatrix M>
1097[[nodiscard]]
constexpr M
inverse(M
const& m)
noexcept
1099 using T =
typename M::value_type;
1100 constexpr auto N = M::rows();
1102 if constexpr (2 == N) {
1105 result[0][0] = m[1][1] * det_inv;
1106 result[0][1] = -m[0][1] * det_inv;
1107 result[1][0] = -m[1][0] * det_inv;
1108 result[1][1] = m[0][0] * det_inv;
1110 }
else if constexpr (3 == N) {
1113 result[0][0] = (m[1][1] * m[2][2] - m[1][2] * m[2][1]) * det_inv;
1114 result[0][1] = -(m[0][1] * m[2][2] - m[0][2] * m[2][1]) * det_inv;
1115 result[0][2] = (m[0][1] * m[1][2] - m[0][2] * m[1][1]) * det_inv;
1116 result[1][0] = -(m[1][0] * m[2][2] - m[1][2] * m[2][0]) * det_inv;
1117 result[1][1] = (m[0][0] * m[2][2] - m[0][2] * m[2][0]) * det_inv;
1118 result[1][2] = -(m[0][0] * m[1][2] - m[0][2] * m[1][0]) * det_inv;
1119 result[2][0] = (m[1][0] * m[2][1] - m[1][1] * m[2][0]) * det_inv;
1120 result[2][1] = -(m[0][0] * m[2][1] - m[0][1] * m[2][0]) * det_inv;
1121 result[2][2] = (m[0][0] * m[1][1] - m[0][1] * m[1][0]) * det_inv;
1123 }
else if constexpr (4 == N) {
1124 T
const A2323 = m[2][2] * m[3][3] - m[2][3] * m[3][2];
1125 T
const A1323 = m[2][1] * m[3][3] - m[2][3] * m[3][1];
1126 T
const A1223 = m[2][1] * m[3][2] - m[2][2] * m[3][1];
1127 T
const A0323 = m[2][0] * m[3][3] - m[2][3] * m[3][0];
1128 T
const A0223 = m[2][0] * m[3][2] - m[2][2] * m[3][0];
1129 T
const A0123 = m[2][0] * m[3][1] - m[2][1] * m[3][0];
1130 T
const A2313 = m[1][2] * m[3][3] - m[1][3] * m[3][2];
1131 T
const A1313 = m[1][1] * m[3][3] - m[1][3] * m[3][1];
1132 T
const A1213 = m[1][1] * m[3][2] - m[1][2] * m[3][1];
1133 T
const A2312 = m[1][2] * m[2][3] - m[1][3] * m[2][2];
1134 T
const A1312 = m[1][1] * m[2][3] - m[1][3] * m[2][1];
1135 T
const A1212 = m[1][1] * m[2][2] - m[1][2] * m[2][1];
1136 T
const A0313 = m[1][0] * m[3][3] - m[1][3] * m[3][0];
1137 T
const A0213 = m[1][0] * m[3][2] - m[1][2] * m[3][0];
1138 T
const A0312 = m[1][0] * m[2][3] - m[1][3] * m[2][0];
1139 T
const A0212 = m[1][0] * m[2][2] - m[1][2] * m[2][0];
1140 T
const A0113 = m[1][0] * m[3][1] - m[1][1] * m[3][0];
1141 T
const A0112 = m[1][0] * m[2][1] - m[1][1] * m[2][0];
1144 T(1) / (m[0][0] * (m[1][1] * A2323 - m[1][2] * A1323 + m[1][3] * A1223) -
1145 m[0][1] * (m[1][0] * A2323 - m[1][2] * A0323 + m[1][3] * A0223) +
1146 m[0][2] * (m[1][0] * A1323 - m[1][1] * A0323 + m[1][3] * A0123) -
1147 m[0][3] * (m[1][0] * A1223 - m[1][1] * A0223 + m[1][2] * A0123));
1150 result[0][0] = (m[1][1] * A2323 - m[1][2] * A1323 + m[1][3] * A1223) * det_inv;
1151 result[0][1] = -(m[0][1] * A2323 - m[0][2] * A1323 + m[0][3] * A1223) * det_inv;
1152 result[0][2] = (m[0][1] * A2313 - m[0][2] * A1313 + m[0][3] * A1213) * det_inv;
1153 result[0][3] = -(m[0][1] * A2312 - m[0][2] * A1312 + m[0][3] * A1212) * det_inv;
1154 result[1][0] = -(m[1][0] * A2323 - m[1][2] * A0323 + m[1][3] * A0223) * det_inv;
1155 result[1][1] = (m[0][0] * A2323 - m[0][2] * A0323 + m[0][3] * A0223) * det_inv;
1156 result[1][2] = -(m[0][0] * A2313 - m[0][2] * A0313 + m[0][3] * A0213) * det_inv;
1157 result[1][3] = (m[0][0] * A2312 - m[0][2] * A0312 + m[0][3] * A0212) * det_inv;
1158 result[2][0] = (m[1][0] * A1323 - m[1][1] * A0323 + m[1][3] * A0123) * det_inv;
1159 result[2][1] = -(m[0][0] * A1323 - m[0][1] * A0323 + m[0][3] * A0123) * det_inv;
1160 result[2][2] = (m[0][0] * A1313 - m[0][1] * A0313 + m[0][3] * A0113) * det_inv;
1161 result[2][3] = -(m[0][0] * A1312 - m[0][1] * A0312 + m[0][3] * A0112) * det_inv;
1162 result[3][0] = -(m[1][0] * A1223 - m[1][1] * A0223 + m[1][2] * A0123) * det_inv;
1163 result[3][1] = (m[0][0] * A1223 - m[0][1] * A0223 + m[0][2] * A0123) * det_inv;
1164 result[3][2] = -(m[0][0] * A1213 - m[0][1] * A0213 + m[0][2] * A0113) * det_inv;
1165 result[3][3] = (m[0][0] * A1212 - m[0][1] * A0212 + m[0][2] * A0112) * det_inv;
1168 []<
bool flag =
false>() {
1169 static_assert(flag,
"inverse only implemented for 2x2, 3x3, and 4x4 matrices");
1183template <std::
size_t Rows, std::
size_t Cols,
class T>
1187 for (std::size_t r = 0; r < Rows; ++r) {
1188 for (std::size_t c = 0; c < Cols; ++c) {
1189 result.fields[c][r] = m.fields[r][c];
1203template <std::
size_t R, std::
size_t C,
class T =
float>
1215template <std::
size_t N,
class T =
float>
1228template <std::
size_t R, std::
size_t C,
class T =
float>
1251template <std::
floating_po
int T>
1254 T
const a = m[0][0], d = m[0][1], f = m[0][2];
1255 T
const b = m[1][1], e = m[1][2];
1256 T
const c = m[2][2];
1259 a *
a +
b *
b + c * c -
a *
b -
a * c -
b * c + T(3) * (d * d + f * f + e * e);
1261 T
const x_2 = -(T(2) *
a -
b - c) * (T(2) *
b -
a - c) * (T(2) * c -
a -
b) +
1262 T(9) * ((T(2) * c -
a -
b) * (d * d) + (T(2) *
b -
a - c) * (f * f) +
1263 (T(2) *
a -
b - c) * (e * e)) -
1264 T(54) * (d * e * f);
1268 ? std::atan(std::sqrt(T(4) * x_1 * x_1 * x_1 - x_2 * x_2) / x_2)
1269 : (T(0) > x_2 ? std::atan(std::sqrt(T(4) * x_1 * x_1 * x_1 - x_2 * x_2) / x_2) +
1270 std::numbers::pi_v<T>
1271 : std::numbers::pi_v<T> / T(2));
1274 (
a +
b + c - T(2) * std::sqrt(x_1) * std::cos(phi / T(3))) / T(3),
1276 T(2) * std::sqrt(x_1) * std::cos((phi + std::numbers::pi_v<T>) / T(3))) /
1279 T(2) * std::sqrt(x_1) * std::cos((phi - std::numbers::pi_v<T>) / T(3))) /
1296template <std::
floating_po
int T>
1317template <std::
floating_po
int T>
1323 T
const d = m[0][1];
1324 T
const f = m[0][2] == T(0) ? std::numeric_limits<T>::epsilon() : m[0][2];
1325 T
const b = m[1][1], e = m[1][2];
1326 T
const c = m[2][2];
1328 T
const l_1 = eigen_values[0];
1329 T
const l_2 = eigen_values[1];
1330 T
const l_3 = eigen_values[2];
1332 T
const m_1 = (d * (c - l_1) - e * f) / (f * (
b - l_1) - d * e);
1333 T
const m_2 = (d * (c - l_2) - e * f) / (f * (
b - l_2) - d * e);
1334 T
const m_3 = (d * (c - l_3) - e * f) / (f * (
b - l_3) - d * e);
1358template <std::
floating_po
int T>
1362 m[0][0] = T(2) / (right - left);
1363 m[1][1] = T(2) / (top - bottom);
1365 m[0][3] = -(right + left) / (right - left);
1366 m[1][3] = -(top + bottom) / (top - bottom);
1384template <std::
floating_po
int T>
1390 m[0][0] = T(2) / (right - left);
1391 m[1][1] = T(2) / (top - bottom);
1392 m[2][2] = T(1) / (zFar - zNear);
1393 m[0][3] = -(right + left) / (right - left);
1394 m[1][3] = -(top + bottom) / (top - bottom);
1395 m[2][3] = -zNear / (zFar - zNear);
1411template <std::
floating_po
int T>
1416 T
const tan_half_fovy = std::tan(fovy / T(2));
1418 m[0][0] = T(1) / (aspect * tan_half_fovy);
1419 m[1][1] = T(1) / tan_half_fovy;
1420 m[2][2] = far / (far - near);
1422 m[2][3] = -(far * near) / (far - near);
1443template <std::
floating_po
int T>
1449 m[0][0] = T(2) * fx / width;
1450 m[1][1] = T(2) * fy / height;
1451 m[0][2] = (T(2) * cx / width) - T(1);
1452 m[1][2] = (T(2) * cy / height) - T(1);
1453 m[2][2] = far / (far - near);
1455 m[2][3] = -(far * near) / (far - near);
1470template <std::
floating_po
int T>
1489 m[0][3] = -
dot(s, eye);
1490 m[1][3] =
dot(u, eye);
1491 m[2][3] = -
dot(f, eye);
1508template <std::
floating_po
int T>
1512 T
const tan_half_fovy = std::tan(fovy / T(2));
1514 m[0][0] = T(1) / (aspect * tan_half_fovy);
1515 m[1][1] = T(1) / tan_half_fovy;
1534template <std::
floating_po
int T>
1537 T
const c = std::cos(
angle);
1538 T
const s = std::sin(
angle);
1545 rot[0][0] = c + temp[0] *
axis[0];
1546 rot[0][1] = temp[1] *
axis[0] - s *
axis[2];
1547 rot[0][2] = temp[2] *
axis[0] + s *
axis[1];
1548 rot[1][0] = temp[0] *
axis[1] + s *
axis[2];
1549 rot[1][1] = c + temp[1] *
axis[1];
1550 rot[1][2] = temp[2] *
axis[1] - s *
axis[0];
1551 rot[2][0] = temp[0] *
axis[2] - s *
axis[1];
1552 rot[2][1] = temp[1] *
axis[2] + s *
axis[0];
1553 rot[2][2] = c + temp[2] *
axis[2];
1557 for (std::size_t i = 0; i < 3; ++i) {
1558 for (std::size_t j = 0; j < 3; ++j) {
1559 for (std::size_t k = 0; k < 3; ++k) {
1560 result[i][j] += m[i][k] * rot[k][j];
1563 result[i][3] = m[i][3];
1584template <std::
size_t I, std::
size_t Rows, std::
size_t Cols,
class T>
1600template <std::
size_t I, std::
size_t Rows, std::
size_t Cols,
class T>
1616template <std::
size_t I, std::
size_t Rows, std::
size_t Cols,
class T>
1620 return std::move(m.fields[I]);
1639template <std::
size_t Rows, std::
size_t Cols,
class T>
1642 for (std::size_t r = 0; r < Rows; ++r) {
1643 if (r > 0) out <<
'\n';
1655template <std::
size_t Rows, std::
size_t Cols,
class T>
1656struct std::tuple_size<
ufo::Mat<Rows, Cols, T>>
1657 : std::integral_constant<std::size_t, Rows> {
1660template <std::
size_t I, std::
size_t Rows, std::
size_t Cols,
class T>
1661struct std::tuple_element<I,
ufo::Mat<Rows, Cols, T>> {
1665template <std::
size_t Rows, std::
size_t Cols, std::formattable<
char> T>
1666struct std::formatter<
ufo::Mat<Rows, Cols, T>> {
1667 constexpr auto parse(std::format_parse_context& ctx) {
return ctx.begin(); }
1671 auto out = ctx.out();
1672 for (std::size_t r = 0; r < Rows; ++r) {
1673 if (r > 0) out = std::format_to(out,
"\n");
1674 out = std::format_to(out,
"{}", m.fields[r]);
Concept satisfied by invertible matrices (square with floating-point elements).
Concept satisfied by any specialization of Mat<Rows, Cols, U>.
Concept satisfied by square matrix types (Rows == Cols).
constexpr T trace() const noexcept
Computes the trace (sum of diagonal elements) of a square matrix.
constexpr Mat & operator+=(T rhs) noexcept
Adds scalar rhs to every element.
constexpr auto & operator[](this auto &self, size_type r, size_type c) noexcept
Accesses the element at row r, column c.
constexpr Mat & operator+=(Mat const &rhs) noexcept
Adds rhs element-wise to this matrix.
T angle(Quat< T > const &q) noexcept
Extracts the rotation angle (in radians) from a unit quaternion.
constexpr Vec< Cols, T > & get(Mat< Rows, Cols, T > &m) noexcept
Structured-binding accessor — lvalue reference to row I.
constexpr auto determinant(M const &m) noexcept
Computes the determinant of a square matrix.
Mat< 4, 4, T > orthogonal(T left, T right, T bottom, T top)
Builds a right-handed 2-D orthographic projection matrix (no depth clipping).
constexpr T dot(Quat< T > const &a, Quat< T > const &b) noexcept
Computes the four-component dot product a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z.
constexpr Mat operator+() const noexcept
Unary identity operator.
constexpr Vec< 3, T > eigenValues(Mat< 3, 3, T > const &m) noexcept
Computes the eigenvalues of a real symmetric 3×3 matrix, sorted ascending.
All vision-related classes and functions.
constexpr T b(Lab< T, Flags > color) noexcept
Returns the un-weighted blue–yellow axis value.
Mat< 4, 4, T > perspective(T fovy, T aspect, T near, T far)
Builds a perspective projection matrix (OpenCV convention).
constexpr Mat< Rows, Cols, T > operator*(Mat< Rows, Cols, T > lhs, T rhs) noexcept
Multiplies every element of lhs by scalar rhs.
constexpr Mat< Rows, Cols, T > operator/(Mat< Rows, Cols, T > lhs, T rhs) noexcept
Divides every element of lhs by scalar rhs.
constexpr Mat< R, C, T > zeros() noexcept
Returns an R × C matrix with all elements set to zero.
constexpr T a(Lab< T, Flags > color) noexcept
Returns the un-weighted green–red axis value.
constexpr Mat< R, C, T > ones() noexcept
Returns an R × C matrix with all elements set to one.
Mat< 4, 4, T > infinitePerspective(T fovy, T aspect, T near)
Builds a right-handed perspective projection matrix with an infinite far plane.
constexpr std::array< Vec< 3, T >, 3 > eigenVectors(Mat< 3, 3, T > const &m) noexcept
Computes the eigenvectors of a real symmetric 3×3 matrix.
constexpr Quat< T > normalize(Quat< T > const &q) noexcept
Returns a unit quaternion in the same direction as q.
constexpr Mat< Cols, Rows, T > transpose(Mat< Rows, Cols, T > const &m) noexcept
Returns the transpose of a matrix.
constexpr Quat< T > cross(Quat< T > const &q1, Quat< T > const &q2) noexcept
Computes the Hamilton cross product of two quaternions.
constexpr M inverse(M const &m) noexcept
Computes the inverse of a square floating-point matrix.
Mat< 4, 4, T > rotate(Mat< 4, 4, T > const &m, T angle, Vec< 3, T > const &v)
Applies an axis-angle rotation to a 4×4 matrix.
Vec< 3, T > axis(Quat< T > const &q) noexcept
Extracts the unit rotation axis from a unit quaternion.
Mat< 4, 4, T > lookAt(Vec< 3, T > const &eye, Vec< 3, T > const &target, Vec< 3, T > const &up)
Builds a view matrix (OpenCV convention).
constexpr Mat< N, N, T > identity() noexcept
Returns an N × N identity matrix.
constexpr T sum(Vec< Dim, T > const &v) noexcept
Computes the sum of all elements in a vector.
A fixed-size matrix with Rows rows and Cols columns.
constexpr auto end(this auto &self) noexcept
Returns a past-the-end row iterator.
static constexpr std::size_t size() noexcept
Returns the total number of elements (Rows * Cols).
constexpr Mat() noexcept=default
Default-constructs a zero-initialized matrix.
constexpr Mat & operator-=(T rhs) noexcept
Subtracts scalar rhs from every element.
constexpr Mat & operator*=(Mat const &rhs) noexcept
Multiplies this square matrix by rhs (matrix product, square matrices only).
constexpr std::optional< Mat > safe_inverse(T epsilon=std::numeric_limits< T >::epsilon() *T(1000)) const noexcept
Computes the inverse, returning std::nullopt for near-singular matrices.
explicit(sizeof...(Args)==1) const expr Mat(Args... args) noexcept
Constructs a matrix from exactly Rows * Cols element values (row-major).
static constexpr std::size_t rows() noexcept
Returns the number of rows.
constexpr Mat & operator-=(Mat const &rhs) noexcept
Subtracts rhs element-wise from this matrix.
constexpr bool isNearZero(T epsilon=std::numeric_limits< T >::epsilon() *T(100)) const noexcept
Returns true if the Frobenius norm is less than epsilon.
constexpr Mat operator-() const noexcept
Unary negation operator.
constexpr bool all_of(Pred &&pred) const noexcept
Returns true if all elements satisfy the predicate.
constexpr T frobenius_norm() const noexcept
Computes the Frobenius norm of the matrix.
static consteval Mat zeros() noexcept
Returns a zero-initialized matrix.
constexpr auto & at(this auto &self, size_type r, size_type c)
Bounds-checked access to the element at row r, column c.
constexpr bool any_of(Pred &&pred) const noexcept
Returns true if at least one element satisfies the predicate.
constexpr bool isSymmetric(T epsilon=std::numeric_limits< T >::epsilon()) const noexcept
Returns true if the matrix equals its transpose (within epsilon).
constexpr auto begin(this auto &self) noexcept
Returns an iterator to the first row.
constexpr bool operator==(Mat const &) const =default
Compares two matrices for equality (element-wise).
constexpr bool isDiagonal(T epsilon=std::numeric_limits< T >::epsilon()) const noexcept
Returns true if all off-diagonal elements are within epsilon of zero.
static constexpr std::size_t cols() noexcept
Returns the number of columns.
constexpr Mat transform(UnaryOp &&op) const noexcept
Applies a unary operation to each element and returns the resulting matrix.
constexpr bool isLowerTriangular(T epsilon=std::numeric_limits< T >::epsilon()) const noexcept
Returns true if all elements above the main diagonal are within epsilon of zero.
constexpr Mat(Mat< R2, C2, T > const &other) noexcept
Cross-size constructor — copies the overlapping region, zero-fills the rest.
constexpr std::size_t rank(T epsilon=std::numeric_limits< T >::epsilon() *T(1000)) const noexcept
Estimates the matrix rank via Gaussian elimination.
constexpr T conditionNumber() const noexcept
Estimates the condition number using the Frobenius norm.
static consteval Mat identity() noexcept
Returns the identity matrix (square matrices only).
constexpr Mat & operator/=(T rhs) noexcept
Divides every element by scalar rhs.
static constexpr bool is_row_major() noexcept
Returns true (storage is row-major).
constexpr auto & operator[](this auto &self, size_type i) noexcept
Accesses row i as a Vec<Cols, T>.
constexpr bool isUpperTriangular(T epsilon=std::numeric_limits< T >::epsilon()) const noexcept
Returns true if all elements below the main diagonal are within epsilon of zero.
static consteval Mat ones() noexcept
Returns a matrix with all elements set to T(1).
static constexpr bool is_column_major() noexcept
Returns false (storage is row-major).
constexpr Mat & operator*=(T rhs) noexcept
Multiplies every element by scalar rhs.
static constexpr std::size_t memory_footprint() noexcept
Returns the size of this matrix type in bytes.
constexpr auto data(this auto &self) noexcept
Returns a pointer to the first row.
constexpr auto block(std::size_t start_row, std::size_t start_col) const noexcept -> Mat< R2, R2, T > requires(R2<=Rows &&R2<=Cols)
Extracts a square sub-matrix of size R2 × R2 starting at (start_row, start_col).
constexpr row_type const & row(size_type r) const noexcept
Returns a const reference to row r.
constexpr col_type col(size_type c) const noexcept
Returns column c as a Vec<Rows, T> (by value).
constexpr auto flat_view(this auto &self) noexcept
Returns a flat std::span<T, Rows * Cols> over all elements (row-major).
constexpr Mat(Rs &&... rs) noexcept
Constructs a matrix from Rows row vectors.
A fixed-size arithmetic vector of up to 4 dimensions.