Flutter Impeller
geometry_unittests.cc
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 #include "gtest/gtest.h"
7 
8 #include <limits>
9 #include <sstream>
10 #include <type_traits>
11 
12 #include "flutter/fml/build_config.h"
13 #include "flutter/testing/testing.h"
17 #include "impeller/geometry/half.h"
18 #include "impeller/geometry/path.h"
22 #include "impeller/geometry/rect.h"
24 #include "impeller/geometry/size.h"
25 
26 // TODO(zanderso): https://github.com/flutter/flutter/issues/127701
27 // NOLINTBEGIN(bugprone-unchecked-optional-access)
28 
29 namespace impeller {
30 namespace testing {
31 
32 TEST(GeometryTest, ScalarNearlyEqual) {
33  ASSERT_FALSE(ScalarNearlyEqual(0.0021f, 0.001f));
34  ASSERT_TRUE(ScalarNearlyEqual(0.0019f, 0.001f));
35  ASSERT_TRUE(ScalarNearlyEqual(0.002f, 0.001f, 0.0011f));
36  ASSERT_FALSE(ScalarNearlyEqual(0.002f, 0.001f, 0.0009f));
37  ASSERT_TRUE(ScalarNearlyEqual(
38  1.0f, 1.0f + std::numeric_limits<float>::epsilon() * 4));
39 }
40 
41 TEST(GeometryTest, MakeColumn) {
42  auto matrix = Matrix::MakeColumn(1, 2, 3, 4, //
43  5, 6, 7, 8, //
44  9, 10, 11, 12, //
45  13, 14, 15, 16);
46 
47  auto expect = Matrix{1, 2, 3, 4, //
48  5, 6, 7, 8, //
49  9, 10, 11, 12, //
50  13, 14, 15, 16};
51 
52  ASSERT_TRUE(matrix == expect);
53 }
54 
55 TEST(GeometryTest, MakeRow) {
56  auto matrix = Matrix::MakeRow(1, 2, 3, 4, //
57  5, 6, 7, 8, //
58  9, 10, 11, 12, //
59  13, 14, 15, 16);
60 
61  auto expect = Matrix{1, 5, 9, 13, //
62  2, 6, 10, 14, //
63  3, 7, 11, 15, //
64  4, 8, 12, 16};
65 
66  ASSERT_TRUE(matrix == expect);
67 }
68 
69 TEST(GeometryTest, RotationMatrix) {
70  auto rotation = Matrix::MakeRotationZ(Radians{kPiOver4});
71  auto expect = Matrix{0.707, 0.707, 0, 0, //
72  -0.707, 0.707, 0, 0, //
73  0, 0, 1, 0, //
74  0, 0, 0, 1};
75  ASSERT_MATRIX_NEAR(rotation, expect);
76 }
77 
78 TEST(GeometryTest, InvertMultMatrix) {
79  {
80  auto rotation = Matrix::MakeRotationZ(Radians{kPiOver4});
81  auto invert = rotation.Invert();
82  auto expect = Matrix{0.707, -0.707, 0, 0, //
83  0.707, 0.707, 0, 0, //
84  0, 0, 1, 0, //
85  0, 0, 0, 1};
86  ASSERT_MATRIX_NEAR(invert, expect);
87  }
88  {
89  auto scale = Matrix::MakeScale(Vector2{2, 4});
90  auto invert = scale.Invert();
91  auto expect = Matrix{0.5, 0, 0, 0, //
92  0, 0.25, 0, 0, //
93  0, 0, 1, 0, //
94  0, 0, 0, 1};
95  ASSERT_MATRIX_NEAR(invert, expect);
96  }
97 }
98 
99 TEST(GeometryTest, MatrixBasis) {
100  auto matrix = Matrix{1, 2, 3, 4, //
101  5, 6, 7, 8, //
102  9, 10, 11, 12, //
103  13, 14, 15, 16};
104  auto basis = matrix.Basis();
105  auto expect = Matrix{1, 2, 3, 0, //
106  5, 6, 7, 0, //
107  9, 10, 11, 0, //
108  0, 0, 0, 1};
109  ASSERT_MATRIX_NEAR(basis, expect);
110 }
111 
112 TEST(GeometryTest, MutliplicationMatrix) {
113  auto rotation = Matrix::MakeRotationZ(Radians{kPiOver4});
114  auto invert = rotation.Invert();
115  ASSERT_MATRIX_NEAR(rotation * invert, Matrix{});
116 }
117 
118 TEST(GeometryTest, DeterminantTest) {
119  auto matrix = Matrix{3, 4, 14, 155, 2, 1, 3, 4, 2, 3, 2, 1, 1, 2, 4, 2};
120  ASSERT_EQ(matrix.GetDeterminant(), -1889);
121 }
122 
123 TEST(GeometryTest, InvertMatrix) {
124  auto inverted = Matrix{10, -9, -12, 8, //
125  7, -12, 11, 22, //
126  -10, 10, 3, 6, //
127  -2, 22, 2, 1}
128  .Invert();
129 
130  auto result = Matrix{
131  438.0 / 85123.0, 1751.0 / 85123.0, -7783.0 / 85123.0, 4672.0 / 85123.0,
132  393.0 / 85123.0, -178.0 / 85123.0, -570.0 / 85123.0, 4192 / 85123.0,
133  -5230.0 / 85123.0, 2802.0 / 85123.0, -3461.0 / 85123.0, 962.0 / 85123.0,
134  2690.0 / 85123.0, 1814.0 / 85123.0, 3896.0 / 85123.0, 319.0 / 85123.0};
135 
136  ASSERT_MATRIX_NEAR(inverted, result);
137 }
138 
139 TEST(GeometryTest, TestDecomposition) {
140  auto rotated = Matrix::MakeRotationZ(Radians{kPiOver4});
141 
142  auto result = rotated.Decompose();
143 
144  ASSERT_TRUE(result.has_value());
145 
146  MatrixDecomposition res = result.value();
147 
148  auto quaternion = Quaternion{{0.0, 0.0, 1.0}, kPiOver4};
149  ASSERT_QUATERNION_NEAR(res.rotation, quaternion);
150 }
151 
152 TEST(GeometryTest, TestDecomposition2) {
153  auto rotated = Matrix::MakeRotationZ(Radians{kPiOver4});
154  auto scaled = Matrix::MakeScale({2.0, 3.0, 1.0});
155  auto translated = Matrix::MakeTranslation({-200, 750, 20});
156 
157  auto result = (translated * rotated * scaled).Decompose();
158 
159  ASSERT_TRUE(result.has_value());
160 
161  MatrixDecomposition res = result.value();
162 
163  auto quaternion = Quaternion{{0.0, 0.0, 1.0}, kPiOver4};
164 
165  ASSERT_QUATERNION_NEAR(res.rotation, quaternion);
166 
167  ASSERT_FLOAT_EQ(res.translation.x, -200);
168  ASSERT_FLOAT_EQ(res.translation.y, 750);
169  ASSERT_FLOAT_EQ(res.translation.z, 20);
170 
171  ASSERT_FLOAT_EQ(res.scale.x, 2);
172  ASSERT_FLOAT_EQ(res.scale.y, 3);
173  ASSERT_FLOAT_EQ(res.scale.z, 1);
174 }
175 
176 TEST(GeometryTest, TestRecomposition) {
177  /*
178  * Decomposition.
179  */
180  auto rotated = Matrix::MakeRotationZ(Radians{kPiOver4});
181 
182  auto result = rotated.Decompose();
183 
184  ASSERT_TRUE(result.has_value());
185 
186  MatrixDecomposition res = result.value();
187 
188  auto quaternion = Quaternion{{0.0, 0.0, 1.0}, kPiOver4};
189 
190  ASSERT_QUATERNION_NEAR(res.rotation, quaternion);
191 
192  /*
193  * Recomposition.
194  */
195  ASSERT_MATRIX_NEAR(rotated, Matrix{res});
196 }
197 
198 TEST(GeometryTest, TestRecomposition2) {
199  auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
201  Matrix::MakeScale({2.0, 2.0, 2.0});
202 
203  auto result = matrix.Decompose();
204 
205  ASSERT_TRUE(result.has_value());
206 
207  ASSERT_MATRIX_NEAR(matrix, Matrix{result.value()});
208 }
209 
210 TEST(GeometryTest, MatrixVectorMultiplication) {
211  {
212  auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
214  Matrix::MakeScale({2.0, 2.0, 2.0});
215  auto vector = Vector4(10, 20, 30, 2);
216 
217  Vector4 result = matrix * vector;
218  auto expected = Vector4(160, 220, 260, 2);
219  ASSERT_VECTOR4_NEAR(result, expected);
220  }
221 
222  {
223  auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
225  Matrix::MakeScale({2.0, 2.0, 2.0});
226  auto vector = Vector3(10, 20, 30);
227 
228  Vector3 result = matrix * vector;
229  auto expected = Vector3(60, 120, 160);
230  ASSERT_VECTOR3_NEAR(result, expected);
231  }
232 
233  {
234  auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
236  Matrix::MakeScale({2.0, 2.0, 2.0});
237  auto vector = Point(10, 20);
238 
239  Point result = matrix * vector;
240  auto expected = Point(60, 120);
241  ASSERT_POINT_NEAR(result, expected);
242  }
243 
244  // Matrix Vector ops should respect perspective transforms.
245  {
246  auto matrix = Matrix::MakePerspective(Radians(kPiOver2), 1, 1, 100);
247  auto vector = Vector3(3, 3, -3);
248 
249  Vector3 result = matrix * vector;
250  auto expected = Vector3(-1, -1, 1.3468);
251  ASSERT_VECTOR3_NEAR(result, expected);
252  }
253 
254  {
255  auto matrix = Matrix::MakePerspective(Radians(kPiOver2), 1, 1, 100) *
256  Matrix::MakeTranslation(Vector3(0, 0, -3));
257  auto point = Point(3, 3);
258 
259  Point result = matrix * point;
260  auto expected = Point(-1, -1);
261  ASSERT_POINT_NEAR(result, expected);
262  }
263 
264  // Resolves to 0 on perspective singularity.
265  {
266  auto matrix = Matrix::MakePerspective(Radians(kPiOver2), 1, 1, 100);
267  auto point = Point(3, 3);
268 
269  Point result = matrix * point;
270  auto expected = Point(0, 0);
271  ASSERT_POINT_NEAR(result, expected);
272  }
273 }
274 
275 TEST(GeometryTest, MatrixMakeRotationFromQuaternion) {
276  {
277  auto matrix = Matrix::MakeRotation(Quaternion({1, 0, 0}, kPiOver2));
278  auto expected = Matrix::MakeRotationX(Radians(kPiOver2));
279  ASSERT_MATRIX_NEAR(matrix, expected);
280  }
281 
282  {
283  auto matrix = Matrix::MakeRotation(Quaternion({0, 1, 0}, kPiOver2));
284  auto expected = Matrix::MakeRotationY(Radians(kPiOver2));
285  ASSERT_MATRIX_NEAR(matrix, expected);
286  }
287 
288  {
289  auto matrix = Matrix::MakeRotation(Quaternion({0, 0, 1}, kPiOver2));
290  auto expected = Matrix::MakeRotationZ(Radians(kPiOver2));
291  ASSERT_MATRIX_NEAR(matrix, expected);
292  }
293 }
294 
295 TEST(GeometryTest, MatrixTransformDirection) {
296  {
297  auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
299  Matrix::MakeScale({2.0, 2.0, 2.0});
300  auto vector = Vector4(10, 20, 30, 2);
301 
302  Vector4 result = matrix.TransformDirection(vector);
303  auto expected = Vector4(-40, 20, 60, 2);
304  ASSERT_VECTOR4_NEAR(result, expected);
305  }
306 
307  {
308  auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
310  Matrix::MakeScale({2.0, 2.0, 2.0});
311  auto vector = Vector3(10, 20, 30);
312 
313  Vector3 result = matrix.TransformDirection(vector);
314  auto expected = Vector3(-40, 20, 60);
315  ASSERT_VECTOR3_NEAR(result, expected);
316  }
317 
318  {
319  auto matrix = Matrix::MakeTranslation({0, -0.4, 100}) *
321  Matrix::MakeScale({2.0, 2.0, 2.0});
322  auto vector = Point(10, 20);
323 
324  Point result = matrix.TransformDirection(vector);
325  auto expected = Point(-40, 20);
326  ASSERT_POINT_NEAR(result, expected);
327  }
328 }
329 
330 TEST(GeometryTest, MatrixGetMaxBasisLength) {
331  {
332  auto m = Matrix::MakeScale({3, 1, 1});
333  ASSERT_EQ(m.GetMaxBasisLength(), 3);
334 
335  m = m * Matrix::MakeSkew(0, 4);
336  ASSERT_EQ(m.GetMaxBasisLength(), 5);
337  }
338 
339  {
340  auto m = Matrix::MakeScale({-3, 4, 2});
341  ASSERT_EQ(m.GetMaxBasisLength(), 4);
342  }
343 }
344 
345 TEST(GeometryTest, MatrixGetMaxBasisLengthXY) {
346  {
347  auto m = Matrix::MakeScale({3, 1, 1});
348  ASSERT_EQ(m.GetMaxBasisLengthXY(), 3);
349 
350  m = m * Matrix::MakeSkew(0, 4);
351  ASSERT_EQ(m.GetMaxBasisLengthXY(), 5);
352  }
353 
354  {
355  auto m = Matrix::MakeScale({-3, 4, 7});
356  ASSERT_EQ(m.GetMaxBasisLengthXY(), 4);
357  }
358 }
359 
360 TEST(GeometryTest, MatrixMakeOrthographic) {
361  {
362  auto m = Matrix::MakeOrthographic(Size(100, 200));
363  auto expect = Matrix{
364  0.02, 0, 0, 0, //
365  0, -0.01, 0, 0, //
366  0, 0, 0, 0, //
367  -1, 1, 0.5, 1, //
368  };
369  ASSERT_MATRIX_NEAR(m, expect);
370  }
371 
372  {
373  auto m = Matrix::MakeOrthographic(Size(400, 100));
374  auto expect = Matrix{
375  0.005, 0, 0, 0, //
376  0, -0.02, 0, 0, //
377  0, 0, 0, 0, //
378  -1, 1, 0.5, 1, //
379  };
380  ASSERT_MATRIX_NEAR(m, expect);
381  }
382 }
383 
384 TEST(GeometryTest, MatrixMakePerspective) {
385  {
386  auto m = Matrix::MakePerspective(Degrees(60), Size(100, 200), 1, 10);
387  auto expect = Matrix{
388  3.4641, 0, 0, 0, //
389  0, 1.73205, 0, 0, //
390  0, 0, 1.11111, 1, //
391  0, 0, -1.11111, 0, //
392  };
393  ASSERT_MATRIX_NEAR(m, expect);
394  }
395 
396  {
397  auto m = Matrix::MakePerspective(Radians(1), 2, 10, 20);
398  auto expect = Matrix{
399  0.915244, 0, 0, 0, //
400  0, 1.83049, 0, 0, //
401  0, 0, 2, 1, //
402  0, 0, -20, 0, //
403  };
404  ASSERT_MATRIX_NEAR(m, expect);
405  }
406 }
407 
408 TEST(GeometryTest, MatrixGetBasisVectors) {
409  {
410  auto m = Matrix();
411  Vector3 x = m.GetBasisX();
412  Vector3 y = m.GetBasisY();
413  Vector3 z = m.GetBasisZ();
414  ASSERT_VECTOR3_NEAR(x, Vector3(1, 0, 0));
415  ASSERT_VECTOR3_NEAR(y, Vector3(0, 1, 0));
416  ASSERT_VECTOR3_NEAR(z, Vector3(0, 0, 1));
417  }
418 
419  {
422  Matrix::MakeScale(Vector3(2, 3, 4));
423  Vector3 x = m.GetBasisX();
424  Vector3 y = m.GetBasisY();
425  Vector3 z = m.GetBasisZ();
426  ASSERT_VECTOR3_NEAR(x, Vector3(0, 2, 0));
427  ASSERT_VECTOR3_NEAR(y, Vector3(0, 0, 3));
428  ASSERT_VECTOR3_NEAR(z, Vector3(4, 0, 0));
429  }
430 }
431 
432 TEST(GeometryTest, MatrixGetDirectionScale) {
433  {
434  auto m = Matrix();
435  Scalar result = m.GetDirectionScale(Vector3{1, 0, 0});
436  ASSERT_FLOAT_EQ(result, 1);
437  }
438 
439  {
440  auto m = Matrix::MakeRotationX(Degrees{10}) *
443  Scalar result = m.GetDirectionScale(Vector3{0, 1, 0});
444  ASSERT_FLOAT_EQ(result, 1);
445  }
446 
447  {
449  Matrix::MakeScale(Vector3(3, 4, 5));
450  Scalar result = m.GetDirectionScale(Vector3{2, 0, 0});
451  ASSERT_FLOAT_EQ(result, 8);
452  }
453 }
454 
455 TEST(GeometryTest, MatrixIsAligned) {
456  {
457  auto m = Matrix::MakeTranslation({1, 2, 3});
458  bool result = m.IsAligned();
459  ASSERT_TRUE(result);
460  }
461 
462  {
463  auto m = Matrix::MakeRotationZ(Degrees{123});
464  bool result = m.IsAligned();
465  ASSERT_FALSE(result);
466  }
467 }
468 
469 TEST(GeometryTest, MatrixTranslationScaleOnly) {
470  {
471  auto m = Matrix();
472  bool result = m.IsTranslationScaleOnly();
473  ASSERT_TRUE(result);
474  }
475 
476  {
477  auto m = Matrix::MakeScale(Vector3(2, 3, 4));
478  bool result = m.IsTranslationScaleOnly();
479  ASSERT_TRUE(result);
480  }
481 
482  {
483  auto m = Matrix::MakeTranslation(Vector3(2, 3, 4));
484  bool result = m.IsTranslationScaleOnly();
485  ASSERT_TRUE(result);
486  }
487 
488  {
489  auto m = Matrix::MakeRotationZ(Degrees(10));
490  bool result = m.IsTranslationScaleOnly();
491  ASSERT_FALSE(result);
492  }
493 }
494 
495 TEST(GeometryTest, MatrixLookAt) {
496  {
497  auto m = Matrix::MakeLookAt(Vector3(0, 0, -1), Vector3(0, 0, 1),
498  Vector3(0, 1, 0));
499  auto expected = Matrix{
500  1, 0, 0, 0, //
501  0, 1, 0, 0, //
502  0, 0, 1, 0, //
503  0, 0, 1, 1, //
504  };
505  ASSERT_MATRIX_NEAR(m, expected);
506  }
507 
508  // Sideways tilt.
509  {
510  auto m = Matrix::MakeLookAt(Vector3(0, 0, -1), Vector3(0, 0, 1),
511  Vector3(1, 1, 0).Normalize());
512 
513  // clang-format off
514  auto expected = Matrix{
515  k1OverSqrt2, k1OverSqrt2, 0, 0,
516  -k1OverSqrt2, k1OverSqrt2, 0, 0,
517  0, 0, 1, 0,
518  0, 0, 1, 1,
519  };
520  // clang-format on
521  ASSERT_MATRIX_NEAR(m, expected);
522  }
523 
524  // Half way between +x and -y, yaw 90
525  {
526  auto m =
527  Matrix::MakeLookAt(Vector3(), Vector3(10, -10, 0), Vector3(0, 0, -1));
528 
529  // clang-format off
530  auto expected = Matrix{
531  -k1OverSqrt2, 0, k1OverSqrt2, 0,
532  -k1OverSqrt2, 0, -k1OverSqrt2, 0,
533  0, -1, 0, 0,
534  0, 0, 0, 1,
535  };
536  // clang-format on
537  ASSERT_MATRIX_NEAR(m, expected);
538  }
539 }
540 
541 TEST(GeometryTest, QuaternionLerp) {
542  auto q1 = Quaternion{{0.0, 0.0, 1.0}, 0.0};
543  auto q2 = Quaternion{{0.0, 0.0, 1.0}, kPiOver4};
544 
545  auto q3 = q1.Slerp(q2, 0.5);
546 
547  auto expected = Quaternion{{0.0, 0.0, 1.0}, kPiOver4 / 2.0};
548 
549  ASSERT_QUATERNION_NEAR(q3, expected);
550 }
551 
552 TEST(GeometryTest, QuaternionVectorMultiply) {
553  {
554  Quaternion q({0, 0, 1}, 0);
555  Vector3 v(0, 1, 0);
556 
557  Vector3 result = q * v;
558  Vector3 expected(0, 1, 0);
559 
560  ASSERT_VECTOR3_NEAR(result, expected);
561  }
562 
563  {
564  Quaternion q({0, 0, 1}, k2Pi);
565  Vector3 v(1, 0, 0);
566 
567  Vector3 result = q * v;
568  Vector3 expected(1, 0, 0);
569 
570  ASSERT_VECTOR3_NEAR(result, expected);
571  }
572 
573  {
574  Quaternion q({0, 0, 1}, kPiOver4);
575  Vector3 v(0, 1, 0);
576 
577  Vector3 result = q * v;
578  Vector3 expected(-k1OverSqrt2, k1OverSqrt2, 0);
579 
580  ASSERT_VECTOR3_NEAR(result, expected);
581  }
582 
583  {
584  Quaternion q(Vector3(1, 0, 1).Normalize(), kPi);
585  Vector3 v(0, 0, -1);
586 
587  Vector3 result = q * v;
588  Vector3 expected(-1, 0, 0);
589 
590  ASSERT_VECTOR3_NEAR(result, expected);
591  }
592 }
593 
594 TEST(GeometryTest, EmptyPath) {
595  auto path = PathBuilder{}.TakePath();
596  ASSERT_EQ(path.GetComponentCount(), 1u);
597 
599  path.GetContourComponentAtIndex(0, c);
601 
602  Path::Polyline polyline = path.CreatePolyline(1.0f);
603  ASSERT_TRUE(polyline.points.empty());
604  ASSERT_TRUE(polyline.contours.empty());
605 }
606 
607 TEST(GeometryTest, SimplePath) {
608  PathBuilder builder;
609 
610  auto path = builder.AddLine({0, 0}, {100, 100})
611  .AddQuadraticCurve({100, 100}, {200, 200}, {300, 300})
612  .AddCubicCurve({300, 300}, {400, 400}, {500, 500}, {600, 600})
613  .TakePath();
614 
615  ASSERT_EQ(path.GetComponentCount(), 6u);
616  ASSERT_EQ(path.GetComponentCount(Path::ComponentType::kLinear), 1u);
617  ASSERT_EQ(path.GetComponentCount(Path::ComponentType::kQuadratic), 1u);
618  ASSERT_EQ(path.GetComponentCount(Path::ComponentType::kCubic), 1u);
619  ASSERT_EQ(path.GetComponentCount(Path::ComponentType::kContour), 3u);
620 
621  path.EnumerateComponents(
622  [](size_t index, const LinearPathComponent& linear) {
623  Point p1(0, 0);
624  Point p2(100, 100);
625  ASSERT_EQ(index, 1u);
626  ASSERT_EQ(linear.p1, p1);
627  ASSERT_EQ(linear.p2, p2);
628  },
629  [](size_t index, const QuadraticPathComponent& quad) {
630  Point p1(100, 100);
631  Point cp(200, 200);
632  Point p2(300, 300);
633  ASSERT_EQ(index, 3u);
634  ASSERT_EQ(quad.p1, p1);
635  ASSERT_EQ(quad.cp, cp);
636  ASSERT_EQ(quad.p2, p2);
637  },
638  [](size_t index, const CubicPathComponent& cubic) {
639  Point p1(300, 300);
640  Point cp1(400, 400);
641  Point cp2(500, 500);
642  Point p2(600, 600);
643  ASSERT_EQ(index, 5u);
644  ASSERT_EQ(cubic.p1, p1);
645  ASSERT_EQ(cubic.cp1, cp1);
646  ASSERT_EQ(cubic.cp2, cp2);
647  ASSERT_EQ(cubic.p2, p2);
648  },
649  [](size_t index, const ContourComponent& contour) {
650  // There is an initial countour added for each curve.
651  if (index == 0u) {
652  Point p1(0, 0);
653  ASSERT_EQ(contour.destination, p1);
654  } else if (index == 2u) {
655  Point p1(100, 100);
656  ASSERT_EQ(contour.destination, p1);
657  } else if (index == 4u) {
658  Point p1(300, 300);
659  ASSERT_EQ(contour.destination, p1);
660  } else {
661  ASSERT_FALSE(true);
662  }
663  ASSERT_FALSE(contour.is_closed);
664  });
665 }
666 
667 TEST(GeometryTest, BoundingBoxCubic) {
668  PathBuilder builder;
669  auto path =
670  builder.AddCubicCurve({120, 160}, {25, 200}, {220, 260}, {220, 40})
671  .TakePath();
672  auto box = path.GetBoundingBox();
673  Rect expected(93.9101, 40, 126.09, 158.862);
674  ASSERT_TRUE(box.has_value());
675  ASSERT_RECT_NEAR(box.value(), expected);
676 }
677 
678 TEST(GeometryTest, BoundingBoxOfCompositePathIsCorrect) {
679  PathBuilder builder;
680  builder.AddRoundedRect({{10, 10}, {300, 300}}, {50, 50, 50, 50});
681  auto path = builder.TakePath();
682  auto actual = path.GetBoundingBox();
683  Rect expected(10, 10, 300, 300);
684  ASSERT_TRUE(actual.has_value());
685  ASSERT_RECT_NEAR(actual.value(), expected);
686 }
687 
688 TEST(GeometryTest, ExtremaOfCubicPathComponentIsCorrect) {
689  CubicPathComponent cubic{{11.769268, 252.883148},
690  {-6.2857933, 204.356461},
691  {-4.53997231, 156.552902},
692  {17.0067291, 109.472488}};
693  auto points = cubic.Extrema();
694  ASSERT_EQ(points.size(), static_cast<size_t>(3));
695  ASSERT_POINT_NEAR(points[2], cubic.Solve(0.455916));
696 }
697 
698 TEST(GeometryTest, PathGetBoundingBoxForCubicWithNoDerivativeRootsIsCorrect) {
699  PathBuilder builder;
700  // Straight diagonal line.
701  builder.AddCubicCurve({0, 1}, {2, 3}, {4, 5}, {6, 7});
702  auto path = builder.TakePath();
703  auto actual = path.GetBoundingBox();
704  auto expected = Rect::MakeLTRB(0, 1, 6, 7);
705  ASSERT_TRUE(actual.has_value());
706  ASSERT_RECT_NEAR(actual.value(), expected);
707 }
708 
709 TEST(GeometryTest, CanGenerateMipCounts) {
710  ASSERT_EQ((Size{128, 128}.MipCount()), 7u);
711  ASSERT_EQ((Size{128, 256}.MipCount()), 8u);
712  ASSERT_EQ((Size{128, 130}.MipCount()), 8u);
713  ASSERT_EQ((Size{128, 257}.MipCount()), 9u);
714  ASSERT_EQ((Size{257, 128}.MipCount()), 9u);
715  ASSERT_EQ((Size{128, 0}.MipCount()), 1u);
716  ASSERT_EQ((Size{128, -25}.MipCount()), 1u);
717  ASSERT_EQ((Size{-128, 25}.MipCount()), 1u);
718  ASSERT_EQ((Size{1, 1}.MipCount()), 1u);
719  ASSERT_EQ((Size{0, 0}.MipCount()), 1u);
720 }
721 
722 TEST(GeometryTest, CanConvertTTypesExplicitly) {
723  {
724  Point p1(1.0, 2.0);
725  IPoint p2 = static_cast<IPoint>(p1);
726  ASSERT_EQ(p2.x, 1u);
727  ASSERT_EQ(p2.y, 2u);
728  }
729 
730  {
731  Size s1(1.0, 2.0);
732  ISize s2 = static_cast<ISize>(s1);
733  ASSERT_EQ(s2.width, 1u);
734  ASSERT_EQ(s2.height, 2u);
735  }
736 
737  {
738  Size s1(1.0, 2.0);
739  Point p1 = static_cast<Point>(s1);
740  ASSERT_EQ(p1.x, 1u);
741  ASSERT_EQ(p1.y, 2u);
742  }
743 
744  {
745  Rect r1(1.0, 2.0, 3.0, 4.0);
746  IRect r2 = static_cast<IRect>(r1);
747  ASSERT_EQ(r2.origin.x, 1u);
748  ASSERT_EQ(r2.origin.y, 2u);
749  ASSERT_EQ(r2.size.width, 3u);
750  ASSERT_EQ(r2.size.height, 4u);
751  }
752 }
753 
754 TEST(GeometryTest, CanPerformAlgebraicPointOps) {
755  {
756  IPoint p1(1, 2);
757  IPoint p2 = p1 + IPoint(1, 2);
758  ASSERT_EQ(p2.x, 2u);
759  ASSERT_EQ(p2.y, 4u);
760  }
761 
762  {
763  IPoint p1(3, 6);
764  IPoint p2 = p1 - IPoint(1, 2);
765  ASSERT_EQ(p2.x, 2u);
766  ASSERT_EQ(p2.y, 4u);
767  }
768 
769  {
770  IPoint p1(1, 2);
771  IPoint p2 = p1 * IPoint(2, 3);
772  ASSERT_EQ(p2.x, 2u);
773  ASSERT_EQ(p2.y, 6u);
774  }
775 
776  {
777  IPoint p1(2, 6);
778  IPoint p2 = p1 / IPoint(2, 3);
779  ASSERT_EQ(p2.x, 1u);
780  ASSERT_EQ(p2.y, 2u);
781  }
782 }
783 
784 TEST(GeometryTest, CanPerformAlgebraicPointOpsWithArithmeticTypes) {
785  // LHS
786  {
787  IPoint p1(1, 2);
788  IPoint p2 = p1 * 2.0f;
789  ASSERT_EQ(p2.x, 2u);
790  ASSERT_EQ(p2.y, 4u);
791  }
792 
793  {
794  IPoint p1(2, 6);
795  IPoint p2 = p1 / 2.0f;
796  ASSERT_EQ(p2.x, 1u);
797  ASSERT_EQ(p2.y, 3u);
798  }
799 
800  // RHS
801  {
802  IPoint p1(1, 2);
803  IPoint p2 = 2.0f * p1;
804  ASSERT_EQ(p2.x, 2u);
805  ASSERT_EQ(p2.y, 4u);
806  }
807 
808  {
809  IPoint p1(2, 6);
810  IPoint p2 = 12.0f / p1;
811  ASSERT_EQ(p2.x, 6u);
812  ASSERT_EQ(p2.y, 2u);
813  }
814 }
815 
816 TEST(GeometryTest, PointIntegerCoercesToFloat) {
817  // Integer on LHS, float on RHS
818  {
819  IPoint p1(1, 2);
820  Point p2 = p1 + Point(1, 2);
821  ASSERT_FLOAT_EQ(p2.x, 2u);
822  ASSERT_FLOAT_EQ(p2.y, 4u);
823  }
824 
825  {
826  IPoint p1(3, 6);
827  Point p2 = p1 - Point(1, 2);
828  ASSERT_FLOAT_EQ(p2.x, 2u);
829  ASSERT_FLOAT_EQ(p2.y, 4u);
830  }
831 
832  {
833  IPoint p1(1, 2);
834  Point p2 = p1 * Point(2, 3);
835  ASSERT_FLOAT_EQ(p2.x, 2u);
836  ASSERT_FLOAT_EQ(p2.y, 6u);
837  }
838 
839  {
840  IPoint p1(2, 6);
841  Point p2 = p1 / Point(2, 3);
842  ASSERT_FLOAT_EQ(p2.x, 1u);
843  ASSERT_FLOAT_EQ(p2.y, 2u);
844  }
845 
846  // Float on LHS, integer on RHS
847  {
848  Point p1(1, 2);
849  Point p2 = p1 + IPoint(1, 2);
850  ASSERT_FLOAT_EQ(p2.x, 2u);
851  ASSERT_FLOAT_EQ(p2.y, 4u);
852  }
853 
854  {
855  Point p1(3, 6);
856  Point p2 = p1 - IPoint(1, 2);
857  ASSERT_FLOAT_EQ(p2.x, 2u);
858  ASSERT_FLOAT_EQ(p2.y, 4u);
859  }
860 
861  {
862  Point p1(1, 2);
863  Point p2 = p1 * IPoint(2, 3);
864  ASSERT_FLOAT_EQ(p2.x, 2u);
865  ASSERT_FLOAT_EQ(p2.y, 6u);
866  }
867 
868  {
869  Point p1(2, 6);
870  Point p2 = p1 / IPoint(2, 3);
871  ASSERT_FLOAT_EQ(p2.x, 1u);
872  ASSERT_FLOAT_EQ(p2.y, 2u);
873  }
874 }
875 
876 TEST(GeometryTest, SizeCoercesToPoint) {
877  // Point on LHS, Size on RHS
878  {
879  IPoint p1(1, 2);
880  IPoint p2 = p1 + ISize(1, 2);
881  ASSERT_EQ(p2.x, 2u);
882  ASSERT_EQ(p2.y, 4u);
883  }
884 
885  {
886  IPoint p1(3, 6);
887  IPoint p2 = p1 - ISize(1, 2);
888  ASSERT_EQ(p2.x, 2u);
889  ASSERT_EQ(p2.y, 4u);
890  }
891 
892  {
893  IPoint p1(1, 2);
894  IPoint p2 = p1 * ISize(2, 3);
895  ASSERT_EQ(p2.x, 2u);
896  ASSERT_EQ(p2.y, 6u);
897  }
898 
899  {
900  IPoint p1(2, 6);
901  IPoint p2 = p1 / ISize(2, 3);
902  ASSERT_EQ(p2.x, 1u);
903  ASSERT_EQ(p2.y, 2u);
904  }
905 
906  // Size on LHS, Point on RHS
907  {
908  ISize p1(1, 2);
909  IPoint p2 = p1 + IPoint(1, 2);
910  ASSERT_EQ(p2.x, 2u);
911  ASSERT_EQ(p2.y, 4u);
912  }
913 
914  {
915  ISize p1(3, 6);
916  IPoint p2 = p1 - IPoint(1, 2);
917  ASSERT_EQ(p2.x, 2u);
918  ASSERT_EQ(p2.y, 4u);
919  }
920 
921  {
922  ISize p1(1, 2);
923  IPoint p2 = p1 * IPoint(2, 3);
924  ASSERT_EQ(p2.x, 2u);
925  ASSERT_EQ(p2.y, 6u);
926  }
927 
928  {
929  ISize p1(2, 6);
930  IPoint p2 = p1 / IPoint(2, 3);
931  ASSERT_EQ(p2.x, 1u);
932  ASSERT_EQ(p2.y, 2u);
933  }
934 }
935 
936 TEST(GeometryTest, CanUsePointAssignmentOperators) {
937  // Point on RHS
938  {
939  IPoint p(1, 2);
940  p += IPoint(1, 2);
941  ASSERT_EQ(p.x, 2u);
942  ASSERT_EQ(p.y, 4u);
943  }
944 
945  {
946  IPoint p(3, 6);
947  p -= IPoint(1, 2);
948  ASSERT_EQ(p.x, 2u);
949  ASSERT_EQ(p.y, 4u);
950  }
951 
952  {
953  IPoint p(1, 2);
954  p *= IPoint(2, 3);
955  ASSERT_EQ(p.x, 2u);
956  ASSERT_EQ(p.y, 6u);
957  }
958 
959  {
960  IPoint p(2, 6);
961  p /= IPoint(2, 3);
962  ASSERT_EQ(p.x, 1u);
963  ASSERT_EQ(p.y, 2u);
964  }
965 
966  // Size on RHS
967  {
968  IPoint p(1, 2);
969  p += ISize(1, 2);
970  ASSERT_EQ(p.x, 2u);
971  ASSERT_EQ(p.y, 4u);
972  }
973 
974  {
975  IPoint p(3, 6);
976  p -= ISize(1, 2);
977  ASSERT_EQ(p.x, 2u);
978  ASSERT_EQ(p.y, 4u);
979  }
980 
981  {
982  IPoint p(1, 2);
983  p *= ISize(2, 3);
984  ASSERT_EQ(p.x, 2u);
985  ASSERT_EQ(p.y, 6u);
986  }
987 
988  {
989  IPoint p(2, 6);
990  p /= ISize(2, 3);
991  ASSERT_EQ(p.x, 1u);
992  ASSERT_EQ(p.y, 2u);
993  }
994 
995  // Arithmetic type on RHS
996  {
997  IPoint p(1, 2);
998  p *= 3;
999  ASSERT_EQ(p.x, 3u);
1000  ASSERT_EQ(p.y, 6u);
1001  }
1002 
1003  {
1004  IPoint p(3, 6);
1005  p /= 3;
1006  ASSERT_EQ(p.x, 1u);
1007  ASSERT_EQ(p.y, 2u);
1008  }
1009 }
1010 
1011 TEST(GeometryTest, PointDotProduct) {
1012  {
1013  Point p(1, 0);
1014  Scalar s = p.Dot(Point(-1, 0));
1015  ASSERT_FLOAT_EQ(s, -1);
1016  }
1017 
1018  {
1019  Point p(0, -1);
1020  Scalar s = p.Dot(Point(-1, 0));
1021  ASSERT_FLOAT_EQ(s, 0);
1022  }
1023 
1024  {
1025  Point p(1, 2);
1026  Scalar s = p.Dot(Point(3, -4));
1027  ASSERT_FLOAT_EQ(s, -5);
1028  }
1029 }
1030 
1031 TEST(GeometryTest, PointCrossProduct) {
1032  {
1033  Point p(1, 0);
1034  Scalar s = p.Cross(Point(-1, 0));
1035  ASSERT_FLOAT_EQ(s, 0);
1036  }
1037 
1038  {
1039  Point p(0, -1);
1040  Scalar s = p.Cross(Point(-1, 0));
1041  ASSERT_FLOAT_EQ(s, -1);
1042  }
1043 
1044  {
1045  Point p(1, 2);
1046  Scalar s = p.Cross(Point(3, -4));
1047  ASSERT_FLOAT_EQ(s, -10);
1048  }
1049 }
1050 
1051 TEST(GeometryTest, PointReflect) {
1052  {
1053  Point axis = Point(0, 1);
1054  Point a(2, 3);
1055  auto reflected = a.Reflect(axis);
1056  auto expected = Point(2, -3);
1057  ASSERT_POINT_NEAR(reflected, expected);
1058  }
1059 
1060  {
1061  Point axis = Point(1, 1).Normalize();
1062  Point a(1, 0);
1063  auto reflected = a.Reflect(axis);
1064  auto expected = Point(0, -1);
1065  ASSERT_POINT_NEAR(reflected, expected);
1066  }
1067 
1068  {
1069  Point axis = Point(1, 1).Normalize();
1070  Point a(-1, -1);
1071  auto reflected = a.Reflect(axis);
1072  ASSERT_POINT_NEAR(reflected, -a);
1073  }
1074 }
1075 
1076 TEST(GeometryTest, PointAbs) {
1077  Point a(-1, -2);
1078  auto a_abs = a.Abs();
1079  auto expected = Point(1, 2);
1080  ASSERT_POINT_NEAR(a_abs, expected);
1081 }
1082 
1083 TEST(GeometryTest, PointAngleTo) {
1084  // Negative result in the CCW (with up = -Y) direction.
1085  {
1086  Point a(1, 1);
1087  Point b(1, -1);
1088  Radians actual = a.AngleTo(b);
1089  Radians expected = Radians{-kPi / 2};
1090  ASSERT_FLOAT_EQ(actual.radians, expected.radians);
1091  }
1092 
1093  // Check the other direction to ensure the result is signed correctly.
1094  {
1095  Point a(1, -1);
1096  Point b(1, 1);
1097  Radians actual = a.AngleTo(b);
1098  Radians expected = Radians{kPi / 2};
1099  ASSERT_FLOAT_EQ(actual.radians, expected.radians);
1100  }
1101 
1102  // Differences in magnitude should have no impact on the result.
1103  {
1104  Point a(100, -100);
1105  Point b(0.01, 0.01);
1106  Radians actual = a.AngleTo(b);
1107  Radians expected = Radians{kPi / 2};
1108  ASSERT_FLOAT_EQ(actual.radians, expected.radians);
1109  }
1110 }
1111 
1112 TEST(GeometryTest, PointMin) {
1113  Point p(1, 2);
1114  Point result = p.Min({0, 10});
1115  Point expected(0, 2);
1116  ASSERT_POINT_NEAR(result, expected);
1117 }
1118 
1119 TEST(GeometryTest, Vector3Min) {
1120  Vector3 p(1, 2, 3);
1121  Vector3 result = p.Min({0, 10, 2});
1122  Vector3 expected(0, 2, 2);
1123  ASSERT_VECTOR3_NEAR(result, expected);
1124 }
1125 
1126 TEST(GeometryTest, Vector4Min) {
1127  Vector4 p(1, 2, 3, 4);
1128  Vector4 result = p.Min({0, 10, 2, 1});
1129  Vector4 expected(0, 2, 2, 1);
1130  ASSERT_VECTOR4_NEAR(result, expected);
1131 }
1132 
1133 TEST(GeometryTest, PointMax) {
1134  Point p(1, 2);
1135  Point result = p.Max({0, 10});
1136  Point expected(1, 10);
1137  ASSERT_POINT_NEAR(result, expected);
1138 }
1139 
1140 TEST(GeometryTest, Vector3Max) {
1141  Vector3 p(1, 2, 3);
1142  Vector3 result = p.Max({0, 10, 2});
1143  Vector3 expected(1, 10, 3);
1144  ASSERT_VECTOR3_NEAR(result, expected);
1145 }
1146 
1147 TEST(GeometryTest, Vector4Max) {
1148  Vector4 p(1, 2, 3, 4);
1149  Vector4 result = p.Max({0, 10, 2, 1});
1150  Vector4 expected(1, 10, 3, 4);
1151  ASSERT_VECTOR4_NEAR(result, expected);
1152 }
1153 
1154 TEST(GeometryTest, PointFloor) {
1155  Point p(1.5, 2.3);
1156  Point result = p.Floor();
1157  Point expected(1, 2);
1158  ASSERT_POINT_NEAR(result, expected);
1159 }
1160 
1161 TEST(GeometryTest, Vector3Floor) {
1162  Vector3 p(1.5, 2.3, 3.9);
1163  Vector3 result = p.Floor();
1164  Vector3 expected(1, 2, 3);
1165  ASSERT_VECTOR3_NEAR(result, expected);
1166 }
1167 
1168 TEST(GeometryTest, Vector4Floor) {
1169  Vector4 p(1.5, 2.3, 3.9, 4.0);
1170  Vector4 result = p.Floor();
1171  Vector4 expected(1, 2, 3, 4);
1172  ASSERT_VECTOR4_NEAR(result, expected);
1173 }
1174 
1175 TEST(GeometryTest, PointCeil) {
1176  Point p(1.5, 2.3);
1177  Point result = p.Ceil();
1178  Point expected(2, 3);
1179  ASSERT_POINT_NEAR(result, expected);
1180 }
1181 
1182 TEST(GeometryTest, Vector3Ceil) {
1183  Vector3 p(1.5, 2.3, 3.9);
1184  Vector3 result = p.Ceil();
1185  Vector3 expected(2, 3, 4);
1186  ASSERT_VECTOR3_NEAR(result, expected);
1187 }
1188 
1189 TEST(GeometryTest, Vector4Ceil) {
1190  Vector4 p(1.5, 2.3, 3.9, 4.0);
1191  Vector4 result = p.Ceil();
1192  Vector4 expected(2, 3, 4, 4);
1193  ASSERT_VECTOR4_NEAR(result, expected);
1194 }
1195 
1196 TEST(GeometryTest, PointRound) {
1197  Point p(1.5, 2.3);
1198  Point result = p.Round();
1199  Point expected(2, 2);
1200  ASSERT_POINT_NEAR(result, expected);
1201 }
1202 
1203 TEST(GeometryTest, Vector3Round) {
1204  Vector3 p(1.5, 2.3, 3.9);
1205  Vector3 result = p.Round();
1206  Vector3 expected(2, 2, 4);
1207  ASSERT_VECTOR3_NEAR(result, expected);
1208 }
1209 
1210 TEST(GeometryTest, Vector4Round) {
1211  Vector4 p(1.5, 2.3, 3.9, 4.0);
1212  Vector4 result = p.Round();
1213  Vector4 expected(2, 2, 4, 4);
1214  ASSERT_VECTOR4_NEAR(result, expected);
1215 }
1216 
1217 TEST(GeometryTest, PointLerp) {
1218  Point p(1, 2);
1219  Point result = p.Lerp({5, 10}, 0.75);
1220  Point expected(4, 8);
1221  ASSERT_POINT_NEAR(result, expected);
1222 }
1223 
1224 TEST(GeometryTest, Vector3Lerp) {
1225  Vector3 p(1, 2, 3);
1226  Vector3 result = p.Lerp({5, 10, 15}, 0.75);
1227  Vector3 expected(4, 8, 12);
1228  ASSERT_VECTOR3_NEAR(result, expected);
1229 }
1230 
1231 TEST(GeometryTest, Vector4Lerp) {
1232  Vector4 p(1, 2, 3, 4);
1233  Vector4 result = p.Lerp({5, 10, 15, 20}, 0.75);
1234  Vector4 expected(4, 8, 12, 16);
1235  ASSERT_VECTOR4_NEAR(result, expected);
1236 }
1237 
1238 TEST(GeometryTest, CanUseVector3AssignmentOperators) {
1239  {
1240  Vector3 p(1, 2, 4);
1241  p += Vector3(1, 2, 4);
1242  ASSERT_EQ(p.x, 2u);
1243  ASSERT_EQ(p.y, 4u);
1244  ASSERT_EQ(p.z, 8u);
1245  }
1246 
1247  {
1248  Vector3 p(3, 6, 8);
1249  p -= Vector3(1, 2, 3);
1250  ASSERT_EQ(p.x, 2u);
1251  ASSERT_EQ(p.y, 4u);
1252  ASSERT_EQ(p.z, 5u);
1253  }
1254 
1255  {
1256  Vector3 p(1, 2, 3);
1257  p *= Vector3(2, 3, 4);
1258  ASSERT_EQ(p.x, 2u);
1259  ASSERT_EQ(p.y, 6u);
1260  ASSERT_EQ(p.z, 12u);
1261  }
1262 
1263  {
1264  Vector3 p(1, 2, 3);
1265  p *= 2;
1266  ASSERT_EQ(p.x, 2u);
1267  ASSERT_EQ(p.y, 4u);
1268  ASSERT_EQ(p.z, 6u);
1269  }
1270 
1271  {
1272  Vector3 p(2, 6, 12);
1273  p /= Vector3(2, 3, 4);
1274  ASSERT_EQ(p.x, 1u);
1275  ASSERT_EQ(p.y, 2u);
1276  ASSERT_EQ(p.z, 3u);
1277  }
1278 
1279  {
1280  Vector3 p(2, 6, 12);
1281  p /= 2;
1282  ASSERT_EQ(p.x, 1u);
1283  ASSERT_EQ(p.y, 3u);
1284  ASSERT_EQ(p.z, 6u);
1285  }
1286 }
1287 
1288 TEST(GeometryTest, CanPerformAlgebraicVector3Ops) {
1289  {
1290  Vector3 p1(1, 2, 3);
1291  Vector3 p2 = p1 + Vector3(1, 2, 3);
1292  ASSERT_EQ(p2.x, 2u);
1293  ASSERT_EQ(p2.y, 4u);
1294  ASSERT_EQ(p2.z, 6u);
1295  }
1296 
1297  {
1298  Vector3 p1(3, 6, 9);
1299  Vector3 p2 = p1 - Vector3(1, 2, 3);
1300  ASSERT_EQ(p2.x, 2u);
1301  ASSERT_EQ(p2.y, 4u);
1302  ASSERT_EQ(p2.z, 6u);
1303  }
1304 
1305  {
1306  Vector3 p1(1, 2, 3);
1307  Vector3 p2 = p1 * Vector3(2, 3, 4);
1308  ASSERT_EQ(p2.x, 2u);
1309  ASSERT_EQ(p2.y, 6u);
1310  ASSERT_EQ(p2.z, 12u);
1311  }
1312 
1313  {
1314  Vector3 p1(2, 6, 12);
1315  Vector3 p2 = p1 / Vector3(2, 3, 4);
1316  ASSERT_EQ(p2.x, 1u);
1317  ASSERT_EQ(p2.y, 2u);
1318  ASSERT_EQ(p2.z, 3u);
1319  }
1320 }
1321 
1322 TEST(GeometryTest, CanPerformAlgebraicVector3OpsWithArithmeticTypes) {
1323  // LHS
1324  {
1325  Vector3 p1(1, 2, 3);
1326  Vector3 p2 = p1 + 2.0f;
1327  ASSERT_EQ(p2.x, 3);
1328  ASSERT_EQ(p2.y, 4);
1329  ASSERT_EQ(p2.z, 5);
1330  }
1331 
1332  {
1333  Vector3 p1(1, 2, 3);
1334  Vector3 p2 = p1 - 2.0f;
1335  ASSERT_EQ(p2.x, -1);
1336  ASSERT_EQ(p2.y, 0);
1337  ASSERT_EQ(p2.z, 1);
1338  }
1339 
1340  {
1341  Vector3 p1(1, 2, 3);
1342  Vector3 p2 = p1 * 2.0f;
1343  ASSERT_EQ(p2.x, 2);
1344  ASSERT_EQ(p2.y, 4);
1345  ASSERT_EQ(p2.z, 6);
1346  }
1347 
1348  {
1349  Vector3 p1(2, 6, 12);
1350  Vector3 p2 = p1 / 2.0f;
1351  ASSERT_EQ(p2.x, 1);
1352  ASSERT_EQ(p2.y, 3);
1353  ASSERT_EQ(p2.z, 6);
1354  }
1355 
1356  // RHS
1357  {
1358  Vector3 p1(1, 2, 3);
1359  Vector3 p2 = 2.0f + p1;
1360  ASSERT_EQ(p2.x, 3);
1361  ASSERT_EQ(p2.y, 4);
1362  ASSERT_EQ(p2.z, 5);
1363  }
1364 
1365  {
1366  Vector3 p1(1, 2, 3);
1367  Vector3 p2 = 2.0f - p1;
1368  ASSERT_EQ(p2.x, 1);
1369  ASSERT_EQ(p2.y, 0);
1370  ASSERT_EQ(p2.z, -1);
1371  }
1372 
1373  {
1374  Vector3 p1(1, 2, 3);
1375  Vector3 p2 = 2.0f * p1;
1376  ASSERT_EQ(p2.x, 2);
1377  ASSERT_EQ(p2.y, 4);
1378  ASSERT_EQ(p2.z, 6);
1379  }
1380 
1381  {
1382  Vector3 p1(2, 6, 12);
1383  Vector3 p2 = 12.0f / p1;
1384  ASSERT_EQ(p2.x, 6);
1385  ASSERT_EQ(p2.y, 2);
1386  ASSERT_EQ(p2.z, 1);
1387  }
1388 }
1389 
1390 TEST(GeometryTest, ColorPremultiply) {
1391  {
1392  Color a(1.0, 0.5, 0.2, 0.5);
1393  Color premultiplied = a.Premultiply();
1394  Color expected = Color(0.5, 0.25, 0.1, 0.5);
1395  ASSERT_COLOR_NEAR(premultiplied, expected);
1396  }
1397 
1398  {
1399  Color a(0.5, 0.25, 0.1, 0.5);
1400  Color unpremultiplied = a.Unpremultiply();
1401  Color expected = Color(1.0, 0.5, 0.2, 0.5);
1402  ASSERT_COLOR_NEAR(unpremultiplied, expected);
1403  }
1404 
1405  {
1406  Color a(0.5, 0.25, 0.1, 0.0);
1407  Color unpremultiplied = a.Unpremultiply();
1408  Color expected = Color(0.0, 0.0, 0.0, 0.0);
1409  ASSERT_COLOR_NEAR(unpremultiplied, expected);
1410  }
1411 }
1412 
1413 TEST(GeometryTest, ColorR8G8B8A8) {
1414  {
1415  Color a(1.0, 0.5, 0.2, 0.5);
1416  std::array<uint8_t, 4> expected = {255, 128, 51, 128};
1417  ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1418  }
1419 
1420  {
1421  Color a(0.0, 0.0, 0.0, 0.0);
1422  std::array<uint8_t, 4> expected = {0, 0, 0, 0};
1423  ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1424  }
1425 
1426  {
1427  Color a(1.0, 1.0, 1.0, 1.0);
1428  std::array<uint8_t, 4> expected = {255, 255, 255, 255};
1429  ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1430  }
1431 }
1432 
1433 TEST(GeometryTest, ColorLerp) {
1434  {
1435  Color a(0.0, 0.0, 0.0, 0.0);
1436  Color b(1.0, 1.0, 1.0, 1.0);
1437 
1438  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.5), Color(0.5, 0.5, 0.5, 0.5));
1439  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.0), a);
1440  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 1.0), b);
1441  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.2), Color(0.2, 0.2, 0.2, 0.2));
1442  }
1443 
1444  {
1445  Color a(0.2, 0.4, 1.0, 0.5);
1446  Color b(0.4, 1.0, 0.2, 0.3);
1447 
1448  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.5), Color(0.3, 0.7, 0.6, 0.4));
1449  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.0), a);
1450  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 1.0), b);
1451  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.2), Color(0.24, 0.52, 0.84, 0.46));
1452  }
1453 }
1454 
1455 TEST(GeometryTest, ColorClamp01) {
1456  {
1457  Color result = Color(0.5, 0.5, 0.5, 0.5).Clamp01();
1458  Color expected = Color(0.5, 0.5, 0.5, 0.5);
1459  ASSERT_COLOR_NEAR(result, expected);
1460  }
1461 
1462  {
1463  Color result = Color(-1, -1, -1, -1).Clamp01();
1464  Color expected = Color(0, 0, 0, 0);
1465  ASSERT_COLOR_NEAR(result, expected);
1466  }
1467 
1468  {
1469  Color result = Color(2, 2, 2, 2).Clamp01();
1470  Color expected = Color(1, 1, 1, 1);
1471  ASSERT_COLOR_NEAR(result, expected);
1472  }
1473 }
1474 
1475 TEST(GeometryTest, ColorMakeRGBA8) {
1476  {
1477  Color a = Color::MakeRGBA8(0, 0, 0, 0);
1479  ASSERT_COLOR_NEAR(a, b);
1480  }
1481 
1482  {
1483  Color a = Color::MakeRGBA8(255, 255, 255, 255);
1484  Color b = Color::White();
1485  ASSERT_COLOR_NEAR(a, b);
1486  }
1487 
1488  {
1489  Color a = Color::MakeRGBA8(63, 127, 191, 127);
1490  Color b(0.247059, 0.498039, 0.74902, 0.498039);
1491  ASSERT_COLOR_NEAR(a, b);
1492  }
1493 }
1494 
1495 TEST(GeometryTest, ColorApplyColorMatrix) {
1496  {
1497  ColorMatrix color_matrix = {
1498  1, 1, 1, 1, 1, //
1499  1, 1, 1, 1, 1, //
1500  1, 1, 1, 1, 1, //
1501  1, 1, 1, 1, 1, //
1502  };
1503  auto result = Color::White().ApplyColorMatrix(color_matrix);
1504  auto expected = Color(1, 1, 1, 1);
1505  ASSERT_COLOR_NEAR(result, expected);
1506  }
1507 
1508  {
1509  ColorMatrix color_matrix = {
1510  0.1, 0, 0, 0, 0.01, //
1511  0, 0.2, 0, 0, 0.02, //
1512  0, 0, 0.3, 0, 0.03, //
1513  0, 0, 0, 0.4, 0.04, //
1514  };
1515  auto result = Color::White().ApplyColorMatrix(color_matrix);
1516  auto expected = Color(0.11, 0.22, 0.33, 0.44);
1517  ASSERT_COLOR_NEAR(result, expected);
1518  }
1519 }
1520 
1521 TEST(GeometryTest, ColorLinearToSRGB) {
1522  {
1523  auto result = Color::White().LinearToSRGB();
1524  auto expected = Color(1, 1, 1, 1);
1525  ASSERT_COLOR_NEAR(result, expected);
1526  }
1527 
1528  {
1529  auto result = Color::BlackTransparent().LinearToSRGB();
1530  auto expected = Color(0, 0, 0, 0);
1531  ASSERT_COLOR_NEAR(result, expected);
1532  }
1533 
1534  {
1535  auto result = Color(0.2, 0.4, 0.6, 0.8).LinearToSRGB();
1536  auto expected = Color(0.484529, 0.665185, 0.797738, 0.8);
1537  ASSERT_COLOR_NEAR(result, expected);
1538  }
1539 }
1540 
1541 TEST(GeometryTest, ColorSRGBToLinear) {
1542  {
1543  auto result = Color::White().SRGBToLinear();
1544  auto expected = Color(1, 1, 1, 1);
1545  ASSERT_COLOR_NEAR(result, expected);
1546  }
1547 
1548  {
1549  auto result = Color::BlackTransparent().SRGBToLinear();
1550  auto expected = Color(0, 0, 0, 0);
1551  ASSERT_COLOR_NEAR(result, expected);
1552  }
1553 
1554  {
1555  auto result = Color(0.2, 0.4, 0.6, 0.8).SRGBToLinear();
1556  auto expected = Color(0.0331048, 0.132868, 0.318547, 0.8);
1557  ASSERT_COLOR_NEAR(result, expected);
1558  }
1559 }
1560 
1561 #define _BLEND_MODE_NAME_CHECK(blend_mode) \
1562  case BlendMode::k##blend_mode: \
1563  ASSERT_STREQ(result, #blend_mode); \
1564  break;
1565 
1566 TEST(GeometryTest, BlendModeToString) {
1567  using BlendT = std::underlying_type_t<BlendMode>;
1568  for (BlendT i = 0; i <= static_cast<BlendT>(BlendMode::kLast); i++) {
1569  auto mode = static_cast<BlendMode>(i);
1570  auto result = BlendModeToString(mode);
1572  }
1573 }
1574 
1575 TEST(GeometryTest, CanConvertBetweenDegressAndRadians) {
1576  {
1577  auto deg = Degrees{90.0};
1578  Radians rad = deg;
1579  ASSERT_FLOAT_EQ(rad.radians, kPiOver2);
1580  }
1581 }
1582 
1583 TEST(GeometryTest, RectMakeSize) {
1584  {
1585  Size s(100, 200);
1586  Rect r = Rect::MakeSize(s);
1587  Rect expected = Rect::MakeLTRB(0, 0, 100, 200);
1588  ASSERT_RECT_NEAR(r, expected);
1589  }
1590 
1591  {
1592  ISize s(100, 200);
1593  Rect r = Rect::MakeSize(s);
1594  Rect expected = Rect::MakeLTRB(0, 0, 100, 200);
1595  ASSERT_RECT_NEAR(r, expected);
1596  }
1597 
1598  {
1599  Size s(100, 200);
1600  IRect r = IRect::MakeSize(s);
1601  IRect expected = IRect::MakeLTRB(0, 0, 100, 200);
1602  ASSERT_EQ(r, expected);
1603  }
1604 
1605  {
1606  ISize s(100, 200);
1607  IRect r = IRect::MakeSize(s);
1608  IRect expected = IRect::MakeLTRB(0, 0, 100, 200);
1609  ASSERT_EQ(r, expected);
1610  }
1611 }
1612 
1613 TEST(GeometryTest, RectUnion) {
1614  {
1615  Rect a(100, 100, 100, 100);
1616  Rect b(0, 0, 0, 0);
1617  auto u = a.Union(b);
1618  auto expected = Rect(0, 0, 200, 200);
1619  ASSERT_RECT_NEAR(u, expected);
1620  }
1621 
1622  {
1623  Rect a(100, 100, 100, 100);
1624  Rect b(10, 10, 0, 0);
1625  auto u = a.Union(b);
1626  auto expected = Rect(10, 10, 190, 190);
1627  ASSERT_RECT_NEAR(u, expected);
1628  }
1629 
1630  {
1631  Rect a(0, 0, 100, 100);
1632  Rect b(10, 10, 100, 100);
1633  auto u = a.Union(b);
1634  auto expected = Rect(0, 0, 110, 110);
1635  ASSERT_RECT_NEAR(u, expected);
1636  }
1637 
1638  {
1639  Rect a(0, 0, 100, 100);
1640  Rect b(100, 100, 100, 100);
1641  auto u = a.Union(b);
1642  auto expected = Rect(0, 0, 200, 200);
1643  ASSERT_RECT_NEAR(u, expected);
1644  }
1645 }
1646 
1647 TEST(GeometryTest, RectIntersection) {
1648  {
1649  Rect a(100, 100, 100, 100);
1650  Rect b(0, 0, 0, 0);
1651 
1652  auto u = a.Intersection(b);
1653  ASSERT_FALSE(u.has_value());
1654  }
1655 
1656  {
1657  Rect a(100, 100, 100, 100);
1658  Rect b(10, 10, 0, 0);
1659  auto u = a.Intersection(b);
1660  ASSERT_FALSE(u.has_value());
1661  }
1662 
1663  {
1664  Rect a(0, 0, 100, 100);
1665  Rect b(10, 10, 100, 100);
1666  auto u = a.Intersection(b);
1667  ASSERT_TRUE(u.has_value());
1668  auto expected = Rect(10, 10, 90, 90);
1669  ASSERT_RECT_NEAR(u.value(), expected);
1670  }
1671 
1672  {
1673  Rect a(0, 0, 100, 100);
1674  Rect b(100, 100, 100, 100);
1675  auto u = a.Intersection(b);
1676  ASSERT_FALSE(u.has_value());
1677  }
1678 
1679  {
1680  Rect a = Rect::MakeMaximum();
1681  Rect b(10, 10, 300, 300);
1682  auto u = a.Intersection(b);
1683  ASSERT_TRUE(u);
1684  ASSERT_RECT_NEAR(u.value(), b);
1685  }
1686 
1687  {
1688  Rect a = Rect::MakeMaximum();
1689  Rect b = Rect::MakeMaximum();
1690  auto u = a.Intersection(b);
1691  ASSERT_TRUE(u);
1692  ASSERT_EQ(u, Rect::MakeMaximum());
1693  }
1694 }
1695 
1696 TEST(GeometryTest, RectIntersectsWithRect) {
1697  {
1698  Rect a(100, 100, 100, 100);
1699  Rect b(0, 0, 0, 0);
1700  ASSERT_FALSE(a.IntersectsWithRect(b));
1701  }
1702 
1703  {
1704  Rect a(100, 100, 100, 100);
1705  Rect b(10, 10, 0, 0);
1706  ASSERT_FALSE(a.IntersectsWithRect(b));
1707  }
1708 
1709  {
1710  Rect a(0, 0, 100, 100);
1711  Rect b(10, 10, 100, 100);
1712  ASSERT_TRUE(a.IntersectsWithRect(b));
1713  }
1714 
1715  {
1716  Rect a(0, 0, 100, 100);
1717  Rect b(100, 100, 100, 100);
1718  ASSERT_FALSE(a.IntersectsWithRect(b));
1719  }
1720 
1721  {
1722  Rect a = Rect::MakeMaximum();
1723  Rect b(10, 10, 100, 100);
1724  ASSERT_TRUE(a.IntersectsWithRect(b));
1725  }
1726 
1727  {
1728  Rect a = Rect::MakeMaximum();
1729  Rect b = Rect::MakeMaximum();
1730  ASSERT_TRUE(a.IntersectsWithRect(b));
1731  }
1732 }
1733 
1734 TEST(GeometryTest, RectCutout) {
1735  // No cutout.
1736  {
1737  Rect a(0, 0, 100, 100);
1738  Rect b(0, 0, 50, 50);
1739  auto u = a.Cutout(b);
1740  ASSERT_TRUE(u.has_value());
1741  ASSERT_RECT_NEAR(u.value(), a);
1742  }
1743 
1744  // Full cutout.
1745  {
1746  Rect a(0, 0, 100, 100);
1747  Rect b(-10, -10, 120, 120);
1748  auto u = a.Cutout(b);
1749  ASSERT_FALSE(u.has_value());
1750  }
1751 
1752  // Cutout from top.
1753  {
1754  auto a = Rect::MakeLTRB(0, 0, 100, 100);
1755  auto b = Rect::MakeLTRB(-10, -10, 110, 90);
1756  auto u = a.Cutout(b);
1757  auto expected = Rect::MakeLTRB(0, 90, 100, 100);
1758  ASSERT_TRUE(u.has_value());
1759  ASSERT_RECT_NEAR(u.value(), expected);
1760  }
1761 
1762  // Cutout from bottom.
1763  {
1764  auto a = Rect::MakeLTRB(0, 0, 100, 100);
1765  auto b = Rect::MakeLTRB(-10, 10, 110, 110);
1766  auto u = a.Cutout(b);
1767  auto expected = Rect::MakeLTRB(0, 0, 100, 10);
1768  ASSERT_TRUE(u.has_value());
1769  ASSERT_RECT_NEAR(u.value(), expected);
1770  }
1771 
1772  // Cutout from left.
1773  {
1774  auto a = Rect::MakeLTRB(0, 0, 100, 100);
1775  auto b = Rect::MakeLTRB(-10, -10, 90, 110);
1776  auto u = a.Cutout(b);
1777  auto expected = Rect::MakeLTRB(90, 0, 100, 100);
1778  ASSERT_TRUE(u.has_value());
1779  ASSERT_RECT_NEAR(u.value(), expected);
1780  }
1781 
1782  // Cutout from right.
1783  {
1784  auto a = Rect::MakeLTRB(0, 0, 100, 100);
1785  auto b = Rect::MakeLTRB(10, -10, 110, 110);
1786  auto u = a.Cutout(b);
1787  auto expected = Rect::MakeLTRB(0, 0, 10, 100);
1788  ASSERT_TRUE(u.has_value());
1789  ASSERT_RECT_NEAR(u.value(), expected);
1790  }
1791 }
1792 
1793 TEST(GeometryTest, RectContainsPoint) {
1794  {
1795  // Origin is inclusive
1796  Rect r(100, 100, 100, 100);
1797  Point p(100, 100);
1798  ASSERT_TRUE(r.Contains(p));
1799  }
1800  {
1801  // Size is exclusive
1802  Rect r(100, 100, 100, 100);
1803  Point p(200, 200);
1804  ASSERT_FALSE(r.Contains(p));
1805  }
1806  {
1807  Rect r(100, 100, 100, 100);
1808  Point p(99, 99);
1809  ASSERT_FALSE(r.Contains(p));
1810  }
1811  {
1812  Rect r(100, 100, 100, 100);
1813  Point p(199, 199);
1814  ASSERT_TRUE(r.Contains(p));
1815  }
1816 
1817  {
1818  Rect r = Rect::MakeMaximum();
1819  Point p(199, 199);
1820  ASSERT_TRUE(r.Contains(p));
1821  }
1822 }
1823 
1824 TEST(GeometryTest, RectContainsRect) {
1825  {
1826  Rect a(100, 100, 100, 100);
1827  ASSERT_TRUE(a.Contains(a));
1828  }
1829  {
1830  Rect a(100, 100, 100, 100);
1831  Rect b(0, 0, 0, 0);
1832  ASSERT_FALSE(a.Contains(b));
1833  }
1834  {
1835  Rect a(100, 100, 100, 100);
1836  Rect b(150, 150, 20, 20);
1837  ASSERT_TRUE(a.Contains(b));
1838  }
1839  {
1840  Rect a(100, 100, 100, 100);
1841  Rect b(150, 150, 100, 100);
1842  ASSERT_FALSE(a.Contains(b));
1843  }
1844  {
1845  Rect a(100, 100, 100, 100);
1846  Rect b(50, 50, 100, 100);
1847  ASSERT_FALSE(a.Contains(b));
1848  }
1849  {
1850  Rect a(100, 100, 100, 100);
1851  Rect b(0, 0, 300, 300);
1852  ASSERT_FALSE(a.Contains(b));
1853  }
1854  {
1855  Rect a = Rect::MakeMaximum();
1856  Rect b(0, 0, 300, 300);
1857  ASSERT_TRUE(a.Contains(b));
1858  }
1859 }
1860 
1861 TEST(GeometryTest, RectGetPoints) {
1862  {
1863  Rect r(100, 200, 300, 400);
1864  auto points = r.GetPoints();
1865  ASSERT_POINT_NEAR(points[0], Point(100, 200));
1866  ASSERT_POINT_NEAR(points[1], Point(400, 200));
1867  ASSERT_POINT_NEAR(points[2], Point(100, 600));
1868  ASSERT_POINT_NEAR(points[3], Point(400, 600));
1869  }
1870 
1871  {
1872  Rect r = Rect::MakeMaximum();
1873  auto points = r.GetPoints();
1874  ASSERT_EQ(points[0], Point(-std::numeric_limits<float>::infinity(),
1875  -std::numeric_limits<float>::infinity()));
1876  ASSERT_EQ(points[1], Point(std::numeric_limits<float>::infinity(),
1877  -std::numeric_limits<float>::infinity()));
1878  ASSERT_EQ(points[2], Point(-std::numeric_limits<float>::infinity(),
1879  std::numeric_limits<float>::infinity()));
1880  ASSERT_EQ(points[3], Point(std::numeric_limits<float>::infinity(),
1881  std::numeric_limits<float>::infinity()));
1882  }
1883 }
1884 
1885 TEST(GeometryTest, RectShift) {
1886  auto r = Rect::MakeLTRB(0, 0, 100, 100);
1887 
1888  ASSERT_EQ(r.Shift(Point(10, 5)), Rect::MakeLTRB(10, 5, 110, 105));
1889  ASSERT_EQ(r.Shift(Point(-10, -5)), Rect::MakeLTRB(-10, -5, 90, 95));
1890 }
1891 
1892 TEST(GeometryTest, RectGetTransformedPoints) {
1893  Rect r(100, 200, 300, 400);
1894  auto points = r.GetTransformedPoints(Matrix::MakeTranslation({10, 20}));
1895  ASSERT_POINT_NEAR(points[0], Point(110, 220));
1896  ASSERT_POINT_NEAR(points[1], Point(410, 220));
1897  ASSERT_POINT_NEAR(points[2], Point(110, 620));
1898  ASSERT_POINT_NEAR(points[3], Point(410, 620));
1899 }
1900 
1901 TEST(GeometryTest, RectMakePointBounds) {
1902  {
1903  std::vector<Point> points{{1, 5}, {4, -1}, {0, 6}};
1904  Rect r = Rect::MakePointBounds(points.begin(), points.end()).value();
1905  auto expected = Rect(0, -1, 4, 7);
1906  ASSERT_RECT_NEAR(r, expected);
1907  }
1908  {
1909  std::vector<Point> points;
1910  std::optional<Rect> r = Rect::MakePointBounds(points.begin(), points.end());
1911  ASSERT_FALSE(r.has_value());
1912  }
1913 }
1914 
1915 TEST(GeometryTest, RectExpand) {
1916  {
1917  auto a = Rect::MakeLTRB(100, 100, 200, 200);
1918  auto b = a.Expand(1);
1919  auto expected = Rect::MakeLTRB(99, 99, 201, 201);
1920  ASSERT_RECT_NEAR(b, expected);
1921  }
1922  {
1923  auto a = Rect::MakeLTRB(100, 100, 200, 200);
1924  auto b = a.Expand(-1);
1925  auto expected = Rect::MakeLTRB(101, 101, 199, 199);
1926  ASSERT_RECT_NEAR(b, expected);
1927  }
1928 
1929  {
1930  auto a = Rect::MakeLTRB(100, 100, 200, 200);
1931  auto b = a.Expand(1, 2, 3, 4);
1932  auto expected = Rect::MakeLTRB(99, 98, 203, 204);
1933  ASSERT_RECT_NEAR(b, expected);
1934  }
1935  {
1936  auto a = Rect::MakeLTRB(100, 100, 200, 200);
1937  auto b = a.Expand(-1, -2, -3, -4);
1938  auto expected = Rect::MakeLTRB(101, 102, 197, 196);
1939  ASSERT_RECT_NEAR(b, expected);
1940  }
1941 }
1942 
1943 TEST(GeometryTest, RectGetPositive) {
1944  {
1945  Rect r{100, 200, 300, 400};
1946  auto actual = r.GetPositive();
1947  ASSERT_RECT_NEAR(r, actual);
1948  }
1949  {
1950  Rect r{100, 200, -100, -100};
1951  auto actual = r.GetPositive();
1952  Rect expected(0, 100, 100, 100);
1953  ASSERT_RECT_NEAR(expected, actual);
1954  }
1955 }
1956 
1957 TEST(GeometryTest, RectScale) {
1958  {
1959  auto r = Rect::MakeLTRB(-100, -100, 100, 100);
1960  auto actual = r.Scale(0);
1961  auto expected = Rect::MakeLTRB(0, 0, 0, 0);
1962  ASSERT_RECT_NEAR(expected, actual);
1963  }
1964  {
1965  auto r = Rect::MakeLTRB(-100, -100, 100, 100);
1966  auto actual = r.Scale(-2);
1967  auto expected = Rect::MakeLTRB(200, 200, -200, -200);
1968  ASSERT_RECT_NEAR(expected, actual);
1969  }
1970  {
1971  auto r = Rect::MakeLTRB(-100, -100, 100, 100);
1972  auto actual = r.Scale(Point{0, 0});
1973  auto expected = Rect::MakeLTRB(0, 0, 0, 0);
1974  ASSERT_RECT_NEAR(expected, actual);
1975  }
1976  {
1977  auto r = Rect::MakeLTRB(-100, -100, 100, 100);
1978  auto actual = r.Scale(Size{-1, -2});
1979  auto expected = Rect::MakeLTRB(100, 200, -100, -200);
1980  ASSERT_RECT_NEAR(expected, actual);
1981  }
1982 }
1983 
1984 TEST(GeometryTest, RectDirections) {
1985  auto r = Rect::MakeLTRB(1, 2, 3, 4);
1986 
1987  ASSERT_EQ(r.GetLeft(), 1);
1988  ASSERT_EQ(r.GetTop(), 2);
1989  ASSERT_EQ(r.GetRight(), 3);
1990  ASSERT_EQ(r.GetBottom(), 4);
1991 
1992  ASSERT_POINT_NEAR(r.GetLeftTop(), Point(1, 2));
1993  ASSERT_POINT_NEAR(r.GetRightTop(), Point(3, 2));
1994  ASSERT_POINT_NEAR(r.GetLeftBottom(), Point(1, 4));
1995  ASSERT_POINT_NEAR(r.GetRightBottom(), Point(3, 4));
1996 }
1997 
1998 TEST(GeometryTest, RectProject) {
1999  {
2000  auto r = Rect::MakeLTRB(-100, -100, 100, 100);
2001  auto actual = r.Project(r);
2002  auto expected = Rect::MakeLTRB(0, 0, 1, 1);
2003  ASSERT_RECT_NEAR(expected, actual);
2004  }
2005  {
2006  auto r = Rect::MakeLTRB(-100, -100, 100, 100);
2007  auto actual = r.Project(Rect::MakeLTRB(0, 0, 100, 100));
2008  auto expected = Rect::MakeLTRB(0.5, 0.5, 1, 1);
2009  ASSERT_RECT_NEAR(expected, actual);
2010  }
2011 }
2012 
2013 TEST(GeometryTest, CubicPathComponentPolylineDoesNotIncludePointOne) {
2014  CubicPathComponent component({10, 10}, {20, 35}, {35, 20}, {40, 40});
2015  auto polyline = component.CreatePolyline(1.0f);
2016  ASSERT_NE(polyline.front().x, 10);
2017  ASSERT_NE(polyline.front().y, 10);
2018  ASSERT_EQ(polyline.back().x, 40);
2019  ASSERT_EQ(polyline.back().y, 40);
2020 }
2021 
2022 TEST(GeometryTest, PathCreatePolyLineDoesNotDuplicatePoints) {
2023  PathBuilder builder;
2024  builder.MoveTo({10, 10});
2025  builder.LineTo({20, 20});
2026  builder.LineTo({30, 30});
2027  builder.MoveTo({40, 40});
2028  builder.LineTo({50, 50});
2029 
2030  auto polyline = builder.TakePath().CreatePolyline(1.0f);
2031 
2032  ASSERT_EQ(polyline.contours.size(), 2u);
2033  ASSERT_EQ(polyline.points.size(), 5u);
2034  ASSERT_EQ(polyline.points[0].x, 10);
2035  ASSERT_EQ(polyline.points[1].x, 20);
2036  ASSERT_EQ(polyline.points[2].x, 30);
2037  ASSERT_EQ(polyline.points[3].x, 40);
2038  ASSERT_EQ(polyline.points[4].x, 50);
2039 }
2040 
2041 TEST(GeometryTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) {
2042  // Closed shapes.
2043  {
2044  Path path = PathBuilder{}.AddCircle({100, 100}, 50).TakePath();
2045  ContourComponent contour;
2046  path.GetContourComponentAtIndex(0, contour);
2047  ASSERT_POINT_NEAR(contour.destination, Point(100, 50));
2048  ASSERT_TRUE(contour.is_closed);
2049  }
2050 
2051  {
2052  Path path = PathBuilder{}.AddOval(Rect(100, 100, 100, 100)).TakePath();
2053  ContourComponent contour;
2054  path.GetContourComponentAtIndex(0, contour);
2055  ASSERT_POINT_NEAR(contour.destination, Point(150, 100));
2056  ASSERT_TRUE(contour.is_closed);
2057  }
2058 
2059  {
2060  Path path = PathBuilder{}.AddRect(Rect(100, 100, 100, 100)).TakePath();
2061  ContourComponent contour;
2062  path.GetContourComponentAtIndex(0, contour);
2063  ASSERT_POINT_NEAR(contour.destination, Point(100, 100));
2064  ASSERT_TRUE(contour.is_closed);
2065  }
2066 
2067  {
2068  Path path =
2069  PathBuilder{}.AddRoundedRect(Rect(100, 100, 100, 100), 10).TakePath();
2070  ContourComponent contour;
2071  path.GetContourComponentAtIndex(0, contour);
2072  ASSERT_POINT_NEAR(contour.destination, Point(110, 100));
2073  ASSERT_TRUE(contour.is_closed);
2074  }
2075 
2076  // Open shapes.
2077  {
2078  Point p(100, 100);
2079  Path path = PathBuilder{}.AddLine(p, {200, 100}).TakePath();
2080  ContourComponent contour;
2081  path.GetContourComponentAtIndex(0, contour);
2082  ASSERT_POINT_NEAR(contour.destination, p);
2083  ASSERT_FALSE(contour.is_closed);
2084  }
2085 
2086  {
2087  Path path =
2088  PathBuilder{}
2089  .AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
2090  .TakePath();
2091  ContourComponent contour;
2092  path.GetContourComponentAtIndex(0, contour);
2093  ASSERT_POINT_NEAR(contour.destination, Point(100, 100));
2094  ASSERT_FALSE(contour.is_closed);
2095  }
2096 
2097  {
2098  Path path = PathBuilder{}
2099  .AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
2100  .TakePath();
2101  ContourComponent contour;
2102  path.GetContourComponentAtIndex(0, contour);
2103  ASSERT_POINT_NEAR(contour.destination, Point(100, 100));
2104  ASSERT_FALSE(contour.is_closed);
2105  }
2106 }
2107 
2108 TEST(GeometryTest, PathCreatePolylineGeneratesCorrectContourData) {
2109  Path::Polyline polyline = PathBuilder{}
2110  .AddLine({100, 100}, {200, 100})
2111  .MoveTo({100, 200})
2112  .LineTo({150, 250})
2113  .LineTo({200, 200})
2114  .Close()
2115  .TakePath()
2116  .CreatePolyline(1.0f);
2117  ASSERT_EQ(polyline.points.size(), 6u);
2118  ASSERT_EQ(polyline.contours.size(), 2u);
2119  ASSERT_EQ(polyline.contours[0].is_closed, false);
2120  ASSERT_EQ(polyline.contours[0].start_index, 0u);
2121  ASSERT_EQ(polyline.contours[1].is_closed, true);
2122  ASSERT_EQ(polyline.contours[1].start_index, 2u);
2123 }
2124 
2125 TEST(GeometryTest, PolylineGetContourPointBoundsReturnsCorrectRanges) {
2126  Path::Polyline polyline = PathBuilder{}
2127  .AddLine({100, 100}, {200, 100})
2128  .MoveTo({100, 200})
2129  .LineTo({150, 250})
2130  .LineTo({200, 200})
2131  .Close()
2132  .TakePath()
2133  .CreatePolyline(1.0f);
2134  size_t a1, a2, b1, b2;
2135  std::tie(a1, a2) = polyline.GetContourPointBounds(0);
2136  std::tie(b1, b2) = polyline.GetContourPointBounds(1);
2137  ASSERT_EQ(a1, 0u);
2138  ASSERT_EQ(a2, 2u);
2139  ASSERT_EQ(b1, 2u);
2140  ASSERT_EQ(b2, 6u);
2141 }
2142 
2143 TEST(GeometryTest, PathAddRectPolylineHasCorrectContourData) {
2144  Path::Polyline polyline = PathBuilder{}
2145  .AddRect(Rect::MakeLTRB(50, 60, 70, 80))
2146  .TakePath()
2147  .CreatePolyline(1.0f);
2148  ASSERT_EQ(polyline.contours.size(), 1u);
2149  ASSERT_TRUE(polyline.contours[0].is_closed);
2150  ASSERT_EQ(polyline.contours[0].start_index, 0u);
2151  ASSERT_EQ(polyline.points.size(), 5u);
2152  ASSERT_EQ(polyline.points[0], Point(50, 60));
2153  ASSERT_EQ(polyline.points[1], Point(70, 60));
2154  ASSERT_EQ(polyline.points[2], Point(70, 80));
2155  ASSERT_EQ(polyline.points[3], Point(50, 80));
2156  ASSERT_EQ(polyline.points[4], Point(50, 60));
2157 }
2158 
2159 TEST(GeometryTest, PathPolylineDuplicatesAreRemovedForSameContour) {
2160  Path::Polyline polyline =
2161  PathBuilder{}
2162  .MoveTo({50, 50})
2163  .LineTo({50, 50}) // Insert duplicate at beginning of contour.
2164  .LineTo({100, 50})
2165  .LineTo({100, 50}) // Insert duplicate at contour join.
2166  .LineTo({100, 100})
2167  .Close() // Implicitly insert duplicate {50, 50} across contours.
2168  .LineTo({0, 50})
2169  .LineTo({0, 100})
2170  .LineTo({0, 100}) // Insert duplicate at end of contour.
2171  .TakePath()
2172  .CreatePolyline(1.0f);
2173  ASSERT_EQ(polyline.contours.size(), 2u);
2174  ASSERT_EQ(polyline.contours[0].start_index, 0u);
2175  ASSERT_TRUE(polyline.contours[0].is_closed);
2176  ASSERT_EQ(polyline.contours[1].start_index, 4u);
2177  ASSERT_FALSE(polyline.contours[1].is_closed);
2178  ASSERT_EQ(polyline.points.size(), 7u);
2179  ASSERT_EQ(polyline.points[0], Point(50, 50));
2180  ASSERT_EQ(polyline.points[1], Point(100, 50));
2181  ASSERT_EQ(polyline.points[2], Point(100, 100));
2182  ASSERT_EQ(polyline.points[3], Point(50, 50));
2183  ASSERT_EQ(polyline.points[4], Point(50, 50));
2184  ASSERT_EQ(polyline.points[5], Point(0, 50));
2185  ASSERT_EQ(polyline.points[6], Point(0, 100));
2186 }
2187 
2188 TEST(GeometryTest, MatrixPrinting) {
2189  {
2190  std::stringstream stream;
2191  Matrix m;
2192  stream << m;
2193  ASSERT_EQ(stream.str(), R"((
2194  1.000000, 0.000000, 0.000000, 0.000000,
2195  0.000000, 1.000000, 0.000000, 0.000000,
2196  0.000000, 0.000000, 1.000000, 0.000000,
2197  0.000000, 0.000000, 0.000000, 1.000000,
2198 ))");
2199  }
2200 
2201  {
2202  std::stringstream stream;
2203  Matrix m = Matrix::MakeTranslation(Vector3(10, 20, 30));
2204  stream << m;
2205 
2206  ASSERT_EQ(stream.str(), R"((
2207  1.000000, 0.000000, 0.000000, 10.000000,
2208  0.000000, 1.000000, 0.000000, 20.000000,
2209  0.000000, 0.000000, 1.000000, 30.000000,
2210  0.000000, 0.000000, 0.000000, 1.000000,
2211 ))");
2212  }
2213 }
2214 
2215 TEST(GeometryTest, PointPrinting) {
2216  {
2217  std::stringstream stream;
2218  Point m;
2219  stream << m;
2220  ASSERT_EQ(stream.str(), "(0, 0)");
2221  }
2222 
2223  {
2224  std::stringstream stream;
2225  Point m(13, 37);
2226  stream << m;
2227  ASSERT_EQ(stream.str(), "(13, 37)");
2228  }
2229 }
2230 
2231 TEST(GeometryTest, Vector3Printing) {
2232  {
2233  std::stringstream stream;
2234  Vector3 m;
2235  stream << m;
2236  ASSERT_EQ(stream.str(), "(0, 0, 0)");
2237  }
2238 
2239  {
2240  std::stringstream stream;
2241  Vector3 m(1, 2, 3);
2242  stream << m;
2243  ASSERT_EQ(stream.str(), "(1, 2, 3)");
2244  }
2245 }
2246 
2247 TEST(GeometryTest, Vector4Printing) {
2248  {
2249  std::stringstream stream;
2250  Vector4 m;
2251  stream << m;
2252  ASSERT_EQ(stream.str(), "(0, 0, 0, 1)");
2253  }
2254 
2255  {
2256  std::stringstream stream;
2257  Vector4 m(1, 2, 3, 4);
2258  stream << m;
2259  ASSERT_EQ(stream.str(), "(1, 2, 3, 4)");
2260  }
2261 }
2262 
2263 TEST(GeometryTest, ColorPrinting) {
2264  {
2265  std::stringstream stream;
2266  Color m;
2267  stream << m;
2268  ASSERT_EQ(stream.str(), "(0, 0, 0, 0)");
2269  }
2270 
2271  {
2272  std::stringstream stream;
2273  Color m(1, 2, 3, 4);
2274  stream << m;
2275  ASSERT_EQ(stream.str(), "(1, 2, 3, 4)");
2276  }
2277 }
2278 
2279 TEST(GeometryTest, ToIColor) {
2280  ASSERT_EQ(Color::ToIColor(Color(0, 0, 0, 0)), 0u);
2281  ASSERT_EQ(Color::ToIColor(Color(1.0, 1.0, 1.0, 1.0)), 0xFFFFFFFF);
2282  ASSERT_EQ(Color::ToIColor(Color(0.5, 0.5, 1.0, 1.0)), 0xFF8080FF);
2283 }
2284 
2285 TEST(GeometryTest, Gradient) {
2286  {
2287  // Simple 2 color gradient produces color buffer containing exactly those
2288  // values.
2289  std::vector<Color> colors = {Color::Red(), Color::Blue()};
2290  std::vector<Scalar> stops = {0.0, 1.0};
2291 
2292  auto gradient = CreateGradientBuffer(colors, stops);
2293 
2294  ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, colors);
2295  ASSERT_EQ(gradient.texture_size, 2u);
2296  }
2297 
2298  {
2299  // Gradient with duplicate stops does not create an empty texture.
2300  std::vector<Color> colors = {Color::Red(), Color::Yellow(), Color::Black(),
2301  Color::Blue()};
2302  std::vector<Scalar> stops = {0.0, 0.25, 0.25, 1.0};
2303 
2304  auto gradient = CreateGradientBuffer(colors, stops);
2305  ASSERT_EQ(gradient.texture_size, 5u);
2306  }
2307 
2308  {
2309  // Simple N color gradient produces color buffer containing exactly those
2310  // values.
2311  std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green(),
2312  Color::White()};
2313  std::vector<Scalar> stops = {0.0, 0.33, 0.66, 1.0};
2314 
2315  auto gradient = CreateGradientBuffer(colors, stops);
2316 
2317  ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, colors);
2318  ASSERT_EQ(gradient.texture_size, 4u);
2319  }
2320 
2321  {
2322  // Gradient with color stops will lerp and scale buffer.
2323  std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green()};
2324  std::vector<Scalar> stops = {0.0, 0.25, 1.0};
2325 
2326  auto gradient = CreateGradientBuffer(colors, stops);
2327 
2328  std::vector<Color> lerped_colors = {
2329  Color::Red(),
2330  Color::Blue(),
2331  Color::Lerp(Color::Blue(), Color::Green(), 0.3333),
2332  Color::Lerp(Color::Blue(), Color::Green(), 0.6666),
2333  Color::Green(),
2334  };
2335  ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, lerped_colors);
2336  ASSERT_EQ(gradient.texture_size, 5u);
2337  }
2338 
2339  {
2340  // Gradient size is capped at 1024.
2341  std::vector<Color> colors = {};
2342  std::vector<Scalar> stops = {};
2343  for (auto i = 0u; i < 1025; i++) {
2344  colors.push_back(Color::Blue());
2345  stops.push_back(i / 1025.0);
2346  }
2347 
2348  auto gradient = CreateGradientBuffer(colors, stops);
2349 
2350  ASSERT_EQ(gradient.texture_size, 1024u);
2351  ASSERT_EQ(gradient.color_bytes.size(), 1024u * 4);
2352  }
2353 }
2354 
2355 TEST(GeometryTest, HalfConversions) {
2356 #ifdef FML_OS_WIN
2357  GTEST_SKIP() << "Half-precision floats (IEEE 754) are not portable and "
2358  "unavailable on Windows.";
2359 #else
2360  ASSERT_EQ(ScalarToHalf(0.0), 0.0f16);
2361  ASSERT_EQ(ScalarToHalf(0.05), 0.05f16);
2362  ASSERT_EQ(ScalarToHalf(2.43), 2.43f16);
2363  ASSERT_EQ(ScalarToHalf(-1.45), -1.45f16);
2364 
2365  // 65504 is the largest possible half.
2366  ASSERT_EQ(ScalarToHalf(65504.0f), 65504.0f16);
2367  ASSERT_EQ(ScalarToHalf(65504.0f + 1), 65504.0f16);
2368 
2369  // Colors
2370  ASSERT_EQ(HalfVector4(Color::Red()),
2371  HalfVector4(1.0f16, 0.0f16, 0.0f16, 1.0f16));
2372  ASSERT_EQ(HalfVector4(Color::Green()),
2373  HalfVector4(0.0f16, 1.0f16, 0.0f16, 1.0f16));
2374  ASSERT_EQ(HalfVector4(Color::Blue()),
2375  HalfVector4(0.0f16, 0.0f16, 1.0f16, 1.0f16));
2376  ASSERT_EQ(HalfVector4(Color::Black().WithAlpha(0)),
2377  HalfVector4(0.0f16, 0.0f16, 0.0f16, 0.0f16));
2378 
2379  ASSERT_EQ(HalfVector3(Vector3(4.0, 6.0, -1.0)),
2380  HalfVector3(4.0f16, 6.0f16, -1.0f16));
2381  ASSERT_EQ(HalfVector2(Vector2(4.0, 6.0)), HalfVector2(4.0f16, 6.0f16));
2382 
2383  ASSERT_EQ(Half(0.5f), Half(0.5f16));
2384  ASSERT_EQ(Half(0.5), Half(0.5f16));
2385  ASSERT_EQ(Half(5), Half(5.0f16));
2386 #endif // FML_OS_WIN
2387 }
2388 
2389 TEST(GeometryTest, PathShifting) {
2390  PathBuilder builder{};
2391  auto path =
2392  builder.AddLine(Point(0, 0), Point(10, 10))
2393  .AddQuadraticCurve(Point(10, 10), Point(15, 15), Point(20, 20))
2394  .AddCubicCurve(Point(20, 20), Point(25, 25), Point(-5, -5),
2395  Point(30, 30))
2396  .Close()
2397  .Shift(Point(1, 1))
2398  .TakePath();
2399 
2400  ContourComponent contour;
2401  LinearPathComponent linear;
2403  CubicPathComponent cubic;
2404 
2405  ASSERT_TRUE(path.GetContourComponentAtIndex(0, contour));
2406  ASSERT_TRUE(path.GetLinearComponentAtIndex(1, linear));
2407  ASSERT_TRUE(path.GetQuadraticComponentAtIndex(3, quad));
2408  ASSERT_TRUE(path.GetCubicComponentAtIndex(5, cubic));
2409 
2410  ASSERT_EQ(contour.destination, Point(1, 1));
2411 
2412  ASSERT_EQ(linear.p1, Point(1, 1));
2413  ASSERT_EQ(linear.p2, Point(11, 11));
2414 
2415  ASSERT_EQ(quad.cp, Point(16, 16));
2416  ASSERT_EQ(quad.p1, Point(11, 11));
2417  ASSERT_EQ(quad.p2, Point(21, 21));
2418 
2419  ASSERT_EQ(cubic.cp1, Point(26, 26));
2420  ASSERT_EQ(cubic.cp2, Point(-4, -4));
2421  ASSERT_EQ(cubic.p1, Point(21, 21));
2422  ASSERT_EQ(cubic.p2, Point(31, 31));
2423 }
2424 
2425 TEST(GeometryTest, PathBuilderWillComputeBounds) {
2426  PathBuilder builder;
2427  auto path_1 = builder.AddLine({0, 0}, {1, 1}).TakePath();
2428 
2429  ASSERT_EQ(path_1.GetBoundingBox().value(), Rect::MakeLTRB(0, 0, 1, 1));
2430 
2431  auto path_2 = builder.AddLine({-1, -1}, {1, 1}).TakePath();
2432 
2433  // Verify that PathBuilder recomputes the bounds.
2434  ASSERT_EQ(path_2.GetBoundingBox().value(), Rect::MakeLTRB(-1, -1, 1, 1));
2435 
2436  // PathBuilder can set the bounds to whatever it wants
2437  auto path_3 = builder.AddLine({0, 0}, {1, 1})
2438  .SetBounds(Rect::MakeLTRB(0, 0, 100, 100))
2439  .TakePath();
2440 
2441  ASSERT_EQ(path_3.GetBoundingBox().value(), Rect::MakeLTRB(0, 0, 100, 100));
2442 }
2443 
2444 } // namespace testing
2445 } // namespace impeller
2446 
2447 // NOLINTEND(bugprone-unchecked-optional-access)
impeller::Matrix::MakeSkew
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
Definition: matrix.h:116
impeller::BlendMode
BlendMode
Definition: color.h:57
impeller::Color::Blue
static constexpr Color Blue()
Definition: color.h:266
path.h
ASSERT_COLOR_NEAR
#define ASSERT_COLOR_NEAR(a, b)
Definition: geometry_asserts.h:155
impeller::Matrix::Decompose
std::optional< MatrixDecomposition > Decompose() const
Definition: matrix.cc:217
impeller::k1OverSqrt2
constexpr float k1OverSqrt2
Definition: constants.h:49
impeller::CubicPathComponent::CreatePolyline
std::vector< Point > CreatePolyline(Scalar scale) const
Definition: path_component.cc:190
impeller::Matrix::MakeRotationX
static Matrix MakeRotationX(Radians r)
Definition: matrix.h:181
impeller::BlendModeToString
const char * BlendModeToString(BlendMode blend_mode)
Definition: color.cc:47
impeller::PathBuilder::AddQuadraticCurve
PathBuilder & AddQuadraticCurve(Point p1, Point cp, Point p2)
Move to point p1, then insert a quadradic curve from p1 to p2 with the control point cp.
Definition: path_builder.cc:166
impeller::TRect::size
TSize< Type > size
Definition: rect.h:24
point.h
impeller::LinearPathComponent
Definition: path_component.h:27
impeller::TPoint::y
Type y
Definition: point.h:24
impeller::Scalar
float Scalar
Definition: scalar.h:15
impeller::HalfVector2
A storage only class for half precision floating point vector 2.
Definition: half.h:125
impeller::TPoint::Ceil
constexpr TPoint Ceil() const
Definition: point.h:189
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:262
geometry_asserts.h
impeller::CubicPathComponent::p1
Point p1
Definition: path_component.h:91
impeller::Vector3::Round
constexpr Vector3 Round() const
Definition: vector.h:83
impeller::Vector4::Max
constexpr Vector4 Max(const Vector4 &p) const
Definition: vector.h:286
impeller::Color::MakeRGBA8
static constexpr Color MakeRGBA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Definition: color.h:152
impeller::Path::ComponentType::kLinear
@ kLinear
impeller::Color
Definition: color.h:122
impeller::LinearPathComponent::p2
Point p2
Definition: path_component.h:29
impeller::QuadraticPathComponent::p1
Point p1
Definition: path_component.h:51
impeller::Color::Unpremultiply
constexpr Color Unpremultiply() const
Definition: color.h:216
impeller::TPoint::Lerp
constexpr TPoint Lerp(const TPoint &p, Scalar t) const
Definition: point.h:223
impeller::Vector4
Definition: vector.h:229
impeller::CubicPathComponent::cp2
Point cp2
Definition: path_component.h:93
impeller::Matrix::MakeRotation
static Matrix MakeRotation(Quaternion q)
Definition: matrix.h:125
impeller::TPoint::Min
constexpr TPoint Min(const TPoint &p) const
Definition: point.h:179
impeller::Path::ComponentType::kCubic
@ kCubic
impeller::PathBuilder
Definition: path_builder.h:13
impeller::QuadraticPathComponent::cp
Point cp
Definition: path_component.h:52
impeller::testing::TEST
TEST(AiksCanvasTest, EmptyCullRect)
Definition: canvas_unittests.cc:17
impeller::TPoint::Round
static constexpr TPoint Round(const TPoint< U > &other)
Definition: point.h:42
impeller::Vector2
Point Vector2
Definition: point.h:310
impeller::Matrix::MakeRotationY
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:194
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:399
impeller::kPi
constexpr float kPi
Definition: constants.h:25
impeller::Path::ComponentType::kQuadratic
@ kQuadratic
impeller::PathBuilder::AddRoundedRect
PathBuilder & AddRoundedRect(Rect rect, RoundingRadii radii)
Definition: path_builder.cc:207
impeller::Color::Yellow
static constexpr Color Yellow()
Definition: color.h:832
impeller::Radians::radians
Scalar radians
Definition: scalar.h:36
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:82
impeller::Size
TSize< Scalar > Size
Definition: size.h:135
ASSERT_POINT_NEAR
#define ASSERT_POINT_NEAR(a, b)
Definition: geometry_asserts.h:156
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:94
impeller::Color::Clamp01
constexpr Color Clamp01() const
Definition: color.h:236
ASSERT_VECTOR4_NEAR
#define ASSERT_VECTOR4_NEAR(a, b)
Definition: geometry_asserts.h:158
impeller::Vector3::x
Scalar x
Definition: vector.h:20
impeller::MoveTo
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:18
impeller::TPoint::Floor
constexpr TPoint Floor() const
Definition: point.h:187
impeller::TPoint::Dot
constexpr Type Dot(const TPoint &p) const
Definition: point.h:213
impeller::PathBuilder::AddRect
PathBuilder & AddRect(Rect rect)
Definition: path_builder.cc:181
impeller::TRect::IntersectsWithRect
constexpr bool IntersectsWithRect(const TRect &o) const
Definition: rect.h:234
impeller::TRect::GetPoints
constexpr std::array< TPoint< T >, 4 > GetPoints() const
Get the points that represent the 4 corners of this rectangle. The order is: Top left,...
Definition: rect.h:187
impeller::MatrixDecomposition
Definition: matrix_decomposition.h:14
impeller::kPiOver2
constexpr float kPiOver2
Definition: constants.h:31
impeller::Matrix::MakeLookAt
static constexpr Matrix MakeLookAt(Vector3 position, Vector3 target, Vector3 up)
Definition: matrix.h:483
impeller::Matrix::MakePerspective
static constexpr Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:457
ASSERT_RECT_NEAR
#define ASSERT_RECT_NEAR(a, b)
Definition: geometry_asserts.h:154
path_builder.h
gradient.h
impeller::Vector4::Round
constexpr Vector4 Round() const
Definition: vector.h:299
impeller::TPoint::Reflect
constexpr TPoint Reflect(const TPoint &axis) const
Definition: point.h:215
impeller::Path::Polyline
Definition: path.h:78
impeller::Matrix::Basis
constexpr Matrix Basis() const
Definition: matrix.h:222
impeller::TSize
Definition: size.h:18
impeller::CubicPathComponent::cp1
Point cp1
Definition: path_component.h:92
impeller::Quaternion
Definition: quaternion.h:13
impeller::Point
TPoint< Scalar > Point
Definition: point.h:306
impeller::k2Pi
constexpr float k2Pi
Definition: constants.h:28
impeller::TRect::Intersection
constexpr std::optional< TRect< T > > Intersection(const TRect &o) const
Definition: rect.h:219
impeller::LinearPathComponent::p1
Point p1
Definition: path_component.h:28
impeller::PathBuilder::Shift
PathBuilder & Shift(Point offset)
Transform the existing path segments and contours by the given offset.
Definition: path_builder.cc:460
impeller::Half
A storage only class for half precision floating point.
Definition: half.h:37
impeller::TPoint::AngleTo
constexpr Radians AngleTo(const TPoint &p) const
Definition: point.h:219
impeller::Color::ToIColor
static constexpr uint32_t ToIColor(Color color)
Convert this color to a 32-bit representation.
Definition: color.h:159
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:54
impeller::Color::SRGBToLinear
Color SRGBToLinear() const
Convert the color from sRGB space to linear space.
Definition: color.cc:387
impeller::TRect::GetTransformedPoints
constexpr std::array< TPoint< T >, 4 > GetTransformedPoints(const Matrix &transform) const
Definition: rect.h:193
impeller::ContourComponent::is_closed
bool is_closed
Definition: path_component.h:139
impeller::Path::Polyline::points
std::vector< Point > points
Definition: path.h:81
impeller::Vector4::Lerp
constexpr Vector4 Lerp(const Vector4 &v, Scalar t) const
Definition: vector.h:303
impeller::ScalarToHalf
constexpr InternalHalf ScalarToHalf(Scalar f)
Convert a scalar to a half precision float.
Definition: half.h:28
impeller::TPoint::Max
constexpr TPoint Max(const TPoint &p) const
Definition: point.h:183
impeller::Vector4::Min
constexpr Vector4 Min(const Vector4 &p) const
Definition: vector.h:281
impeller::Path::Polyline::GetContourPointBounds
std::tuple< size_t, size_t > GetContourPointBounds(size_t contour_index) const
Definition: path.cc:20
impeller::Color::ToR8G8B8A8
constexpr std::array< uint8_t, 4 > ToR8G8B8A8() const
Convert to R8G8B8A8 representation.
Definition: color.h:246
impeller::PathBuilder::LineTo
PathBuilder & LineTo(Point point, bool relative=false)
Insert a line from the current position to point.
Definition: path_builder.cc:46
impeller::Color::White
static constexpr Color White()
Definition: color.h:254
impeller::PathBuilder::AddCubicCurve
PathBuilder & AddCubicCurve(Point p1, Point cp1, Point cp2, Point p2)
Move to point p1, then insert a cubic curve from p1 to p2 with control points cp1 and cp2.
Definition: path_builder.cc:172
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:68
impeller::Vector3::Floor
constexpr Vector3 Floor() const
Definition: vector.h:75
impeller::Vector3::Ceil
constexpr Vector3 Ceil() const
Definition: vector.h:79
impeller::Vector3::z
Scalar z
Definition: vector.h:22
impeller::Radians
Definition: scalar.h:35
impeller::MatrixDecomposition::translation
Vector3 translation
Definition: matrix_decomposition.h:15
impeller::TRect::origin
TPoint< Type > origin
Definition: rect.h:23
impeller::TRect< Scalar >::MakePointBounds
constexpr static std::optional< TRect > MakePointBounds(const PointIter first, const PointIter last)
Definition: rect.h:57
impeller::PathBuilder::AddLine
PathBuilder & AddLine(const Point &p1, const Point &p2)
Move to point p1, then insert a line from p1 to p2.
Definition: path_builder.cc:433
impeller::HalfVector3
A storage only class for half precision floating point vector 3.
Definition: half.h:97
_BLEND_MODE_NAME_CHECK
#define _BLEND_MODE_NAME_CHECK(blend_mode)
Definition: geometry_unittests.cc:1561
impeller::Color::Green
static constexpr Color Green()
Definition: color.h:264
impeller::PathBuilder::TakePath
Path TakePath(FillType fill=FillType::kNonZero)
Definition: path_builder.cc:21
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:306
ASSERT_MATRIX_NEAR
#define ASSERT_MATRIX_NEAR(a, b)
Definition: geometry_asserts.h:152
impeller::TPoint::Cross
constexpr Type Cross(const TPoint &p) const
Definition: point.h:211
impeller::Vector3::y
Scalar y
Definition: vector.h:21
impeller::CreateGradientBuffer
GradientData CreateGradientBuffer(const std::vector< Color > &colors, const std::vector< Scalar > &stops)
Populate a vector with the interpolated color bytes for the linear gradient described by colors and s...
Definition: gradient.cc:20
impeller::MatrixDecomposition::rotation
Quaternion rotation
Definition: matrix_decomposition.h:19
impeller::TSize::width
Type width
Definition: size.h:21
impeller::ContourComponent::destination
Point destination
Definition: path_component.h:138
IMPELLER_FOR_EACH_BLEND_MODE
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition: color.h:17
impeller::Matrix::Invert
Matrix Invert() const
Definition: matrix.cc:97
impeller::TRect::Contains
constexpr bool Contains(const TPoint< Type > &p) const
Definition: rect.h:122
impeller::TPoint::x
Type x
Definition: point.h:23
impeller::Path::GetContourComponentAtIndex
bool GetContourComponentAtIndex(size_t index, ContourComponent &contour) const
Definition: path.cc:201
ASSERT_QUATERNION_NEAR
#define ASSERT_QUATERNION_NEAR(a, b)
Definition: geometry_asserts.h:153
impeller::Matrix::GetDirectionScale
constexpr Scalar GetDirectionScale(Vector3 direction) const
Definition: matrix.h:306
impeller::CubicPathComponent
Definition: path_component.h:90
scalar.h
impeller::Close
void Close(PathBuilder *builder)
Definition: tessellator.cc:36
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:136
impeller::MatrixDecomposition::scale
Vector3 scale
Definition: matrix_decomposition.h:16
impeller::TPoint::Abs
constexpr TPoint Abs() const
Definition: point.h:209
impeller::TRect::Cutout
constexpr std::optional< TRect< T > > Cutout(const TRect &o) const
Returns the new boundary rectangle that would result from the rectangle being cutout by a second rect...
Definition: rect.h:240
half.h
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:208
impeller::PathBuilder::Close
PathBuilder & Close()
Definition: path_builder.cc:39
impeller::LineTo
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:22
impeller::TRect< Scalar >::MakeSize
constexpr static TRect MakeSize(const TSize< U > &size)
Definition: rect.h:52
constants.h
impeller::IPoint
TPoint< int64_t > IPoint
Definition: point.h:307
ASSERT_VECTOR3_NEAR
#define ASSERT_VECTOR3_NEAR(a, b)
Definition: geometry_asserts.h:157
rect.h
impeller::QuadraticPathComponent::p2
Point p2
Definition: path_component.h:53
impeller::TPoint< Scalar >
impeller::TRect< Scalar >::MakeMaximum
constexpr static TRect MakeMaximum()
Definition: rect.h:75
impeller::PathBuilder::MoveTo
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:32
impeller::Color::BlackTransparent
static constexpr Color BlackTransparent()
Definition: color.h:260
impeller::Color::Black
static constexpr Color Black()
Definition: color.h:256
impeller::Path::Polyline::contours
std::vector< PolylineContour > contours
Definition: path.h:82
impeller::Vector4::Floor
constexpr Vector4 Floor() const
Definition: vector.h:291
impeller::HalfVector4
A storage only class for half precision floating point vector 4.
Definition: half.h:56
impeller::ScalarNearlyEqual
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:27
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:448
impeller::BlendMode::kLast
@ kLast
impeller::TRect::Union
constexpr TRect Union(const TRect &o) const
Definition: rect.h:209
impeller::Vector3::Min
constexpr Vector3 Min(const Vector3 &p) const
Definition: vector.h:67
impeller::Degrees
Definition: scalar.h:43
color.h
impeller::CubicPathComponent::p2
Point p2
Definition: path_component.h:94
impeller::PathBuilder::AddCircle
PathBuilder & AddCircle(const Point &center, Scalar radius)
Definition: path_builder.cc:198
impeller::TSize::height
Type height
Definition: size.h:22
impeller::CubicPathComponent::Extrema
std::vector< Point > Extrema() const
Definition: path_component.cc:310
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:40
impeller::PathBuilder::AddOval
PathBuilder & AddOval(const Rect &rect)
Definition: path_builder.cc:385
impeller::TRect::GetPositive
constexpr TRect GetPositive() const
Get a version of this rectangle that has a non-negative size.
Definition: rect.h:180
path_component.h
impeller::ContourComponent
Definition: path_component.h:137
impeller::TSize::MipCount
constexpr size_t MipCount() const
Definition: size.h:113
impeller::Color::Lerp
constexpr static Color Lerp(Color a, Color b, Scalar t)
Return a color that is linearly interpolated between colors a and b, according to the value of t.
Definition: color.h:232
impeller::TPoint::Normalize
constexpr TPoint Normalize() const
Definition: point.h:201
impeller::ColorMatrix
Definition: color.h:115
impeller::Color::ApplyColorMatrix
Color ApplyColorMatrix(const ColorMatrix &color_matrix) const
A color filter that transforms colors through a 4x5 color matrix.
Definition: color.cc:366
impeller::Color::LinearToSRGB
Color LinearToSRGB() const
Convert the color from linear space to sRGB space.
Definition: color.cc:376
ASSERT_COLOR_BUFFER_NEAR
#define ASSERT_COLOR_BUFFER_NEAR(a, b)
Definition: geometry_asserts.h:161
impeller
Definition: aiks_context.cc:10
impeller::QuadraticPathComponent
Definition: path_component.h:50
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:103
impeller::kPiOver4
constexpr float kPiOver4
Definition: constants.h:34
impeller::Path::ComponentType::kContour
@ kContour
impeller::Color::Premultiply
constexpr Color Premultiply() const
Definition: color.h:212
impeller::TRect< Scalar >
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:36
impeller::Vector3
Definition: vector.h:17
size.h
impeller::Vector3::Lerp
constexpr Vector3 Lerp(const Vector3 &v, Scalar t) const
Definition: vector.h:175
ASSERT_ARRAY_4_NEAR
#define ASSERT_ARRAY_4_NEAR(a, b)
Definition: geometry_asserts.h:160
impeller::Vector3::Max
constexpr Vector3 Max(const Vector3 &p) const
Definition: vector.h:71
impeller::Matrix::IsAligned
constexpr bool IsAligned(Scalar tolerance=0) const
Definition: matrix.h:320
impeller::Vector4::Ceil
constexpr Vector4 Ceil() const
Definition: vector.h:295
impeller::Path::CreatePolyline
Polyline CreatePolyline(Scalar scale) const
Definition: path.cc:272