| Flutter Impeller |

matrix.h
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef FLUTTER_IMPELLER_GEOMETRY_MATRIX_H_
6 #define FLUTTER_IMPELLER_GEOMETRY_MATRIX_H_
7
8 #include <cmath>
9 #include <iomanip>
10 #include <limits>
11 #include <optional>
12 #include <ostream>
13 #include <utility>
14
15 #include "impeller/geometry/matrix_decomposition.h"
16 #include "impeller/geometry/point.h"
17 #include "impeller/geometry/quaternion.h"
18 #include "impeller/geometry/scalar.h"
19 #include "impeller/geometry/shear.h"
20 #include "impeller/geometry/size.h"
21 #include "impeller/geometry/vector.h"
22
23 namespace impeller {
24
25 //------------------------------------------------------------------------------
26 /// @brief A 4x4 matrix using column-major storage.
27 ///
28 /// Utility methods that need to make assumptions about normalized
29 /// device coordinates must use the following convention:
30 /// * Left-handed coordinate system. Positive rotation is
31 /// clockwise about axis of rotation.
32 /// * Lower left corner is -1.0f, -1.0.
33 /// * Upper right corner is 1.0f, 1.0.
34 /// * Visible z-space is from 0.0 to 1.0.
35 /// * This is NOT the same as OpenGL! Be careful.
36 /// * NDC origin is at (0.0f, 0.0f, 0.5f).
37 struct Matrix {
38 union {
39 Scalar m[16];
40 Scalar e[4][4];
41 Vector4 vec[4];
42 };
43
44 //----------------------------------------------------------------------------
45 /// Constructs a default identity matrix.
46 ///
47 constexpr Matrix()
48 // clang-format off
49 : vec{ Vector4(1.0f, 0.0f, 0.0f, 0.0f),
50 Vector4(0.0f, 1.0f, 0.0f, 0.0f),
51 Vector4(0.0f, 0.0f, 1.0f, 0.0f),
52 Vector4(0.0f, 0.0f, 0.0f, 1.0f)} {}
53 // clang-format on
54
55 // clang-format off
56 constexpr Matrix(Scalar m0, Scalar m1, Scalar m2, Scalar m3,
57 Scalar m4, Scalar m5, Scalar m6, Scalar m7,
58 Scalar m8, Scalar m9, Scalar m10, Scalar m11,
59 Scalar m12, Scalar m13, Scalar m14, Scalar m15)
60 : vec{Vector4(m0, m1, m2, m3),
61 Vector4(m4, m5, m6, m7),
62 Vector4(m8, m9, m10, m11),
63 Vector4(m12, m13, m14, m15)} {}
64 // clang-format on
65
66 explicit Matrix(const MatrixDecomposition& decomposition);
67
68 // clang-format off
69 static constexpr Matrix MakeColumn(
70 Scalar m0, Scalar m1, Scalar m2, Scalar m3,
71 Scalar m4, Scalar m5, Scalar m6, Scalar m7,
72 Scalar m8, Scalar m9, Scalar m10, Scalar m11,
73 Scalar m12, Scalar m13, Scalar m14, Scalar m15){
74 return Matrix(m0, m1, m2, m3,
75 m4, m5, m6, m7,
76 m8, m9, m10, m11,
77 m12, m13, m14, m15);
78
79 }
80 // clang-format on
81
82 // clang-format off
83 static constexpr Matrix MakeRow(
84 Scalar m0, Scalar m1, Scalar m2, Scalar m3,
85 Scalar m4, Scalar m5, Scalar m6, Scalar m7,
86 Scalar m8, Scalar m9, Scalar m10, Scalar m11,
87 Scalar m12, Scalar m13, Scalar m14, Scalar m15){
88 return Matrix(m0, m4, m8, m12,
89 m1, m5, m9, m13,
90 m2, m6, m10, m14,
91 m3, m7, m11, m15);
92 }
93 // clang-format on
94
95 static constexpr Matrix MakeTranslation(const Vector3& t) {
96 // clang-format off
97 return Matrix(1.0f, 0.0f, 0.0f, 0.0f,
98 0.0f, 1.0f, 0.0f, 0.0f,
99 0.0f, 0.0f, 1.0f, 0.0f,
100 t.x, t.y, t.z, 1.0f);
101 // clang-format on
102 }
103
104 static constexpr Matrix MakeScale(const Vector3& s) {
105 // clang-format off
106 return Matrix(s.x, 0.0f, 0.0f, 0.0f,
107 0.0f, s.y, 0.0f, 0.0f,
108 0.0f, 0.0f, s.z, 0.0f,
109 0.0f, 0.0f, 0.0f, 1.0f);
110 // clang-format on
111 }
112
113 static constexpr Matrix MakeScale(const Vector2& s) {
114 return MakeScale(Vector3(s.x, s.y, 1.0f));
115 }
116
117 static constexpr Matrix MakeSkew(Scalar sx, Scalar sy) {
118 // clang-format off
119 return Matrix(1.0f, sy , 0.0f, 0.0f,
120 sx , 1.0f, 0.0f, 0.0f,
121 0.0f, 0.0f, 1.0f, 0.0f,
122 0.0f, 0.0f, 0.0f, 1.0f);
123 // clang-format on
124 }
125
126 static Matrix MakeRotation(Quaternion q) {
127 // clang-format off
128 return Matrix(
129 1.0f - 2.0f * q.y * q.y - 2.0f * q.z * q.z,
130 2.0f * q.x * q.y + 2.0f * q.z * q.w,
131 2.0f * q.x * q.z - 2.0f * q.y * q.w,
132 0.0f,
133
134 2.0f * q.x * q.y - 2.0f * q.z * q.w,
135 1.0f - 2.0f * q.x * q.x - 2.0f * q.z * q.z,
136 2.0f * q.y * q.z + 2.0f * q.x * q.w,
137 0.0f,
138
139 2.0f * q.x * q.z + 2.0f * q.y * q.w,
140 2.0f * q.y * q.z - 2.0f * q.x * q.w,
141 1.0f - 2.0f * q.x * q.x - 2.0f * q.y * q.y,
142 0.0f,
143
144 0.0f,
145 0.0f,
146 0.0f,
147 1.0f);
148 // clang-format on
149 }
150
151 static Matrix MakeRotation(Radians radians, const Vector4& r) {
152 const Vector4 v = r.Normalize();
153
154 const Vector2 cos_sin = CosSin(radians);
155 const Scalar cosine = cos_sin.x;
156 const Scalar cosp = 1.0f - cosine;
157 const Scalar sine = cos_sin.y;
158
159 // clang-format off
160 return Matrix(
161 cosine + cosp * v.x * v.x,
162 cosp * v.x * v.y + v.z * sine,
163 cosp * v.x * v.z - v.y * sine,
164 0.0f,
165
166 cosp * v.x * v.y - v.z * sine,
167 cosine + cosp * v.y * v.y,
168 cosp * v.y * v.z + v.x * sine,
169 0.0f,
170
171 cosp * v.x * v.z + v.y * sine,
172 cosp * v.y * v.z - v.x * sine,
173 cosine + cosp * v.z * v.z,
174 0.0f,
175
176 0.0f,
177 0.0f,
178 0.0f,
179 1.0f);
180 // clang-format on
181 }
182
183 static Matrix MakeRotationX(Radians r) {
184 const Vector2 cos_sin = CosSin(r);
185 const Scalar cosine = cos_sin.x;
186 const Scalar sine = cos_sin.y;
187
188 // clang-format off
189 return Matrix(
190 1.0f, 0.0f, 0.0f, 0.0f,
191 0.0f, cosine, sine, 0.0f,
192 0.0f, -sine, cosine, 0.0f,
193 0.0f, 0.0f, 0.0f, 1.0f
194 );
195 // clang-format on
196 }
197
198 static Matrix MakeRotationY(Radians r) {
199 const Vector2 cos_sin = CosSin(r);
200 const Scalar cosine = cos_sin.x;
201 const Scalar sine = cos_sin.y;
202
203 // clang-format off
204 return Matrix(
205 cosine, 0.0f, -sine, 0.0f,
206 0.0f, 1.0f, 0.0f, 0.0f,
207 sine, 0.0f, cosine, 0.0f,
208 0.0f, 0.0f, 0.0f, 1.0f
209 );
210 // clang-format on
211 }
212
213 static Matrix MakeRotationZ(Radians r) {
214 const Vector2 cos_sin = CosSin(r);
215 const Scalar cosine = cos_sin.x;
216 const Scalar sine = cos_sin.y;
217
218 // clang-format off
219 return Matrix (
220 cosine, sine, 0.0f, 0.0f,
221 -sine, cosine, 0.0f, 0.0f,
222 0.0f, 0.0f, 1.0f, 0.0f,
223 0.0f, 0.0f, 0.0f, 1.0
224 );
225 // clang-format on
226 }
227
228 /// The Matrix without its `w` components (without translation).
229 constexpr Matrix Basis() const {
230 // clang-format off
231 return Matrix(
232 m[0], m[1], m[2], 0.0f,
233 m[4], m[5], m[6], 0.0f,
234 m[8], m[9], m[10], 0.0f,
235 0.0f, 0.0f, 0.0f, 1.0
236 );
237 // clang-format on
238 }
239
240 constexpr Matrix Translate(const Vector3& t) const {
241 // clang-format off
242 return Matrix(m[0], m[1], m[2], m[3],
243 m[4], m[5], m[6], m[7],
244 m[8], m[9], m[10], m[11],
245 m[0] * t.x + m[4] * t.y + m[8] * t.z + m[12],
246 m[1] * t.x + m[5] * t.y + m[9] * t.z + m[13],
247 m[2] * t.x + m[6] * t.y + m[10] * t.z + m[14],
248 m[3] * t.x + m[7] * t.y + m[11] * t.z + m[15]);
249 // clang-format on
250 }
251
252 constexpr Matrix Scale(const Vector3& s) const {
253 // clang-format off
254 return Matrix(m[0] * s.x, m[1] * s.x, m[2] * s.x, m[3] * s.x,
255 m[4] * s.y, m[5] * s.y, m[6] * s.y, m[7] * s.y,
256 m[8] * s.z, m[9] * s.z, m[10] * s.z, m[11] * s.z,
257 m[12] , m[13] , m[14] , m[15] );
258 // clang-format on
259 }
260
261 constexpr Matrix Multiply(const Matrix& o) const {
262 // clang-format off
263 return Matrix(
264 m[0] * o.m[0] + m[4] * o.m[1] + m[8] * o.m[2] + m[12] * o.m[3],
265 m[1] * o.m[0] + m[5] * o.m[1] + m[9] * o.m[2] + m[13] * o.m[3],
266 m[2] * o.m[0] + m[6] * o.m[1] + m[10] * o.m[2] + m[14] * o.m[3],
267 m[3] * o.m[0] + m[7] * o.m[1] + m[11] * o.m[2] + m[15] * o.m[3],
268 m[0] * o.m[4] + m[4] * o.m[5] + m[8] * o.m[6] + m[12] * o.m[7],
269 m[1] * o.m[4] + m[5] * o.m[5] + m[9] * o.m[6] + m[13] * o.m[7],
270 m[2] * o.m[4] + m[6] * o.m[5] + m[10] * o.m[6] + m[14] * o.m[7],
271 m[3] * o.m[4] + m[7] * o.m[5] + m[11] * o.m[6] + m[15] * o.m[7],
272 m[0] * o.m[8] + m[4] * o.m[9] + m[8] * o.m[10] + m[12] * o.m[11],
273 m[1] * o.m[8] + m[5] * o.m[9] + m[9] * o.m[10] + m[13] * o.m[11],
274 m[2] * o.m[8] + m[6] * o.m[9] + m[10] * o.m[10] + m[14] * o.m[11],
275 m[3] * o.m[8] + m[7] * o.m[9] + m[11] * o.m[10] + m[15] * o.m[11],
276 m[0] * o.m[12] + m[4] * o.m[13] + m[8] * o.m[14] + m[12] * o.m[15],
277 m[1] * o.m[12] + m[5] * o.m[13] + m[9] * o.m[14] + m[13] * o.m[15],
278 m[2] * o.m[12] + m[6] * o.m[13] + m[10] * o.m[14] + m[14] * o.m[15],
279 m[3] * o.m[12] + m[7] * o.m[13] + m[11] * o.m[14] + m[15] * o.m[15]);
280 // clang-format on
281 }
282
283 constexpr Matrix Transpose() const {
284 // clang-format off
285 return {
286 m[0], m[4], m[8], m[12],
287 m[1], m[5], m[9], m[13],
288 m[2], m[6], m[10], m[14],
289 m[3], m[7], m[11], m[15],
290 };
291 // clang-format on
292 }
293
294 Matrix Invert() const;
295
296 Scalar GetDeterminant() const;
297
298 constexpr Scalar GetMaxBasisLengthXY() const {
299 // The full basis computation requires computing the squared scaling factor
300 // for translate/scale only matrices. This substantially limits the range of
301 // precision for small and large scales. Instead, check for the common cases
302 // and directly return the max scaling factor.
303 if (e[0][1] == 0 && e[1][0] == 0) {
304 return std::max(std::abs(e[0][0]), std::abs(e[1][1]));
305 }
306 return std::sqrt(std::max(e[0][0] * e[0][0] + e[0][1] * e[0][1],
307 e[1][0] * e[1][0] + e[1][1] * e[1][1]));
308 }
309
310 constexpr Vector3 GetBasisX() const { return Vector3(m[0], m[1], m[2]); }
311
312 constexpr Vector3 GetBasisY() const { return Vector3(m[4], m[5], m[6]); }
313
314 constexpr Vector3 GetBasisZ() const { return Vector3(m[8], m[9], m[10]); }
315
316 constexpr Vector3 GetScale() const {
317 return Vector3(GetBasisX().GetLength(), GetBasisY().GetLength(),
318 GetBasisZ().GetLength());
319 }
320
321 constexpr Scalar GetDirectionScale(Vector3 direction) const {
322 return 1.0f / (this->Basis().Invert() * direction.Normalize()).GetLength() *
323 direction.GetLength();
324 }
325
326 constexpr bool IsAffine() const {
327 return (m[2] == 0 && m[3] == 0 && m[6] == 0 && m[7] == 0 && m[8] == 0 &&
328 m[9] == 0 && m[10] == 1 && m[11] == 0 && m[14] == 0 && m[15] == 1);
329 }
330
331 constexpr bool HasPerspective2D() const {
332 return m[3] != 0 || m[7] != 0 || m[15] != 1;
333 }
334
335 constexpr bool HasPerspective() const {
336 return m[3] != 0 || m[7] != 0 || m[11] != 0 || m[15] != 1;
337 }
338
339 constexpr bool HasTranslation() const { return m[12] != 0 || m[13] != 0; }
340
341 constexpr bool IsAligned2D(Scalar tolerance = 0) const {
342 if (HasPerspective2D()) {
343 return false;
344 }
345 if (ScalarNearlyZero(m[1], tolerance) &&
346 ScalarNearlyZero(m[4], tolerance)) {
347 return true;
348 }
349 if (ScalarNearlyZero(m[0], tolerance) &&
350 ScalarNearlyZero(m[5], tolerance)) {
351 return true;
352 }
353 return false;
354 }
355
356 constexpr bool IsAligned(Scalar tolerance = 0) const {
357 if (HasPerspective()) {
358 return false;
359 }
360 int v[] = {!ScalarNearlyZero(m[0], tolerance), //
361 !ScalarNearlyZero(m[1], tolerance), //
362 !ScalarNearlyZero(m[2], tolerance), //
363 !ScalarNearlyZero(m[4], tolerance), //
364 !ScalarNearlyZero(m[5], tolerance), //
365 !ScalarNearlyZero(m[6], tolerance), //
366 !ScalarNearlyZero(m[8], tolerance), //
367 !ScalarNearlyZero(m[9], tolerance), //
368 !ScalarNearlyZero(m[10], tolerance)};
369 // Check if all three basis vectors are aligned to an axis.
370 if (v[0] + v[1] + v[2] != 1 || //
371 v[3] + v[4] + v[5] != 1 || //
372 v[6] + v[7] + v[8] != 1) {
373 return false;
374 }
375 // Ensure that none of the basis vectors overlap.
376 if (v[0] + v[3] + v[6] != 1 || //
377 v[1] + v[4] + v[7] != 1 || //
378 v[2] + v[5] + v[8] != 1) {
379 return false;
380 }
381 return true;
382 }
383
384 constexpr bool IsIdentity() const {
385 return (
386 // clang-format off
387 m[0] == 1.0f && m[1] == 0.0f && m[2] == 0.0f && m[3] == 0.0f &&
388 m[4] == 0.0f && m[5] == 1.0f && m[6] == 0.0f && m[7] == 0.0f &&
389 m[8] == 0.0f && m[9] == 0.0f && m[10] == 1.0f && m[11] == 0.0f &&
390 m[12] == 0.0f && m[13] == 0.0f && m[14] == 0.0f && m[15] == 1.0f
391 // clang-format on
392 );
393 }
394
395 /// @brief Returns true if the matrix has a scale-only basis and is
396 /// non-projective. Note that an identity matrix meets this criteria.
397 constexpr bool IsTranslationScaleOnly() const {
398 return (
399 // clang-format off
400 m[0] != 0.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 0.0 &&
401 m[4] == 0.0 && m[5] != 0.0 && m[6] == 0.0 && m[7] == 0.0 &&
402 m[8] == 0.0 && m[9] == 0.0 && m[10] != 0.0 && m[11] == 0.0 &&
403 m[15] == 1.0
404 // clang-format on
405 );
406 }
407
408 std::optional<MatrixDecomposition> Decompose() const;
409
410 bool Equals(const Matrix& matrix, Scalar epsilon = 1e-5f) const {
411 const Scalar* a = m;
412 const Scalar* b = matrix.m;
413 return ScalarNearlyEqual(a[0], b[0], epsilon) &&
414 ScalarNearlyEqual(a[1], b[1], epsilon) &&
415 ScalarNearlyEqual(a[2], b[2], epsilon) &&
416 ScalarNearlyEqual(a[3], b[3], epsilon) &&
417 ScalarNearlyEqual(a[4], b[4], epsilon) &&
418 ScalarNearlyEqual(a[5], b[5], epsilon) &&
419 ScalarNearlyEqual(a[6], b[6], epsilon) &&
420 ScalarNearlyEqual(a[7], b[7], epsilon) &&
421 ScalarNearlyEqual(a[8], b[8], epsilon) &&
422 ScalarNearlyEqual(a[9], b[9], epsilon) &&
423 ScalarNearlyEqual(a[10], b[10], epsilon) &&
424 ScalarNearlyEqual(a[11], b[11], epsilon) &&
425 ScalarNearlyEqual(a[12], b[12], epsilon) &&
426 ScalarNearlyEqual(a[13], b[13], epsilon) &&
427 ScalarNearlyEqual(a[14], b[14], epsilon) &&
428 ScalarNearlyEqual(a[15], b[15], epsilon);
429 }
430
431 constexpr bool operator==(const Matrix& m) const {
432 // clang-format off
433 return vec[0] == m.vec[0]
434 && vec[1] == m.vec[1]
435 && vec[2] == m.vec[2]
436 && vec[3] == m.vec[3];
437 // clang-format on
438 }
439
440 constexpr bool operator!=(const Matrix& m) const {
441 // clang-format off
442 return vec[0] != m.vec[0]
443 || vec[1] != m.vec[1]
444 || vec[2] != m.vec[2]
445 || vec[3] != m.vec[3];
446 // clang-format on
447 }
448
449 Matrix operator+(const Vector3& t) const { return Translate(t); }
450
451 Matrix operator-(const Vector3& t) const { return Translate(-t); }
452
453 Matrix operator*(const Matrix& m) const { return Multiply(m); }
454
455 Matrix operator+(const Matrix& m) const;
456
457 constexpr Vector4 operator*(const Vector4& v) const {
458 return Vector4(v.x * m[0] + v.y * m[4] + v.z * m[8] + v.w * m[12],
459 v.x * m[1] + v.y * m[5] + v.z * m[9] + v.w * m[13],
460 v.x * m[2] + v.y * m[6] + v.z * m[10] + v.w * m[14],
461 v.x * m[3] + v.y * m[7] + v.z * m[11] + v.w * m[15]);
462 }
463
464 constexpr Vector3 operator*(const Vector3& v) const {
465 Scalar w = v.x * m[3] + v.y * m[7] + v.z * m[11] + m[15];
466 Vector3 result(v.x * m[0] + v.y * m[4] + v.z * m[8] + m[12],
467 v.x * m[1] + v.y * m[5] + v.z * m[9] + m[13],
468 v.x * m[2] + v.y * m[6] + v.z * m[10] + m[14]);
469
470 // This is Skia's behavior, but it may be reasonable to allow UB for the w=0
471 // case.
472 if (w) {
473 w = 1 / w;
474 }
475 return result * w;
476 }
477
478 constexpr Point operator*(const Point& v) const {
479 Scalar w = v.x * m[3] + v.y * m[7] + m[15];
480 Point result(v.x * m[0] + v.y * m[4] + m[12],
481 v.x * m[1] + v.y * m[5] + m[13]);
482
483 // This is Skia's behavior, but it may be reasonable to allow UB for the w=0
484 // case.
485 if (w) {
486 w = 1 / w;
487 }
488 return result * w;
489 }
490
491 constexpr Vector3 TransformHomogenous(const Point& v) const {
492 return Vector3(v.x * m[0] + v.y * m[4] + m[12],
493 v.x * m[1] + v.y * m[5] + m[13],
494 v.x * m[3] + v.y * m[7] + m[15]);
495 }
496
497 constexpr Vector4 TransformDirection(const Vector4& v) const {
498 return Vector4(v.x * m[0] + v.y * m[4] + v.z * m[8],
499 v.x * m[1] + v.y * m[5] + v.z * m[9],
500 v.x * m[2] + v.y * m[6] + v.z * m[10], v.w);
501 }
502
503 constexpr Vector3 TransformDirection(const Vector3& v) const {
504 return Vector3(v.x * m[0] + v.y * m[4] + v.z * m[8],
505 v.x * m[1] + v.y * m[5] + v.z * m[9],
506 v.x * m[2] + v.y * m[6] + v.z * m[10]);
507 }
508
509 constexpr Vector2 TransformDirection(const Vector2& v) const {
510 return Vector2(v.x * m[0] + v.y * m[4], v.x * m[1] + v.y * m[5]);
511 }
512
513 constexpr Quad Transform(const Quad& quad) const {
514 return {
515 *this * quad[0],
516 *this * quad[1],
517 *this * quad[2],
518 *this * quad[3],
519 };
520 }
521
522 template <class T>
523 static constexpr Matrix MakeOrthographic(TSize<T> size) {
524 // Per assumptions about NDC documented above.
525 const auto scale =
526 MakeScale({2.0f / static_cast<Scalar>(size.width),
527 -2.0f / static_cast<Scalar>(size.height), 0.0f});
528 const auto translate = MakeTranslation({-1.0f, 1.0f, 0.5f});
529 return translate * scale;
530 }
531
532 static constexpr Matrix MakePerspective(Radians fov_y,
533 Scalar aspect_ratio,
534 Scalar z_near,
535 Scalar z_far) {
536 Scalar height = std::tan(fov_y.radians * 0.5f);
537 Scalar width = height * aspect_ratio;
538
539 // clang-format off
540 return {
541 1.0f / width, 0.0f, 0.0f, 0.0f,
542 0.0f, 1.0f / height, 0.0f, 0.0f,
543 0.0f, 0.0f, z_far / (z_far - z_near), 1.0f,
544 0.0f, 0.0f, -(z_far * z_near) / (z_far - z_near), 0.0f,
545 };
546 // clang-format on
547 }
548
549 template <class T>
550 static constexpr Matrix MakePerspective(Radians fov_y,
551 TSize<T> size,
552 Scalar z_near,
553 Scalar z_far) {
554 return MakePerspective(fov_y, static_cast<Scalar>(size.width) / size.height,
555 z_near, z_far);
556 }
557
558 static constexpr Matrix MakeLookAt(Vector3 position,
559 Vector3 target,
560 Vector3 up) {
561 Vector3 forward = (target - position).Normalize();
562 Vector3 right = up.Cross(forward);
563 up = forward.Cross(right);
564
565 // clang-format off
566 return {
567 right.x, up.x, forward.x, 0.0f,
568 right.y, up.y, forward.y, 0.0f,
569 right.z, up.z, forward.z, 0.0f,
570 -right.Dot(position), -up.Dot(position), -forward.Dot(position), 1.0f
571 };
572 // clang-format on
573 }
574
575 private:
576 static constexpr Vector2 CosSin(Radians radians) {
577 // The precision of a float around 1.0 is much lower than it is
578 // around 0.0, so we end up with cases on quadrant rotations where
579 // we get a +/-1.0 for one of the values and a non-zero value for
580 // the other. This happens around quadrant rotations which makes it
581 // especially common and results in unclean quadrant rotation
582 // matrices which do not return true from |IsAligned[2D]| even
583 // though that is exactly where you need them to exhibit that property.
584 // It also injects small floating point mantissa errors into the
585 // matrices whenever you concatenate them with a quadrant rotation.
586 //
587 // This issue is also exacerbated by the fact that, in radians, the
588 // angles for quadrant rotations are irrational numbers. The measuring
589 // error for representing 90 degree multiples is small enough that
590 // either sin or cos will return a value near +/-1.0, but not small
591 // enough that the other value will be a clean 0.0.
592 //
593 // Some geometry packages simply discard very small numbers from
594 // sin/cos, but the following approach specifically targets just the
595 // area around a quadrant rotation (where either the sin or cos are
596 // measuring as +/-1.0) for symmetry of precision.
597
598 Scalar sin = std::sin(radians.radians);
599 if (std::abs(sin) == 1.0f) {
600 // 90 or 270 degrees (mod 360)
601 return {0.0f, sin};
602 } else {
603 Scalar cos = std::cos(radians.radians);
604 if (std::abs(cos) == 1.0f) {
605 // 0 or 180 degrees (mod 360)
606 return {cos, 0.0f};
607 }
608 return {cos, sin};
609 }
610 }
611 };
612
613 static_assert(sizeof(struct Matrix) == sizeof(Scalar) * 16,
614 "The matrix must be of consistent size.");
615
616 } // namespace impeller
617
618 namespace std {
619 inline std::ostream& operator<<(std::ostream& out, const impeller::Matrix& m) {
620 out << "(" << std::endl << std::fixed;
621 for (size_t i = 0; i < 4u; i++) {
622 for (size_t j = 0; j < 4u; j++) {
623 out << std::setw(15) << m.e[j][i] << ",";
624 }
625 out << std::endl;
626 }
627 out << ")";
628 return out;
629 }
630
631 } // namespace std
632
633 #endif // FLUTTER_IMPELLER_GEOMETRY_MATRIX_H_
impeller::Matrix::MakeSkew
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
Definition: matrix.h:117
impeller::Matrix::HasPerspective
constexpr bool HasPerspective() const
Definition: matrix.h:335
impeller::Matrix::operator+
Matrix operator+(const Vector3 &t) const
Definition: matrix.h:449
impeller::Matrix::m
Scalar m[16]
Definition: matrix.h:39
impeller::Vector3::Dot
constexpr Scalar Dot(const Vector3 &other) const
Definition: vector.h:54
impeller::Matrix::TransformHomogenous
constexpr Vector3 TransformHomogenous(const Point &v) const
Definition: matrix.h:491
impeller::Matrix::Decompose
std::optional< MatrixDecomposition > Decompose() const
Definition: matrix.cc:200
impeller::Matrix::Equals
bool Equals(const Matrix &matrix, Scalar epsilon=1e-5f) const
Definition: matrix.h:410
impeller::Matrix::MakeRotationX
static Matrix MakeRotationX(Radians r)
Definition: matrix.h:183
impeller::Matrix::Matrix
constexpr Matrix()
Definition: matrix.h:47
impeller::Quaternion::z
Scalar z
Definition: quaternion.h:19
point.h
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::Vector3::Cross
constexpr Vector3 Cross(const Vector3 &other) const
Definition: vector.h:62
impeller::Matrix::MakePerspective
static constexpr Matrix MakePerspective(Radians fov_y, TSize< T > size, Scalar z_near, Scalar z_far)
Definition: matrix.h:550
impeller::Vector4::Normalize
Vector4 Normalize() const
Definition: vector.h:258
impeller::Quaternion::w
Scalar w
Definition: quaternion.h:20
impeller::Matrix::operator-
Matrix operator-(const Vector3 &t) const
Definition: matrix.h:451
impeller::Matrix::operator*
constexpr Point operator*(const Point &v) const
Definition: matrix.h:478
impeller::Vector4
Definition: vector.h:232
impeller::Matrix::MakeRotation
static Matrix MakeRotation(Quaternion q)
Definition: matrix.h:126
impeller::Vector2
Point Vector2
Definition: point.h:331
impeller::Matrix::MakeRotationY
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:198
impeller::Matrix::GetDeterminant
Scalar GetDeterminant() const
Definition: matrix.cc:162
quaternion.h
std::operator<<
std::ostream & operator<<(std::ostream &out, const impeller::Color &c)
Definition: color.h:926
impeller::Matrix::operator*
Matrix operator*(const Matrix &m) const
Definition: matrix.h:453
impeller::Radians::radians
Scalar radians
Definition: scalar.h:44
impeller::Matrix::MakeRow
static constexpr Matrix MakeRow(Scalar m0, Scalar m1, Scalar m2, Scalar m3, Scalar m4, Scalar m5, Scalar m6, Scalar m7, Scalar m8, Scalar m9, Scalar m10, Scalar m11, Scalar m12, Scalar m13, Scalar m14, Scalar m15)
Definition: matrix.h:83
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::Vector3::x
Scalar x
Definition: vector.h:23
impeller::Matrix::MakeRotation
static Matrix MakeRotation(Radians radians, const Vector4 &r)
Definition: matrix.h:151
impeller::Quaternion::x
Scalar x
Definition: quaternion.h:17
impeller::Matrix::e
Scalar e[4][4]
Definition: matrix.h:40
impeller::Matrix::vec
Vector4 vec[4]
Definition: matrix.h:41
impeller::Matrix::MakeLookAt
static constexpr Matrix MakeLookAt(Vector3 position, Vector3 target, Vector3 up)
Definition: matrix.h:558
impeller::Matrix::MakePerspective
static constexpr Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:532
impeller::Matrix::Basis
constexpr Matrix Basis() const
The Matrix without its w components (without translation).
Definition: matrix.h:229
impeller::TSize
Definition: size.h:19
impeller::Matrix::TransformDirection
constexpr Vector3 TransformDirection(const Vector3 &v) const
Definition: matrix.h:503
impeller::Quaternion
Definition: quaternion.h:14
impeller::Quad
std::array< Point, 4 > Quad
Definition: point.h:332
impeller::Matrix::Multiply
constexpr Matrix Multiply(const Matrix &o) const
Definition: matrix.h:261
impeller::Matrix::GetScale
constexpr Vector3 GetScale() const
Definition: matrix.h:316
impeller::Matrix::operator==
constexpr bool operator==(const Matrix &m) const
Definition: matrix.h:431
impeller::Matrix::Transpose
constexpr Matrix Transpose() const
Definition: matrix.h:283
impeller::Matrix::operator*
constexpr Vector4 operator*(const Vector4 &v) const
Definition: matrix.h:457
impeller::Matrix::IsTranslationScaleOnly
constexpr bool IsTranslationScaleOnly() const
Returns true if the matrix has a scale-only basis and is non-projective. Note that an identity matrix...
Definition: matrix.h:397
impeller::Matrix::Translate
constexpr Matrix Translate(const Vector3 &t) const
Definition: matrix.h:240
impeller::Matrix::Matrix
constexpr Matrix(Scalar m0, Scalar m1, Scalar m2, Scalar m3, Scalar m4, Scalar m5, Scalar m6, Scalar m7, Scalar m8, Scalar m9, Scalar m10, Scalar m11, Scalar m12, Scalar m13, Scalar m14, Scalar m15)
Definition: matrix.h:56
impeller::Matrix::MakeColumn
static constexpr Matrix MakeColumn(Scalar m0, Scalar m1, Scalar m2, Scalar m3, Scalar m4, Scalar m5, Scalar m6, Scalar m7, Scalar m8, Scalar m9, Scalar m10, Scalar m11, Scalar m12, Scalar m13, Scalar m14, Scalar m15)
Definition: matrix.h:69
impeller::Matrix::TransformDirection
constexpr Vector2 TransformDirection(const Vector2 &v) const
Definition: matrix.h:509
impeller::Vector3::z
Scalar z
Definition: vector.h:25
impeller::Matrix::IsAffine
constexpr bool IsAffine() const
Definition: matrix.h:326
impeller::Radians
Definition: scalar.h:43
impeller::ScalarNearlyZero
constexpr bool ScalarNearlyZero(Scalar x, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:30
impeller::Vector4::x
Scalar x
Definition: vector.h:235
impeller::Vector3::y
Scalar y
Definition: vector.h:24
impeller::Vector3::GetLength
constexpr Scalar GetLength() const
Definition: vector.h:47
impeller::Matrix::GetBasisZ
constexpr Vector3 GetBasisZ() const
Definition: matrix.h:314
impeller::TSize::width
Type width
Definition: size.h:22
impeller::Matrix::Transform
constexpr Quad Transform(const Quad &quad) const
Definition: matrix.h:513
impeller::Matrix::Invert
Matrix Invert() const
Definition: matrix.cc:97
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::Matrix::HasTranslation
constexpr bool HasTranslation() const
Definition: matrix.h:339
impeller::Matrix::IsIdentity
constexpr bool IsIdentity() const
Definition: matrix.h:384
impeller::Matrix::GetDirectionScale
constexpr Scalar GetDirectionScale(Vector3 direction) const
Definition: matrix.h:321
scalar.h
impeller::Vector3::Normalize
constexpr Vector3 Normalize() const
Definition: vector.h:49
impeller::Matrix::TransformDirection
constexpr Vector4 TransformDirection(const Vector4 &v) const
Definition: matrix.h:497
impeller::Vector4::w
Scalar w
Definition: vector.h:238
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:213
vector.h
impeller::Matrix::GetMaxBasisLengthXY
constexpr Scalar GetMaxBasisLengthXY() const
Definition: matrix.h:298
std
Definition: comparable.h:95
impeller::Vector4::y
Scalar y
Definition: vector.h:236
shear.h
impeller::TPoint< Scalar >
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::Matrix::operator*
constexpr Vector3 operator*(const Vector3 &v) const
Definition: matrix.h:464
impeller::Matrix::operator!=
constexpr bool operator!=(const Matrix &m) const
Definition: matrix.h:440
impeller::Quaternion::y
Scalar y
Definition: quaternion.h:18
scale
const Scalar scale
Definition: stroke_path_geometry.cc:301
impeller::ScalarNearlyEqual
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:35
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:523
matrix_decomposition.h
impeller::TSize::height
Type height
Definition: size.h:23
impeller::Matrix::GetBasisY
constexpr Vector3 GetBasisY() const
Definition: matrix.h:312
impeller::Vector4::z
Scalar z
Definition: vector.h:237
impeller
Definition: allocation.cc:12
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::Matrix::IsAligned2D
constexpr bool IsAligned2D(Scalar tolerance=0) const
Definition: matrix.h:341
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector2 &s)
Definition: matrix.h:113
impeller::Matrix::GetBasisX
constexpr Vector3 GetBasisX() const
Definition: matrix.h:310
impeller::Matrix::HasPerspective2D
constexpr bool HasPerspective2D() const
Definition: matrix.h:331
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Vector3
Definition: vector.h:20
size.h
impeller::Matrix::Scale
constexpr Matrix Scale(const Vector3 &s) const
Definition: matrix.h:252
impeller::Matrix::IsAligned
constexpr bool IsAligned(Scalar tolerance=0) const
Definition: matrix.h:356