HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
matrix.hpp
1// Copyright Take Vos 2021-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "vector.hpp"
8#include "extent.hpp"
9#include "point.hpp"
10#include "rectangle.hpp"
11#include "quad.hpp"
12#include "circle.hpp"
13#include "line_segment.hpp"
14#include "corner_radii.hpp"
15#include "axis_aligned_rectangle.hpp"
16#include "../color/color.hpp"
17#include <array>
18
19namespace hi::inline v1 {
20namespace geo {
21
22template<int D>
23class matrix {
24public:
25 static_assert(D == 2 || D == 3, "Only 2D or 3D rotation-matrices are supported");
26
27 constexpr matrix(matrix const&) noexcept = default;
28 constexpr matrix(matrix&&) noexcept = default;
29 constexpr matrix& operator=(matrix const&) noexcept = default;
30 constexpr matrix& operator=(matrix&&) noexcept = default;
31
32 constexpr matrix() noexcept
33 {
34 hilet a = f32x4::broadcast(1.0f);
35 _col0 = a.x000();
36 _col1 = a._0y00();
37 _col2 = a._00z0();
38 _col3 = a._000w();
39 };
40
41 constexpr matrix(f32x4 col0, f32x4 col1, f32x4 col2, f32x4 col3 = f32x4{0.0f, 0.0f, 0.0f, 1.0f}) noexcept :
42 _col0(col0), _col1(col1), _col2(col2), _col3(col3)
43 {
44 }
45
46 constexpr matrix(vector3 col0, vector3 col1, vector3 col2) noexcept
47 requires(D == 3)
48 :
49 _col0(static_cast<f32x4>(col0)),
50 _col1(static_cast<f32x4>(col1)),
51 _col2(static_cast<f32x4>(col2)),
52 _col3{0.0f, 0.0f, 0.0f, 1.0f}
53 {
54 }
55
56 constexpr matrix(
57 float c0r0,
58 float c1r0,
59 float c2r0,
60 float c0r1,
61 float c1r1,
62 float c2r1,
63 float c0r2,
64 float c1r2,
65 float c2r2) noexcept
66 requires(D == 3)
67 :
68 _col0(c0r0, c0r1, c0r2, 0.0f), _col1(c1r0, c1r1, c1r2, 0.0f), _col2(c2r0, c2r1, c2r2, 0.0f), _col3(0.0f, 0.0f, 0.0f, 1.0f)
69 {
70 }
71
72 constexpr matrix(
73 float c0r0,
74 float c1r0,
75 float c2r0,
76 float c3r0,
77 float c0r1,
78 float c1r1,
79 float c2r1,
80 float c3r1,
81 float c0r2,
82 float c1r2,
83 float c2r2,
84 float c3r2,
85 float c0r3,
86 float c1r3,
87 float c2r3,
88 float c3r3) noexcept
89 requires(D == 3)
90 :
91 _col0(c0r0, c0r1, c0r2, c0r3), _col1(c1r0, c1r1, c1r2, c1r3), _col2(c2r0, c2r1, c2r2, c2r3), _col3(c3r0, c3r1, c3r2, c3r3)
92 {
93 }
94
95 template<int E>
96 requires(E < D)
97 [[nodiscard]] constexpr matrix(matrix<E> const& other) noexcept :
98 _col0(get<0>(other)), _col1(get<1>(other)), _col2(get<2>(other)), _col3(get<3>(other))
99 {
100 }
101
102 template<int E>
103 requires(E < D)
104 constexpr matrix& operator=(matrix<E> const& rhs) noexcept
105 {
106 _col0 = get<0>(rhs);
107 _col1 = get<1>(rhs);
108 _col2 = get<2>(rhs);
109 _col3 = get<3>(rhs);
110 return *this;
111 }
112
115 [[nodiscard]] constexpr explicit operator std::array<f32x4, 4>() const noexcept
116 {
117 hi_axiom(holds_invariant());
118 return {_col0, _col1, _col2, _col3};
119 }
120
130 [[nodiscard]] constexpr static matrix
131 uniform(aarectangle src_rectangle, aarectangle dst_rectangle, alignment alignment) noexcept;
132
133 template<int I>
134 [[nodiscard]] friend constexpr f32x4 const& get(matrix const& rhs) noexcept
135 {
136 if constexpr (I == 0) {
137 return rhs._col0;
138 } else if constexpr (I == 1) {
139 return rhs._col1;
140 } else if constexpr (I == 2) {
141 return rhs._col2;
142 } else if constexpr (I == 3) {
143 return rhs._col3;
144 } else {
146 }
147 }
148
149 template<int I>
150 [[nodiscard]] friend constexpr f32x4& get(matrix& rhs) noexcept
151 {
152 if constexpr (I == 0) {
153 return rhs._col0;
154 } else if constexpr (I == 1) {
155 return rhs._col1;
156 } else if constexpr (I == 2) {
157 return rhs._col2;
158 } else if constexpr (I == 3) {
159 return rhs._col3;
160 } else {
162 }
163 }
164
165 [[nodiscard]] constexpr bool holds_invariant() const noexcept
166 {
167 return true;
168 }
169
170 [[nodiscard]] constexpr auto operator*(f32x4 const& rhs) const noexcept
171 {
172 return f32x4{_col0 * rhs.xxxx() + _col1 * rhs.yyyy() + _col2 * rhs.zzzz() + _col3 * rhs.wwww()};
173 }
174
180 [[nodiscard]] constexpr float operator*(float const& rhs) const noexcept
181 {
182 // As if _col0 * rhs.xxxx() in operator*(f32x4 rhs)
183 auto abs_scale = hypot<D>(_col0 * f32x4::broadcast(rhs));
184
185 // We want to keep the sign of the original scaler, even if the matrix has rotation.
186 return std::copysign(abs_scale, rhs);
187 }
188
194 [[nodiscard]] constexpr corner_radii operator*(corner_radii const& rhs) const noexcept
195 {
196 return {*this * get<0>(rhs), *this * get<1>(rhs), *this * get<2>(rhs), *this * get<3>(rhs)};
197 }
198
199 template<int E>
200 [[nodiscard]] constexpr auto operator*(vector<E> const& rhs) const noexcept
201 {
202 hi_axiom(rhs.holds_invariant());
203 return vector<std::max(D, E)>{
204 _col0 * static_cast<f32x4>(rhs).xxxx() + _col1 * static_cast<f32x4>(rhs).yyyy() +
205 _col2 * static_cast<f32x4>(rhs).zzzz()};
206 }
207
208 template<int E>
209 [[nodiscard]] constexpr auto operator*(extent<E> const& rhs) const noexcept
210 {
211 hi_axiom(rhs.holds_invariant());
212 return extent<std::max(D, E)>{
213 _col0 * static_cast<f32x4>(rhs).xxxx() + _col1 * static_cast<f32x4>(rhs).yyyy() +
214 _col2 * static_cast<f32x4>(rhs).zzzz()};
215 }
216
217 template<int E>
218 [[nodiscard]] constexpr auto operator*(point<E> const& rhs) const noexcept
219 {
220 hi_axiom(rhs.holds_invariant());
221 return point<std::max(D, E)>{
222 _col0 * static_cast<f32x4>(rhs).xxxx() + _col1 * static_cast<f32x4>(rhs).yyyy() +
223 _col2 * static_cast<f32x4>(rhs).zzzz() + _col3 * static_cast<f32x4>(rhs).wwww()};
224 }
225
226 [[nodiscard]] constexpr rectangle operator*(aarectangle const& rhs) const noexcept
227 {
228 return *this * rectangle{rhs};
229 }
230
231 // XXX rectangle -> quad, perspective operation.
232 [[nodiscard]] constexpr rectangle operator*(rectangle const& rhs) const noexcept
233 {
234 return rectangle{*this * rhs.origin, *this * rhs.right, *this * rhs.up};
235 }
236
237 [[nodiscard]] constexpr quad operator*(quad const& rhs) const noexcept
238 {
239 return quad{*this * rhs.p0, *this * rhs.p1, *this * rhs.p2, *this * rhs.p3};
240 }
241
242 [[nodiscard]] constexpr circle operator*(circle const& rhs) const noexcept
243 {
244 return circle{*this * midpoint(rhs), *this * rhs.radius()};
245 }
246
247 [[nodiscard]] constexpr line_segment operator*(line_segment const& rhs) const noexcept
248 {
249 return line_segment{*this * rhs.origin(), *this * rhs.direction()};
250 }
251
256 [[nodiscard]] constexpr auto operator*(color const& rhs) const noexcept
257 {
258 hi_axiom(rhs.holds_invariant());
259 auto r = color{
260 _col0 * static_cast<f32x4>(rhs).xxxx() + _col1 * static_cast<f32x4>(rhs).yyyy() +
261 _col2 * static_cast<f32x4>(rhs).zzzz() + _col3};
262
263 r.a() = rhs.a();
264 return r;
265 }
266
269 [[nodiscard]] constexpr auto operator*(matrix const& rhs) const noexcept
270 {
271 return matrix{*this * get<0>(rhs), *this * get<1>(rhs), *this * get<2>(rhs), *this * get<3>(rhs)};
272 }
273
276 [[nodiscard]] friend constexpr matrix transpose(matrix const& rhs) noexcept
277 {
278 auto tmp = transpose(rhs._col0, rhs._col1, rhs._col2, rhs._col3);
279 return {std::get<0>(tmp), std::get<1>(tmp), std::get<2>(tmp), std::get<3>(tmp)};
280 }
281
282 template<char Axis>
283 [[nodiscard]] static constexpr f32x4 reflect_column() noexcept
284 {
285 if constexpr (Axis == 'x') {
286 return f32x4{1.0f, 0.0f, 0.0f, 0.0f};
287 } else if constexpr (Axis == 'X') {
288 return f32x4{-1.0f, 0.0f, 0.0f, 0.0f};
289 } else if constexpr (Axis == 'y') {
290 return f32x4{0.0f, 1.0f, 0.0f, 0.0f};
291 } else if constexpr (Axis == 'Y') {
292 return f32x4{0.0f, -1.0f, 0.0f, 0.0f};
293 } else if constexpr (Axis == 'z') {
294 return f32x4{0.0f, 0.0f, 1.0f, 0.0f};
295 } else if constexpr (Axis == 'Z') {
296 return f32x4{0.0f, 0.0f, -1.0f, 0.0f};
297 } else if constexpr (Axis == 'w') {
298 return f32x4{0.0f, 0.0f, 0.0f, 1.0f};
299 } else if constexpr (Axis == 'W') {
300 return f32x4{0.0f, 0.0f, 0.0f, -1.0f};
301 } else {
303 }
304 }
305
337 template<char DstX, char DstY, char DstZ, char DstW = 'w'>
338 [[nodiscard]] friend constexpr matrix reflect(matrix const& rhs) noexcept
339 requires(D == 3)
340 {
341 return matrix{reflect_column<DstX>(), reflect_column<DstY>(), reflect_column<DstZ>(), reflect_column<DstW>()} * rhs;
342 }
343
344 template<int E>
345 [[nodiscard]] constexpr bool operator==(matrix<E> const& rhs) const noexcept
346 {
347 return _col0 == rhs._col0 && _col1 == rhs._col1 && _col2 == rhs._col2 && _col3 == rhs._col3;
348 }
349
352 [[nodiscard]] constexpr matrix operator~() const
353 {
354 // rc
355 // var s0 : Number = i00 * i11 -
356 // i10 * i01;
357 // var c0 : Number = i20 * i31 -
358 // i30 * i21;
359 hilet s0c0 = _col0 * _col1.yxwz();
360
361 // var s1 : Number = i00 * i12 -
362 // i10 * i02;
363 // var c1 : Number = i20 * i32 -
364 // i30 * i22;
365 hilet s1c1 = _col0 * _col2.yxwz();
366 hilet s0c0s1c1 = hsub(s0c0, s1c1);
367
368 // var s2 : Number = i00 * i13 -
369 // i10 * i03;
370 // var c2 : Number = i20 * i33 -
371 // i30 * i23;
372 hilet s2c2 = _col0 * _col3.yxwz();
373
374 // var s3 : Number = i01 * i12 -
375 // i11 * i02;
376 // var c3 : Number = i21 * i32 -
377 // i31 * i22;
378 hilet s3c3 = _col1 * _col2.yxwz();
379 hilet s2c2s3c3 = hsub(s2c2, s3c3);
380
381 // var s4 : Number = i01 * i13 -
382 // i11 * i03;
383 // var c4 : Number = i21 * i33 -
384 // i31 * i23;
385 hilet s4c4 = _col1 * _col3.yxwz();
386
387 // var s5 : Number = i02 * i13 -
388 // i12 * i03;
389 // var c5 : Number = i22 * i33 -
390 // i32 * i23;
391 hilet s5c5 = _col2 * _col3.yxwz();
392 hilet s4c4s5c5 = hsub(s4c4, s5c5);
393
394 // det = (s0 * c5 +
395 // -s1 * c4 +
396 // s2 * c3 +
397 // s3 * c2 +
398 // -s4 * c1 +
399 // s5 * c0)
400 hilet s0123 = s0c0s1c1.xz00() + s2c2s3c3._00xz();
401 hilet s45__ = s4c4s5c5.xz00();
402
403 hilet c5432 = s4c4s5c5.wy00() + s2c2s3c3._00wy();
404 hilet c10__ = s0c0s1c1.wy00();
405
406 hilet det_prod_half0 = neg<0b0010>(s0123 * c5432);
407 hilet det_prod_half1 = neg<0b0001>(s45__ * c10__);
408
409 hilet det_sum0 = hadd(det_prod_half0, det_prod_half1);
410 hilet det_sum1 = hadd(det_sum0, det_sum0);
411 hilet det = hadd(det_sum1, det_sum1).xxxx();
412
413 if (det.x() == 0.0f) {
414 throw std::domain_error("Divide by zero");
415 }
416
417 hilet invdet = rcp(det);
418
419 hilet t = transpose(*this);
420
421 // rc rc rc rc
422 // m.i00 := (i11 * c5 + i12 * -c4 + i13 * c3) * invdet;
423 // m.i10 := (i10 * -c5 + i12 * c2 + i13 * -c1) * invdet;
424 // m.i20 := (i10 * c4 + i11 * -c2 + i13 * c0) * invdet;
425 // m.i30 := (i10 * -c3 + i11 * c1 + i12 * -c0) * invdet;
426 auto tmp_c5543 = neg<0b1010>(c5432.xxyz());
427 auto tmp_c4221 = neg<0b0101>(c5432.yww0() + c10__._000x());
428 auto tmp_c3100 = neg<0b1010>(c5432.z000() + c10__._0xyy());
429 hilet inv_col0 = ((t._col1.yxxx() * tmp_c5543) + (t._col1.zzyy() * tmp_c4221) + (t._col1.wwwz() * tmp_c3100)) * invdet;
430
431 // m.i01 := (i01 * -c5 + i02 * c4 + i03 * -c3) * invdet;
432 // m.i11 := (i00 * c5 + i02 * -c2 + i03 * c1) * invdet;
433 // m.i21 := (i00 * -c4 + i01 * c2 + i03 * -c0) * invdet;
434 // m.i31 := (i00 * c3 + i01 * -c1 + i02 * c0) * invdet;
435 tmp_c5543 = -tmp_c5543;
436 tmp_c4221 = -tmp_c4221;
437 tmp_c3100 = -tmp_c3100;
438 hilet inv_col1 = ((t._col0.yxxx() * tmp_c5543) + (t._col0.zzyy() * tmp_c4221) + (t._col0.wwwz() * tmp_c3100)) * invdet;
439
440 // m.i02 := (i31 * s5 + i32 * -s4 + i33 * s3) * invdet;
441 // m.i12 := (i30 * -s5 + i32 * s2 + i33 * -s1) * invdet;
442 // m.i22 := (i30 * s4 + i31 * -s2 + i33 * s0) * invdet;
443 // m.i32 := (i30 * -s3 + i31 * s1 + i32 * -s0) * invdet;
444 auto tmp_s5543 = neg<0b1010>(s45__.yyx0() + s0123._000w());
445 auto tmp_s4221 = neg<0b0101>(s45__.x000() + s0123._0zzy());
446 auto tmp_s3100 = neg<0b1010>(s0123.wyxx());
447 hilet inv_col2 = ((t._col3.yxxx() * tmp_s5543) + (t._col3.zzyy() * tmp_s4221) + (t._col3.wwwz() * tmp_s3100)) * invdet;
448
449 // m.i03 := (i21 * -s5 + i22 * s4 + i23 * -s3) * invdet;
450 // m.i13 := (i20 * s5 + i22 * -s2 + i23 * s1) * invdet;
451 // m.i23 := (i20 * -s4 + i21 * s2 + i23 * -s0) * invdet;
452 // m.i33 := (i20 * s3 + i21 * -s1 + i22 * s0) * invdet;
453 tmp_s5543 = -tmp_s5543;
454 tmp_s4221 = -tmp_s4221;
455 tmp_s3100 = -tmp_s3100;
456 hilet inv_col3 = ((t._col2.yxxx() * tmp_s5543) + (t._col2.zzyy() * tmp_s4221) + (t._col2.wwwz() * tmp_s3100)) * invdet;
457
458 return {inv_col0, inv_col1, inv_col2, inv_col3};
459 }
460
461private:
462 f32x4 _col0;
463 f32x4 _col1;
464 f32x4 _col2;
465 f32x4 _col3;
466};
467
468} // namespace geo
469
470using matrix2 = geo::matrix<2>;
471using matrix3 = geo::matrix<3>;
472
473} // namespace hi::inline v1
#define hi_axiom(expression)
Specify an axiom; an expression that is true.
Definition assert.hpp:133
#define hi_static_no_default()
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:172
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
@ rectangle
The gui_event has rectangle data.
DOXYGEN BUG.
Definition algorithm.hpp:15
@ circle
<circle> Encircled form.
Definition alignment.hpp:75
This is a RGBA floating point color.
Definition color.hpp:39
Class which represents an axis-aligned rectangle.
Definition axis_aligned_rectangle.hpp:20
Definition corner_radii.hpp:9
Definition matrix.hpp:23
friend constexpr matrix reflect(matrix const &rhs) noexcept
Reflect axis of a matrix.
Definition matrix.hpp:338
constexpr auto operator*(matrix const &rhs) const noexcept
Matrix/Matrix multiplication.
Definition matrix.hpp:269
constexpr corner_radii operator*(corner_radii const &rhs) const noexcept
Transform a float by the scaling factor of the matrix.
Definition matrix.hpp:194
constexpr float operator*(float const &rhs) const noexcept
Transform a float by the scaling factor of the matrix.
Definition matrix.hpp:180
constexpr matrix operator~() const
Invert matrix.
Definition matrix.hpp:352
constexpr auto operator*(color const &rhs) const noexcept
Transform a color by a color matrix.
Definition matrix.hpp:256
friend constexpr matrix transpose(matrix const &rhs) noexcept
Matrix transpose.
Definition matrix.hpp:276
T copysign(T... args)
T max(T... args)