Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
22
23namespace 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).
37struct 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|
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,
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
730static_assert(sizeof(struct Matrix) == sizeof(Scalar) * 16,
731 "The matrix must be of consistent size.");
732
733} // namespace impeller
734
735namespace std {
736inline 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_
uint32_t * target
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 ref_ptr.h:261
std::ostream & operator<<(std::ostream &out, const impeller::Arc &a)
Definition arc.h:141
int32_t height
int32_t width
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
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
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
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
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:256
Vector4 Normalize() const
Definition vector.h:261