Flutter Engine
 
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 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,
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
722static_assert(sizeof(struct Matrix) == sizeof(Scalar) * 16,
723 "The matrix must be of consistent size.");
724
725} // namespace impeller
726
727namespace std {
728inline 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_
uint32_t * target
Point Vector2
Definition point.h:331
float Scalar
Definition scalar.h:19
constexpr bool ScalarNearlyZero(Scalar x, Scalar tolerance=kEhCloseEnough)
Definition scalar.h:31
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition scalar.h:36
std::array< Point, 4 > Quad
Definition point.h:332
Definition 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:480
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition matrix.h:633
constexpr Matrix Multiply(const Matrix &o) const
Definition matrix.h:284
constexpr bool IsAffine() const
Definition matrix.h:409
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:467
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
Scalar m[16]
Definition matrix.h:39
constexpr bool HasTranslation() const
Definition matrix.h:422
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:619
static constexpr Matrix MakeScale(const Vector2 &s)
Definition matrix.h:123
Matrix operator+(const Vector3 &t) const
Definition matrix.h:559
Scalar GetDirectionScale(Vector3 direction) const
Definition matrix.h:399
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:563
constexpr bool IsAligned(Scalar tolerance=0) const
Definition matrix.h:439
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:414
bool IsFinite() const
Definition matrix.h:404
Matrix operator-(const Vector3 &t) const
Definition matrix.h:561
constexpr Vector4 operator*(const Vector4 &v) const
Definition matrix.h:567
static Vector2 CosSin(Radians radians)
Definition matrix.h:685
constexpr bool operator!=(const Matrix &m) const
Definition matrix.h:550
static Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition matrix.h:642
constexpr Point operator*(const Point &v) const
Definition matrix.h:588
constexpr Vector3 operator*(const Vector3 &v) const
Definition matrix.h:574
constexpr Vector3 TransformDirection(const Vector3 &v) const
Definition matrix.h:613
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:623
constexpr bool operator==(const Matrix &m) const
Definition matrix.h:541
constexpr Matrix Scale(const Vector3 &s) const
Definition matrix.h:275
constexpr Vector3 TransformHomogenous(const Point &v) const
Definition matrix.h:601
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
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:660
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:418
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
static Matrix MakeLookAt(Vector3 position, Vector3 target, Vector3 up)
Definition matrix.h:668
constexpr Vector4 TransformDirection(const Vector4 &v) const
Definition matrix.h:607
constexpr Vector3 GetBasisX() const
Definition matrix.h:388
bool Equals(const Matrix &matrix, Scalar epsilon=1e-5f) const
Definition matrix.h:520
constexpr Matrix To3x3() const
Definition matrix.h:252
constexpr bool IsAligned2D(Scalar tolerance=0) const
Definition matrix.h:424
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:258
Vector4 Normalize() const
Definition vector.h:263