| 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"
6 #include "impeller/geometry/geometry_asserts.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"
14 #include "impeller/geometry/color.h"
15 #include "impeller/geometry/constants.h"
16 #include "impeller/geometry/gradient.h"
17 #include "impeller/geometry/half.h"
18 #include "impeller/geometry/path.h"
19 #include "impeller/geometry/path_builder.h"
20 #include "impeller/geometry/path_component.h"
21 #include "impeller/geometry/point.h"
22 #include "impeller/geometry/rect.h"
23 #include "impeller/geometry/scalar.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}) *
200 Matrix::MakeRotationZ(Radians{kPiOver4}) *
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}) *
213 Matrix::MakeRotationZ(Radians{kPiOver2}) *
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}) *
224 Matrix::MakeRotationZ(Radians{kPiOver2}) *
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}) *
235 Matrix::MakeRotationZ(Radians{kPiOver2}) *
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}) *
298 Matrix::MakeRotationZ(Radians{kPiOver2}) *
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}) *
309 Matrix::MakeRotationZ(Radians{kPiOver2}) *
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}) *
320 Matrix::MakeRotationZ(Radians{kPiOver2}) *
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 {
420 auto m = Matrix::MakeRotationZ(Radians{kPiOver2}) *
421 Matrix::MakeRotationX(Radians{kPiOver2}) *
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}) *
441 Matrix::MakeRotationY(Degrees{83}) *
442 Matrix::MakeRotationZ(Degrees{172});
443 Scalar result = m.GetDirectionScale(Vector3{0, 1, 0});
444 ASSERT_FLOAT_EQ(result, 1);
445 }
446
447 {
448 auto m = Matrix::MakeRotationZ(Radians{kPiOver2}) *
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
598 ContourComponent c;
599 path.GetContourComponentAtIndex(0, c);
600 ASSERT_POINT_NEAR(c.destination, Point());
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);
1478 Color b = Color::BlackTransparent();
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);
1571 switch (mode) { IMPELLER_FOR_EACH_BLEND_MODE(_BLEND_MODE_NAME_CHECK) }
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;
2402 QuadraticPathComponent quad;
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