/src/serenity/Userland/Libraries/LibGfx/VectorN.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@serenityos.org> |
3 | | * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl> |
4 | | * Copyright (c) 2022, the SerenityOS developers. |
5 | | * |
6 | | * SPDX-License-Identifier: BSD-2-Clause |
7 | | */ |
8 | | |
9 | | #pragma once |
10 | | |
11 | | #include <AK/Array.h> |
12 | | #include <AK/ByteString.h> |
13 | | #include <AK/Error.h> |
14 | | #include <AK/Math/Rounding.h> |
15 | | #include <AK/Math/Sqrt.h> |
16 | | #include <AK/StdLibExtras.h> |
17 | | #include <AK/StringView.h> |
18 | | |
19 | | #define LOOP_UNROLL_N 4 |
20 | | |
21 | | #define STRINGIFY_HELPER(x) #x |
22 | | #define STRINGIFY(x) STRINGIFY_HELPER(x) |
23 | | |
24 | | #if defined(AK_COMPILER_CLANG) |
25 | 0 | # define UNROLL_LOOP _Pragma(STRINGIFY(unroll)) |
26 | | #else |
27 | | # define UNROLL_LOOP _Pragma(STRINGIFY(GCC unroll(LOOP_UNROLL_N))) |
28 | | #endif |
29 | | |
30 | | namespace Gfx { |
31 | | |
32 | | template<size_t N, typename T> |
33 | | requires(N >= 2 && N <= 4) class VectorN final { |
34 | | static_assert(LOOP_UNROLL_N >= N, "Unroll the entire loop for performance."); |
35 | | |
36 | | public: |
37 | | [[nodiscard]] constexpr VectorN() = default; |
38 | | [[nodiscard]] constexpr VectorN(T x, T y) |
39 | | requires(N == 2) |
40 | 0 | : m_data { x, y } |
41 | 0 | { |
42 | 0 | } |
43 | | [[nodiscard]] constexpr VectorN(T x, T y, T z) |
44 | | requires(N == 3) |
45 | 0 | : m_data { x, y, z } |
46 | 0 | { |
47 | 0 | } Unexecuted instantiation: _ZN3Gfx7VectorNILm3EfEC2EfffQeqT_Li3E Unexecuted instantiation: _ZN3Gfx7VectorNILm3EiEC2EiiiQeqT_Li3E Unexecuted instantiation: _ZN3Gfx7VectorNILm3EdEC2EdddQeqT_Li3E |
48 | | [[nodiscard]] constexpr VectorN(T x, T y, T z, T w) |
49 | | requires(N == 4) |
50 | 0 | : m_data { x, y, z, w } |
51 | 0 | { |
52 | 0 | } Unexecuted instantiation: _ZN3Gfx7VectorNILm4EfEC2EffffQeqT_Li4E Unexecuted instantiation: _ZN3Gfx7VectorNILm4EdEC2EddddQeqT_Li4E |
53 | | |
54 | 0 | [[nodiscard]] constexpr T x() const { return m_data[0]; }Unexecuted instantiation: Gfx::VectorN<3ul, float>::x() const Unexecuted instantiation: Gfx::VectorN<2ul, float>::x() const Unexecuted instantiation: Gfx::VectorN<4ul, float>::x() const Unexecuted instantiation: Gfx::VectorN<4ul, double>::x() const Unexecuted instantiation: Gfx::VectorN<3ul, double>::x() const |
55 | 0 | [[nodiscard]] constexpr T y() const { return m_data[1]; }Unexecuted instantiation: Gfx::VectorN<3ul, float>::y() const Unexecuted instantiation: Gfx::VectorN<2ul, float>::y() const Unexecuted instantiation: Gfx::VectorN<4ul, float>::y() const Unexecuted instantiation: Gfx::VectorN<4ul, double>::y() const Unexecuted instantiation: Gfx::VectorN<3ul, double>::y() const |
56 | | [[nodiscard]] constexpr T z() const |
57 | | requires(N >= 3) |
58 | 0 | { |
59 | 0 | return m_data[2]; |
60 | 0 | } Unexecuted instantiation: _ZNK3Gfx7VectorNILm3EfE1zEvQgeT_Li3E Unexecuted instantiation: _ZNK3Gfx7VectorNILm4EfE1zEvQgeT_Li3E Unexecuted instantiation: _ZNK3Gfx7VectorNILm4EdE1zEvQgeT_Li3E Unexecuted instantiation: _ZNK3Gfx7VectorNILm3EdE1zEvQgeT_Li3E |
61 | | [[nodiscard]] constexpr T w() const |
62 | | requires(N >= 4) |
63 | 0 | { |
64 | 0 | return m_data[3]; |
65 | 0 | } Unexecuted instantiation: _ZNK3Gfx7VectorNILm4EfE1wEvQgeT_Li4E Unexecuted instantiation: _ZNK3Gfx7VectorNILm4EdE1wEvQgeT_Li4E |
66 | | |
67 | | constexpr void set_x(T value) { m_data[0] = value; } |
68 | | constexpr void set_y(T value) { m_data[1] = value; } |
69 | | constexpr void set_z(T value) |
70 | | requires(N >= 3) |
71 | | { |
72 | | m_data[2] = value; |
73 | | } |
74 | | constexpr void set_w(T value) |
75 | | requires(N >= 4) |
76 | | { |
77 | | m_data[3] = value; |
78 | | } |
79 | | |
80 | | [[nodiscard]] constexpr T const& operator[](size_t index) const |
81 | 0 | { |
82 | 0 | VERIFY(index < N); |
83 | 0 | return m_data[index]; |
84 | 0 | } Unexecuted instantiation: Gfx::VectorN<3ul, float>::operator[](unsigned long) const Unexecuted instantiation: Gfx::VectorN<3ul, int>::operator[](unsigned long) const Unexecuted instantiation: Gfx::VectorN<4ul, float>::operator[](unsigned long) const |
85 | | |
86 | | [[nodiscard]] constexpr T& operator[](size_t index) |
87 | 0 | { |
88 | 0 | VERIFY(index < N); |
89 | 0 | return m_data[index]; |
90 | 0 | } Unexecuted instantiation: Gfx::VectorN<3ul, int>::operator[](unsigned long) Unexecuted instantiation: Gfx::VectorN<3ul, float>::operator[](unsigned long) Unexecuted instantiation: Gfx::VectorN<4ul, float>::operator[](unsigned long) |
91 | | |
92 | | constexpr VectorN& operator+=(VectorN const& other) |
93 | 0 | { |
94 | 0 | UNROLL_LOOP |
95 | 0 | for (auto i = 0u; i < N; ++i) |
96 | 0 | m_data[i] += other.data()[i]; |
97 | 0 | return *this; |
98 | 0 | } |
99 | | |
100 | | constexpr VectorN& operator-=(VectorN const& other) |
101 | | { |
102 | | UNROLL_LOOP |
103 | | for (auto i = 0u; i < N; ++i) |
104 | | m_data[i] -= other.data()[i]; |
105 | | return *this; |
106 | | } |
107 | | |
108 | | constexpr VectorN& operator*=(T const& t) |
109 | 0 | { |
110 | 0 | UNROLL_LOOP |
111 | 0 | for (auto i = 0u; i < N; ++i) |
112 | 0 | m_data[i] *= t; |
113 | 0 | return *this; |
114 | 0 | } Unexecuted instantiation: Gfx::VectorN<3ul, float>::operator*=(float const&) Unexecuted instantiation: Gfx::VectorN<2ul, float>::operator*=(float const&) Unexecuted instantiation: Gfx::VectorN<3ul, double>::operator*=(double const&) |
115 | | |
116 | | [[nodiscard]] constexpr VectorN operator+(VectorN const& other) const |
117 | 0 | { |
118 | 0 | VectorN result; |
119 | 0 | UNROLL_LOOP |
120 | 0 | for (auto i = 0u; i < N; ++i) |
121 | 0 | result.m_data[i] = m_data[i] + other.data()[i]; |
122 | 0 | return result; |
123 | 0 | } Unexecuted instantiation: Gfx::VectorN<3ul, float>::operator+(Gfx::VectorN<3ul, float> const&) const Unexecuted instantiation: Gfx::VectorN<4ul, float>::operator+(Gfx::VectorN<4ul, float> const&) const |
124 | | |
125 | | [[nodiscard]] constexpr VectorN operator-(VectorN const& other) const |
126 | 0 | { |
127 | 0 | VectorN result; |
128 | 0 | UNROLL_LOOP |
129 | 0 | for (auto i = 0u; i < N; ++i) |
130 | 0 | result.m_data[i] = m_data[i] - other.data()[i]; |
131 | 0 | return result; |
132 | 0 | } Unexecuted instantiation: Gfx::VectorN<3ul, float>::operator-(Gfx::VectorN<3ul, float> const&) const Unexecuted instantiation: Gfx::VectorN<4ul, float>::operator-(Gfx::VectorN<4ul, float> const&) const |
133 | | |
134 | | [[nodiscard]] constexpr VectorN operator*(VectorN const& other) const |
135 | 0 | { |
136 | 0 | VectorN result; |
137 | 0 | UNROLL_LOOP |
138 | 0 | for (auto i = 0u; i < N; ++i) |
139 | 0 | result.m_data[i] = m_data[i] * other.data()[i]; |
140 | 0 | return result; |
141 | 0 | } Unexecuted instantiation: Gfx::VectorN<4ul, float>::operator*(Gfx::VectorN<4ul, float> const&) const Unexecuted instantiation: Gfx::VectorN<3ul, float>::operator*(Gfx::VectorN<3ul, float> const&) const |
142 | | |
143 | | [[nodiscard]] constexpr VectorN operator-() const |
144 | | { |
145 | | VectorN result; |
146 | | UNROLL_LOOP |
147 | | for (auto i = 0u; i < N; ++i) |
148 | | result.m_data[i] = -m_data[i]; |
149 | | return result; |
150 | | } |
151 | | |
152 | | [[nodiscard]] constexpr VectorN operator/(VectorN const& other) const |
153 | 0 | { |
154 | 0 | VectorN result; |
155 | 0 | UNROLL_LOOP |
156 | 0 | for (auto i = 0u; i < N; ++i) |
157 | 0 | result.m_data[i] = m_data[i] / other.data()[i]; |
158 | 0 | return result; |
159 | 0 | } |
160 | | |
161 | | template<typename U> |
162 | | [[nodiscard]] constexpr VectorN operator+(U f) const |
163 | | { |
164 | | VectorN result; |
165 | | UNROLL_LOOP |
166 | | for (auto i = 0u; i < N; ++i) |
167 | | result.m_data[i] = m_data[i] + f; |
168 | | return result; |
169 | | } |
170 | | |
171 | | template<typename U> |
172 | | [[nodiscard]] constexpr VectorN operator-(U f) const |
173 | | { |
174 | | VectorN result; |
175 | | UNROLL_LOOP |
176 | | for (auto i = 0u; i < N; ++i) |
177 | | result.m_data[i] = m_data[i] - f; |
178 | | return result; |
179 | | } |
180 | | |
181 | | template<typename U> |
182 | | [[nodiscard]] constexpr VectorN operator*(U f) const |
183 | 0 | { |
184 | 0 | VectorN result; |
185 | 0 | UNROLL_LOOP |
186 | 0 | for (auto i = 0u; i < N; ++i) |
187 | 0 | result.m_data[i] = m_data[i] * f; |
188 | 0 | return result; |
189 | 0 | } Unexecuted instantiation: Gfx::VectorN<3ul, float> Gfx::VectorN<3ul, float>::operator*<int>(int) const Unexecuted instantiation: Gfx::VectorN<3ul, float> Gfx::VectorN<3ul, float>::operator*<float>(float) const Unexecuted instantiation: Gfx::VectorN<2ul, float> Gfx::VectorN<2ul, float>::operator*<float>(float) const Unexecuted instantiation: Gfx::VectorN<4ul, float> Gfx::VectorN<4ul, float>::operator*<float>(float) const |
190 | | |
191 | | template<typename U> |
192 | | [[nodiscard]] constexpr VectorN operator/(U f) const |
193 | 0 | { |
194 | 0 | VectorN result; |
195 | 0 | UNROLL_LOOP |
196 | 0 | for (auto i = 0u; i < N; ++i) |
197 | 0 | result.m_data[i] = m_data[i] / f; |
198 | 0 | return result; |
199 | 0 | } |
200 | | |
201 | | template<ConvertibleTo<T> U> |
202 | | constexpr bool operator==(VectorN<N, U> const& other) const |
203 | | { |
204 | | UNROLL_LOOP |
205 | | for (auto i = 0u; i < N; ++i) { |
206 | | if (m_data[i] != static_cast<T>(other.m_data[i])) |
207 | | return false; |
208 | | } |
209 | | return true; |
210 | | } |
211 | | |
212 | | [[nodiscard]] constexpr T dot(VectorN const& other) const |
213 | 0 | { |
214 | 0 | T result {}; |
215 | 0 | UNROLL_LOOP |
216 | 0 | for (auto i = 0u; i < N; ++i) |
217 | 0 | result += m_data[i] * other.data()[i]; |
218 | 0 | return result; |
219 | 0 | } Unexecuted instantiation: Gfx::VectorN<2ul, float>::dot(Gfx::VectorN<2ul, float> const&) const Unexecuted instantiation: Gfx::VectorN<3ul, float>::dot(Gfx::VectorN<3ul, float> const&) const Unexecuted instantiation: Gfx::VectorN<4ul, float>::dot(Gfx::VectorN<4ul, float> const&) const Unexecuted instantiation: Gfx::VectorN<3ul, double>::dot(Gfx::VectorN<3ul, double> const&) const |
220 | | |
221 | | [[nodiscard]] constexpr VectorN cross(VectorN const& other) const |
222 | | requires(N == 3) |
223 | 0 | { |
224 | 0 | return VectorN( |
225 | 0 | y() * other.z() - z() * other.y(), |
226 | 0 | z() * other.x() - x() * other.z(), |
227 | 0 | x() * other.y() - y() * other.x()); |
228 | 0 | } |
229 | | |
230 | | [[nodiscard]] constexpr VectorN normalized() const |
231 | 0 | { |
232 | 0 | VectorN copy { *this }; |
233 | 0 | copy.normalize(); |
234 | 0 | return copy; |
235 | 0 | } Unexecuted instantiation: Gfx::VectorN<2ul, float>::normalized() const Unexecuted instantiation: Gfx::VectorN<3ul, double>::normalized() const |
236 | | |
237 | | [[nodiscard]] constexpr VectorN clamped(T m, T x) const |
238 | 0 | { |
239 | 0 | VectorN copy { *this }; |
240 | 0 | copy.clamp(m, x); |
241 | 0 | return copy; |
242 | 0 | } |
243 | | |
244 | | constexpr void clamp(T min_value, T max_value) |
245 | 0 | { |
246 | 0 | UNROLL_LOOP |
247 | 0 | for (auto i = 0u; i < N; ++i) { |
248 | 0 | m_data[i] = max(min_value, m_data[i]); |
249 | 0 | m_data[i] = min(max_value, m_data[i]); |
250 | 0 | } |
251 | 0 | } Unexecuted instantiation: Gfx::VectorN<3ul, float>::clamp(float, float) Unexecuted instantiation: Gfx::VectorN<4ul, float>::clamp(float, float) |
252 | | |
253 | | constexpr void normalize() |
254 | 0 | { |
255 | 0 | T const inv_length = 1 / length(); |
256 | 0 | operator*=(inv_length); |
257 | 0 | } Unexecuted instantiation: Gfx::VectorN<2ul, float>::normalize() Unexecuted instantiation: Gfx::VectorN<3ul, float>::normalize() Unexecuted instantiation: Gfx::VectorN<3ul, double>::normalize() |
258 | | |
259 | | template<typename O = T> |
260 | | [[nodiscard]] constexpr O length() const |
261 | 0 | { |
262 | 0 | return AK::sqrt<O>(dot(*this)); |
263 | 0 | } Unexecuted instantiation: float Gfx::VectorN<2ul, float>::length<float>() const Unexecuted instantiation: float Gfx::VectorN<3ul, float>::length<float>() const Unexecuted instantiation: double Gfx::VectorN<3ul, double>::length<double>() const |
264 | | |
265 | | [[nodiscard]] constexpr VectorN<2, T> xy() const |
266 | | requires(N >= 3) |
267 | | { |
268 | | return VectorN<2, T>(x(), y()); |
269 | | } |
270 | | |
271 | | [[nodiscard]] constexpr VectorN<3, T> xyz() const |
272 | | requires(N >= 4) |
273 | | { |
274 | | return VectorN<3, T>(x(), y(), z()); |
275 | | } |
276 | | |
277 | | [[nodiscard]] ByteString to_byte_string() const |
278 | | { |
279 | | if constexpr (N == 2) |
280 | | return ByteString::formatted("[{},{}]", x(), y()); |
281 | | else if constexpr (N == 3) |
282 | | return ByteString::formatted("[{},{},{}]", x(), y(), z()); |
283 | | else |
284 | | return ByteString::formatted("[{},{},{},{}]", x(), y(), z(), w()); |
285 | | } |
286 | | |
287 | | template<typename U> |
288 | | [[nodiscard]] VectorN<N, U> to_type() const |
289 | | { |
290 | | VectorN<N, U> result; |
291 | | UNROLL_LOOP |
292 | | for (auto i = 0u; i < N; ++i) |
293 | | result.data()[i] = static_cast<U>(m_data[i]); |
294 | | return result; |
295 | | } |
296 | | |
297 | | template<typename U> |
298 | | [[nodiscard]] VectorN<N, U> to_rounded() const |
299 | | { |
300 | | VectorN<N, U> result; |
301 | | UNROLL_LOOP |
302 | | for (auto i = 0u; i < N; ++i) |
303 | | result.data()[i] = round_to<U>(m_data[i]); |
304 | | return result; |
305 | | } |
306 | | |
307 | | constexpr auto& data() { return m_data; } |
308 | 0 | constexpr auto const& data() const { return m_data; }Unexecuted instantiation: Gfx::VectorN<3ul, float>::data() const Unexecuted instantiation: Gfx::VectorN<2ul, float>::data() const Unexecuted instantiation: Gfx::VectorN<4ul, float>::data() const Unexecuted instantiation: Gfx::VectorN<3ul, double>::data() const |
309 | | |
310 | | private: |
311 | | Array<T, N> m_data; |
312 | | }; |
313 | | |
314 | | } |
315 | | |
316 | | namespace AK { |
317 | | |
318 | | template<size_t N, typename T> |
319 | | constexpr Gfx::VectorN<N, T> min(Gfx::VectorN<N, T> const& a, Gfx::VectorN<N, T> const& b) |
320 | | { |
321 | | Gfx::VectorN<N, T> result; |
322 | | UNROLL_LOOP |
323 | | for (auto i = 0u; i < N; ++i) |
324 | | result[i] = min(a[i], b[i]); |
325 | | return result; |
326 | | } |
327 | | |
328 | | template<size_t N, typename T> |
329 | | constexpr Gfx::VectorN<N, T> max(Gfx::VectorN<N, T> const& a, Gfx::VectorN<N, T> const& b) |
330 | | { |
331 | | Gfx::VectorN<N, T> result; |
332 | | UNROLL_LOOP |
333 | | for (auto i = 0u; i < N; ++i) |
334 | | result[i] = max(a[i], b[i]); |
335 | | return result; |
336 | | } |
337 | | |
338 | | } |