| Flutter Impeller |

matrix.h
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef FLUTTER_IMPELLER_GEOMETRY_MATRIX_H_
6 #define FLUTTER_IMPELLER_GEOMETRY_MATRIX_H_
7
8 #include <cmath>
9 #include <iomanip>
10 #include <limits>
11 #include <optional>
12 #include <ostream>
13 #include <utility>
14
15 #include "impeller/geometry/matrix_decomposition.h"
16 #include "impeller/geometry/point.h"
17 #include "impeller/geometry/quaternion.h"
18 #include "impeller/geometry/scalar.h"
19 #include "impeller/geometry/shear.h"
20 #include "impeller/geometry/size.h"
21 #include "impeller/geometry/vector.h"
22
23 namespace impeller {
24
25 //------------------------------------------------------------------------------
26 /// @brief A 4x4 matrix using column-major storage.
27 ///
28 /// Utility methods that need to make assumptions about normalized
29 /// device coordinates must use the following convention:
30 /// * Left-handed coordinate system. Positive rotation is
31 /// clockwise about axis of rotation.
32 /// * Lower left corner is -1.0f, -1.0.
33 /// * Upper right corner is 1.0f, 1.0.
34 /// * Visible z-space is from 0.0 to 1.0.
35 /// * This is NOT the same as OpenGL! Be careful.
36 /// * NDC origin is at (0.0f, 0.0f, 0.5f).
37 struct Matrix {
38 union {
39 Scalar m[16];
40 Scalar e[4][4];
41 Vector4 vec[4];
42 };
43
44 //----------------------------------------------------------------------------
45 /// Constructs a default identity matrix.
46 ///
47 constexpr Matrix()
48 // clang-format off
49 : vec{ Vector4(1.0f, 0.0f, 0.0f, 0.0f),
50 Vector4(0.0f, 1.0f, 0.0f, 0.0f),
51 Vector4(0.0f, 0.0f, 1.0f, 0.0f),
52 Vector4(0.0f, 0.0f, 0.0f, 1.0f)} {}
53 // clang-format on
54
55 // clang-format off
56 constexpr Matrix(Scalar m0, Scalar m1, Scalar m2, Scalar m3,
57 Scalar m4, Scalar m5, Scalar m6, Scalar m7,
58 Scalar m8, Scalar m9, Scalar m10, Scalar m11,
59 Scalar m12, Scalar m13, Scalar m14, Scalar m15)
60 : vec{Vector4(m0, m1, m2, m3),
61 Vector4(m4, m5, m6, m7),
62 Vector4(m8, m9, m10, m11),
63 Vector4(m12, m13, m14, m15)} {}
64 // clang-format on
65
66 explicit Matrix(const MatrixDecomposition& decomposition);
67
68 // clang-format off
69 static constexpr Matrix MakeColumn(
70 Scalar m0, Scalar m1, Scalar m2, Scalar m3,
71 Scalar m4, Scalar m5, Scalar m6, Scalar m7,
72 Scalar m8, Scalar m9, Scalar m10, Scalar m11,
73 Scalar m12, Scalar m13, Scalar m14, Scalar m15){
74 return Matrix(m0, m1, m2, m3,
75 m4, m5, m6, m7,
76 m8, m9, m10, m11,
77 m12, m13, m14, m15);
78
79 }
80 // clang-format on
81
82 // clang-format off
83 static constexpr Matrix MakeRow(
84 Scalar m0, Scalar m1, Scalar m2, Scalar m3,
85 Scalar m4, Scalar m5, Scalar m6, Scalar m7,
86 Scalar m8, Scalar m9, Scalar m10, Scalar m11,
87 Scalar m12, Scalar m13, Scalar m14, Scalar m15){
88 return Matrix(m0, m4, m8, m12,
89 m1, m5, m9, m13,
90 m2, m6, m10, m14,
91 m3, m7, m11, m15);
92 }
93 // clang-format on
94
95 static constexpr Matrix MakeTranslation(const Vector3& t) {
96 // clang-format off
97 return Matrix(1.0f, 0.0f, 0.0f, 0.0f,
98 0.0f, 1.0f, 0.0f, 0.0f,
99 0.0f, 0.0f, 1.0f, 0.0f,
100 t.x, t.y, t.z, 1.0f);
101 // clang-format on
102 }
103
104 static constexpr Matrix MakeScale(const Vector3& s) {
105 // clang-format off
106 return Matrix(s.x, 0.0f, 0.0f, 0.0f,
107 0.0f, s.y, 0.0f, 0.0f,
108 0.0f, 0.0f, s.z, 0.0f,
109 0.0f, 0.0f, 0.0f, 1.0f);
110 // clang-format on
111 }
112
113 static constexpr Matrix 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
136 static Matrix MakeRotation(Quaternion q) {
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
193 static Matrix MakeRotationX(Radians r) {
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
208 static Matrix MakeRotationY(Radians r) {
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
223 static Matrix MakeRotationZ(Radians r) {
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 inline Scalar GetDirectionScale(Vector3 direction) const {
400 return 1.0f / (this->Basis().Invert() * direction.Normalize()).GetLength() *
401 direction.GetLength();
402 }
403
404 inline bool IsFinite() const {
405 return vec[0].IsFinite() && vec[1].IsFinite() && vec[2].IsFinite() &&
406 vec[3].IsFinite();
407 }
408
409 constexpr bool IsAffine() const {
410 return (m[2] == 0 && m[3] == 0 && m[6] == 0 && m[7] == 0 && m[8] == 0 &&
411 m[9] == 0 && m[10] == 1 && m[11] == 0 && m[14] == 0 && m[15] == 1);
412 }
413
414 constexpr bool HasPerspective2D() const {
415 return m[3] != 0 || m[7] != 0 || m[15] != 1;
416 }
417
418 constexpr bool HasPerspective() const {
419 return m[3] != 0 || m[7] != 0 || m[11] != 0 || m[15] != 1;
420 }
421
422 constexpr bool HasTranslation() const { return m[12] != 0 || m[13] != 0; }
423
424 constexpr bool IsAligned2D(Scalar tolerance = 0) const {
425 if (HasPerspective2D()) {
426 return false;
427 }
428 if (ScalarNearlyZero(m[1], tolerance) &&
429 ScalarNearlyZero(m[4], tolerance)) {
430 return true;
431 }
432 if (ScalarNearlyZero(m[0], tolerance) &&
433 ScalarNearlyZero(m[5], tolerance)) {
434 return true;
435 }
436 return false;
437 }
438
439 constexpr bool IsAligned(Scalar tolerance = 0) const {
440 if (HasPerspective()) {
441 return false;
442 }
443 int v[] = {!ScalarNearlyZero(m[0], tolerance), //
444 !ScalarNearlyZero(m[1], tolerance), //
445 !ScalarNearlyZero(m[2], tolerance), //
446 !ScalarNearlyZero(m[4], tolerance), //
447 !ScalarNearlyZero(m[5], tolerance), //
448 !ScalarNearlyZero(m[6], tolerance), //
449 !ScalarNearlyZero(m[8], tolerance), //
450 !ScalarNearlyZero(m[9], tolerance), //
451 !ScalarNearlyZero(m[10], tolerance)};
452 // Check if all three basis vectors are aligned to an axis.
453 if (v[0] + v[1] + v[2] != 1 || //
454 v[3] + v[4] + v[5] != 1 || //
455 v[6] + v[7] + v[8] != 1) {
456 return false;
457 }
458 // Ensure that none of the basis vectors overlap.
459 if (v[0] + v[3] + v[6] != 1 || //
460 v[1] + v[4] + v[7] != 1 || //
461 v[2] + v[5] + v[8] != 1) {
462 return false;
463 }
464 return true;
465 }
466
467 constexpr bool IsIdentity() const {
468 return (
469 // clang-format off
470 m[0] == 1.0f && m[1] == 0.0f && m[2] == 0.0f && m[3] == 0.0f &&
471 m[4] == 0.0f && m[5] == 1.0f && m[6] == 0.0f && m[7] == 0.0f &&
472 m[8] == 0.0f && m[9] == 0.0f && m[10] == 1.0f && m[11] == 0.0f &&
473 m[12] == 0.0f && m[13] == 0.0f && m[14] == 0.0f && m[15] == 1.0f
474 // clang-format on
475 );
476 }
477
478 /// @brief Returns true if the matrix has no entries other than translation
479 /// components. Note that an identity matrix meets this criteria.
480 constexpr bool IsTranslationOnly() const {
481 return (
482 // clang-format off
483 m[0] == 1.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 0.0 &&
484 m[4] == 0.0 && m[5] == 1.0 && m[6] == 0.0 && m[7] == 0.0 &&
485 m[8] == 0.0 && m[9] == 0.0 && m[10] == 1.0 && m[11] == 0.0 &&
486 m[15] == 1.0
487 // clang-format on
488 );
489 }
490
491 /// @brief Returns true if the matrix has a scale-only basis and is
492 /// non-projective. Note that an identity matrix meets this criteria.
493 constexpr bool IsTranslationScaleOnly() const {
494 return (
495 // clang-format off
496 m[0] != 0.0 && m[1] == 0.0 && m[2] == 0.0 && m[3] == 0.0 &&
497 m[4] == 0.0 && m[5] != 0.0 && m[6] == 0.0 && m[7] == 0.0 &&
498 m[8] == 0.0 && m[9] == 0.0 && m[10] != 0.0 && m[11] == 0.0 &&
499 m[15] == 1.0
500 // clang-format on
501 );
502 }
503
504 std::optional<MatrixDecomposition> Decompose() const;
505
506 /// @brief Compute the two non-negative scales applied by this matrix to
507 /// 2D coordinates and return them as an optional pair of Scalar
508 /// values in any order. If the matrix has perspective elements,
509 /// this method will return a nullopt.
510 ///
511 /// Note that negative scale factors really represent a positive scale
512 /// factor with a flip, so the absolute value (the positive scale factor)
513 /// is returned instead so that the results can be directly applied to
514 /// rendering calculations to compute the potential size of an operation.
515 ///
516 /// @see |GetMinScale2D|
517 /// @see |GetMaxScale2D|
518 std::optional<std::pair<Scalar, Scalar>> GetScales2D() const;
519
520 bool Equals(const Matrix& matrix, Scalar epsilon = 1e-5f) const {
521 const Scalar* a = m;
522 const Scalar* b = matrix.m;
523 return ScalarNearlyEqual(a[0], b[0], epsilon) &&
524 ScalarNearlyEqual(a[1], b[1], epsilon) &&
525 ScalarNearlyEqual(a[2], b[2], epsilon) &&
526 ScalarNearlyEqual(a[3], b[3], epsilon) &&
527 ScalarNearlyEqual(a[4], b[4], epsilon) &&
528 ScalarNearlyEqual(a[5], b[5], epsilon) &&
529 ScalarNearlyEqual(a[6], b[6], epsilon) &&
530 ScalarNearlyEqual(a[7], b[7], epsilon) &&
531 ScalarNearlyEqual(a[8], b[8], epsilon) &&
532 ScalarNearlyEqual(a[9], b[9], epsilon) &&
533 ScalarNearlyEqual(a[10], b[10], epsilon) &&
534 ScalarNearlyEqual(a[11], b[11], epsilon) &&
535 ScalarNearlyEqual(a[12], b[12], epsilon) &&
536 ScalarNearlyEqual(a[13], b[13], epsilon) &&
537 ScalarNearlyEqual(a[14], b[14], epsilon) &&
538 ScalarNearlyEqual(a[15], b[15], epsilon);
539 }
540
541 constexpr bool operator==(const Matrix& m) const {
542 // clang-format off
543 return vec[0] == m.vec[0]
544 && vec[1] == m.vec[1]
545 && vec[2] == m.vec[2]
546 && vec[3] == m.vec[3];
547 // clang-format on
548 }
549
550 constexpr bool operator!=(const Matrix& m) const {
551 // clang-format off
552 return vec[0] != m.vec[0]
553 || vec[1] != m.vec[1]
554 || vec[2] != m.vec[2]
555 || vec[3] != m.vec[3];
556 // clang-format on
557 }
558
559 Matrix operator+(const Vector3& t) const { return Translate(t); }
560
561 Matrix operator-(const Vector3& t) const { return Translate(-t); }
562
563 Matrix operator*(const Matrix& m) const { return Multiply(m); }
564
565 Matrix operator+(const Matrix& m) const;
566
567 constexpr Vector4 operator*(const Vector4& v) const {
568 return Vector4(v.x * m[0] + v.y * m[4] + v.z * m[8] + v.w * m[12],
569 v.x * m[1] + v.y * m[5] + v.z * m[9] + v.w * m[13],
570 v.x * m[2] + v.y * m[6] + v.z * m[10] + v.w * m[14],
571 v.x * m[3] + v.y * m[7] + v.z * m[11] + v.w * m[15]);
572 }
573
574 constexpr Vector3 operator*(const Vector3& v) const {
575 Scalar w = v.x * m[3] + v.y * m[7] + v.z * m[11] + m[15];
576 Vector3 result(v.x * m[0] + v.y * m[4] + v.z * m[8] + m[12],
577 v.x * m[1] + v.y * m[5] + v.z * m[9] + m[13],
578 v.x * m[2] + v.y * m[6] + v.z * m[10] + m[14]);
579
580 // This is Skia's behavior, but it may be reasonable to allow UB for the w=0
581 // case.
582 if (w) {
583 w = 1 / w;
584 }
585 return result * w;
586 }
587
588 constexpr Point operator*(const Point& v) const {
589 Scalar w = v.x * m[3] + v.y * m[7] + m[15];
590 Point result(v.x * m[0] + v.y * m[4] + m[12],
591 v.x * m[1] + v.y * m[5] + m[13]);
592
593 // This is Skia's behavior, but it may be reasonable to allow UB for the w=0
594 // case.
595 if (w) {
596 w = 1 / w;
597 }
598 return result * w;
599 }
600
601 constexpr Vector3 TransformHomogenous(const Point& v) const {
602 return Vector3(v.x * m[0] + v.y * m[4] + m[12],
603 v.x * m[1] + v.y * m[5] + m[13],
604 v.x * m[3] + v.y * m[7] + m[15]);
605 }
606
607 constexpr Vector4 TransformDirection(const Vector4& v) const {
608 return Vector4(v.x * m[0] + v.y * m[4] + v.z * m[8],
609 v.x * m[1] + v.y * m[5] + v.z * m[9],
610 v.x * m[2] + v.y * m[6] + v.z * m[10], v.w);
611 }
612
613 constexpr Vector3 TransformDirection(const Vector3& v) const {
614 return Vector3(v.x * m[0] + v.y * m[4] + v.z * m[8],
615 v.x * m[1] + v.y * m[5] + v.z * m[9],
616 v.x * m[2] + v.y * m[6] + v.z * m[10]);
617 }
618
619 constexpr Vector2 TransformDirection(const Vector2& v) const {
620 return Vector2(v.x * m[0] + v.y * m[4], v.x * m[1] + v.y * m[5]);
621 }
622
623 constexpr Quad Transform(const Quad& quad) const {
624 return {
625 *this * quad[0],
626 *this * quad[1],
627 *this * quad[2],
628 *this * quad[3],
629 };
630 }
631
632 template <class T>
633 static constexpr Matrix MakeOrthographic(TSize<T> size) {
634 // Per assumptions about NDC documented above.
635 const auto scale =
636 MakeScale({2.0f / static_cast<Scalar>(size.width),
637 -2.0f / static_cast<Scalar>(size.height), 0.0f});
638 const auto translate = MakeTranslation({-1.0f, 1.0f, 0.5f});
639 return translate * scale;
640 }
641
642 static inline Matrix MakePerspective(Radians fov_y,
643 Scalar aspect_ratio,
644 Scalar z_near,
645 Scalar z_far) {
646 Scalar height = std::tan(fov_y.radians * 0.5f);
647 Scalar width = height * aspect_ratio;
648
649 // clang-format off
650 return {
651 1.0f / width, 0.0f, 0.0f, 0.0f,
652 0.0f, 1.0f / height, 0.0f, 0.0f,
653 0.0f, 0.0f, z_far / (z_far - z_near), 1.0f,
654 0.0f, 0.0f, -(z_far * z_near) / (z_far - z_near), 0.0f,
655 };
656 // clang-format on
657 }
658
659 template <class T>
660 static constexpr Matrix MakePerspective(Radians fov_y,
661 TSize<T> size,
662 Scalar z_near,
663 Scalar z_far) {
664 return MakePerspective(fov_y, static_cast<Scalar>(size.width) / size.height,
665 z_near, z_far);
666 }
667
668 static inline Matrix MakeLookAt(Vector3 position,
669 Vector3 target,
670 Vector3 up) {
671 Vector3 forward = (target - position).Normalize();
672 Vector3 right = up.Cross(forward);
673 up = forward.Cross(right);
674
675 // clang-format off
676 return {
677 right.x, up.x, forward.x, 0.0f,
678 right.y, up.y, forward.y, 0.0f,
679 right.z, up.z, forward.z, 0.0f,
680 -right.Dot(position), -up.Dot(position), -forward.Dot(position), 1.0f
681 };
682 // clang-format on
683 }
684
685 static inline Vector2 CosSin(Radians radians) {
686 // The precision of a float around 1.0 is much lower than it is
687 // around 0.0, so we end up with cases on quadrant rotations where
688 // we get a +/-1.0 for one of the values and a non-zero value for
689 // the other. This happens around quadrant rotations which makes it
690 // especially common and results in unclean quadrant rotation
691 // matrices which do not return true from |IsAligned[2D]| even
692 // though that is exactly where you need them to exhibit that property.
693 // It also injects small floating point mantissa errors into the
694 // matrices whenever you concatenate them with a quadrant rotation.
695 //
696 // This issue is also exacerbated by the fact that, in radians, the
697 // angles for quadrant rotations are irrational numbers. The measuring
698 // error for representing 90 degree multiples is small enough that
699 // either sin or cos will return a value near +/-1.0, but not small
700 // enough that the other value will be a clean 0.0.
701 //
702 // Some geometry packages simply discard very small numbers from
703 // sin/cos, but the following approach specifically targets just the
704 // area around a quadrant rotation (where either the sin or cos are
705 // measuring as +/-1.0) for symmetry of precision.
706
707 Scalar sin = std::sin(radians.radians);
708 if (std::abs(sin) == 1.0f) {
709 // 90 or 270 degrees (mod 360)
710 return {0.0f, sin};
711 } else {
712 Scalar cos = std::cos(radians.radians);
713 if (std::abs(cos) == 1.0f) {
714 // 0 or 180 degrees (mod 360)
715 return {cos, 0.0f};
716 }
717 return {cos, sin};
718 }
719 }
720 };
721
722 static_assert(sizeof(struct Matrix) == sizeof(Scalar) * 16,
723 "The matrix must be of consistent size.");
724
725 } // namespace impeller
726
727 namespace std {
728 inline std::ostream& operator<<(std::ostream& out, const impeller::Matrix& m) {
729 out << "(" << std::endl << std::fixed;
730 for (size_t i = 0; i < 4u; i++) {
731 for (size_t j = 0; j < 4u; j++) {
732 out << std::setw(15) << m.e[j][i] << ",";
733 }
734 out << std::endl;
735 }
736 out << ")";
737 return out;
738 }
739
740 } // namespace std
741
742 #endif // FLUTTER_IMPELLER_GEOMETRY_MATRIX_H_
matrix_decomposition.h
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller
Definition: allocation.cc:12
impeller::Vector2
Point Vector2
Definition: point.h:429
impeller::Scalar
float Scalar
Definition: scalar.h:19
impeller::ScalarNearlyZero
constexpr bool ScalarNearlyZero(Scalar x, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:31
impeller::ScalarNearlyEqual
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:36
impeller::Quad
std::array< Point, 4 > Quad
Definition: point.h:430
std
Definition: comparable.h:95
std::operator<<
std::ostream & operator<<(std::ostream &out, const impeller::Arc &a)
Definition: arc.h:141
point.h
quaternion.h
scalar.h
shear.h
size.h
impeller::MatrixDecomposition
Definition: matrix_decomposition.h:15
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Matrix::IsTranslationOnly
constexpr bool IsTranslationOnly() const
Returns true if the matrix has no entries other than translation components. Note that an identity ma...
Definition: matrix.h:480
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:633
impeller::Matrix::Multiply
constexpr Matrix Multiply(const Matrix &o) const
Definition: matrix.h:284
impeller::Matrix::IsAffine
constexpr bool IsAffine() const
Definition: matrix.h:409
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::Matrix::GetBasisY
constexpr Vector3 GetBasisY() const
Definition: matrix.h:390
impeller::Matrix::Matrix
constexpr Matrix()
Definition: matrix.h:47
impeller::Matrix::IsIdentity
constexpr bool IsIdentity() const
Definition: matrix.h:467
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:493
impeller::Matrix::m
Scalar m[16]
Definition: matrix.h:39
impeller::Matrix::HasTranslation
constexpr bool HasTranslation() const
Definition: matrix.h:422
impeller::Matrix::Translate
constexpr Matrix Translate(const Vector3 &t) const
Definition: matrix.h:263
impeller::Matrix::GetMinScale2D
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
impeller::Matrix::IsInvertible
bool IsInvertible() const
Definition: matrix.h:321
impeller::Matrix::TransformDirection
constexpr Vector2 TransformDirection(const Vector2 &v) const
Definition: matrix.h:619
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector2 &s)
Definition: matrix.h:123
impeller::Matrix::operator+
Matrix operator+(const Vector3 &t) const
Definition: matrix.h:559
impeller::Matrix::GetDirectionScale
Scalar GetDirectionScale(Vector3 direction) const
Definition: matrix.h:399
impeller::Matrix::Basis
constexpr Matrix Basis() const
The Matrix without its w components (without translation).
Definition: matrix.h:239
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::operator*
Matrix operator*(const Matrix &m) const
Definition: matrix.h:563
impeller::Matrix::IsAligned
constexpr bool IsAligned(Scalar tolerance=0) const
Definition: matrix.h:439
impeller::Matrix::Invert
Matrix Invert() const
Definition: matrix.cc:99
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::MakeRotationY
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:208
impeller::Matrix::GetBasisZ
constexpr Vector3 GetBasisZ() const
Definition: matrix.h:392
impeller::Matrix::vec
Vector4 vec[4]
Definition: matrix.h:41
impeller::Matrix::Decompose
std::optional< MatrixDecomposition > Decompose() const
Definition: matrix.cc:202
impeller::Matrix::HasPerspective2D
constexpr bool HasPerspective2D() const
Definition: matrix.h:414
impeller::Matrix::IsFinite
bool IsFinite() const
Definition: matrix.h:404
impeller::Matrix::operator-
Matrix operator-(const Vector3 &t) const
Definition: matrix.h:561
impeller::Matrix::operator*
constexpr Vector4 operator*(const Vector4 &v) const
Definition: matrix.h:567
impeller::Matrix::CosSin
static Vector2 CosSin(Radians radians)
Definition: matrix.h:685
impeller::Matrix::operator!=
constexpr bool operator!=(const Matrix &m) const
Definition: matrix.h:550
impeller::Matrix::MakePerspective
static Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:642
impeller::Matrix::operator*
constexpr Point operator*(const Point &v) const
Definition: matrix.h:588
impeller::Matrix::operator*
constexpr Vector3 operator*(const Vector3 &v) const
Definition: matrix.h:574
impeller::Matrix::TransformDirection
constexpr Vector3 TransformDirection(const Vector3 &v) const
Definition: matrix.h:613
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::GetScale
Vector3 GetScale() const
Definition: matrix.h:394
impeller::Matrix::MakeSkew
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
Definition: matrix.h:127
impeller::Matrix::Transform
constexpr Quad Transform(const Quad &quad) const
Definition: matrix.h:623
impeller::Matrix::operator==
constexpr bool operator==(const Matrix &m) const
Definition: matrix.h:541
impeller::Matrix::Scale
constexpr Matrix Scale(const Vector3 &s) const
Definition: matrix.h:275
impeller::Matrix::TransformHomogenous
constexpr Vector3 TransformHomogenous(const Point &v) const
Definition: matrix.h:601
impeller::Matrix::MakeTranslateScale
static constexpr Matrix MakeTranslateScale(const Vector3 &s, const Vector3 &t)
Definition: matrix.h:113
impeller::Matrix::e
Scalar e[4][4]
Definition: matrix.h:40
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:223
impeller::Matrix::GetScales2D
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
impeller::Matrix::GetMaxScale2D
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
impeller::Matrix::MakeRotation
static Matrix MakeRotation(Radians radians, const Vector4 &r)
Definition: matrix.h:161
impeller::Matrix::MakePerspective
static constexpr Matrix MakePerspective(Radians fov_y, TSize< T > size, Scalar z_near, Scalar z_far)
Definition: matrix.h:660
impeller::Matrix::GetDeterminant
Scalar GetDeterminant() const
Definition: matrix.cc:164
impeller::Matrix::HasPerspective
constexpr bool HasPerspective() const
Definition: matrix.h:418
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::Matrix::MakeLookAt
static Matrix MakeLookAt(Vector3 position, Vector3 target, Vector3 up)
Definition: matrix.h:668
impeller::Matrix::TransformDirection
constexpr Vector4 TransformDirection(const Vector4 &v) const
Definition: matrix.h:607
impeller::Matrix::GetBasisX
constexpr Vector3 GetBasisX() const
Definition: matrix.h:388
impeller::Matrix::Equals
bool Equals(const Matrix &matrix, Scalar epsilon=1e-5f) const
Definition: matrix.h:520
impeller::Matrix::To3x3
constexpr Matrix To3x3() const
Definition: matrix.h:252
impeller::Matrix::IsAligned2D
constexpr bool IsAligned2D(Scalar tolerance=0) const
Definition: matrix.h:424
impeller::Matrix::MakeRotation
static Matrix MakeRotation(Quaternion q)
Definition: matrix.h:136
impeller::Matrix::Transpose
constexpr Matrix Transpose() const
Definition: matrix.h:306
impeller::Matrix::GetMaxBasisLengthXY
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
impeller::Matrix::MakeRotationX
static Matrix MakeRotationX(Radians r)
Definition: matrix.h:193
impeller::Quaternion
Definition: quaternion.h:14
impeller::Quaternion::y
Scalar y
Definition: quaternion.h:18
impeller::Quaternion::z
Scalar z
Definition: quaternion.h:19
impeller::Quaternion::w
Scalar w
Definition: quaternion.h:20
impeller::Quaternion::x
Scalar x
Definition: quaternion.h:17
impeller::Radians
Definition: scalar.h:44
impeller::Radians::radians
Scalar radians
Definition: scalar.h:45
impeller::TPoint< Scalar >
impeller::TPoint::y
Type y
Definition: point.h:32
impeller::TPoint::x
Type x
Definition: point.h:31
impeller::TSize
Definition: size.h:25
impeller::TSize::height
Type height
Definition: size.h:29
impeller::TSize::width
Type width
Definition: size.h:28
impeller::Vector3
Definition: vector.h:20
impeller::Vector3::Normalize
Vector3 Normalize() const
Definition: vector.h:49
impeller::Vector3::x
Scalar x
Definition: vector.h:23
impeller::Vector3::Cross
constexpr Vector3 Cross(const Vector3 &other) const
Definition: vector.h:62
impeller::Vector3::y
Scalar y
Definition: vector.h:24
impeller::Vector3::Dot
constexpr Scalar Dot(const Vector3 &other) const
Definition: vector.h:54
impeller::Vector3::GetLength
Scalar GetLength() const
Definition: vector.h:47
impeller::Vector3::z
Scalar z
Definition: vector.h:25
impeller::Vector4
Definition: vector.h:232
impeller::Vector4::IsFinite
bool IsFinite() const
Definition: vector.h:258
impeller::Vector4::Normalize
Vector4 Normalize() const
Definition: vector.h:263
impeller::Vector4::y
Scalar y
Definition: vector.h:236
impeller::Vector4::w
Scalar w
Definition: vector.h:238
impeller::Vector4::x
Scalar x
Definition: vector.h:235
impeller::Vector4::z
Scalar z
Definition: vector.h:237
vector.h