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