Line | Count | Source |
1 | /* | |
2 | * Copyright (c) 2020, the SerenityOS developers. | |
3 | * | |
4 | * SPDX-License-Identifier: BSD-2-Clause | |
5 | */ | |
6 | ||
7 | #pragma once | |
8 | ||
9 | #include <AK/Types.h> | |
10 | #include <initializer_list> | |
11 | ||
12 | namespace Gfx { | |
13 | ||
14 | template<size_t N, typename T> | |
15 | class Matrix { | |
16 | template<size_t U, typename V> | |
17 | friend class Matrix; | |
18 | ||
19 | public: | |
20 | static constexpr size_t Size = N; | |
21 | ||
22 | constexpr Matrix() = default; | |
23 | constexpr Matrix(std::initializer_list<T> elements) | |
24 | 0 | { |
25 | 0 | VERIFY(elements.size() == N * N); |
26 | 0 | size_t i = 0; |
27 | 0 | for (auto& element : elements) { |
28 | 0 | m_elements[i / N][i % N] = element; |
29 | 0 | ++i; |
30 | 0 | } |
31 | 0 | }Unexecuted instantiation: Gfx::Matrix<3ul, float>::Matrix(std::initializer_list<float>) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix(std::initializer_list<float>) Unexecuted instantiation: Gfx::Matrix<4ul, double>::Matrix(std::initializer_list<double>) |
32 | ||
33 | template<typename... Args> | |
34 | constexpr Matrix(Args... args) | |
35 | 0 | : Matrix({ (T)args... }) |
36 | 0 | { |
37 | 0 | }Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, int, int, int, int, int, int, int, int, int, int, int, float, int>(int, int, int, int, int, int, int, int, int, int, int, int, int, int, float, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<float, float, int, float, float, float, int, float, int, int, int, int, int, int, int, int>(float, float, int, float, float, float, int, float, int, int, int, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float>(float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, float, int, int, int, int, int, int, int, int, int, int, int, int>(int, int, int, float, int, int, int, int, int, int, int, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, float, int, int, int, float, int, int, int, int, int, int, int, int>(int, int, int, float, int, int, int, float, int, int, int, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, float, int, int, int, float, int, int, int, float, int, int, int, int>(int, int, int, float, int, int, int, float, int, int, int, float, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, int, int, int, int, float, int, int, int, int, int, int, int, int>(int, int, int, int, int, int, int, float, int, int, int, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, int, int, int, int, int, int, int, int, float, int, int, int, int>(int, int, int, int, int, int, int, int, int, int, int, float, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<float, int, int, int, int, float, int, int, int, int, int, int, int, int, int, int>(float, int, int, int, int, float, int, int, int, int, int, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<float, int, int, int, int, float, int, int, int, int, float, int, int, int, int, int>(float, int, int, int, int, float, int, int, int, int, float, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<float, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>(float, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, int, int, float, int, int, int, int, int, int, int, int, int, int>(int, int, int, int, int, float, int, int, int, int, int, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, int, int, int, int, int, int, int, float, int, int, int, int, int>(int, int, int, int, int, int, int, int, int, int, float, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<float, float, float, int, float, float, float, int, float, float, float, int, int, int, int, int>(float, float, float, int, float, float, float, int, float, float, float, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, float, int, int, int, int, int, int, int, int, int, int, int, int, int, int>(int, float, int, int, int, int, int, int, int, int, int, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, float, int, int, float, int, int, int, int, int, int, int, int, int, int, int>(int, float, int, int, float, int, int, int, int, int, int, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, int, float, int, int, int, int, int, int, int, int, int, int, int>(int, int, int, int, float, int, int, int, int, int, int, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, double>::Matrix<int, int, int, double, int, int, int, double, int, int, int, double, int, int, int, int>(int, int, int, double, int, int, int, double, int, int, int, double, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, double>::Matrix<double, int, int, int, int, double, int, int, int, int, double, int, int, int, int, int>(double, int, int, int, int, double, int, int, int, int, double, int, int, int, int, int) Unexecuted instantiation: Gfx::Matrix<4ul, double>::Matrix<double, double, double, int, double, double, double, int, double, double, double, int, int, int, int, int>(double, double, double, int, double, double, double, int, double, double, double, int, int, int, int, int) |
38 | ||
39 | constexpr Matrix(Matrix const& other) | |
40 | 0 | { |
41 | 0 | *this = other; |
42 | 0 | }Unexecuted instantiation: Gfx::Matrix<3ul, float>::Matrix(Gfx::Matrix<3ul, float> const&) Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix(Gfx::Matrix<4ul, float> const&) Unexecuted instantiation: Gfx::Matrix<4ul, double>::Matrix(Gfx::Matrix<4ul, double> const&) |
43 | ||
44 | constexpr Matrix& operator=(Matrix const& other) | |
45 | 0 | { |
46 | #ifndef __clang__ | |
47 | if (is_constant_evaluated()) { | |
48 | for (size_t i = 0; i < N; i++) { | |
49 | for (size_t j = 0; j < N; j++) { | |
50 | m_elements[i][j] = other.elements()[i][j]; | |
51 | } | |
52 | } | |
53 | return *this; | |
54 | } | |
55 | #endif | |
56 | 0 | __builtin_memcpy(m_elements, other.elements(), sizeof(T) * N * N); |
57 | 0 | return *this; |
58 | 0 | }Unexecuted instantiation: Gfx::Matrix<3ul, float>::operator=(Gfx::Matrix<3ul, float> const&) Unexecuted instantiation: Gfx::Matrix<4ul, float>::operator=(Gfx::Matrix<4ul, float> const&) Unexecuted instantiation: Gfx::Matrix<4ul, double>::operator=(Gfx::Matrix<4ul, double> const&) |
59 | ||
60 | 0 | constexpr auto elements() const { return m_elements; }Unexecuted instantiation: Gfx::Matrix<3ul, float>::elements() const Unexecuted instantiation: Gfx::Matrix<4ul, float>::elements() const Unexecuted instantiation: Gfx::Matrix<4ul, double>::elements() const |
61 | 0 | constexpr auto elements() { return m_elements; }Unexecuted instantiation: Gfx::Matrix<2ul, float>::elements() Unexecuted instantiation: Gfx::Matrix<1ul, float>::elements() Unexecuted instantiation: Gfx::Matrix<3ul, float>::elements() Unexecuted instantiation: Gfx::Matrix<4ul, float>::elements() Unexecuted instantiation: Gfx::Matrix<4ul, double>::elements() Unexecuted instantiation: Gfx::Matrix<3ul, double>::elements() Unexecuted instantiation: Gfx::Matrix<2ul, double>::elements() Unexecuted instantiation: Gfx::Matrix<1ul, double>::elements() |
62 | ||
63 | // FIXME: Change to multi-arg operator[] once we upgrade to C++23 | |
64 | constexpr auto const& operator()(size_t row, size_t col) const { return m_elements[row][col]; } | |
65 | 0 | constexpr auto& operator()(size_t row, size_t col) { return m_elements[row][col]; }Unexecuted instantiation: Gfx::Matrix<3ul, float>::operator()(unsigned long, unsigned long) Unexecuted instantiation: Gfx::Matrix<4ul, float>::operator()(unsigned long, unsigned long) |
66 | ||
67 | [[nodiscard]] constexpr Matrix operator*(Matrix const& other) const | |
68 | 0 | { |
69 | 0 | Matrix product; |
70 | 0 | for (size_t i = 0; i < N; ++i) { |
71 | 0 | for (size_t j = 0; j < N; ++j) { |
72 | 0 | auto& element = product.m_elements[i][j]; |
73 | ||
74 | 0 | if constexpr (N == 4) { |
75 | 0 | element = m_elements[i][0] * other.m_elements[0][j] |
76 | 0 | + m_elements[i][1] * other.m_elements[1][j] |
77 | 0 | + m_elements[i][2] * other.m_elements[2][j] |
78 | 0 | + m_elements[i][3] * other.m_elements[3][j]; |
79 | 0 | } else if constexpr (N == 3) { |
80 | 0 | element = m_elements[i][0] * other.m_elements[0][j] |
81 | 0 | + m_elements[i][1] * other.m_elements[1][j] |
82 | 0 | + m_elements[i][2] * other.m_elements[2][j]; |
83 | } else if constexpr (N == 2) { | |
84 | element = m_elements[i][0] * other.m_elements[0][j] | |
85 | + m_elements[i][1] * other.m_elements[1][j]; | |
86 | } else if constexpr (N == 1) { | |
87 | element = m_elements[i][0] * other.m_elements[0][j]; | |
88 | } else { | |
89 | T value {}; | |
90 | for (size_t k = 0; k < N; ++k) | |
91 | value += m_elements[i][k] * other.m_elements[k][j]; | |
92 | ||
93 | element = value; | |
94 | } | |
95 | 0 | } |
96 | 0 | } |
97 | ||
98 | 0 | return product; |
99 | 0 | }Unexecuted instantiation: Gfx::Matrix<3ul, float>::operator*(Gfx::Matrix<3ul, float> const&) const Unexecuted instantiation: Gfx::Matrix<4ul, float>::operator*(Gfx::Matrix<4ul, float> const&) const Unexecuted instantiation: Gfx::Matrix<4ul, double>::operator*(Gfx::Matrix<4ul, double> const&) const |
100 | ||
101 | [[nodiscard]] constexpr Matrix operator+(Matrix const& other) const | |
102 | 0 | { |
103 | 0 | Matrix sum; |
104 | 0 | for (size_t i = 0; i < N; ++i) { |
105 | 0 | for (size_t j = 0; j < N; ++j) |
106 | 0 | sum.m_elements[i][j] = m_elements[i][j] + other.m_elements[i][j]; |
107 | 0 | } |
108 | 0 | return sum; |
109 | 0 | } |
110 | ||
111 | [[nodiscard]] constexpr Matrix operator/(T divisor) const | |
112 | 0 | { |
113 | 0 | Matrix division; |
114 | 0 | for (size_t i = 0; i < N; ++i) { |
115 | 0 | for (size_t j = 0; j < N; ++j) |
116 | 0 | division.m_elements[i][j] = m_elements[i][j] / divisor; |
117 | 0 | } |
118 | 0 | return division; |
119 | 0 | }Unexecuted instantiation: Gfx::Matrix<3ul, float>::operator/(float) const Unexecuted instantiation: Gfx::Matrix<4ul, float>::operator/(float) const Unexecuted instantiation: Gfx::Matrix<4ul, double>::operator/(double) const |
120 | ||
121 | [[nodiscard]] friend constexpr Matrix operator*(Matrix const& matrix, T scalar) | |
122 | 0 | { |
123 | 0 | Matrix scaled; |
124 | 0 | for (size_t i = 0; i < N; ++i) { |
125 | 0 | for (size_t j = 0; j < N; ++j) |
126 | 0 | scaled.m_elements[i][j] = matrix.m_elements[i][j] * scalar; |
127 | 0 | } |
128 | 0 | return scaled; |
129 | 0 | } |
130 | ||
131 | [[nodiscard]] friend constexpr Matrix operator*(T scalar, Matrix const& matrix) | |
132 | 0 | { |
133 | 0 | return matrix * scalar; |
134 | 0 | } |
135 | ||
136 | [[nodiscard]] constexpr Matrix adjugate() const | |
137 | 0 | { |
138 | if constexpr (N == 1) | |
139 | return Matrix(1); | |
140 | ||
141 | 0 | Matrix adjugate; |
142 | 0 | for (size_t i = 0; i < N; ++i) { |
143 | 0 | for (size_t j = 0; j < N; ++j) { |
144 | 0 | int sign = (i + j) % 2 == 0 ? 1 : -1; |
145 | 0 | adjugate.m_elements[j][i] = sign * first_minor(i, j); |
146 | 0 | } |
147 | 0 | } |
148 | 0 | return adjugate; |
149 | 0 | }Unexecuted instantiation: Gfx::Matrix<3ul, float>::adjugate() const Unexecuted instantiation: Gfx::Matrix<4ul, float>::adjugate() const Unexecuted instantiation: Gfx::Matrix<4ul, double>::adjugate() const |
150 | ||
151 | [[nodiscard]] constexpr T determinant() const | |
152 | 0 | { |
153 | 0 | if constexpr (N == 1) { |
154 | 0 | return m_elements[0][0]; |
155 | 0 | } else { |
156 | 0 | T result = {}; |
157 | 0 | int sign = 1; |
158 | 0 | for (size_t j = 0; j < N; ++j) { |
159 | 0 | result += sign * m_elements[0][j] * first_minor(0, j); |
160 | 0 | sign *= -1; |
161 | 0 | } |
162 | 0 | return result; |
163 | 0 | } |
164 | 0 | }Unexecuted instantiation: Gfx::Matrix<3ul, float>::determinant() const Unexecuted instantiation: Gfx::Matrix<2ul, float>::determinant() const Unexecuted instantiation: Gfx::Matrix<1ul, float>::determinant() const Unexecuted instantiation: Gfx::Matrix<4ul, float>::determinant() const Unexecuted instantiation: Gfx::Matrix<4ul, double>::determinant() const Unexecuted instantiation: Gfx::Matrix<3ul, double>::determinant() const Unexecuted instantiation: Gfx::Matrix<2ul, double>::determinant() const Unexecuted instantiation: Gfx::Matrix<1ul, double>::determinant() const |
165 | ||
166 | [[nodiscard]] constexpr T first_minor(size_t skip_row, size_t skip_column) const | |
167 | 0 | { |
168 | 0 | static_assert(N > 1); |
169 | 0 | VERIFY(skip_row < N); |
170 | 0 | VERIFY(skip_column < N); |
171 | ||
172 | 0 | Matrix<N - 1, T> first_minor; |
173 | 0 | constexpr auto new_size = N - 1; |
174 | 0 | size_t k = 0; |
175 | ||
176 | 0 | for (size_t i = 0; i < N; ++i) { |
177 | 0 | for (size_t j = 0; j < N; ++j) { |
178 | 0 | if (i == skip_row || j == skip_column) |
179 | 0 | continue; |
180 | ||
181 | 0 | first_minor.elements()[k / new_size][k % new_size] = m_elements[i][j]; |
182 | 0 | ++k; |
183 | 0 | } |
184 | 0 | } |
185 | ||
186 | 0 | return first_minor.determinant(); |
187 | 0 | }Unexecuted instantiation: Gfx::Matrix<3ul, float>::first_minor(unsigned long, unsigned long) const Unexecuted instantiation: Gfx::Matrix<2ul, float>::first_minor(unsigned long, unsigned long) const Unexecuted instantiation: Gfx::Matrix<4ul, float>::first_minor(unsigned long, unsigned long) const Unexecuted instantiation: Gfx::Matrix<4ul, double>::first_minor(unsigned long, unsigned long) const Unexecuted instantiation: Gfx::Matrix<3ul, double>::first_minor(unsigned long, unsigned long) const Unexecuted instantiation: Gfx::Matrix<2ul, double>::first_minor(unsigned long, unsigned long) const |
188 | ||
189 | [[nodiscard]] constexpr static Matrix identity() | |
190 | 0 | { |
191 | 0 | Matrix result; |
192 | 0 | for (size_t i = 0; i < N; ++i) { |
193 | 0 | for (size_t j = 0; j < N; ++j) { |
194 | 0 | if (i == j) |
195 | 0 | result.m_elements[i][j] = 1; |
196 | 0 | else |
197 | 0 | result.m_elements[i][j] = 0; |
198 | 0 | } |
199 | 0 | } |
200 | 0 | return result; |
201 | 0 | }Unexecuted instantiation: Gfx::Matrix<4ul, float>::identity() Unexecuted instantiation: Gfx::Matrix<4ul, double>::identity() |
202 | ||
203 | [[nodiscard]] constexpr Matrix inverse() const | |
204 | 0 | { |
205 | 0 | return adjugate() / determinant(); |
206 | 0 | }Unexecuted instantiation: Gfx::Matrix<3ul, float>::inverse() const Unexecuted instantiation: Gfx::Matrix<4ul, float>::inverse() const Unexecuted instantiation: Gfx::Matrix<4ul, double>::inverse() const |
207 | ||
208 | [[nodiscard]] constexpr Matrix transpose() const | |
209 | 0 | { |
210 | 0 | Matrix result; |
211 | 0 | for (size_t i = 0; i < N; ++i) { |
212 | 0 | for (size_t j = 0; j < N; ++j) |
213 | 0 | result.m_elements[i][j] = m_elements[j][i]; |
214 | 0 | } |
215 | 0 | return result; |
216 | 0 | }Unexecuted instantiation: Gfx::Matrix<4ul, float>::transpose() const Unexecuted instantiation: Gfx::Matrix<3ul, float>::transpose() const |
217 | ||
218 | [[nodiscard]] constexpr T element_sum() const | |
219 | 0 | { |
220 | 0 | T s = 0; |
221 | 0 | for (size_t i = 0; i < N; ++i) { |
222 | 0 | for (size_t j = 0; j < N; ++j) |
223 | 0 | s += m_elements[i][j]; |
224 | 0 | } |
225 | 0 | return s; |
226 | 0 | } |
227 | ||
228 | [[nodiscard]] constexpr Matrix hadamard_product(Matrix const& matrix) | |
229 | 0 | { |
230 | 0 | Matrix product; |
231 | 0 | for (size_t i = 0; i < N; ++i) { |
232 | 0 | for (size_t j = 0; j < N; ++j) |
233 | 0 | product.m_elements[i][j] = m_elements[i][j] * matrix.m_elements[i][j]; |
234 | 0 | } |
235 | 0 | return product; |
236 | 0 | } |
237 | ||
238 | template<size_t U> | |
239 | [[nodiscard]] constexpr Matrix<U, T> submatrix_from_topleft() const | |
240 | requires(U > 0 && U < N) | |
241 | { | |
242 | Matrix<U, T> result; | |
243 | for (size_t i = 0; i < U; ++i) { | |
244 | for (size_t j = 0; j < U; ++j) | |
245 | result.m_elements[i][j] = m_elements[i][j]; | |
246 | } | |
247 | return result; | |
248 | } | |
249 | ||
250 | constexpr bool is_invertible() const | |
251 | 0 | { |
252 | 0 | return determinant() != static_cast<T>(0.0); |
253 | 0 | }Unexecuted instantiation: Gfx::Matrix<3ul, float>::is_invertible() const Unexecuted instantiation: Gfx::Matrix<4ul, float>::is_invertible() const Unexecuted instantiation: Gfx::Matrix<4ul, double>::is_invertible() const |
254 | ||
255 | private: | |
256 | T m_elements[N][N]; | |
257 | }; | |
258 | ||
259 | } | |
260 | ||
261 | namespace AK { | |
262 | ||
263 | template<size_t N, typename T> | |
264 | struct Formatter<Gfx::Matrix<N, T>> : Formatter<FormatString> { | |
265 | ErrorOr<void> format(FormatBuilder& builder, Gfx::Matrix<N, T> const& value) | |
266 | { | |
267 | TRY(Formatter<FormatString>::format(builder, "[ "sv)); | |
268 | for (u32 i = 0; i < N; ++i) { | |
269 | TRY(Formatter<FormatString>::format(builder, "[ "sv)); | |
270 | ||
271 | for (u32 j = 0; j < N; ++j) { | |
272 | TRY(Formatter<FormatString>::format(builder, "{}"sv, value(i, j))); | |
273 | if (j != N - 1) | |
274 | TRY(Formatter<FormatString>::format(builder, ", "sv, value(i, j))); | |
275 | } | |
276 | TRY(Formatter<FormatString>::format(builder, " ]"sv)); | |
277 | if (i != N - 1) | |
278 | TRY(Formatter<FormatString>::format(builder, ",\n"sv)); | |
279 | } | |
280 | ||
281 | return Formatter<FormatString>::format(builder, " ]\n"sv); | |
282 | } | |
283 | }; | |
284 | ||
285 | } | |
286 | ||
287 | using Gfx::Matrix; |