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  // Converts the second row/col to identity to make this an equivalent
251  // to a Skia 3x3 Matrix.
252  constexpr Matrix To3x3() const {
253  // clang-format off
254  return Matrix(
255  m[0], m[1], 0, m[3],
256  m[4], m[5], 0, m[7],
257  0, 0, 1, 0,
258  m[12], m[13], 0, m[15]
259  );
260  // clang-format on
261  }
262 
263  constexpr Matrix Translate(const Vector3& t) const {
264  // clang-format off
265  return Matrix(m[0], m[1], m[2], m[3],
266  m[4], m[5], m[6], m[7],
267  m[8], m[9], m[10], m[11],
268  m[0] * t.x + m[4] * t.y + m[8] * t.z + m[12],
269  m[1] * t.x + m[5] * t.y + m[9] * t.z + m[13],
270  m[2] * t.x + m[6] * t.y + m[10] * t.z + m[14],
271  m[3] * t.x + m[7] * t.y + m[11] * t.z + m[15]);
272  // clang-format on
273  }
274 
275  constexpr Matrix Scale(const Vector3& s) const {
276  // clang-format off
277  return Matrix(m[0] * s.x, m[1] * s.x, m[2] * s.x, m[3] * s.x,
278  m[4] * s.y, m[5] * s.y, m[6] * s.y, m[7] * s.y,
279  m[8] * s.z, m[9] * s.z, m[10] * s.z, m[11] * s.z,
280  m[12] , m[13] , m[14] , m[15] );
281  // clang-format on
282  }
283 
284  constexpr Matrix Multiply(const Matrix& o) const {
285  // clang-format off
286  return Matrix(
287  m[0] * o.m[0] + m[4] * o.m[1] + m[8] * o.m[2] + m[12] * o.m[3],
288  m[1] * o.m[0] + m[5] * o.m[1] + m[9] * o.m[2] + m[13] * o.m[3],
289  m[2] * o.m[0] + m[6] * o.m[1] + m[10] * o.m[2] + m[14] * o.m[3],
290  m[3] * o.m[0] + m[7] * o.m[1] + m[11] * o.m[2] + m[15] * o.m[3],
291  m[0] * o.m[4] + m[4] * o.m[5] + m[8] * o.m[6] + m[12] * o.m[7],
292  m[1] * o.m[4] + m[5] * o.m[5] + m[9] * o.m[6] + m[13] * o.m[7],
293  m[2] * o.m[4] + m[6] * o.m[5] + m[10] * o.m[6] + m[14] * o.m[7],
294  m[3] * o.m[4] + m[7] * o.m[5] + m[11] * o.m[6] + m[15] * o.m[7],
295  m[0] * o.m[8] + m[4] * o.m[9] + m[8] * o.m[10] + m[12] * o.m[11],
296  m[1] * o.m[8] + m[5] * o.m[9] + m[9] * o.m[10] + m[13] * o.m[11],
297  m[2] * o.m[8] + m[6] * o.m[9] + m[10] * o.m[10] + m[14] * o.m[11],
298  m[3] * o.m[8] + m[7] * o.m[9] + m[11] * o.m[10] + m[15] * o.m[11],
299  m[0] * o.m[12] + m[4] * o.m[13] + m[8] * o.m[14] + m[12] * o.m[15],
300  m[1] * o.m[12] + m[5] * o.m[13] + m[9] * o.m[14] + m[13] * o.m[15],
301  m[2] * o.m[12] + m[6] * o.m[13] + m[10] * o.m[14] + m[14] * o.m[15],
302  m[3] * o.m[12] + m[7] * o.m[13] + m[11] * o.m[14] + m[15] * o.m[15]);
303  // clang-format on
304  }
305 
306  constexpr Matrix Transpose() const {
307  // clang-format off
308  return {
309  m[0], m[4], m[8], m[12],
310  m[1], m[5], m[9], m[13],
311  m[2], m[6], m[10], m[14],
312  m[3], m[7], m[11], m[15],
313  };
314  // clang-format on
315  }
316 
317  Matrix Invert() const;
318 
319  Scalar GetDeterminant() const;
320 
321  bool IsInvertible() const { return GetDeterminant() != 0; }
322 
323  inline Scalar GetMaxBasisLengthXY() const {
324  // The full basis computation requires computing the squared scaling factor
325  // for translate/scale only matrices. This substantially limits the range of
326  // precision for small and large scales. Instead, check for the common cases
327  // and directly return the max scaling factor.
328  if (e[0][1] == 0 && e[1][0] == 0) {
329  return std::max(std::abs(e[0][0]), std::abs(e[1][1]));
330  }
331  return std::sqrt(std::max(e[0][0] * e[0][0] + e[0][1] * e[0][1],
332  e[1][0] * e[1][0] + e[1][1] * e[1][1]));
333  }
334 
335  constexpr Vector3 GetBasisX() const { return Vector3(m[0], m[1], m[2]); }
336 
337  constexpr Vector3 GetBasisY() const { return Vector3(m[4], m[5], m[6]); }
338 
339  constexpr Vector3 GetBasisZ() const { return Vector3(m[8], m[9], m[10]); }
340 
341  inline Vector3 GetScale() const {
342  return Vector3(GetBasisX().GetLength(), GetBasisY().GetLength(),
343  GetBasisZ().GetLength());
344  }
345 
346  inline Scalar GetDirectionScale(Vector3 direction) const {
347  return 1.0f / (this->Basis().Invert() * direction.Normalize()).GetLength() *
348  direction.GetLength();
349  }
350 
351  inline bool IsFinite() const {
352  return vec[0].IsFinite() && vec[1].IsFinite() && vec[2].IsFinite() &&
353  vec[3].IsFinite();
354  }
355 
356  constexpr bool IsAffine() const {
357  return (m[2] == 0 && m[3] == 0 && m[6] == 0 && m[7] == 0 && m[8] == 0 &&
358  m[9] == 0 && m[10] == 1 && m[11] == 0 && m[14] == 0 && m[15] == 1);
359  }
360 
361  constexpr bool HasPerspective2D() const {
362  return m[3] != 0 || m[7] != 0 || m[15] != 1;
363  }
364 
365  constexpr bool HasPerspective() const {
366  return m[3] != 0 || m[7] != 0 || m[11] != 0 || m[15] != 1;
367  }
368 
369  constexpr bool HasTranslation() const { return m[12] != 0 || m[13] != 0; }
370 
371  constexpr bool IsAligned2D(Scalar tolerance = 0) const {
372  if (HasPerspective2D()) {
373  return false;
374  }
375  if (ScalarNearlyZero(m[1], tolerance) &&
376  ScalarNearlyZero(m[4], tolerance)) {
377  return true;
378  }
379  if (ScalarNearlyZero(m[0], tolerance) &&
380  ScalarNearlyZero(m[5], tolerance)) {
381  return true;
382  }
383  return false;
384  }
385 
386  constexpr bool IsAligned(Scalar tolerance = 0) const {
387  if (HasPerspective()) {
388  return false;
389  }
390  int v[] = {!ScalarNearlyZero(m[0], tolerance), //
391  !ScalarNearlyZero(m[1], tolerance), //
392  !ScalarNearlyZero(m[2], tolerance), //
393  !ScalarNearlyZero(m[4], tolerance), //
394  !ScalarNearlyZero(m[5], tolerance), //
395  !ScalarNearlyZero(m[6], tolerance), //
396  !ScalarNearlyZero(m[8], tolerance), //
397  !ScalarNearlyZero(m[9], tolerance), //
398  !ScalarNearlyZero(m[10], tolerance)};
399  // Check if all three basis vectors are aligned to an axis.
400  if (v[0] + v[1] + v[2] != 1 || //
401  v[3] + v[4] + v[5] != 1 || //
402  v[6] + v[7] + v[8] != 1) {
403  return false;
404  }
405  // Ensure that none of the basis vectors overlap.
406  if (v[0] + v[3] + v[6] != 1 || //
407  v[1] + v[4] + v[7] != 1 || //
408  v[2] + v[5] + v[8] != 1) {
409  return false;
410  }
411  return true;
412  }
413 
414  constexpr bool IsIdentity() const {
415  return (
416  // clang-format off
417  m[0] == 1.0f && m[1] == 0.0f && m[2] == 0.0f && m[3] == 0.0f &&
418  m[4] == 0.0f && m[5] == 1.0f && m[6] == 0.0f && m[7] == 0.0f &&
419  m[8] == 0.0f && m[9] == 0.0f && m[10] == 1.0f && m[11] == 0.0f &&
420  m[12] == 0.0f && m[13] == 0.0f && m[14] == 0.0f && m[15] == 1.0f
421  // clang-format on
422  );
423  }
424 
425  /// @brief Returns true if the matrix has no entries other than translation
426  /// components. Note that an identity matrix meets this criteria.
427  constexpr bool IsTranslationOnly() const {
428  return (
429  // clang-format off
430  m[0] == 1.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 0.0 &&
431  m[4] == 0.0 && m[5] == 1.0 && m[6] == 0.0 && m[7] == 0.0 &&
432  m[8] == 0.0 && m[9] == 0.0 && m[10] == 1.0 && m[11] == 0.0 &&
433  m[15] == 1.0
434  // clang-format on
435  );
436  }
437 
438  /// @brief Returns true if the matrix has a scale-only basis and is
439  /// non-projective. Note that an identity matrix meets this criteria.
440  constexpr bool IsTranslationScaleOnly() const {
441  return (
442  // clang-format off
443  m[0] != 0.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 0.0 &&
444  m[4] == 0.0 && m[5] != 0.0 && m[6] == 0.0 && m[7] == 0.0 &&
445  m[8] == 0.0 && m[9] == 0.0 && m[10] != 0.0 && m[11] == 0.0 &&
446  m[15] == 1.0
447  // clang-format on
448  );
449  }
450 
451  std::optional<MatrixDecomposition> Decompose() const;
452 
453  bool Equals(const Matrix& matrix, Scalar epsilon = 1e-5f) const {
454  const Scalar* a = m;
455  const Scalar* b = matrix.m;
456  return ScalarNearlyEqual(a[0], b[0], epsilon) &&
457  ScalarNearlyEqual(a[1], b[1], epsilon) &&
458  ScalarNearlyEqual(a[2], b[2], epsilon) &&
459  ScalarNearlyEqual(a[3], b[3], epsilon) &&
460  ScalarNearlyEqual(a[4], b[4], epsilon) &&
461  ScalarNearlyEqual(a[5], b[5], epsilon) &&
462  ScalarNearlyEqual(a[6], b[6], epsilon) &&
463  ScalarNearlyEqual(a[7], b[7], epsilon) &&
464  ScalarNearlyEqual(a[8], b[8], epsilon) &&
465  ScalarNearlyEqual(a[9], b[9], epsilon) &&
466  ScalarNearlyEqual(a[10], b[10], epsilon) &&
467  ScalarNearlyEqual(a[11], b[11], epsilon) &&
468  ScalarNearlyEqual(a[12], b[12], epsilon) &&
469  ScalarNearlyEqual(a[13], b[13], epsilon) &&
470  ScalarNearlyEqual(a[14], b[14], epsilon) &&
471  ScalarNearlyEqual(a[15], b[15], epsilon);
472  }
473 
474  constexpr bool operator==(const Matrix& m) const {
475  // clang-format off
476  return vec[0] == m.vec[0]
477  && vec[1] == m.vec[1]
478  && vec[2] == m.vec[2]
479  && vec[3] == m.vec[3];
480  // clang-format on
481  }
482 
483  constexpr bool operator!=(const Matrix& m) const {
484  // clang-format off
485  return vec[0] != m.vec[0]
486  || vec[1] != m.vec[1]
487  || vec[2] != m.vec[2]
488  || vec[3] != m.vec[3];
489  // clang-format on
490  }
491 
492  Matrix operator+(const Vector3& t) const { return Translate(t); }
493 
494  Matrix operator-(const Vector3& t) const { return Translate(-t); }
495 
496  Matrix operator*(const Matrix& m) const { return Multiply(m); }
497 
498  Matrix operator+(const Matrix& m) const;
499 
500  constexpr Vector4 operator*(const Vector4& v) const {
501  return Vector4(v.x * m[0] + v.y * m[4] + v.z * m[8] + v.w * m[12],
502  v.x * m[1] + v.y * m[5] + v.z * m[9] + v.w * m[13],
503  v.x * m[2] + v.y * m[6] + v.z * m[10] + v.w * m[14],
504  v.x * m[3] + v.y * m[7] + v.z * m[11] + v.w * m[15]);
505  }
506 
507  constexpr Vector3 operator*(const Vector3& v) const {
508  Scalar w = v.x * m[3] + v.y * m[7] + v.z * m[11] + m[15];
509  Vector3 result(v.x * m[0] + v.y * m[4] + v.z * m[8] + m[12],
510  v.x * m[1] + v.y * m[5] + v.z * m[9] + m[13],
511  v.x * m[2] + v.y * m[6] + v.z * m[10] + m[14]);
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 Point operator*(const Point& v) const {
522  Scalar w = v.x * m[3] + v.y * m[7] + m[15];
523  Point result(v.x * m[0] + v.y * m[4] + m[12],
524  v.x * m[1] + v.y * m[5] + m[13]);
525 
526  // This is Skia's behavior, but it may be reasonable to allow UB for the w=0
527  // case.
528  if (w) {
529  w = 1 / w;
530  }
531  return result * w;
532  }
533 
534  constexpr Vector3 TransformHomogenous(const Point& v) const {
535  return Vector3(v.x * m[0] + v.y * m[4] + m[12],
536  v.x * m[1] + v.y * m[5] + m[13],
537  v.x * m[3] + v.y * m[7] + m[15]);
538  }
539 
540  constexpr Vector4 TransformDirection(const Vector4& v) const {
541  return Vector4(v.x * m[0] + v.y * m[4] + v.z * m[8],
542  v.x * m[1] + v.y * m[5] + v.z * m[9],
543  v.x * m[2] + v.y * m[6] + v.z * m[10], v.w);
544  }
545 
546  constexpr Vector3 TransformDirection(const Vector3& v) const {
547  return Vector3(v.x * m[0] + v.y * m[4] + v.z * m[8],
548  v.x * m[1] + v.y * m[5] + v.z * m[9],
549  v.x * m[2] + v.y * m[6] + v.z * m[10]);
550  }
551 
552  constexpr Vector2 TransformDirection(const Vector2& v) const {
553  return Vector2(v.x * m[0] + v.y * m[4], v.x * m[1] + v.y * m[5]);
554  }
555 
556  constexpr Quad Transform(const Quad& quad) const {
557  return {
558  *this * quad[0],
559  *this * quad[1],
560  *this * quad[2],
561  *this * quad[3],
562  };
563  }
564 
565  template <class T>
566  static constexpr Matrix MakeOrthographic(TSize<T> size) {
567  // Per assumptions about NDC documented above.
568  const auto scale =
569  MakeScale({2.0f / static_cast<Scalar>(size.width),
570  -2.0f / static_cast<Scalar>(size.height), 0.0f});
571  const auto translate = MakeTranslation({-1.0f, 1.0f, 0.5f});
572  return translate * scale;
573  }
574 
575  static inline Matrix MakePerspective(Radians fov_y,
576  Scalar aspect_ratio,
577  Scalar z_near,
578  Scalar z_far) {
579  Scalar height = std::tan(fov_y.radians * 0.5f);
580  Scalar width = height * aspect_ratio;
581 
582  // clang-format off
583  return {
584  1.0f / width, 0.0f, 0.0f, 0.0f,
585  0.0f, 1.0f / height, 0.0f, 0.0f,
586  0.0f, 0.0f, z_far / (z_far - z_near), 1.0f,
587  0.0f, 0.0f, -(z_far * z_near) / (z_far - z_near), 0.0f,
588  };
589  // clang-format on
590  }
591 
592  template <class T>
593  static constexpr Matrix MakePerspective(Radians fov_y,
594  TSize<T> size,
595  Scalar z_near,
596  Scalar z_far) {
597  return MakePerspective(fov_y, static_cast<Scalar>(size.width) / size.height,
598  z_near, z_far);
599  }
600 
601  static inline Matrix MakeLookAt(Vector3 position,
602  Vector3 target,
603  Vector3 up) {
604  Vector3 forward = (target - position).Normalize();
605  Vector3 right = up.Cross(forward);
606  up = forward.Cross(right);
607 
608  // clang-format off
609  return {
610  right.x, up.x, forward.x, 0.0f,
611  right.y, up.y, forward.y, 0.0f,
612  right.z, up.z, forward.z, 0.0f,
613  -right.Dot(position), -up.Dot(position), -forward.Dot(position), 1.0f
614  };
615  // clang-format on
616  }
617 
618  static inline Vector2 CosSin(Radians radians) {
619  // The precision of a float around 1.0 is much lower than it is
620  // around 0.0, so we end up with cases on quadrant rotations where
621  // we get a +/-1.0 for one of the values and a non-zero value for
622  // the other. This happens around quadrant rotations which makes it
623  // especially common and results in unclean quadrant rotation
624  // matrices which do not return true from |IsAligned[2D]| even
625  // though that is exactly where you need them to exhibit that property.
626  // It also injects small floating point mantissa errors into the
627  // matrices whenever you concatenate them with a quadrant rotation.
628  //
629  // This issue is also exacerbated by the fact that, in radians, the
630  // angles for quadrant rotations are irrational numbers. The measuring
631  // error for representing 90 degree multiples is small enough that
632  // either sin or cos will return a value near +/-1.0, but not small
633  // enough that the other value will be a clean 0.0.
634  //
635  // Some geometry packages simply discard very small numbers from
636  // sin/cos, but the following approach specifically targets just the
637  // area around a quadrant rotation (where either the sin or cos are
638  // measuring as +/-1.0) for symmetry of precision.
639 
640  Scalar sin = std::sin(radians.radians);
641  if (std::abs(sin) == 1.0f) {
642  // 90 or 270 degrees (mod 360)
643  return {0.0f, sin};
644  } else {
645  Scalar cos = std::cos(radians.radians);
646  if (std::abs(cos) == 1.0f) {
647  // 0 or 180 degrees (mod 360)
648  return {cos, 0.0f};
649  }
650  return {cos, sin};
651  }
652  }
653 };
654 
655 static_assert(sizeof(struct Matrix) == sizeof(Scalar) * 16,
656  "The matrix must be of consistent size.");
657 
658 } // namespace impeller
659 
660 namespace std {
661 inline std::ostream& operator<<(std::ostream& out, const impeller::Matrix& m) {
662  out << "(" << std::endl << std::fixed;
663  for (size_t i = 0; i < 4u; i++) {
664  for (size_t j = 0; j < 4u; j++) {
665  out << std::setw(15) << m.e[j][i] << ",";
666  }
667  out << std::endl;
668  }
669  out << ")";
670  return out;
671 }
672 
673 } // namespace std
674 
675 #endif // FLUTTER_IMPELLER_GEOMETRY_MATRIX_H_
Point Vector2
Definition: point.h:331
float Scalar
Definition: scalar.h:19
constexpr bool ScalarNearlyZero(Scalar x, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:31
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:36
std::array< Point, 4 > Quad
Definition: point.h:332
Definition: comparable.h:95
std::ostream & operator<<(std::ostream &out, const impeller::Arc &a)
Definition: arc.h:141
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:427
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:566
constexpr Matrix Multiply(const Matrix &o) const
Definition: matrix.h:284
constexpr bool IsAffine() const
Definition: matrix.h:356
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
constexpr Vector3 GetBasisY() const
Definition: matrix.h:337
constexpr Matrix()
Definition: matrix.h:47
constexpr bool IsIdentity() const
Definition: matrix.h:414
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:440
Scalar m[16]
Definition: matrix.h:39
constexpr bool HasTranslation() const
Definition: matrix.h:369
constexpr Matrix Translate(const Vector3 &t) const
Definition: matrix.h:263
bool IsInvertible() const
Definition: matrix.h:321
constexpr Vector2 TransformDirection(const Vector2 &v) const
Definition: matrix.h:552
static constexpr Matrix MakeScale(const Vector2 &s)
Definition: matrix.h:123
Matrix operator+(const Vector3 &t) const
Definition: matrix.h:492
Scalar GetDirectionScale(Vector3 direction) const
Definition: matrix.h:346
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:496
constexpr bool IsAligned(Scalar tolerance=0) const
Definition: matrix.h:386
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:339
Vector4 vec[4]
Definition: matrix.h:41
std::optional< MatrixDecomposition > Decompose() const
Definition: matrix.cc:200
constexpr bool HasPerspective2D() const
Definition: matrix.h:361
bool IsFinite() const
Definition: matrix.h:351
Matrix operator-(const Vector3 &t) const
Definition: matrix.h:494
constexpr Vector4 operator*(const Vector4 &v) const
Definition: matrix.h:500
static Vector2 CosSin(Radians radians)
Definition: matrix.h:618
constexpr bool operator!=(const Matrix &m) const
Definition: matrix.h:483
static Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:575
constexpr Point operator*(const Point &v) const
Definition: matrix.h:521
constexpr Vector3 operator*(const Vector3 &v) const
Definition: matrix.h:507
constexpr Vector3 TransformDirection(const Vector3 &v) const
Definition: matrix.h:546
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
Vector3 GetScale() const
Definition: matrix.h:341
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
Definition: matrix.h:127
constexpr Quad Transform(const Quad &quad) const
Definition: matrix.h:556
constexpr bool operator==(const Matrix &m) const
Definition: matrix.h:474
constexpr Matrix Scale(const Vector3 &s) const
Definition: matrix.h:275
constexpr Vector3 TransformHomogenous(const Point &v) const
Definition: matrix.h:534
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:593
Scalar GetDeterminant() const
Definition: matrix.cc:162
constexpr bool HasPerspective() const
Definition: matrix.h:365
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
static Matrix MakeLookAt(Vector3 position, Vector3 target, Vector3 up)
Definition: matrix.h:601
constexpr Vector4 TransformDirection(const Vector4 &v) const
Definition: matrix.h:540
constexpr Vector3 GetBasisX() const
Definition: matrix.h:335
bool Equals(const Matrix &matrix, Scalar epsilon=1e-5f) const
Definition: matrix.h:453
constexpr Matrix To3x3() const
Definition: matrix.h:252
constexpr bool IsAligned2D(Scalar tolerance=0) const
Definition: matrix.h:371
static Matrix MakeRotation(Quaternion q)
Definition: matrix.h:136
constexpr Matrix Transpose() const
Definition: matrix.h:306
Scalar GetMaxBasisLengthXY() const
Definition: matrix.h:323
static Matrix MakeRotationX(Radians r)
Definition: matrix.h:193
Scalar radians
Definition: scalar.h:45
Type height
Definition: size.h:29
Type width
Definition: size.h:28
Vector3 Normalize() const
Definition: vector.h:49
constexpr Vector3 Cross(const Vector3 &other) const
Definition: vector.h:62
constexpr Scalar Dot(const Vector3 &other) const
Definition: vector.h:54
Scalar GetLength() const
Definition: vector.h:47
bool IsFinite() const
Definition: vector.h:258
Vector4 Normalize() const
Definition: vector.h:263