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 
20 #include "impeller/geometry/size.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];
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 
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 
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 
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 
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