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  /// @brief Return the maximum scale applied specifically to either the
324  /// X axis or Y axis unit vectors (the bases). The matrix might
325  /// lengthen a non-axis-aligned vector by more than this value.
326  ///
327  /// @see |GetMaxScale2D|
328  inline Scalar GetMaxBasisLengthXY() const {
329  // The full basis computation requires computing the squared scaling factor
330  // for translate/scale only matrices. This substantially limits the range of
331  // precision for small and large scales. Instead, check for the common cases
332  // and directly return the max scaling factor.
333  if (e[0][1] == 0 && e[1][0] == 0) {
334  return std::max(std::abs(e[0][0]), std::abs(e[1][1]));
335  }
336  return std::sqrt(std::max(e[0][0] * e[0][0] + e[0][1] * e[0][1],
337  e[1][0] * e[1][0] + e[1][1] * e[1][1]));
338  }
339 
340  /// @brief Return the smaller of the two non-negative scales that will
341  /// be applied to 2D coordinates by this matrix. If the matrix
342  /// has perspective components, the method will return a nullopt.
343  ///
344  /// Note that negative scale factors really represent a positive scale
345  /// factor with a flip, so the absolute value (the positive scale factor)
346  /// is returned instead so that the results can be directly applied to
347  /// rendering calculations to compute the potential size of an operation.
348  ///
349  /// This method differs from the "basis length" methods in that those
350  /// methods answer the question "how much does this transform stretch
351  /// perfectly horizontal or vertical source vectors, whereas this method
352  /// can answer "what's the smallest scale applied to any vector regardless
353  /// of direction".
354  ///
355  /// @see |GetScales2D|
356  std::optional<Scalar> GetMinScale2D() const {
357  auto scales = GetScales2D();
358  if (!scales.has_value()) {
359  return std::nullopt;
360  }
361  return std::min(scales->first, scales->second);
362  }
363 
364  /// @brief Return the smaller of the two non-negative scales that will
365  /// be applied to 2D coordinates by this matrix. If the matrix
366  /// has perspective components, the method will return a nullopt.
367  ///
368  /// Note that negative scale factors really represent a positive scale
369  /// factor with a flip, so the absolute value (the positive scale factor)
370  /// is returned instead so that the results can be directly applied to
371  /// rendering calculations to compute the potential size of an operation.
372  ///
373  /// This method differs from the "basis length" methods in that those
374  /// methods answer the question "how much does this transform stretch
375  /// perfectly horizontal or vertical source vectors, whereas this method
376  /// can answer "what's the largest scale applied to any vector regardless
377  /// of direction".
378  ///
379  /// @see |GetScales2D|
380  std::optional<Scalar> GetMaxScale2D() const {
381  auto scales = GetScales2D();
382  if (!scales.has_value()) {
383  return std::nullopt;
384  }
385  return std::max(scales->first, scales->second);
386  }
387 
388  constexpr Vector3 GetBasisX() const { return Vector3(m[0], m[1], m[2]); }
389 
390  constexpr Vector3 GetBasisY() const { return Vector3(m[4], m[5], m[6]); }
391 
392  constexpr Vector3 GetBasisZ() const { return Vector3(m[8], m[9], m[10]); }
393 
394  inline Vector3 GetScale() const {
395  return Vector3(GetBasisX().GetLength(), GetBasisY().GetLength(),
396  GetBasisZ().GetLength());
397  }
398 
399  constexpr Vector2 GetBasisX2D() const { return Vector2(m[0], m[1]); }
400 
401  constexpr Vector2 GetBasisY2D() const { return Vector2(m[4], m[5]); }
402 
403  inline Vector2 GetBasisScaleXY() const {
404  return Vector2(GetBasisX2D().GetLength(), GetBasisY2D().GetLength());
405  }
406 
407  inline Scalar GetDirectionScale(Vector3 direction) const {
408  return 1.0f / (this->Basis().Invert() * direction.Normalize()).GetLength() *
409  direction.GetLength();
410  }
411 
412  inline bool IsFinite() const {
413  return vec[0].IsFinite() && vec[1].IsFinite() && vec[2].IsFinite() &&
414  vec[3].IsFinite();
415  }
416 
417  constexpr bool IsAffine() const {
418  return (m[2] == 0 && m[3] == 0 && m[6] == 0 && m[7] == 0 && m[8] == 0 &&
419  m[9] == 0 && m[10] == 1 && m[11] == 0 && m[14] == 0 && m[15] == 1);
420  }
421 
422  constexpr bool HasPerspective2D() const {
423  return m[3] != 0 || m[7] != 0 || m[15] != 1;
424  }
425 
426  constexpr bool HasPerspective() const {
427  return m[3] != 0 || m[7] != 0 || m[11] != 0 || m[15] != 1;
428  }
429 
430  constexpr bool HasTranslation() const { return m[12] != 0 || m[13] != 0; }
431 
432  constexpr bool IsAligned2D(Scalar tolerance = 0) const {
433  if (HasPerspective2D()) {
434  return false;
435  }
436  if (ScalarNearlyZero(m[1], tolerance) &&
437  ScalarNearlyZero(m[4], tolerance)) {
438  return true;
439  }
440  if (ScalarNearlyZero(m[0], tolerance) &&
441  ScalarNearlyZero(m[5], tolerance)) {
442  return true;
443  }
444  return false;
445  }
446 
447  constexpr bool IsAligned(Scalar tolerance = 0) const {
448  if (HasPerspective()) {
449  return false;
450  }
451  int v[] = {!ScalarNearlyZero(m[0], tolerance), //
452  !ScalarNearlyZero(m[1], tolerance), //
453  !ScalarNearlyZero(m[2], tolerance), //
454  !ScalarNearlyZero(m[4], tolerance), //
455  !ScalarNearlyZero(m[5], tolerance), //
456  !ScalarNearlyZero(m[6], tolerance), //
457  !ScalarNearlyZero(m[8], tolerance), //
458  !ScalarNearlyZero(m[9], tolerance), //
459  !ScalarNearlyZero(m[10], tolerance)};
460  // Check if all three basis vectors are aligned to an axis.
461  if (v[0] + v[1] + v[2] != 1 || //
462  v[3] + v[4] + v[5] != 1 || //
463  v[6] + v[7] + v[8] != 1) {
464  return false;
465  }
466  // Ensure that none of the basis vectors overlap.
467  if (v[0] + v[3] + v[6] != 1 || //
468  v[1] + v[4] + v[7] != 1 || //
469  v[2] + v[5] + v[8] != 1) {
470  return false;
471  }
472  return true;
473  }
474 
475  constexpr bool IsIdentity() const {
476  return (
477  // clang-format off
478  m[0] == 1.0f && m[1] == 0.0f && m[2] == 0.0f && m[3] == 0.0f &&
479  m[4] == 0.0f && m[5] == 1.0f && m[6] == 0.0f && m[7] == 0.0f &&
480  m[8] == 0.0f && m[9] == 0.0f && m[10] == 1.0f && m[11] == 0.0f &&
481  m[12] == 0.0f && m[13] == 0.0f && m[14] == 0.0f && m[15] == 1.0f
482  // clang-format on
483  );
484  }
485 
486  /// @brief Returns true if the matrix has no entries other than translation
487  /// components. Note that an identity matrix meets this criteria.
488  constexpr bool IsTranslationOnly() const {
489  return (
490  // clang-format off
491  m[0] == 1.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 0.0 &&
492  m[4] == 0.0 && m[5] == 1.0 && m[6] == 0.0 && m[7] == 0.0 &&
493  m[8] == 0.0 && m[9] == 0.0 && m[10] == 1.0 && m[11] == 0.0 &&
494  m[15] == 1.0
495  // clang-format on
496  );
497  }
498 
499  /// @brief Returns true if the matrix has a scale-only basis and is
500  /// non-projective. Note that an identity matrix meets this criteria.
501  constexpr bool IsTranslationScaleOnly() const {
502  return (
503  // clang-format off
504  m[0] != 0.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 0.0 &&
505  m[4] == 0.0 && m[5] != 0.0 && m[6] == 0.0 && m[7] == 0.0 &&
506  m[8] == 0.0 && m[9] == 0.0 && m[10] != 0.0 && m[11] == 0.0 &&
507  m[15] == 1.0
508  // clang-format on
509  );
510  }
511 
512  std::optional<MatrixDecomposition> Decompose() const;
513 
514  /// @brief Compute the two non-negative scales applied by this matrix to
515  /// 2D coordinates and return them as an optional pair of Scalar
516  /// values in any order. If the matrix has perspective elements,
517  /// this method will return a nullopt.
518  ///
519  /// Note that negative scale factors really represent a positive scale
520  /// factor with a flip, so the absolute value (the positive scale factor)
521  /// is returned instead so that the results can be directly applied to
522  /// rendering calculations to compute the potential size of an operation.
523  ///
524  /// @see |GetMinScale2D|
525  /// @see |GetMaxScale2D|
526  std::optional<std::pair<Scalar, Scalar>> GetScales2D() const;
527 
528  bool Equals(const Matrix& matrix, Scalar epsilon = 1e-5f) const {
529  const Scalar* a = m;
530  const Scalar* b = matrix.m;
531  return ScalarNearlyEqual(a[0], b[0], epsilon) &&
532  ScalarNearlyEqual(a[1], b[1], epsilon) &&
533  ScalarNearlyEqual(a[2], b[2], epsilon) &&
534  ScalarNearlyEqual(a[3], b[3], epsilon) &&
535  ScalarNearlyEqual(a[4], b[4], epsilon) &&
536  ScalarNearlyEqual(a[5], b[5], epsilon) &&
537  ScalarNearlyEqual(a[6], b[6], epsilon) &&
538  ScalarNearlyEqual(a[7], b[7], epsilon) &&
539  ScalarNearlyEqual(a[8], b[8], epsilon) &&
540  ScalarNearlyEqual(a[9], b[9], epsilon) &&
541  ScalarNearlyEqual(a[10], b[10], epsilon) &&
542  ScalarNearlyEqual(a[11], b[11], epsilon) &&
543  ScalarNearlyEqual(a[12], b[12], epsilon) &&
544  ScalarNearlyEqual(a[13], b[13], epsilon) &&
545  ScalarNearlyEqual(a[14], b[14], epsilon) &&
546  ScalarNearlyEqual(a[15], b[15], epsilon);
547  }
548 
549  constexpr bool operator==(const Matrix& m) const {
550  // clang-format off
551  return vec[0] == m.vec[0]
552  && vec[1] == m.vec[1]
553  && vec[2] == m.vec[2]
554  && vec[3] == m.vec[3];
555  // clang-format on
556  }
557 
558  constexpr bool operator!=(const Matrix& m) const {
559  // clang-format off
560  return vec[0] != m.vec[0]
561  || vec[1] != m.vec[1]
562  || vec[2] != m.vec[2]
563  || vec[3] != m.vec[3];
564  // clang-format on
565  }
566 
567  Matrix operator+(const Vector3& t) const { return Translate(t); }
568 
569  Matrix operator-(const Vector3& t) const { return Translate(-t); }
570 
571  Matrix operator*(const Matrix& m) const { return Multiply(m); }
572 
573  Matrix operator+(const Matrix& m) const;
574 
575  constexpr Vector4 operator*(const Vector4& v) const {
576  return Vector4(v.x * m[0] + v.y * m[4] + v.z * m[8] + v.w * m[12],
577  v.x * m[1] + v.y * m[5] + v.z * m[9] + v.w * m[13],
578  v.x * m[2] + v.y * m[6] + v.z * m[10] + v.w * m[14],
579  v.x * m[3] + v.y * m[7] + v.z * m[11] + v.w * m[15]);
580  }
581 
582  constexpr Vector3 operator*(const Vector3& v) const {
583  Scalar w = v.x * m[3] + v.y * m[7] + v.z * m[11] + m[15];
584  Vector3 result(v.x * m[0] + v.y * m[4] + v.z * m[8] + m[12],
585  v.x * m[1] + v.y * m[5] + v.z * m[9] + m[13],
586  v.x * m[2] + v.y * m[6] + v.z * m[10] + m[14]);
587 
588  // This is Skia's behavior, but it may be reasonable to allow UB for the w=0
589  // case.
590  if (w) {
591  w = 1 / w;
592  }
593  return result * w;
594  }
595 
596  constexpr Point operator*(const Point& v) const {
597  Scalar w = v.x * m[3] + v.y * m[7] + m[15];
598  Point result(v.x * m[0] + v.y * m[4] + m[12],
599  v.x * m[1] + v.y * m[5] + m[13]);
600 
601  // This is Skia's behavior, but it may be reasonable to allow UB for the w=0
602  // case.
603  if (w) {
604  w = 1 / w;
605  }
606  return result * w;
607  }
608 
609  constexpr Vector3 TransformHomogenous(const Point& v) const {
610  return Vector3(v.x * m[0] + v.y * m[4] + m[12],
611  v.x * m[1] + v.y * m[5] + m[13],
612  v.x * m[3] + v.y * m[7] + m[15]);
613  }
614 
615  constexpr Vector4 TransformDirection(const Vector4& v) const {
616  return Vector4(v.x * m[0] + v.y * m[4] + v.z * m[8],
617  v.x * m[1] + v.y * m[5] + v.z * m[9],
618  v.x * m[2] + v.y * m[6] + v.z * m[10], v.w);
619  }
620 
621  constexpr Vector3 TransformDirection(const Vector3& v) const {
622  return Vector3(v.x * m[0] + v.y * m[4] + v.z * m[8],
623  v.x * m[1] + v.y * m[5] + v.z * m[9],
624  v.x * m[2] + v.y * m[6] + v.z * m[10]);
625  }
626 
627  constexpr Vector2 TransformDirection(const Vector2& v) const {
628  return Vector2(v.x * m[0] + v.y * m[4], v.x * m[1] + v.y * m[5]);
629  }
630 
631  constexpr Quad Transform(const Quad& quad) const {
632  return {
633  *this * quad[0],
634  *this * quad[1],
635  *this * quad[2],
636  *this * quad[3],
637  };
638  }
639 
640  template <class T>
641  static constexpr Matrix MakeOrthographic(TSize<T> size) {
642  // Per assumptions about NDC documented above.
643  const auto scale =
644  MakeScale({2.0f / static_cast<Scalar>(size.width),
645  -2.0f / static_cast<Scalar>(size.height), 0.0f});
646  const auto translate = MakeTranslation({-1.0f, 1.0f, 0.5f});
647  return translate * scale;
648  }
649 
650  static inline Matrix MakePerspective(Radians fov_y,
651  Scalar aspect_ratio,
652  Scalar z_near,
653  Scalar z_far) {
654  Scalar height = std::tan(fov_y.radians * 0.5f);
655  Scalar width = height * aspect_ratio;
656 
657  // clang-format off
658  return {
659  1.0f / width, 0.0f, 0.0f, 0.0f,
660  0.0f, 1.0f / height, 0.0f, 0.0f,
661  0.0f, 0.0f, z_far / (z_far - z_near), 1.0f,
662  0.0f, 0.0f, -(z_far * z_near) / (z_far - z_near), 0.0f,
663  };
664  // clang-format on
665  }
666 
667  template <class T>
668  static constexpr Matrix MakePerspective(Radians fov_y,
669  TSize<T> size,
670  Scalar z_near,
671  Scalar z_far) {
672  return MakePerspective(fov_y, static_cast<Scalar>(size.width) / size.height,
673  z_near, z_far);
674  }
675 
676  static inline Matrix MakeLookAt(Vector3 position,
677  Vector3 target,
678  Vector3 up) {
679  Vector3 forward = (target - position).Normalize();
680  Vector3 right = up.Cross(forward);
681  up = forward.Cross(right);
682 
683  // clang-format off
684  return {
685  right.x, up.x, forward.x, 0.0f,
686  right.y, up.y, forward.y, 0.0f,
687  right.z, up.z, forward.z, 0.0f,
688  -right.Dot(position), -up.Dot(position), -forward.Dot(position), 1.0f
689  };
690  // clang-format on
691  }
692 
693  static inline Vector2 CosSin(Radians radians) {
694  // The precision of a float around 1.0 is much lower than it is
695  // around 0.0, so we end up with cases on quadrant rotations where
696  // we get a +/-1.0 for one of the values and a non-zero value for
697  // the other. This happens around quadrant rotations which makes it
698  // especially common and results in unclean quadrant rotation
699  // matrices which do not return true from |IsAligned[2D]| even
700  // though that is exactly where you need them to exhibit that property.
701  // It also injects small floating point mantissa errors into the
702  // matrices whenever you concatenate them with a quadrant rotation.
703  //
704  // This issue is also exacerbated by the fact that, in radians, the
705  // angles for quadrant rotations are irrational numbers. The measuring
706  // error for representing 90 degree multiples is small enough that
707  // either sin or cos will return a value near +/-1.0, but not small
708  // enough that the other value will be a clean 0.0.
709  //
710  // Some geometry packages simply discard very small numbers from
711  // sin/cos, but the following approach specifically targets just the
712  // area around a quadrant rotation (where either the sin or cos are
713  // measuring as +/-1.0) for symmetry of precision.
714 
715  Scalar sin = std::sin(radians.radians);
716  if (std::abs(sin) == 1.0f) {
717  // 90 or 270 degrees (mod 360)
718  return {0.0f, sin};
719  } else {
720  Scalar cos = std::cos(radians.radians);
721  if (std::abs(cos) == 1.0f) {
722  // 0 or 180 degrees (mod 360)
723  return {cos, 0.0f};
724  }
725  return {cos, sin};
726  }
727  }
728 };
729 
730 static_assert(sizeof(struct Matrix) == sizeof(Scalar) * 16,
731  "The matrix must be of consistent size.");
732 
733 } // namespace impeller
734 
735 namespace std {
736 inline std::ostream& operator<<(std::ostream& out, const impeller::Matrix& m) {
737  out << "(" << std::endl << std::fixed;
738  for (size_t i = 0; i < 4u; i++) {
739  for (size_t j = 0; j < 4u; j++) {
740  out << std::setw(15) << m.e[j][i] << ",";
741  }
742  out << std::endl;
743  }
744  out << ")";
745  return out;
746 }
747 
748 } // namespace std
749 
750 #endif // FLUTTER_IMPELLER_GEOMETRY_MATRIX_H_
Point Vector2
Definition: point.h:430
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:431
Definition: comparable.h:93
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:488
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:641
constexpr Matrix Multiply(const Matrix &o) const
Definition: matrix.h:284
constexpr bool IsAffine() const
Definition: matrix.h:417
Vector2 GetBasisScaleXY() const
Definition: matrix.h:403
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
constexpr Vector3 GetBasisY() const
Definition: matrix.h:390
constexpr Matrix()
Definition: matrix.h:47
constexpr bool IsIdentity() const
Definition: matrix.h:475
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:501
Scalar m[16]
Definition: matrix.h:39
constexpr bool HasTranslation() const
Definition: matrix.h:430
constexpr Matrix Translate(const Vector3 &t) const
Definition: matrix.h:263
std::optional< Scalar > GetMinScale2D() const
Return the smaller of the two non-negative scales that will be applied to 2D coordinates by this matr...
Definition: matrix.h:356
bool IsInvertible() const
Definition: matrix.h:321
constexpr Vector2 TransformDirection(const Vector2 &v) const
Definition: matrix.h:627
static constexpr Matrix MakeScale(const Vector2 &s)
Definition: matrix.h:123
Matrix operator+(const Vector3 &t) const
Definition: matrix.h:567
Scalar GetDirectionScale(Vector3 direction) const
Definition: matrix.h:407
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:571
constexpr bool IsAligned(Scalar tolerance=0) const
Definition: matrix.h:447
Matrix Invert() const
Definition: matrix.cc:99
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:392
Vector4 vec[4]
Definition: matrix.h:41
std::optional< MatrixDecomposition > Decompose() const
Definition: matrix.cc:202
constexpr bool HasPerspective2D() const
Definition: matrix.h:422
bool IsFinite() const
Definition: matrix.h:412
Matrix operator-(const Vector3 &t) const
Definition: matrix.h:569
constexpr Vector2 GetBasisX2D() const
Definition: matrix.h:399
constexpr Vector4 operator*(const Vector4 &v) const
Definition: matrix.h:575
static Vector2 CosSin(Radians radians)
Definition: matrix.h:693
constexpr bool operator!=(const Matrix &m) const
Definition: matrix.h:558
static Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:650
constexpr Point operator*(const Point &v) const
Definition: matrix.h:596
constexpr Vector3 operator*(const Vector3 &v) const
Definition: matrix.h:582
constexpr Vector3 TransformDirection(const Vector3 &v) const
Definition: matrix.h:621
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:394
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
Definition: matrix.h:127
constexpr Quad Transform(const Quad &quad) const
Definition: matrix.h:631
constexpr bool operator==(const Matrix &m) const
Definition: matrix.h:549
constexpr Matrix Scale(const Vector3 &s) const
Definition: matrix.h:275
constexpr Vector3 TransformHomogenous(const Point &v) const
Definition: matrix.h:609
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
std::optional< std::pair< Scalar, Scalar > > GetScales2D() const
Compute the two non-negative scales applied by this matrix to 2D coordinates and return them as an op...
Definition: matrix.cc:363
std::optional< Scalar > GetMaxScale2D() const
Return the smaller of the two non-negative scales that will be applied to 2D coordinates by this matr...
Definition: matrix.h:380
constexpr Vector2 GetBasisY2D() const
Definition: matrix.h:401
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:668
Scalar GetDeterminant() const
Definition: matrix.cc:164
constexpr bool HasPerspective() const
Definition: matrix.h:426
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
static Matrix MakeLookAt(Vector3 position, Vector3 target, Vector3 up)
Definition: matrix.h:676
constexpr Vector4 TransformDirection(const Vector4 &v) const
Definition: matrix.h:615
constexpr Vector3 GetBasisX() const
Definition: matrix.h:388
bool Equals(const Matrix &matrix, Scalar epsilon=1e-5f) const
Definition: matrix.h:528
constexpr Matrix To3x3() const
Definition: matrix.h:252
constexpr bool IsAligned2D(Scalar tolerance=0) const
Definition: matrix.h:432
static Matrix MakeRotation(Quaternion q)
Definition: matrix.h:136
constexpr Matrix Transpose() const
Definition: matrix.h:306
Scalar GetMaxBasisLengthXY() const
Return the maximum scale applied specifically to either the X axis or Y axis unit vectors (the bases)...
Definition: matrix.h:328
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