HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
matrix.hpp
1// Copyright Take Vos 2021.
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
18namespace hi::inline v1 {
19namespace geo {
20
21template<int D>
22class matrix {
23public:
24 static_assert(D == 2 || D == 3, "Only 2D or 3D rotation-matrices are supported");
25
26 constexpr matrix(matrix const &) noexcept = default;
27 constexpr matrix(matrix &&) noexcept = default;
28 constexpr matrix &operator=(matrix const &) noexcept = default;
29 constexpr matrix &operator=(matrix &&) noexcept = default;
30
31 constexpr matrix() noexcept
32 {
33 hilet a = f32x4::broadcast(1.0f);
34 _col0 = a.x000();
35 _col1 = a._0y00();
36 _col2 = a._00z0();
37 _col3 = a._000w();
38 };
39
40 constexpr matrix(f32x4 col0, f32x4 col1, f32x4 col2, f32x4 col3 = f32x4{0.0f, 0.0f, 0.0f, 1.0f}) noexcept :
41 _col0(col0), _col1(col1), _col2(col2), _col3(col3)
42 {
43 }
44
45 constexpr matrix(vector3 col0, vector3 col1, vector3 col2) noexcept requires(D == 3) :
46 _col0(static_cast<f32x4>(col0)),
47 _col1(static_cast<f32x4>(col1)),
48 _col2(static_cast<f32x4>(col2)),
49 _col3{0.0f, 0.0f, 0.0f, 1.0f}
50 {
51 }
52
53 constexpr matrix(
54 float c0r0,
55 float c1r0,
56 float c2r0,
57 float c0r1,
58 float c1r1,
59 float c2r1,
60 float c0r2,
61 float c1r2,
62 float c2r2) noexcept requires(D == 3) :
63 _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)
64 {
65 }
66
67 constexpr matrix(
68 float c0r0,
69 float c1r0,
70 float c2r0,
71 float c3r0,
72 float c0r1,
73 float c1r1,
74 float c2r1,
75 float c3r1,
76 float c0r2,
77 float c1r2,
78 float c2r2,
79 float c3r2,
80 float c0r3,
81 float c1r3,
82 float c2r3,
83 float c3r3) noexcept requires(D == 3) :
84 _col0(c0r0, c0r1, c0r2, c0r3), _col1(c1r0, c1r1, c1r2, c1r3), _col2(c2r0, c2r1, c2r2, c2r3), _col3(c3r0, c3r1, c3r2, c3r3)
85 {
86 }
87
88 template<int E>
89 requires(E < D) [[nodiscard]] constexpr matrix(matrix<E> const &other) noexcept :
90 _col0(get<0>(other)), _col1(get<1>(other)), _col2(get<2>(other)), _col3(get<3>(other))
91 {
92 }
93
94 template<int E>
95 requires(E < D) constexpr matrix &operator=(matrix<E> const &rhs) noexcept
96 {
97 _col0 = get<0>(rhs);
98 _col1 = get<1>(rhs);
99 _col2 = get<2>(rhs);
100 _col3 = get<3>(rhs);
101 return *this;
102 }
103
113 [[nodiscard]] constexpr static matrix
114 uniform(aarectangle src_rectangle, aarectangle dst_rectangle, alignment alignment) noexcept;
115
116 template<int I>
117 [[nodiscard]] friend constexpr f32x4 get(matrix const &rhs) noexcept
118 {
119 if constexpr (I == 0) {
120 return rhs._col0;
121 } else if constexpr (I == 1) {
122 return rhs._col1;
123 } else if constexpr (I == 2) {
124 return rhs._col2;
125 } else if constexpr (I == 3) {
126 return rhs._col3;
127 } else {
128 hi_static_no_default();
129 }
130 }
131
132 [[nodiscard]] constexpr bool holds_invariant() const noexcept
133 {
134 return true;
135 }
136
137 [[nodiscard]] constexpr auto operator*(f32x4 const &rhs) const noexcept
138 {
139 return f32x4{_col0 * rhs.xxxx() + _col1 * rhs.yyyy() + _col2 * rhs.zzzz() + _col3 * rhs.wwww()};
140 }
141
147 [[nodiscard]] constexpr float operator*(float const &rhs) const noexcept
148 {
149 // As if _col0 * rhs.xxxx() in operator*(f32x4 rhs)
150 auto abs_scale = hypot<D>(_col0 * f32x4::broadcast(rhs));
151
152 // We want to keep the sign of the original scaler, even if the matrix has rotation.
153 return std::copysign(abs_scale, rhs);
154 }
155
161 [[nodiscard]] constexpr corner_radii operator*(corner_radii const &rhs) const noexcept
162 {
163 return {*this * get<0>(rhs), *this * get<1>(rhs), *this * get<2>(rhs), *this * get<3>(rhs)};
164 }
165
166 template<int E>
167 [[nodiscard]] constexpr auto operator*(vector<E> const &rhs) const noexcept
168 {
169 hi_axiom(rhs.holds_invariant());
170 return vector<std::max(D, E)>{
171 _col0 * static_cast<f32x4>(rhs).xxxx() + _col1 * static_cast<f32x4>(rhs).yyyy() +
172 _col2 * static_cast<f32x4>(rhs).zzzz()};
173 }
174
175 template<int E>
176 [[nodiscard]] constexpr auto operator*(extent<E> const &rhs) const noexcept
177 {
178 hi_axiom(rhs.holds_invariant());
179 return extent<std::max(D, E)>{
180 _col0 * static_cast<f32x4>(rhs).xxxx() + _col1 * static_cast<f32x4>(rhs).yyyy() +
181 _col2 * static_cast<f32x4>(rhs).zzzz()};
182 }
183
184 template<int E>
185 [[nodiscard]] constexpr auto operator*(point<E> const &rhs) const noexcept
186 {
187 hi_axiom(rhs.holds_invariant());
188 return point<std::max(D, E)>{
189 _col0 * static_cast<f32x4>(rhs).xxxx() + _col1 * static_cast<f32x4>(rhs).yyyy() +
190 _col2 * static_cast<f32x4>(rhs).zzzz() + _col3 * static_cast<f32x4>(rhs).wwww()};
191 }
192
193 [[nodiscard]] constexpr rectangle operator*(aarectangle const &rhs) const noexcept
194 {
195 return *this * rectangle{rhs};
196 }
197
198 // XXX rectangle -> quad, perspective operation.
199 [[nodiscard]] constexpr rectangle operator*(rectangle const &rhs) const noexcept
200 {
201 return rectangle{*this * rhs.origin, *this * rhs.right, *this * rhs.up};
202 }
203
204 [[nodiscard]] constexpr quad operator*(quad const &rhs) const noexcept
205 {
206 return quad{*this * rhs.p0, *this * rhs.p1, *this * rhs.p2, *this * rhs.p3};
207 }
208
209 [[nodiscard]] constexpr circle operator*(circle const &rhs) const noexcept
210 {
211 return circle{*this * midpoint(rhs), *this * rhs.radius()};
212 }
213
214 [[nodiscard]] constexpr line_segment operator*(line_segment const &rhs) const noexcept
215 {
216 return line_segment{*this * rhs.origin(), *this * rhs.direction()};
217 }
218
223 [[nodiscard]] constexpr auto operator*(color const &rhs) const noexcept
224 {
225 hi_axiom(rhs.holds_invariant());
226 auto r = color{
227 _col0 * static_cast<f32x4>(rhs).xxxx() + _col1 * static_cast<f32x4>(rhs).yyyy() +
228 _col2 * static_cast<f32x4>(rhs).zzzz() + _col3};
229
230 r.a() = rhs.a();
231 return r;
232 }
233
236 [[nodiscard]] constexpr auto operator*(matrix const &rhs) const noexcept
237 {
238 return matrix{*this * get<0>(rhs), *this * get<1>(rhs), *this * get<2>(rhs), *this * get<3>(rhs)};
239 }
240
243 [[nodiscard]] friend constexpr matrix transpose(matrix rhs) noexcept
244 {
245 auto tmp = transpose(rhs._col0, rhs._col1, rhs._col2, rhs._col3);
246 return {std::get<0>(tmp), std::get<1>(tmp), std::get<2>(tmp), std::get<3>(tmp)};
247 }
248
249 template<int E>
250 [[nodiscard]] constexpr bool operator==(matrix<E> const &rhs) const noexcept
251 {
252 return _col0 == rhs._col0 && _col1 == rhs._col1 && _col2 == rhs._col2 && _col3 == rhs._col3;
253 }
254
257 [[nodiscard]] constexpr matrix operator~() const
258 {
259 // rc
260 // var s0 : Number = i00 * i11 -
261 // i10 * i01;
262 // var c0 : Number = i20 * i31 -
263 // i30 * i21;
264 hilet s0c0 = _col0 * _col1.yxwz();
265
266 // var s1 : Number = i00 * i12 -
267 // i10 * i02;
268 // var c1 : Number = i20 * i32 -
269 // i30 * i22;
270 hilet s1c1 = _col0 * _col2.yxwz();
271 hilet s0c0s1c1 = hsub(s0c0, s1c1);
272
273 // var s2 : Number = i00 * i13 -
274 // i10 * i03;
275 // var c2 : Number = i20 * i33 -
276 // i30 * i23;
277 hilet s2c2 = _col0 * _col3.yxwz();
278
279 // var s3 : Number = i01 * i12 -
280 // i11 * i02;
281 // var c3 : Number = i21 * i32 -
282 // i31 * i22;
283 hilet s3c3 = _col1 * _col2.yxwz();
284 hilet s2c2s3c3 = hsub(s2c2, s3c3);
285
286 // var s4 : Number = i01 * i13 -
287 // i11 * i03;
288 // var c4 : Number = i21 * i33 -
289 // i31 * i23;
290 hilet s4c4 = _col1 * _col3.yxwz();
291
292 // var s5 : Number = i02 * i13 -
293 // i12 * i03;
294 // var c5 : Number = i22 * i33 -
295 // i32 * i23;
296 hilet s5c5 = _col2 * _col3.yxwz();
297 hilet s4c4s5c5 = hsub(s4c4, s5c5);
298
299 // det = (s0 * c5 +
300 // -s1 * c4 +
301 // s2 * c3 +
302 // s3 * c2 +
303 // -s4 * c1 +
304 // s5 * c0)
305 hilet s0123 = s0c0s1c1.xz00() + s2c2s3c3._00xz();
306 hilet s45__ = s4c4s5c5.xz00();
307
308 hilet c5432 = s4c4s5c5.wy00() + s2c2s3c3._00wy();
309 hilet c10__ = s0c0s1c1.wy00();
310
311 hilet det_prod_half0 = neg<0b0010>(s0123 * c5432);
312 hilet det_prod_half1 = neg<0b0001>(s45__ * c10__);
313
314 hilet det_sum0 = hadd(det_prod_half0, det_prod_half1);
315 hilet det_sum1 = hadd(det_sum0, det_sum0);
316 hilet det = hadd(det_sum1, det_sum1).xxxx();
317
318 if (det.x() == 0.0f) {
319 throw std::domain_error("Divide by zero");
320 }
321
322 hilet invdet = rcp(det);
323
324 hilet t = transpose(*this);
325
326 // rc rc rc rc
327 // m.i00 := (i11 * c5 + i12 * -c4 + i13 * c3) * invdet;
328 // m.i10 := (i10 * -c5 + i12 * c2 + i13 * -c1) * invdet;
329 // m.i20 := (i10 * c4 + i11 * -c2 + i13 * c0) * invdet;
330 // m.i30 := (i10 * -c3 + i11 * c1 + i12 * -c0) * invdet;
331 auto tmp_c5543 = neg<0b1010>(c5432.xxyz());
332 auto tmp_c4221 = neg<0b0101>(c5432.yww0() + c10__._000x());
333 auto tmp_c3100 = neg<0b1010>(c5432.z000() + c10__._0xyy());
334 hilet inv_col0 = ((t._col1.yxxx() * tmp_c5543) + (t._col1.zzyy() * tmp_c4221) + (t._col1.wwwz() * tmp_c3100)) * invdet;
335
336 // m.i01 := (i01 * -c5 + i02 * c4 + i03 * -c3) * invdet;
337 // m.i11 := (i00 * c5 + i02 * -c2 + i03 * c1) * invdet;
338 // m.i21 := (i00 * -c4 + i01 * c2 + i03 * -c0) * invdet;
339 // m.i31 := (i00 * c3 + i01 * -c1 + i02 * c0) * invdet;
340 tmp_c5543 = -tmp_c5543;
341 tmp_c4221 = -tmp_c4221;
342 tmp_c3100 = -tmp_c3100;
343 hilet inv_col1 = ((t._col0.yxxx() * tmp_c5543) + (t._col0.zzyy() * tmp_c4221) + (t._col0.wwwz() * tmp_c3100)) * invdet;
344
345 // m.i02 := (i31 * s5 + i32 * -s4 + i33 * s3) * invdet;
346 // m.i12 := (i30 * -s5 + i32 * s2 + i33 * -s1) * invdet;
347 // m.i22 := (i30 * s4 + i31 * -s2 + i33 * s0) * invdet;
348 // m.i32 := (i30 * -s3 + i31 * s1 + i32 * -s0) * invdet;
349 auto tmp_s5543 = neg<0b1010>(s45__.yyx0() + s0123._000w());
350 auto tmp_s4221 = neg<0b0101>(s45__.x000() + s0123._0zzy());
351 auto tmp_s3100 = neg<0b1010>(s0123.wyxx());
352 hilet inv_col2 = ((t._col3.yxxx() * tmp_s5543) + (t._col3.zzyy() * tmp_s4221) + (t._col3.wwwz() * tmp_s3100)) * invdet;
353
354 // m.i03 := (i21 * -s5 + i22 * s4 + i23 * -s3) * invdet;
355 // m.i13 := (i20 * s5 + i22 * -s2 + i23 * s1) * invdet;
356 // m.i23 := (i20 * -s4 + i21 * s2 + i23 * -s0) * invdet;
357 // m.i33 := (i20 * s3 + i21 * -s1 + i22 * s0) * invdet;
358 tmp_s5543 = -tmp_s5543;
359 tmp_s4221 = -tmp_s4221;
360 tmp_s3100 = -tmp_s3100;
361 hilet inv_col3 = ((t._col2.yxxx() * tmp_s5543) + (t._col2.zzyy() * tmp_s4221) + (t._col2.wwwz() * tmp_s3100)) * invdet;
362
363 return {inv_col0, inv_col1, inv_col2, inv_col3};
364 }
365
366private:
367 f32x4 _col0;
368 f32x4 _col1;
369 f32x4 _col2;
370 f32x4 _col3;
371};
372
373} // namespace geo
374
375using matrix2 = geo::matrix<2>;
376using matrix3 = geo::matrix<3>;
377
378} // namespace hi::inline v1
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
Definition alignment.hpp:64
This is a RGBA floating point color.
Definition color.hpp:37
Class which represents an axis-aligned rectangle.
Definition axis_aligned_rectangle.hpp:20
Definition corner_radii.hpp:9
Definition matrix.hpp:22
constexpr auto operator*(matrix const &rhs) const noexcept
Matrix/Matrix multiplication.
Definition matrix.hpp:236
constexpr corner_radii operator*(corner_radii const &rhs) const noexcept
Transform a float by the scaling factor of the matrix.
Definition matrix.hpp:161
constexpr float operator*(float const &rhs) const noexcept
Transform a float by the scaling factor of the matrix.
Definition matrix.hpp:147
constexpr matrix operator~() const
Invert matrix.
Definition matrix.hpp:257
friend constexpr matrix transpose(matrix rhs) noexcept
Matrix transpose.
Definition matrix.hpp:243
constexpr auto operator*(color const &rhs) const noexcept
Transform a color by a color matrix.
Definition matrix.hpp:223
T copysign(T... args)
T max(T... args)