| 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 <map>
10 #include <sstream>
11 #include <type_traits>
12
13 #include "flutter/fml/build_config.h"
14 #include "flutter/testing/testing.h"
15 #include "impeller/geometry/color.h"
16 #include "impeller/geometry/constants.h"
17 #include "impeller/geometry/gradient.h"
18 #include "impeller/geometry/half.h"
19 #include "impeller/geometry/point.h"
20 #include "impeller/geometry/rect.h"
21 #include "impeller/geometry/scalar.h"
22 #include "impeller/geometry/separated_vector.h"
23 #include "impeller/geometry/size.h"
24
25 // TODO(zanderso): https://github.com/flutter/flutter/issues/127701
26 // NOLINTBEGIN(bugprone-unchecked-optional-access)
27
28 namespace impeller {
29 namespace testing {
30
31 TEST(GeometryTest, ScalarNearlyEqual) {
32 ASSERT_FALSE(ScalarNearlyEqual(0.0021f, 0.001f));
33 ASSERT_TRUE(ScalarNearlyEqual(0.0019f, 0.001f));
34 ASSERT_TRUE(ScalarNearlyEqual(0.002f, 0.001f, 0.0011f));
35 ASSERT_FALSE(ScalarNearlyEqual(0.002f, 0.001f, 0.0009f));
36 ASSERT_TRUE(ScalarNearlyEqual(
37 1.0f, 1.0f + std::numeric_limits<float>::epsilon() * 4));
38 }
39
40 TEST(GeometryTest, MakeColumn) {
41 auto matrix = Matrix::MakeColumn(1, 2, 3, 4, //
42 5, 6, 7, 8, //
43 9, 10, 11, 12, //
44 13, 14, 15, 16);
45
46 auto expect = Matrix{1, 2, 3, 4, //
47 5, 6, 7, 8, //
48 9, 10, 11, 12, //
49 13, 14, 15, 16};
50
51 ASSERT_TRUE(matrix == expect);
52 }
53
54 TEST(GeometryTest, MakeRow) {
55 auto matrix = Matrix::MakeRow(1, 2, 3, 4, //
56 5, 6, 7, 8, //
57 9, 10, 11, 12, //
58 13, 14, 15, 16);
59
60 auto expect = Matrix{1, 5, 9, 13, //
61 2, 6, 10, 14, //
62 3, 7, 11, 15, //
63 4, 8, 12, 16};
64
65 ASSERT_TRUE(matrix == expect);
66 }
67
68 TEST(GeometryTest, RotationMatrix) {
69 auto rotation = Matrix::MakeRotationZ(Radians{kPiOver4});
70 // clang-format off
71 auto expect = Matrix{k1OverSqrt2, k1OverSqrt2, 0, 0,
72 -k1OverSqrt2, k1OverSqrt2, 0, 0,
73 0, 0, 1, 0,
74 0, 0, 0, 1};
75 // clang-format on
76 ASSERT_MATRIX_NEAR(rotation, expect);
77 }
78
79 TEST(GeometryTest, InvertMultMatrix) {
80 {
81 auto rotation = Matrix::MakeRotationZ(Radians{kPiOver4});
82 auto invert = rotation.Invert();
83 // clang-format off
84 auto expect = Matrix{k1OverSqrt2, -k1OverSqrt2, 0, 0,
85 k1OverSqrt2, k1OverSqrt2, 0, 0,
86 0, 0, 1, 0,
87 0, 0, 0, 1};
88 // clang-format on
89 ASSERT_MATRIX_NEAR(invert, expect);
90 }
91 {
92 auto scale = Matrix::MakeScale(Vector2{2, 4});
93 auto invert = scale.Invert();
94 auto expect = Matrix{0.5, 0, 0, 0, //
95 0, 0.25, 0, 0, //
96 0, 0, 1, 0, //
97 0, 0, 0, 1};
98 ASSERT_MATRIX_NEAR(invert, expect);
99 }
100 }
101
102 TEST(GeometryTest, MatrixBasis) {
103 auto matrix = Matrix{1, 2, 3, 4, //
104 5, 6, 7, 8, //
105 9, 10, 11, 12, //
106 13, 14, 15, 16};
107 auto basis = matrix.Basis();
108 auto expect = Matrix{1, 2, 3, 0, //
109 5, 6, 7, 0, //
110 9, 10, 11, 0, //
111 0, 0, 0, 1};
112 ASSERT_MATRIX_NEAR(basis, expect);
113 }
114
115 TEST(GeometryTest, MutliplicationMatrix) {
116 auto rotation = Matrix::MakeRotationZ(Radians{kPiOver4});
117 auto invert = rotation.Invert();
118 ASSERT_MATRIX_NEAR(rotation * invert, Matrix{});
119 }
120
121 TEST(GeometryTest, DeterminantTest) {
122 auto matrix = Matrix{3, 4, 14, 155, 2, 1, 3, 4, 2, 3, 2, 1, 1, 2, 4, 2};
123 ASSERT_EQ(matrix.GetDeterminant(), -1889);
124 }
125
126 TEST(GeometryTest, InvertMatrix) {
127 auto inverted = Matrix{10, -9, -12, 8, //
128 7, -12, 11, 22, //
129 -10, 10, 3, 6, //
130 -2, 22, 2, 1}
131 .Invert();
132
133 auto result = Matrix{
134 438.0 / 85123.0, 1751.0 / 85123.0, -7783.0 / 85123.0, 4672.0 / 85123.0,
135 393.0 / 85123.0, -178.0 / 85123.0, -570.0 / 85123.0, 4192 / 85123.0,
136 -5230.0 / 85123.0, 2802.0 / 85123.0, -3461.0 / 85123.0, 962.0 / 85123.0,
137 2690.0 / 85123.0, 1814.0 / 85123.0, 3896.0 / 85123.0, 319.0 / 85123.0};
138
139 ASSERT_MATRIX_NEAR(inverted, result);
140 }
141
142 TEST(GeometryTest, TestDecomposition) {
143 auto rotated = Matrix::MakeRotationZ(Radians{kPiOver4});
144
145 auto result = rotated.Decompose();
146
147 ASSERT_TRUE(result.has_value());
148
149 MatrixDecomposition res = result.value();
150
151 auto quaternion = Quaternion{{0.0, 0.0, 1.0}, kPiOver4};
152 ASSERT_QUATERNION_NEAR(res.rotation, quaternion);
153 }
154
155 TEST(GeometryTest, TestDecomposition2) {
156 auto rotated = Matrix::MakeRotationZ(Radians{kPiOver4});
157 auto scaled = Matrix::MakeScale({2.0, 3.0, 1.0});
158 auto translated = Matrix::MakeTranslation({-200, 750, 20});
159
160 auto result = (translated * rotated * scaled).Decompose();
161
162 ASSERT_TRUE(result.has_value());
163
164 MatrixDecomposition res = result.value();
165
166 auto quaternion = Quaternion{{0.0, 0.0, 1.0}, kPiOver4};
167
168 ASSERT_QUATERNION_NEAR(res.rotation, quaternion);
169
170 ASSERT_FLOAT_EQ(res.translation.x, -200);
171 ASSERT_FLOAT_EQ(res.translation.y, 750);
172 ASSERT_FLOAT_EQ(res.translation.z, 20);
173
174 ASSERT_FLOAT_EQ(res.scale.x, 2);
175 ASSERT_FLOAT_EQ(res.scale.y, 3);
176 ASSERT_FLOAT_EQ(res.scale.z, 1);
177 }
178
179 TEST(GeometryTest, TestRecomposition) {
180 /*
181 * Decomposition.
182 */
183 auto rotated = Matrix::MakeRotationZ(Radians{kPiOver4});
184
185 auto result = rotated.Decompose();
186
187 ASSERT_TRUE(result.has_value());
188
189 MatrixDecomposition res = result.value();
190
191 auto quaternion = Quaternion{{0.0, 0.0, 1.0}, kPiOver4};
192
193 ASSERT_QUATERNION_NEAR(res.rotation, quaternion);
194
195 /*
196 * Recomposition.
197 */
198 ASSERT_MATRIX_NEAR(rotated, Matrix{res});
199 }
200
201 TEST(GeometryTest, TestRecomposition2) {
202 auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
203 Matrix::MakeRotationZ(Radians{kPiOver4}) *
204 Matrix::MakeScale({2.0, 2.0, 2.0});
205
206 auto result = matrix.Decompose();
207
208 ASSERT_TRUE(result.has_value());
209
210 ASSERT_MATRIX_NEAR(matrix, Matrix{result.value()});
211 }
212
213 TEST(GeometryTest, MatrixVectorMultiplication) {
214 {
215 auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
216 Matrix::MakeRotationZ(Radians{kPiOver2}) *
217 Matrix::MakeScale({2.0, 2.0, 2.0});
218 auto vector = Vector4(10, 20, 30, 2);
219
220 Vector4 result = matrix * vector;
221 auto expected = Vector4(160, 220, 260, 2);
222 ASSERT_VECTOR4_NEAR(result, expected);
223 }
224
225 {
226 auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
227 Matrix::MakeRotationZ(Radians{kPiOver2}) *
228 Matrix::MakeScale({2.0, 2.0, 2.0});
229 auto vector = Vector3(10, 20, 30);
230
231 Vector3 result = matrix * vector;
232 auto expected = Vector3(60, 120, 160);
233 ASSERT_VECTOR3_NEAR(result, expected);
234 }
235
236 {
237 auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
238 Matrix::MakeRotationZ(Radians{kPiOver2}) *
239 Matrix::MakeScale({2.0, 2.0, 2.0});
240 auto vector = Point(10, 20);
241
242 Point result = matrix * vector;
243 auto expected = Point(60, 120);
244 ASSERT_POINT_NEAR(result, expected);
245 }
246
247 // Matrix Vector ops should respect perspective transforms.
248 {
249 auto matrix = Matrix::MakePerspective(Radians(kPiOver2), 1, 1, 100);
250 auto vector = Vector3(3, 3, -3);
251
252 Vector3 result = matrix * vector;
253 auto expected = Vector3(-1, -1, 1.3468);
254 ASSERT_VECTOR3_NEAR(result, expected);
255 }
256
257 {
258 auto matrix = Matrix::MakePerspective(Radians(kPiOver2), 1, 1, 100) *
259 Matrix::MakeTranslation(Vector3(0, 0, -3));
260 auto point = Point(3, 3);
261
262 Point result = matrix * point;
263 auto expected = Point(-1, -1);
264 ASSERT_POINT_NEAR(result, expected);
265 }
266
267 // Resolves to 0 on perspective singularity.
268 {
269 auto matrix = Matrix::MakePerspective(Radians(kPiOver2), 1, 1, 100);
270 auto point = Point(3, 3);
271
272 Point result = matrix * point;
273 auto expected = Point(0, 0);
274 ASSERT_POINT_NEAR(result, expected);
275 }
276 }
277
278 TEST(GeometryTest, MatrixMakeRotationFromQuaternion) {
279 {
280 auto matrix = Matrix::MakeRotation(Quaternion({1, 0, 0}, kPiOver2));
281 auto expected = Matrix::MakeRotationX(Radians(kPiOver2));
282 ASSERT_MATRIX_NEAR(matrix, expected);
283 }
284
285 {
286 auto matrix = Matrix::MakeRotation(Quaternion({0, 1, 0}, kPiOver2));
287 auto expected = Matrix::MakeRotationY(Radians(kPiOver2));
288 ASSERT_MATRIX_NEAR(matrix, expected);
289 }
290
291 {
292 auto matrix = Matrix::MakeRotation(Quaternion({0, 0, 1}, kPiOver2));
293 auto expected = Matrix::MakeRotationZ(Radians(kPiOver2));
294 ASSERT_MATRIX_NEAR(matrix, expected);
295 }
296 }
297
298 TEST(GeometryTest, MatrixTransformDirection) {
299 {
300 auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
301 Matrix::MakeRotationZ(Radians{kPiOver2}) *
302 Matrix::MakeScale({2.0, 2.0, 2.0});
303 auto vector = Vector4(10, 20, 30, 2);
304
305 Vector4 result = matrix.TransformDirection(vector);
306 auto expected = Vector4(-40, 20, 60, 2);
307 ASSERT_VECTOR4_NEAR(result, expected);
308 }
309
310 {
311 auto matrix = Matrix::MakeTranslation({100, 100, 100}) *
312 Matrix::MakeRotationZ(Radians{kPiOver2}) *
313 Matrix::MakeScale({2.0, 2.0, 2.0});
314 auto vector = Vector3(10, 20, 30);
315
316 Vector3 result = matrix.TransformDirection(vector);
317 auto expected = Vector3(-40, 20, 60);
318 ASSERT_VECTOR3_NEAR(result, expected);
319 }
320
321 {
322 auto matrix = Matrix::MakeTranslation({0, -0.4, 100}) *
323 Matrix::MakeRotationZ(Radians{kPiOver2}) *
324 Matrix::MakeScale({2.0, 2.0, 2.0});
325 auto vector = Point(10, 20);
326
327 Point result = matrix.TransformDirection(vector);
328 auto expected = Point(-40, 20);
329 ASSERT_POINT_NEAR(result, expected);
330 }
331 }
332
333 TEST(GeometryTest, MatrixGetMaxBasisLength) {
334 {
335 auto m = Matrix::MakeScale({3, 1, 1});
336 ASSERT_EQ(m.GetMaxBasisLength(), 3);
337
338 m = m * Matrix::MakeSkew(0, 4);
339 ASSERT_EQ(m.GetMaxBasisLength(), 5);
340 }
341
342 {
343 auto m = Matrix::MakeScale({-3, 4, 2});
344 ASSERT_EQ(m.GetMaxBasisLength(), 4);
345 }
346 }
347
348 TEST(GeometryTest, MatrixGetMaxBasisLengthXY) {
349 {
350 auto m = Matrix::MakeScale({3, 1, 1});
351 ASSERT_EQ(m.GetMaxBasisLengthXY(), 3);
352
353 m = m * Matrix::MakeSkew(0, 4);
354 ASSERT_EQ(m.GetMaxBasisLengthXY(), 5);
355 }
356
357 {
358 auto m = Matrix::MakeScale({-3, 4, 7});
359 ASSERT_EQ(m.GetMaxBasisLengthXY(), 4);
360 }
361
362 {
363 // clang-format off
364 auto m = Matrix::MakeColumn(
365 1.0f, 0.0f, 0.0f, 0.0f,
366 0.0f, 1.0f, 0.0f, 0.0f,
367 4.0f, 0.0f, 1.0f, 0.0f,
368 0.0f, 0.0f, 0.0f, 1.0f
369 );
370 // clang-format on
371 ASSERT_EQ(m.GetMaxBasisLengthXY(), 1.0f);
372 }
373 }
374
375 TEST(GeometryTest, MatrixMakeOrthographic) {
376 {
377 auto m = Matrix::MakeOrthographic(Size(100, 200));
378 auto expect = Matrix{
379 0.02, 0, 0, 0, //
380 0, -0.01, 0, 0, //
381 0, 0, 0, 0, //
382 -1, 1, 0.5, 1, //
383 };
384 ASSERT_MATRIX_NEAR(m, expect);
385 }
386
387 {
388 auto m = Matrix::MakeOrthographic(Size(400, 100));
389 auto expect = Matrix{
390 0.005, 0, 0, 0, //
391 0, -0.02, 0, 0, //
392 0, 0, 0, 0, //
393 -1, 1, 0.5, 1, //
394 };
395 ASSERT_MATRIX_NEAR(m, expect);
396 }
397 }
398
399 TEST(GeometryTest, MatrixMakePerspective) {
400 {
401 auto m = Matrix::MakePerspective(Degrees(60), Size(100, 200), 1, 10);
402 auto expect = Matrix{
403 3.4641, 0, 0, 0, //
404 0, 1.73205, 0, 0, //
405 0, 0, 1.11111, 1, //
406 0, 0, -1.11111, 0, //
407 };
408 ASSERT_MATRIX_NEAR(m, expect);
409 }
410
411 {
412 auto m = Matrix::MakePerspective(Radians(1), 2, 10, 20);
413 auto expect = Matrix{
414 0.915244, 0, 0, 0, //
415 0, 1.83049, 0, 0, //
416 0, 0, 2, 1, //
417 0, 0, -20, 0, //
418 };
419 ASSERT_MATRIX_NEAR(m, expect);
420 }
421 }
422
423 TEST(GeometryTest, MatrixGetBasisVectors) {
424 {
425 auto m = Matrix();
426 Vector3 x = m.GetBasisX();
427 Vector3 y = m.GetBasisY();
428 Vector3 z = m.GetBasisZ();
429 ASSERT_VECTOR3_NEAR(x, Vector3(1, 0, 0));
430 ASSERT_VECTOR3_NEAR(y, Vector3(0, 1, 0));
431 ASSERT_VECTOR3_NEAR(z, Vector3(0, 0, 1));
432 }
433
434 {
435 auto m = Matrix::MakeRotationZ(Radians{kPiOver2}) *
436 Matrix::MakeRotationX(Radians{kPiOver2}) *
437 Matrix::MakeScale(Vector3(2, 3, 4));
438 Vector3 x = m.GetBasisX();
439 Vector3 y = m.GetBasisY();
440 Vector3 z = m.GetBasisZ();
441 ASSERT_VECTOR3_NEAR(x, Vector3(0, 2, 0));
442 ASSERT_VECTOR3_NEAR(y, Vector3(0, 0, 3));
443 ASSERT_VECTOR3_NEAR(z, Vector3(4, 0, 0));
444 }
445 }
446
447 TEST(GeometryTest, MatrixGetDirectionScale) {
448 {
449 auto m = Matrix();
450 Scalar result = m.GetDirectionScale(Vector3{1, 0, 0});
451 ASSERT_FLOAT_EQ(result, 1);
452 }
453
454 {
455 auto m = Matrix::MakeRotationX(Degrees{10}) *
456 Matrix::MakeRotationY(Degrees{83}) *
457 Matrix::MakeRotationZ(Degrees{172});
458 Scalar result = m.GetDirectionScale(Vector3{0, 1, 0});
459 ASSERT_FLOAT_EQ(result, 1);
460 }
461
462 {
463 auto m = Matrix::MakeRotationZ(Radians{kPiOver2}) *
464 Matrix::MakeScale(Vector3(3, 4, 5));
465 Scalar result = m.GetDirectionScale(Vector3{2, 0, 0});
466 ASSERT_FLOAT_EQ(result, 8);
467 }
468 }
469
470 TEST(GeometryTest, MatrixTranslationScaleOnly) {
471 {
472 auto m = Matrix();
473 bool result = m.IsTranslationScaleOnly();
474 ASSERT_TRUE(result);
475 }
476
477 {
478 auto m = Matrix::MakeScale(Vector3(2, 3, 4));
479 bool result = m.IsTranslationScaleOnly();
480 ASSERT_TRUE(result);
481 }
482
483 {
484 auto m = Matrix::MakeTranslation(Vector3(2, 3, 4));
485 bool result = m.IsTranslationScaleOnly();
486 ASSERT_TRUE(result);
487 }
488
489 {
490 auto m = Matrix::MakeRotationZ(Degrees(10));
491 bool result = m.IsTranslationScaleOnly();
492 ASSERT_FALSE(result);
493 }
494 }
495
496 TEST(GeometryTest, MatrixLookAt) {
497 {
498 auto m = Matrix::MakeLookAt(Vector3(0, 0, -1), Vector3(0, 0, 1),
499 Vector3(0, 1, 0));
500 auto expected = Matrix{
501 1, 0, 0, 0, //
502 0, 1, 0, 0, //
503 0, 0, 1, 0, //
504 0, 0, 1, 1, //
505 };
506 ASSERT_MATRIX_NEAR(m, expected);
507 }
508
509 // Sideways tilt.
510 {
511 auto m = Matrix::MakeLookAt(Vector3(0, 0, -1), Vector3(0, 0, 1),
512 Vector3(1, 1, 0).Normalize());
513
514 // clang-format off
515 auto expected = Matrix{
516 k1OverSqrt2, k1OverSqrt2, 0, 0,
517 -k1OverSqrt2, k1OverSqrt2, 0, 0,
518 0, 0, 1, 0,
519 0, 0, 1, 1,
520 };
521 // clang-format on
522 ASSERT_MATRIX_NEAR(m, expected);
523 }
524
525 // Half way between +x and -y, yaw 90
526 {
527 auto m =
528 Matrix::MakeLookAt(Vector3(), Vector3(10, -10, 0), Vector3(0, 0, -1));
529
530 // clang-format off
531 auto expected = Matrix{
532 -k1OverSqrt2, 0, k1OverSqrt2, 0,
533 -k1OverSqrt2, 0, -k1OverSqrt2, 0,
534 0, -1, 0, 0,
535 0, 0, 0, 1,
536 };
537 // clang-format on
538 ASSERT_MATRIX_NEAR(m, expected);
539 }
540 }
541
542 TEST(GeometryTest, QuaternionLerp) {
543 auto q1 = Quaternion{{0.0, 0.0, 1.0}, 0.0};
544 auto q2 = Quaternion{{0.0, 0.0, 1.0}, kPiOver4};
545
546 auto q3 = q1.Slerp(q2, 0.5);
547
548 auto expected = Quaternion{{0.0, 0.0, 1.0}, kPiOver4 / 2.0};
549
550 ASSERT_QUATERNION_NEAR(q3, expected);
551 }
552
553 TEST(GeometryTest, QuaternionVectorMultiply) {
554 {
555 Quaternion q({0, 0, 1}, 0);
556 Vector3 v(0, 1, 0);
557
558 Vector3 result = q * v;
559 Vector3 expected(0, 1, 0);
560
561 ASSERT_VECTOR3_NEAR(result, expected);
562 }
563
564 {
565 Quaternion q({0, 0, 1}, k2Pi);
566 Vector3 v(1, 0, 0);
567
568 Vector3 result = q * v;
569 Vector3 expected(1, 0, 0);
570
571 ASSERT_VECTOR3_NEAR(result, expected);
572 }
573
574 {
575 Quaternion q({0, 0, 1}, kPiOver4);
576 Vector3 v(0, 1, 0);
577
578 Vector3 result = q * v;
579 Vector3 expected(-k1OverSqrt2, k1OverSqrt2, 0);
580
581 ASSERT_VECTOR3_NEAR(result, expected);
582 }
583
584 {
585 Quaternion q(Vector3(1, 0, 1).Normalize(), kPi);
586 Vector3 v(0, 0, -1);
587
588 Vector3 result = q * v;
589 Vector3 expected(-1, 0, 0);
590
591 ASSERT_VECTOR3_NEAR(result, expected);
592 }
593 }
594
595 TEST(GeometryTest, CanGenerateMipCounts) {
596 ASSERT_EQ((Size{128, 128}.MipCount()), 7u);
597 ASSERT_EQ((Size{128, 256}.MipCount()), 8u);
598 ASSERT_EQ((Size{128, 130}.MipCount()), 8u);
599 ASSERT_EQ((Size{128, 257}.MipCount()), 9u);
600 ASSERT_EQ((Size{257, 128}.MipCount()), 9u);
601 ASSERT_EQ((Size{128, 0}.MipCount()), 1u);
602 ASSERT_EQ((Size{128, -25}.MipCount()), 1u);
603 ASSERT_EQ((Size{-128, 25}.MipCount()), 1u);
604 ASSERT_EQ((Size{1, 1}.MipCount()), 1u);
605 ASSERT_EQ((Size{0, 0}.MipCount()), 1u);
606 }
607
608 TEST(GeometryTest, CanConvertTTypesExplicitly) {
609 {
610 Point p1(1.0, 2.0);
611 IPoint p2 = static_cast<IPoint>(p1);
612 ASSERT_EQ(p2.x, 1u);
613 ASSERT_EQ(p2.y, 2u);
614 }
615
616 {
617 Size s1(1.0, 2.0);
618 ISize s2 = static_cast<ISize>(s1);
619 ASSERT_EQ(s2.width, 1u);
620 ASSERT_EQ(s2.height, 2u);
621 }
622
623 {
624 Size s1(1.0, 2.0);
625 Point p1 = static_cast<Point>(s1);
626 ASSERT_EQ(p1.x, 1u);
627 ASSERT_EQ(p1.y, 2u);
628 }
629 }
630
631 TEST(GeometryTest, CanPerformAlgebraicPointOps) {
632 {
633 IPoint p1(1, 2);
634 IPoint p2 = p1 + IPoint(1, 2);
635 ASSERT_EQ(p2.x, 2u);
636 ASSERT_EQ(p2.y, 4u);
637 }
638
639 {
640 IPoint p1(3, 6);
641 IPoint p2 = p1 - IPoint(1, 2);
642 ASSERT_EQ(p2.x, 2u);
643 ASSERT_EQ(p2.y, 4u);
644 }
645
646 {
647 IPoint p1(1, 2);
648 IPoint p2 = p1 * IPoint(2, 3);
649 ASSERT_EQ(p2.x, 2u);
650 ASSERT_EQ(p2.y, 6u);
651 }
652
653 {
654 IPoint p1(2, 6);
655 IPoint p2 = p1 / IPoint(2, 3);
656 ASSERT_EQ(p2.x, 1u);
657 ASSERT_EQ(p2.y, 2u);
658 }
659 }
660
661 TEST(GeometryTest, CanPerformAlgebraicPointOpsWithArithmeticTypes) {
662 // LHS
663 {
664 IPoint p1(1, 2);
665 IPoint p2 = p1 * 2.0f;
666 ASSERT_EQ(p2.x, 2u);
667 ASSERT_EQ(p2.y, 4u);
668 }
669
670 {
671 IPoint p1(2, 6);
672 IPoint p2 = p1 / 2.0f;
673 ASSERT_EQ(p2.x, 1u);
674 ASSERT_EQ(p2.y, 3u);
675 }
676
677 // RHS
678 {
679 IPoint p1(1, 2);
680 IPoint p2 = 2.0f * p1;
681 ASSERT_EQ(p2.x, 2u);
682 ASSERT_EQ(p2.y, 4u);
683 }
684
685 {
686 IPoint p1(2, 6);
687 IPoint p2 = 12.0f / p1;
688 ASSERT_EQ(p2.x, 6u);
689 ASSERT_EQ(p2.y, 2u);
690 }
691 }
692
693 TEST(GeometryTest, PointIntegerCoercesToFloat) {
694 // Integer on LHS, float on RHS
695 {
696 IPoint p1(1, 2);
697 Point p2 = p1 + Point(1, 2);
698 ASSERT_FLOAT_EQ(p2.x, 2u);
699 ASSERT_FLOAT_EQ(p2.y, 4u);
700 }
701
702 {
703 IPoint p1(3, 6);
704 Point p2 = p1 - Point(1, 2);
705 ASSERT_FLOAT_EQ(p2.x, 2u);
706 ASSERT_FLOAT_EQ(p2.y, 4u);
707 }
708
709 {
710 IPoint p1(1, 2);
711 Point p2 = p1 * Point(2, 3);
712 ASSERT_FLOAT_EQ(p2.x, 2u);
713 ASSERT_FLOAT_EQ(p2.y, 6u);
714 }
715
716 {
717 IPoint p1(2, 6);
718 Point p2 = p1 / Point(2, 3);
719 ASSERT_FLOAT_EQ(p2.x, 1u);
720 ASSERT_FLOAT_EQ(p2.y, 2u);
721 }
722
723 // Float on LHS, integer on RHS
724 {
725 Point p1(1, 2);
726 Point p2 = p1 + IPoint(1, 2);
727 ASSERT_FLOAT_EQ(p2.x, 2u);
728 ASSERT_FLOAT_EQ(p2.y, 4u);
729 }
730
731 {
732 Point p1(3, 6);
733 Point p2 = p1 - IPoint(1, 2);
734 ASSERT_FLOAT_EQ(p2.x, 2u);
735 ASSERT_FLOAT_EQ(p2.y, 4u);
736 }
737
738 {
739 Point p1(1, 2);
740 Point p2 = p1 * IPoint(2, 3);
741 ASSERT_FLOAT_EQ(p2.x, 2u);
742 ASSERT_FLOAT_EQ(p2.y, 6u);
743 }
744
745 {
746 Point p1(2, 6);
747 Point p2 = p1 / IPoint(2, 3);
748 ASSERT_FLOAT_EQ(p2.x, 1u);
749 ASSERT_FLOAT_EQ(p2.y, 2u);
750 }
751 }
752
753 TEST(GeometryTest, SizeCoercesToPoint) {
754 // Point on LHS, Size on RHS
755 {
756 IPoint p1(1, 2);
757 IPoint p2 = p1 + ISize(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 - ISize(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 * ISize(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 / ISize(2, 3);
779 ASSERT_EQ(p2.x, 1u);
780 ASSERT_EQ(p2.y, 2u);
781 }
782
783 // Size on LHS, Point on RHS
784 {
785 ISize p1(1, 2);
786 IPoint p2 = p1 + IPoint(1, 2);
787 ASSERT_EQ(p2.x, 2u);
788 ASSERT_EQ(p2.y, 4u);
789 }
790
791 {
792 ISize p1(3, 6);
793 IPoint p2 = p1 - IPoint(1, 2);
794 ASSERT_EQ(p2.x, 2u);
795 ASSERT_EQ(p2.y, 4u);
796 }
797
798 {
799 ISize p1(1, 2);
800 IPoint p2 = p1 * IPoint(2, 3);
801 ASSERT_EQ(p2.x, 2u);
802 ASSERT_EQ(p2.y, 6u);
803 }
804
805 {
806 ISize p1(2, 6);
807 IPoint p2 = p1 / IPoint(2, 3);
808 ASSERT_EQ(p2.x, 1u);
809 ASSERT_EQ(p2.y, 2u);
810 }
811 }
812
813 TEST(GeometryTest, CanUsePointAssignmentOperators) {
814 // Point on RHS
815 {
816 IPoint p(1, 2);
817 p += IPoint(1, 2);
818 ASSERT_EQ(p.x, 2u);
819 ASSERT_EQ(p.y, 4u);
820 }
821
822 {
823 IPoint p(3, 6);
824 p -= IPoint(1, 2);
825 ASSERT_EQ(p.x, 2u);
826 ASSERT_EQ(p.y, 4u);
827 }
828
829 {
830 IPoint p(1, 2);
831 p *= IPoint(2, 3);
832 ASSERT_EQ(p.x, 2u);
833 ASSERT_EQ(p.y, 6u);
834 }
835
836 {
837 IPoint p(2, 6);
838 p /= IPoint(2, 3);
839 ASSERT_EQ(p.x, 1u);
840 ASSERT_EQ(p.y, 2u);
841 }
842
843 // Size on RHS
844 {
845 IPoint p(1, 2);
846 p += ISize(1, 2);
847 ASSERT_EQ(p.x, 2u);
848 ASSERT_EQ(p.y, 4u);
849 }
850
851 {
852 IPoint p(3, 6);
853 p -= ISize(1, 2);
854 ASSERT_EQ(p.x, 2u);
855 ASSERT_EQ(p.y, 4u);
856 }
857
858 {
859 IPoint p(1, 2);
860 p *= ISize(2, 3);
861 ASSERT_EQ(p.x, 2u);
862 ASSERT_EQ(p.y, 6u);
863 }
864
865 {
866 IPoint p(2, 6);
867 p /= ISize(2, 3);
868 ASSERT_EQ(p.x, 1u);
869 ASSERT_EQ(p.y, 2u);
870 }
871
872 // Arithmetic type on RHS
873 {
874 IPoint p(1, 2);
875 p *= 3;
876 ASSERT_EQ(p.x, 3u);
877 ASSERT_EQ(p.y, 6u);
878 }
879
880 {
881 IPoint p(3, 6);
882 p /= 3;
883 ASSERT_EQ(p.x, 1u);
884 ASSERT_EQ(p.y, 2u);
885 }
886 }
887
888 TEST(GeometryTest, PointDotProduct) {
889 {
890 Point p(1, 0);
891 Scalar s = p.Dot(Point(-1, 0));
892 ASSERT_FLOAT_EQ(s, -1);
893 }
894
895 {
896 Point p(0, -1);
897 Scalar s = p.Dot(Point(-1, 0));
898 ASSERT_FLOAT_EQ(s, 0);
899 }
900
901 {
902 Point p(1, 2);
903 Scalar s = p.Dot(Point(3, -4));
904 ASSERT_FLOAT_EQ(s, -5);
905 }
906 }
907
908 TEST(GeometryTest, PointCrossProduct) {
909 {
910 Point p(1, 0);
911 Scalar s = p.Cross(Point(-1, 0));
912 ASSERT_FLOAT_EQ(s, 0);
913 }
914
915 {
916 Point p(0, -1);
917 Scalar s = p.Cross(Point(-1, 0));
918 ASSERT_FLOAT_EQ(s, -1);
919 }
920
921 {
922 Point p(1, 2);
923 Scalar s = p.Cross(Point(3, -4));
924 ASSERT_FLOAT_EQ(s, -10);
925 }
926 }
927
928 TEST(GeometryTest, PointReflect) {
929 {
930 Point axis = Point(0, 1);
931 Point a(2, 3);
932 auto reflected = a.Reflect(axis);
933 auto expected = Point(2, -3);
934 ASSERT_POINT_NEAR(reflected, expected);
935 }
936
937 {
938 Point axis = Point(1, 1).Normalize();
939 Point a(1, 0);
940 auto reflected = a.Reflect(axis);
941 auto expected = Point(0, -1);
942 ASSERT_POINT_NEAR(reflected, expected);
943 }
944
945 {
946 Point axis = Point(1, 1).Normalize();
947 Point a(-1, -1);
948 auto reflected = a.Reflect(axis);
949 ASSERT_POINT_NEAR(reflected, -a);
950 }
951 }
952
953 TEST(GeometryTest, PointAbs) {
954 Point a(-1, -2);
955 auto a_abs = a.Abs();
956 auto expected = Point(1, 2);
957 ASSERT_POINT_NEAR(a_abs, expected);
958 }
959
960 TEST(GeometryTest, PointRotate) {
961 {
962 Point a(1, 0);
963 auto rotated = a.Rotate(Radians{kPiOver2});
964 auto expected = Point(0, 1);
965 ASSERT_POINT_NEAR(rotated, expected);
966 }
967
968 {
969 Point a(1, 0);
970 auto rotated = a.Rotate(Radians{-kPiOver2});
971 auto expected = Point(0, -1);
972 ASSERT_POINT_NEAR(rotated, expected);
973 }
974
975 {
976 Point a(1, 0);
977 auto rotated = a.Rotate(Radians{kPi});
978 auto expected = Point(-1, 0);
979 ASSERT_POINT_NEAR(rotated, expected);
980 }
981
982 {
983 Point a(1, 0);
984 auto rotated = a.Rotate(Radians{kPi * 1.5});
985 auto expected = Point(0, -1);
986 ASSERT_POINT_NEAR(rotated, expected);
987 }
988 }
989
990 TEST(GeometryTest, PointAngleTo) {
991 // Negative result in the CCW (with up = -Y) direction.
992 {
993 Point a(1, 1);
994 Point b(1, -1);
995 Radians actual = a.AngleTo(b);
996 Radians expected = Radians{-kPi / 2};
997 ASSERT_FLOAT_EQ(actual.radians, expected.radians);
998 }
999
1000 // Check the other direction to ensure the result is signed correctly.
1001 {
1002 Point a(1, -1);
1003 Point b(1, 1);
1004 Radians actual = a.AngleTo(b);
1005 Radians expected = Radians{kPi / 2};
1006 ASSERT_FLOAT_EQ(actual.radians, expected.radians);
1007 }
1008
1009 // Differences in magnitude should have no impact on the result.
1010 {
1011 Point a(100, -100);
1012 Point b(0.01, 0.01);
1013 Radians actual = a.AngleTo(b);
1014 Radians expected = Radians{kPi / 2};
1015 ASSERT_FLOAT_EQ(actual.radians, expected.radians);
1016 }
1017 }
1018
1019 TEST(GeometryTest, PointMin) {
1020 Point p(1, 2);
1021 Point result = p.Min({0, 10});
1022 Point expected(0, 2);
1023 ASSERT_POINT_NEAR(result, expected);
1024 }
1025
1026 TEST(GeometryTest, Vector3Min) {
1027 Vector3 p(1, 2, 3);
1028 Vector3 result = p.Min({0, 10, 2});
1029 Vector3 expected(0, 2, 2);
1030 ASSERT_VECTOR3_NEAR(result, expected);
1031 }
1032
1033 TEST(GeometryTest, Vector4Min) {
1034 Vector4 p(1, 2, 3, 4);
1035 Vector4 result = p.Min({0, 10, 2, 1});
1036 Vector4 expected(0, 2, 2, 1);
1037 ASSERT_VECTOR4_NEAR(result, expected);
1038 }
1039
1040 TEST(GeometryTest, PointMax) {
1041 Point p(1, 2);
1042 Point result = p.Max({0, 10});
1043 Point expected(1, 10);
1044 ASSERT_POINT_NEAR(result, expected);
1045 }
1046
1047 TEST(GeometryTest, Vector3Max) {
1048 Vector3 p(1, 2, 3);
1049 Vector3 result = p.Max({0, 10, 2});
1050 Vector3 expected(1, 10, 3);
1051 ASSERT_VECTOR3_NEAR(result, expected);
1052 }
1053
1054 TEST(GeometryTest, Vector4Max) {
1055 Vector4 p(1, 2, 3, 4);
1056 Vector4 result = p.Max({0, 10, 2, 1});
1057 Vector4 expected(1, 10, 3, 4);
1058 ASSERT_VECTOR4_NEAR(result, expected);
1059 }
1060
1061 TEST(GeometryTest, PointFloor) {
1062 Point p(1.5, 2.3);
1063 Point result = p.Floor();
1064 Point expected(1, 2);
1065 ASSERT_POINT_NEAR(result, expected);
1066 }
1067
1068 TEST(GeometryTest, Vector3Floor) {
1069 Vector3 p(1.5, 2.3, 3.9);
1070 Vector3 result = p.Floor();
1071 Vector3 expected(1, 2, 3);
1072 ASSERT_VECTOR3_NEAR(result, expected);
1073 }
1074
1075 TEST(GeometryTest, Vector4Floor) {
1076 Vector4 p(1.5, 2.3, 3.9, 4.0);
1077 Vector4 result = p.Floor();
1078 Vector4 expected(1, 2, 3, 4);
1079 ASSERT_VECTOR4_NEAR(result, expected);
1080 }
1081
1082 TEST(GeometryTest, PointCeil) {
1083 Point p(1.5, 2.3);
1084 Point result = p.Ceil();
1085 Point expected(2, 3);
1086 ASSERT_POINT_NEAR(result, expected);
1087 }
1088
1089 TEST(GeometryTest, Vector3Ceil) {
1090 Vector3 p(1.5, 2.3, 3.9);
1091 Vector3 result = p.Ceil();
1092 Vector3 expected(2, 3, 4);
1093 ASSERT_VECTOR3_NEAR(result, expected);
1094 }
1095
1096 TEST(GeometryTest, Vector4Ceil) {
1097 Vector4 p(1.5, 2.3, 3.9, 4.0);
1098 Vector4 result = p.Ceil();
1099 Vector4 expected(2, 3, 4, 4);
1100 ASSERT_VECTOR4_NEAR(result, expected);
1101 }
1102
1103 TEST(GeometryTest, PointRound) {
1104 Point p(1.5, 2.3);
1105 Point result = p.Round();
1106 Point expected(2, 2);
1107 ASSERT_POINT_NEAR(result, expected);
1108 }
1109
1110 TEST(GeometryTest, Vector3Round) {
1111 Vector3 p(1.5, 2.3, 3.9);
1112 Vector3 result = p.Round();
1113 Vector3 expected(2, 2, 4);
1114 ASSERT_VECTOR3_NEAR(result, expected);
1115 }
1116
1117 TEST(GeometryTest, Vector4Round) {
1118 Vector4 p(1.5, 2.3, 3.9, 4.0);
1119 Vector4 result = p.Round();
1120 Vector4 expected(2, 2, 4, 4);
1121 ASSERT_VECTOR4_NEAR(result, expected);
1122 }
1123
1124 TEST(GeometryTest, PointLerp) {
1125 Point p(1, 2);
1126 Point result = p.Lerp({5, 10}, 0.75);
1127 Point expected(4, 8);
1128 ASSERT_POINT_NEAR(result, expected);
1129 }
1130
1131 TEST(GeometryTest, Vector3Lerp) {
1132 Vector3 p(1, 2, 3);
1133 Vector3 result = p.Lerp({5, 10, 15}, 0.75);
1134 Vector3 expected(4, 8, 12);
1135 ASSERT_VECTOR3_NEAR(result, expected);
1136 }
1137
1138 TEST(GeometryTest, Vector4Lerp) {
1139 Vector4 p(1, 2, 3, 4);
1140 Vector4 result = p.Lerp({5, 10, 15, 20}, 0.75);
1141 Vector4 expected(4, 8, 12, 16);
1142 ASSERT_VECTOR4_NEAR(result, expected);
1143 }
1144
1145 TEST(GeometryTest, SeparatedVector2NormalizesWithConstructor) {
1146 SeparatedVector2 v(Vector2(10, 0));
1147 ASSERT_POINT_NEAR(v.direction, Vector2(1, 0));
1148 ASSERT_NEAR(v.magnitude, 10, kEhCloseEnough);
1149 }
1150
1151 TEST(GeometryTest, SeparatedVector2GetVector) {
1152 SeparatedVector2 v(Vector2(10, 0));
1153 ASSERT_POINT_NEAR(v.GetVector(), Vector2(10, 0));
1154 }
1155
1156 TEST(GeometryTest, SeparatedVector2GetAlignment) {
1157 // Parallel
1158 {
1159 SeparatedVector2 v(Vector2(10, 0));
1160 Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(5, 0)));
1161 ASSERT_NEAR(actual, 1, kEhCloseEnough);
1162 }
1163
1164 // Perpendicular
1165 {
1166 SeparatedVector2 v(Vector2(10, 0));
1167 Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(0, 5)));
1168 ASSERT_NEAR(actual, 0, kEhCloseEnough);
1169 }
1170
1171 // Opposite parallel
1172 {
1173 SeparatedVector2 v(Vector2(0, 10));
1174 Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(0, -5)));
1175 ASSERT_NEAR(actual, -1, kEhCloseEnough);
1176 }
1177 }
1178
1179 TEST(GeometryTest, SeparatedVector2AngleTo) {
1180 {
1181 SeparatedVector2 v(Vector2(10, 0));
1182 Radians actual = v.AngleTo(SeparatedVector2(Vector2(5, 0)));
1183 Radians expected = Radians{0};
1184 ASSERT_NEAR(actual.radians, expected.radians, kEhCloseEnough);
1185 }
1186
1187 {
1188 SeparatedVector2 v(Vector2(10, 0));
1189 Radians actual = v.AngleTo(SeparatedVector2(Vector2(0, -5)));
1190 Radians expected = Radians{-kPi / 2};
1191 ASSERT_NEAR(actual.radians, expected.radians, kEhCloseEnough);
1192 }
1193 }
1194
1195 TEST(GeometryTest, CanUseVector3AssignmentOperators) {
1196 {
1197 Vector3 p(1, 2, 4);
1198 p += Vector3(1, 2, 4);
1199 ASSERT_EQ(p.x, 2u);
1200 ASSERT_EQ(p.y, 4u);
1201 ASSERT_EQ(p.z, 8u);
1202 }
1203
1204 {
1205 Vector3 p(3, 6, 8);
1206 p -= Vector3(1, 2, 3);
1207 ASSERT_EQ(p.x, 2u);
1208 ASSERT_EQ(p.y, 4u);
1209 ASSERT_EQ(p.z, 5u);
1210 }
1211
1212 {
1213 Vector3 p(1, 2, 3);
1214 p *= Vector3(2, 3, 4);
1215 ASSERT_EQ(p.x, 2u);
1216 ASSERT_EQ(p.y, 6u);
1217 ASSERT_EQ(p.z, 12u);
1218 }
1219
1220 {
1221 Vector3 p(1, 2, 3);
1222 p *= 2;
1223 ASSERT_EQ(p.x, 2u);
1224 ASSERT_EQ(p.y, 4u);
1225 ASSERT_EQ(p.z, 6u);
1226 }
1227
1228 {
1229 Vector3 p(2, 6, 12);
1230 p /= Vector3(2, 3, 4);
1231 ASSERT_EQ(p.x, 1u);
1232 ASSERT_EQ(p.y, 2u);
1233 ASSERT_EQ(p.z, 3u);
1234 }
1235
1236 {
1237 Vector3 p(2, 6, 12);
1238 p /= 2;
1239 ASSERT_EQ(p.x, 1u);
1240 ASSERT_EQ(p.y, 3u);
1241 ASSERT_EQ(p.z, 6u);
1242 }
1243 }
1244
1245 TEST(GeometryTest, CanPerformAlgebraicVector3Ops) {
1246 {
1247 Vector3 p1(1, 2, 3);
1248 Vector3 p2 = p1 + Vector3(1, 2, 3);
1249 ASSERT_EQ(p2.x, 2u);
1250 ASSERT_EQ(p2.y, 4u);
1251 ASSERT_EQ(p2.z, 6u);
1252 }
1253
1254 {
1255 Vector3 p1(3, 6, 9);
1256 Vector3 p2 = p1 - Vector3(1, 2, 3);
1257 ASSERT_EQ(p2.x, 2u);
1258 ASSERT_EQ(p2.y, 4u);
1259 ASSERT_EQ(p2.z, 6u);
1260 }
1261
1262 {
1263 Vector3 p1(1, 2, 3);
1264 Vector3 p2 = p1 * Vector3(2, 3, 4);
1265 ASSERT_EQ(p2.x, 2u);
1266 ASSERT_EQ(p2.y, 6u);
1267 ASSERT_EQ(p2.z, 12u);
1268 }
1269
1270 {
1271 Vector3 p1(2, 6, 12);
1272 Vector3 p2 = p1 / Vector3(2, 3, 4);
1273 ASSERT_EQ(p2.x, 1u);
1274 ASSERT_EQ(p2.y, 2u);
1275 ASSERT_EQ(p2.z, 3u);
1276 }
1277 }
1278
1279 TEST(GeometryTest, CanPerformAlgebraicVector3OpsWithArithmeticTypes) {
1280 // LHS
1281 {
1282 Vector3 p1(1, 2, 3);
1283 Vector3 p2 = p1 + 2.0f;
1284 ASSERT_EQ(p2.x, 3);
1285 ASSERT_EQ(p2.y, 4);
1286 ASSERT_EQ(p2.z, 5);
1287 }
1288
1289 {
1290 Vector3 p1(1, 2, 3);
1291 Vector3 p2 = p1 - 2.0f;
1292 ASSERT_EQ(p2.x, -1);
1293 ASSERT_EQ(p2.y, 0);
1294 ASSERT_EQ(p2.z, 1);
1295 }
1296
1297 {
1298 Vector3 p1(1, 2, 3);
1299 Vector3 p2 = p1 * 2.0f;
1300 ASSERT_EQ(p2.x, 2);
1301 ASSERT_EQ(p2.y, 4);
1302 ASSERT_EQ(p2.z, 6);
1303 }
1304
1305 {
1306 Vector3 p1(2, 6, 12);
1307 Vector3 p2 = p1 / 2.0f;
1308 ASSERT_EQ(p2.x, 1);
1309 ASSERT_EQ(p2.y, 3);
1310 ASSERT_EQ(p2.z, 6);
1311 }
1312
1313 // RHS
1314 {
1315 Vector3 p1(1, 2, 3);
1316 Vector3 p2 = 2.0f + p1;
1317 ASSERT_EQ(p2.x, 3);
1318 ASSERT_EQ(p2.y, 4);
1319 ASSERT_EQ(p2.z, 5);
1320 }
1321
1322 {
1323 Vector3 p1(1, 2, 3);
1324 Vector3 p2 = 2.0f - p1;
1325 ASSERT_EQ(p2.x, 1);
1326 ASSERT_EQ(p2.y, 0);
1327 ASSERT_EQ(p2.z, -1);
1328 }
1329
1330 {
1331 Vector3 p1(1, 2, 3);
1332 Vector3 p2 = 2.0f * p1;
1333 ASSERT_EQ(p2.x, 2);
1334 ASSERT_EQ(p2.y, 4);
1335 ASSERT_EQ(p2.z, 6);
1336 }
1337
1338 {
1339 Vector3 p1(2, 6, 12);
1340 Vector3 p2 = 12.0f / p1;
1341 ASSERT_EQ(p2.x, 6);
1342 ASSERT_EQ(p2.y, 2);
1343 ASSERT_EQ(p2.z, 1);
1344 }
1345 }
1346
1347 TEST(GeometryTest, ColorPremultiply) {
1348 {
1349 Color a(1.0, 0.5, 0.2, 0.5);
1350 Color premultiplied = a.Premultiply();
1351 Color expected = Color(0.5, 0.25, 0.1, 0.5);
1352 ASSERT_COLOR_NEAR(premultiplied, expected);
1353 }
1354
1355 {
1356 Color a(0.5, 0.25, 0.1, 0.5);
1357 Color unpremultiplied = a.Unpremultiply();
1358 Color expected = Color(1.0, 0.5, 0.2, 0.5);
1359 ASSERT_COLOR_NEAR(unpremultiplied, expected);
1360 }
1361
1362 {
1363 Color a(0.5, 0.25, 0.1, 0.0);
1364 Color unpremultiplied = a.Unpremultiply();
1365 Color expected = Color(0.0, 0.0, 0.0, 0.0);
1366 ASSERT_COLOR_NEAR(unpremultiplied, expected);
1367 }
1368 }
1369
1370 TEST(GeometryTest, ColorR8G8B8A8) {
1371 {
1372 Color a(1.0, 0.5, 0.2, 0.5);
1373 std::array<uint8_t, 4> expected = {255, 128, 51, 128};
1374 ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1375 }
1376
1377 {
1378 Color a(0.0, 0.0, 0.0, 0.0);
1379 std::array<uint8_t, 4> expected = {0, 0, 0, 0};
1380 ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1381 }
1382
1383 {
1384 Color a(1.0, 1.0, 1.0, 1.0);
1385 std::array<uint8_t, 4> expected = {255, 255, 255, 255};
1386 ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1387 }
1388 }
1389
1390 TEST(GeometryTest, ColorLerp) {
1391 {
1392 Color a(0.0, 0.0, 0.0, 0.0);
1393 Color b(1.0, 1.0, 1.0, 1.0);
1394
1395 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.5), Color(0.5, 0.5, 0.5, 0.5));
1396 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.0), a);
1397 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 1.0), b);
1398 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.2), Color(0.2, 0.2, 0.2, 0.2));
1399 }
1400
1401 {
1402 Color a(0.2, 0.4, 1.0, 0.5);
1403 Color b(0.4, 1.0, 0.2, 0.3);
1404
1405 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.5), Color(0.3, 0.7, 0.6, 0.4));
1406 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.0), a);
1407 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 1.0), b);
1408 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.2), Color(0.24, 0.52, 0.84, 0.46));
1409 }
1410 }
1411
1412 TEST(GeometryTest, ColorClamp01) {
1413 {
1414 Color result = Color(0.5, 0.5, 0.5, 0.5).Clamp01();
1415 Color expected = Color(0.5, 0.5, 0.5, 0.5);
1416 ASSERT_COLOR_NEAR(result, expected);
1417 }
1418
1419 {
1420 Color result = Color(-1, -1, -1, -1).Clamp01();
1421 Color expected = Color(0, 0, 0, 0);
1422 ASSERT_COLOR_NEAR(result, expected);
1423 }
1424
1425 {
1426 Color result = Color(2, 2, 2, 2).Clamp01();
1427 Color expected = Color(1, 1, 1, 1);
1428 ASSERT_COLOR_NEAR(result, expected);
1429 }
1430 }
1431
1432 TEST(GeometryTest, ColorMakeRGBA8) {
1433 {
1434 Color a = Color::MakeRGBA8(0, 0, 0, 0);
1435 Color b = Color::BlackTransparent();
1436 ASSERT_COLOR_NEAR(a, b);
1437 }
1438
1439 {
1440 Color a = Color::MakeRGBA8(255, 255, 255, 255);
1441 Color b = Color::White();
1442 ASSERT_COLOR_NEAR(a, b);
1443 }
1444
1445 {
1446 Color a = Color::MakeRGBA8(63, 127, 191, 127);
1447 Color b(0.247059, 0.498039, 0.74902, 0.498039);
1448 ASSERT_COLOR_NEAR(a, b);
1449 }
1450 }
1451
1452 TEST(GeometryTest, ColorApplyColorMatrix) {
1453 {
1454 ColorMatrix color_matrix = {
1455 1, 1, 1, 1, 1, //
1456 1, 1, 1, 1, 1, //
1457 1, 1, 1, 1, 1, //
1458 1, 1, 1, 1, 1, //
1459 };
1460 auto result = Color::White().ApplyColorMatrix(color_matrix);
1461 auto expected = Color(1, 1, 1, 1);
1462 ASSERT_COLOR_NEAR(result, expected);
1463 }
1464
1465 {
1466 ColorMatrix color_matrix = {
1467 0.1, 0, 0, 0, 0.01, //
1468 0, 0.2, 0, 0, 0.02, //
1469 0, 0, 0.3, 0, 0.03, //
1470 0, 0, 0, 0.4, 0.04, //
1471 };
1472 auto result = Color::White().ApplyColorMatrix(color_matrix);
1473 auto expected = Color(0.11, 0.22, 0.33, 0.44);
1474 ASSERT_COLOR_NEAR(result, expected);
1475 }
1476 }
1477
1478 TEST(GeometryTest, ColorLinearToSRGB) {
1479 {
1480 auto result = Color::White().LinearToSRGB();
1481 auto expected = Color(1, 1, 1, 1);
1482 ASSERT_COLOR_NEAR(result, expected);
1483 }
1484
1485 {
1486 auto result = Color::BlackTransparent().LinearToSRGB();
1487 auto expected = Color(0, 0, 0, 0);
1488 ASSERT_COLOR_NEAR(result, expected);
1489 }
1490
1491 {
1492 auto result = Color(0.2, 0.4, 0.6, 0.8).LinearToSRGB();
1493 auto expected = Color(0.484529, 0.665185, 0.797738, 0.8);
1494 ASSERT_COLOR_NEAR(result, expected);
1495 }
1496 }
1497
1498 TEST(GeometryTest, ColorSRGBToLinear) {
1499 {
1500 auto result = Color::White().SRGBToLinear();
1501 auto expected = Color(1, 1, 1, 1);
1502 ASSERT_COLOR_NEAR(result, expected);
1503 }
1504
1505 {
1506 auto result = Color::BlackTransparent().SRGBToLinear();
1507 auto expected = Color(0, 0, 0, 0);
1508 ASSERT_COLOR_NEAR(result, expected);
1509 }
1510
1511 {
1512 auto result = Color(0.2, 0.4, 0.6, 0.8).SRGBToLinear();
1513 auto expected = Color(0.0331048, 0.132868, 0.318547, 0.8);
1514 ASSERT_COLOR_NEAR(result, expected);
1515 }
1516 }
1517
1518 struct ColorBlendTestData {
1519 static constexpr Color kDestinationColor =
1520 Color::CornflowerBlue().WithAlpha(0.75);
1521 static constexpr Color kSourceColors[] = {Color::White().WithAlpha(0.75),
1522 Color::LimeGreen().WithAlpha(0.75),
1523 Color::Black().WithAlpha(0.75)};
1524
1525 static const std::map<BlendMode, Color>
1526 kExpectedResults[sizeof(kSourceColors)];
1527 };
1528
1529 // THIS RESULT TABLE IS GENERATED!
1530 //
1531 // Uncomment the `GenerateColorBlendResults` test below to print a new table
1532 // after making changes to `Color::Blend`.
1533 const std::map<BlendMode, Color> ColorBlendTestData::kExpectedResults[sizeof(
1534 ColorBlendTestData::kSourceColors)] = {
1535 {
1536 {BlendMode::kClear, {0, 0, 0, 0}},
1537 {BlendMode::kSource, {1, 1, 1, 0.75}},
1538 {BlendMode::kDestination, {0.392157, 0.584314, 0.929412, 0.75}},
1539 {BlendMode::kSourceOver, {0.878431, 0.916863, 0.985882, 0.9375}},
1540 {BlendMode::kDestinationOver, {0.513726, 0.667451, 0.943529, 0.9375}},
1541 {BlendMode::kSourceIn, {1, 1, 1, 0.5625}},
1542 {BlendMode::kDestinationIn, {0.392157, 0.584314, 0.929412, 0.5625}},
1543 {BlendMode::kSourceOut, {1, 1, 1, 0.1875}},
1544 {BlendMode::kDestinationOut, {0.392157, 0.584314, 0.929412, 0.1875}},
1545 {BlendMode::kSourceATop, {0.848039, 0.896078, 0.982353, 0.75}},
1546 {BlendMode::kDestinationATop, {0.544118, 0.688235, 0.947059, 0.75}},
1547 {BlendMode::kXor, {0.696078, 0.792157, 0.964706, 0.375}},
1548 {BlendMode::kPlus, {1, 1, 1, 1}},
1549 {BlendMode::kModulate, {0.392157, 0.584314, 0.929412, 0.5625}},
1550 {BlendMode::kScreen, {0.878431, 0.916863, 0.985882, 0.9375}},
1551 {BlendMode::kOverlay, {0.74902, 0.916863, 0.985882, 0.9375}},
1552 {BlendMode::kDarken, {0.513726, 0.667451, 0.943529, 0.9375}},
1553 {BlendMode::kLighten, {0.878431, 0.916863, 0.985882, 0.9375}},
1554 {BlendMode::kColorDodge, {0.878431, 0.916863, 0.985882, 0.9375}},
1555 {BlendMode::kColorBurn, {0.513725, 0.667451, 0.943529, 0.9375}},
1556 {BlendMode::kHardLight, {0.878431, 0.916863, 0.985882, 0.9375}},
1557 {BlendMode::kSoftLight, {0.654166, 0.775505, 0.964318, 0.9375}},
1558 {BlendMode::kDifference, {0.643137, 0.566275, 0.428235, 0.9375}},
1559 {BlendMode::kExclusion, {0.643137, 0.566275, 0.428235, 0.9375}},
1560 {BlendMode::kMultiply, {0.513726, 0.667451, 0.943529, 0.9375}},
1561 {BlendMode::kHue, {0.617208, 0.655639, 0.724659, 0.9375}},
1562 {BlendMode::kSaturation, {0.617208, 0.655639, 0.724659, 0.9375}},
1563 {BlendMode::kColor, {0.617208, 0.655639, 0.724659, 0.9375}},
1564 {BlendMode::kLuminosity, {0.878431, 0.916863, 0.985882, 0.9375}},
1565 },
1566 {
1567 {BlendMode::kClear, {0, 0, 0, 0}},
1568 {BlendMode::kSource, {0.196078, 0.803922, 0.196078, 0.75}},
1569 {BlendMode::kDestination, {0.392157, 0.584314, 0.929412, 0.75}},
1570 {BlendMode::kSourceOver, {0.235294, 0.76, 0.342745, 0.9375}},
1571 {BlendMode::kDestinationOver, {0.352941, 0.628235, 0.782745, 0.9375}},
1572 {BlendMode::kSourceIn, {0.196078, 0.803922, 0.196078, 0.5625}},
1573 {BlendMode::kDestinationIn, {0.392157, 0.584314, 0.929412, 0.5625}},
1574 {BlendMode::kSourceOut, {0.196078, 0.803922, 0.196078, 0.1875}},
1575 {BlendMode::kDestinationOut, {0.392157, 0.584314, 0.929412, 0.1875}},
1576 {BlendMode::kSourceATop, {0.245098, 0.74902, 0.379412, 0.75}},
1577 {BlendMode::kDestinationATop, {0.343137, 0.639216, 0.746078, 0.75}},
1578 {BlendMode::kXor, {0.294118, 0.694118, 0.562745, 0.375}},
1579 {BlendMode::kPlus, {0.441176, 1, 0.844118, 1}},
1580 {BlendMode::kModulate, {0.0768935, 0.469742, 0.182238, 0.5625}},
1581 {BlendMode::kScreen, {0.424452, 0.828743, 0.79105, 0.9375}},
1582 {BlendMode::kOverlay, {0.209919, 0.779839, 0.757001, 0.9375}},
1583 {BlendMode::kDarken, {0.235294, 0.628235, 0.342745, 0.9375}},
1584 {BlendMode::kLighten, {0.352941, 0.76, 0.782745, 0.9375}},
1585 {BlendMode::kColorDodge, {0.41033, 0.877647, 0.825098, 0.9375}},
1586 {BlendMode::kColorBurn, {0.117647, 0.567403, 0.609098, 0.9375}},
1587 {BlendMode::kHardLight, {0.209919, 0.779839, 0.443783, 0.9375}},
1588 {BlendMode::kSoftLight, {0.266006, 0.693915, 0.758818, 0.9375}},
1589 {BlendMode::kDifference, {0.235294, 0.409412, 0.665098, 0.9375}},
1590 {BlendMode::kExclusion, {0.378316, 0.546897, 0.681707, 0.9375}},
1591 {BlendMode::kMultiply, {0.163783, 0.559493, 0.334441, 0.9375}},
1592 {BlendMode::kHue, {0.266235, 0.748588, 0.373686, 0.9375}},
1593 {BlendMode::kSaturation, {0.339345, 0.629787, 0.811502, 0.9375}},
1594 {BlendMode::kColor, {0.241247, 0.765953, 0.348698, 0.9375}},
1595 {BlendMode::kLuminosity, {0.346988, 0.622282, 0.776792, 0.9375}},
1596 },
1597 {
1598 {BlendMode::kClear, {0, 0, 0, 0}},
1599 {BlendMode::kSource, {0, 0, 0, 0.75}},
1600 {BlendMode::kDestination, {0.392157, 0.584314, 0.929412, 0.75}},
1601 {BlendMode::kSourceOver, {0.0784314, 0.116863, 0.185882, 0.9375}},
1602 {BlendMode::kDestinationOver, {0.313726, 0.467451, 0.743529, 0.9375}},
1603 {BlendMode::kSourceIn, {0, 0, 0, 0.5625}},
1604 {BlendMode::kDestinationIn, {0.392157, 0.584314, 0.929412, 0.5625}},
1605 {BlendMode::kSourceOut, {0, 0, 0, 0.1875}},
1606 {BlendMode::kDestinationOut, {0.392157, 0.584314, 0.929412, 0.1875}},
1607 {BlendMode::kSourceATop, {0.0980392, 0.146078, 0.232353, 0.75}},
1608 {BlendMode::kDestinationATop, {0.294118, 0.438235, 0.697059, 0.75}},
1609 {BlendMode::kXor, {0.196078, 0.292157, 0.464706, 0.375}},
1610 {BlendMode::kPlus, {0.294118, 0.438235, 0.697059, 1}},
1611 {BlendMode::kModulate, {0, 0, 0, 0.5625}},
1612 {BlendMode::kScreen, {0.313726, 0.467451, 0.743529, 0.9375}},
1613 {BlendMode::kOverlay, {0.0784314, 0.218039, 0.701176, 0.9375}},
1614 {BlendMode::kDarken, {0.0784314, 0.116863, 0.185882, 0.9375}},
1615 {BlendMode::kLighten, {0.313726, 0.467451, 0.743529, 0.9375}},
1616 {BlendMode::kColorDodge, {0.313726, 0.467451, 0.743529, 0.9375}},
1617 {BlendMode::kColorBurn, {0.0784314, 0.116863, 0.185882, 0.9375}},
1618 {BlendMode::kHardLight, {0.0784314, 0.116863, 0.185882, 0.9375}},
1619 {BlendMode::kSoftLight, {0.170704, 0.321716, 0.704166, 0.9375}},
1620 {BlendMode::kDifference, {0.313726, 0.467451, 0.743529, 0.9375}},
1621 {BlendMode::kExclusion, {0.313726, 0.467451, 0.743529, 0.9375}},
1622 {BlendMode::kMultiply, {0.0784314, 0.116863, 0.185882, 0.9375}},
1623 {BlendMode::kHue, {0.417208, 0.455639, 0.524659, 0.9375}},
1624 {BlendMode::kSaturation, {0.417208, 0.455639, 0.524659, 0.9375}},
1625 {BlendMode::kColor, {0.417208, 0.455639, 0.524659, 0.9375}},
1626 {BlendMode::kLuminosity, {0.0784314, 0.116863, 0.185882, 0.9375}},
1627 },
1628 };
1629
1630 /// To print a new ColorBlendTestData::kExpectedResults table, uncomment this
1631 /// test and run with:
1632 /// --gtest_filter="GeometryTest.GenerateColorBlendResults"
1633 /*
1634 TEST(GeometryTest, GenerateColorBlendResults) {
1635 auto& o = std::cout;
1636 using BlendT = std::underlying_type_t<BlendMode>;
1637 o << "{";
1638 for (const auto& source : ColorBlendTestData::kSourceColors) {
1639 o << "{";
1640 for (BlendT blend_i = 0;
1641 blend_i < static_cast<BlendT>(BlendMode::kLast) + 1; blend_i++) {
1642 auto blend = static_cast<BlendMode>(blend_i);
1643 Color c = ColorBlendTestData::kDestinationColor.Blend(source, blend);
1644 o << "{ BlendMode::k" << BlendModeToString(blend) << ", ";
1645 o << "{" << c.red << "," << c.green << "," << c.blue << "," << c.alpha
1646 << "}";
1647 o << "}," << std::endl;
1648 }
1649 o << "},";
1650 }
1651 o << "};" << std::endl;
1652 }
1653 */
1654
1655 #define _BLEND_MODE_RESULT_CHECK(blend_mode) \
1656 expected = ColorBlendTestData::kExpectedResults[source_i] \
1657 .find(BlendMode::k##blend_mode) \
1658 ->second; \
1659 EXPECT_COLOR_NEAR(dst.Blend(src, BlendMode::k##blend_mode), expected);
1660
1661 TEST(GeometryTest, ColorBlendReturnsExpectedResults) {
1662 Color dst = ColorBlendTestData::kDestinationColor;
1663 for (size_t source_i = 0;
1664 source_i < sizeof(ColorBlendTestData::kSourceColors) / sizeof(Color);
1665 source_i++) {
1666 Color src = ColorBlendTestData::kSourceColors[source_i];
1667
1668 Color expected;
1669 IMPELLER_FOR_EACH_BLEND_MODE(_BLEND_MODE_RESULT_CHECK)
1670 }
1671 }
1672
1673 #define _BLEND_MODE_NAME_CHECK(blend_mode) \
1674 case BlendMode::k##blend_mode: \
1675 ASSERT_STREQ(result, #blend_mode); \
1676 break;
1677
1678 TEST(GeometryTest, BlendModeToString) {
1679 using BlendT = std::underlying_type_t<BlendMode>;
1680 for (BlendT i = 0; i <= static_cast<BlendT>(BlendMode::kLast); i++) {
1681 auto mode = static_cast<BlendMode>(i);
1682 auto result = BlendModeToString(mode);
1683 switch (mode) { IMPELLER_FOR_EACH_BLEND_MODE(_BLEND_MODE_NAME_CHECK) }
1684 }
1685 }
1686
1687 TEST(GeometryTest, CanConvertBetweenDegressAndRadians) {
1688 {
1689 auto deg = Degrees{90.0};
1690 Radians rad = deg;
1691 ASSERT_FLOAT_EQ(rad.radians, kPiOver2);
1692 }
1693 }
1694
1695 TEST(GeometryTest, MatrixPrinting) {
1696 {
1697 std::stringstream stream;
1698 Matrix m;
1699 stream << m;
1700 ASSERT_EQ(stream.str(), R"((
1701 1.000000, 0.000000, 0.000000, 0.000000,
1702 0.000000, 1.000000, 0.000000, 0.000000,
1703 0.000000, 0.000000, 1.000000, 0.000000,
1704 0.000000, 0.000000, 0.000000, 1.000000,
1705 ))");
1706 }
1707
1708 {
1709 std::stringstream stream;
1710 Matrix m = Matrix::MakeTranslation(Vector3(10, 20, 30));
1711 stream << m;
1712
1713 ASSERT_EQ(stream.str(), R"((
1714 1.000000, 0.000000, 0.000000, 10.000000,
1715 0.000000, 1.000000, 0.000000, 20.000000,
1716 0.000000, 0.000000, 1.000000, 30.000000,
1717 0.000000, 0.000000, 0.000000, 1.000000,
1718 ))");
1719 }
1720 }
1721
1722 TEST(GeometryTest, PointPrinting) {
1723 {
1724 std::stringstream stream;
1725 Point m;
1726 stream << m;
1727 ASSERT_EQ(stream.str(), "(0, 0)");
1728 }
1729
1730 {
1731 std::stringstream stream;
1732 Point m(13, 37);
1733 stream << m;
1734 ASSERT_EQ(stream.str(), "(13, 37)");
1735 }
1736 }
1737
1738 TEST(GeometryTest, Vector3Printing) {
1739 {
1740 std::stringstream stream;
1741 Vector3 m;
1742 stream << m;
1743 ASSERT_EQ(stream.str(), "(0, 0, 0)");
1744 }
1745
1746 {
1747 std::stringstream stream;
1748 Vector3 m(1, 2, 3);
1749 stream << m;
1750 ASSERT_EQ(stream.str(), "(1, 2, 3)");
1751 }
1752 }
1753
1754 TEST(GeometryTest, Vector4Printing) {
1755 {
1756 std::stringstream stream;
1757 Vector4 m;
1758 stream << m;
1759 ASSERT_EQ(stream.str(), "(0, 0, 0, 1)");
1760 }
1761
1762 {
1763 std::stringstream stream;
1764 Vector4 m(1, 2, 3, 4);
1765 stream << m;
1766 ASSERT_EQ(stream.str(), "(1, 2, 3, 4)");
1767 }
1768 }
1769
1770 TEST(GeometryTest, ColorPrinting) {
1771 {
1772 std::stringstream stream;
1773 Color m;
1774 stream << m;
1775 ASSERT_EQ(stream.str(), "(0, 0, 0, 0)");
1776 }
1777
1778 {
1779 std::stringstream stream;
1780 Color m(1, 2, 3, 4);
1781 stream << m;
1782 ASSERT_EQ(stream.str(), "(1, 2, 3, 4)");
1783 }
1784 }
1785
1786 TEST(GeometryTest, ToIColor) {
1787 ASSERT_EQ(Color::ToIColor(Color(0, 0, 0, 0)), 0u);
1788 ASSERT_EQ(Color::ToIColor(Color(1.0, 1.0, 1.0, 1.0)), 0xFFFFFFFF);
1789 ASSERT_EQ(Color::ToIColor(Color(0.5, 0.5, 1.0, 1.0)), 0xFF8080FF);
1790 }
1791
1792 TEST(GeometryTest, Gradient) {
1793 {
1794 // Simple 2 color gradient produces color buffer containing exactly those
1795 // values.
1796 std::vector<Color> colors = {Color::Red(), Color::Blue()};
1797 std::vector<Scalar> stops = {0.0, 1.0};
1798
1799 auto gradient = CreateGradientBuffer(colors, stops);
1800
1801 ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, colors);
1802 ASSERT_EQ(gradient.texture_size, 2u);
1803 }
1804
1805 {
1806 // Gradient with duplicate stops does not create an empty texture.
1807 std::vector<Color> colors = {Color::Red(), Color::Yellow(), Color::Black(),
1808 Color::Blue()};
1809 std::vector<Scalar> stops = {0.0, 0.25, 0.25, 1.0};
1810
1811 auto gradient = CreateGradientBuffer(colors, stops);
1812 ASSERT_EQ(gradient.texture_size, 5u);
1813 }
1814
1815 {
1816 // Simple N color gradient produces color buffer containing exactly those
1817 // values.
1818 std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green(),
1819 Color::White()};
1820 std::vector<Scalar> stops = {0.0, 0.33, 0.66, 1.0};
1821
1822 auto gradient = CreateGradientBuffer(colors, stops);
1823
1824 ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, colors);
1825 ASSERT_EQ(gradient.texture_size, 4u);
1826 }
1827
1828 {
1829 // Gradient with color stops will lerp and scale buffer.
1830 std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green()};
1831 std::vector<Scalar> stops = {0.0, 0.25, 1.0};
1832
1833 auto gradient = CreateGradientBuffer(colors, stops);
1834
1835 std::vector<Color> lerped_colors = {
1836 Color::Red(),
1837 Color::Blue(),
1838 Color::Lerp(Color::Blue(), Color::Green(), 0.3333),
1839 Color::Lerp(Color::Blue(), Color::Green(), 0.6666),
1840 Color::Green(),
1841 };
1842 ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, lerped_colors);
1843 ASSERT_EQ(gradient.texture_size, 5u);
1844 }
1845
1846 {
1847 // Gradient size is capped at 1024.
1848 std::vector<Color> colors = {};
1849 std::vector<Scalar> stops = {};
1850 for (auto i = 0u; i < 1025; i++) {
1851 colors.push_back(Color::Blue());
1852 stops.push_back(i / 1025.0);
1853 }
1854
1855 auto gradient = CreateGradientBuffer(colors, stops);
1856
1857 ASSERT_EQ(gradient.texture_size, 1024u);
1858 ASSERT_EQ(gradient.color_bytes.size(), 1024u * 4);
1859 }
1860 }
1861
1862 TEST(GeometryTest, HalfConversions) {
1863 #if defined(FML_OS_MACOSX) || defined(FML_OS_IOS) || \
1864 defined(FML_OS_IOS_SIMULATOR)
1865 ASSERT_EQ(ScalarToHalf(0.0), 0.0f16);
1866 ASSERT_EQ(ScalarToHalf(0.05), 0.05f16);
1867 ASSERT_EQ(ScalarToHalf(2.43), 2.43f16);
1868 ASSERT_EQ(ScalarToHalf(-1.45), -1.45f16);
1869
1870 // 65504 is the largest possible half.
1871 ASSERT_EQ(ScalarToHalf(65504.0f), 65504.0f16);
1872 ASSERT_EQ(ScalarToHalf(65504.0f + 1), 65504.0f16);
1873
1874 // Colors
1875 ASSERT_EQ(HalfVector4(Color::Red()),
1876 HalfVector4(1.0f16, 0.0f16, 0.0f16, 1.0f16));
1877 ASSERT_EQ(HalfVector4(Color::Green()),
1878 HalfVector4(0.0f16, 1.0f16, 0.0f16, 1.0f16));
1879 ASSERT_EQ(HalfVector4(Color::Blue()),
1880 HalfVector4(0.0f16, 0.0f16, 1.0f16, 1.0f16));
1881 ASSERT_EQ(HalfVector4(Color::Black().WithAlpha(0)),
1882 HalfVector4(0.0f16, 0.0f16, 0.0f16, 0.0f16));
1883
1884 ASSERT_EQ(HalfVector3(Vector3(4.0, 6.0, -1.0)),
1885 HalfVector3(4.0f16, 6.0f16, -1.0f16));
1886 ASSERT_EQ(HalfVector2(Vector2(4.0, 6.0)), HalfVector2(4.0f16, 6.0f16));
1887
1888 ASSERT_EQ(Half(0.5f), Half(0.5f16));
1889 ASSERT_EQ(Half(0.5), Half(0.5f16));
1890 ASSERT_EQ(Half(5), Half(5.0f16));
1891 #else
1892 GTEST_SKIP() << "Half-precision floats (IEEE 754) are not portable and "
1893 "only used on Apple platforms.";
1894 #endif // FML_OS_MACOSX || FML_OS_IOS || FML_OS_IOS_SIMULATOR
1895 }
1896
1897 } // namespace testing
1898 } // namespace impeller
1899
1900 // NOLINTEND(bugprone-unchecked-optional-access)
impeller::Matrix::MakeSkew
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
Definition: matrix.h:117
impeller::ISize
ISize64 ISize
Definition: size.h:140
_BLEND_MODE_RESULT_CHECK
#define _BLEND_MODE_RESULT_CHECK(blend_mode)
Definition: geometry_unittests.cc:1655
impeller::Color::Blue
static constexpr Color Blue()
Definition: color.h:278
ASSERT_COLOR_NEAR
#define ASSERT_COLOR_NEAR(a, b)
Definition: geometry_asserts.h:192
impeller::Matrix::Decompose
std::optional< MatrixDecomposition > Decompose() const
Definition: matrix.cc:209
impeller::k1OverSqrt2
constexpr float k1OverSqrt2
Definition: constants.h:50
impeller::Matrix::MakeRotationX
static Matrix MakeRotationX(Radians r)
Definition: matrix.h:183
separated_vector.h
impeller::BlendModeToString
const char * BlendModeToString(BlendMode blend_mode)
Definition: color.cc:47
impeller::BlendMode::kDestinationATop
@ kDestinationATop
point.h
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::HalfVector2
A storage only class for half precision floating point vector 2.
Definition: half.h:129
impeller::TPoint::Ceil
constexpr TPoint Ceil() const
Definition: point.h:196
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:274
geometry_asserts.h
impeller::Vector3::Round
constexpr Vector3 Round() const
Definition: vector.h:86
impeller::Vector4::Max
constexpr Vector4 Max(const Vector4 &p) const
Definition: vector.h:292
impeller::BlendMode
BlendMode
Definition: color.h:59
impeller::Color::MakeRGBA8
static constexpr Color MakeRGBA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Definition: color.h:154
impeller::Color
Definition: color.h:124
impeller::Color::Unpremultiply
constexpr Color Unpremultiply() const
Definition: color.h:218
impeller::TPoint::Lerp
constexpr TPoint Lerp(const TPoint &p, Scalar t) const
Definition: point.h:236
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:56
impeller::SeparatedVector2
A Vector2, broken down as a separate magnitude and direction. Assumes that the direction given is nor...
Definition: separated_vector.h:21
impeller::Vector4
Definition: vector.h:232
impeller::BlendMode::kLuminosity
@ kLuminosity
impeller::BlendMode::kSource
@ kSource
impeller::BlendMode::kColorDodge
@ kColorDodge
impeller::Matrix::MakeRotation
static Matrix MakeRotation(Quaternion q)
Definition: matrix.h:126
impeller::BlendMode::kDestination
@ kDestination
impeller::TPoint::Min
constexpr TPoint Min(const TPoint &p) const
Definition: point.h:186
impeller::SeparatedVector2::magnitude
Scalar magnitude
The magnitude of the vector.
Definition: separated_vector.h:26
impeller::BlendMode::kDarken
@ kDarken
impeller::SeparatedVector2::direction
Vector2 direction
The normalized direction of the vector.
Definition: separated_vector.h:23
impeller::testing::TEST
TEST(AiksCanvasTest, EmptyCullRect)
Definition: canvas_unittests.cc:18
impeller::TPoint::Round
static constexpr TPoint Round(const TPoint< U > &other)
Definition: point.h:49
impeller::Vector2
Point Vector2
Definition: point.h:326
impeller::BlendMode::kColor
@ kColor
impeller::Matrix::MakeRotationY
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:198
impeller::kPi
constexpr float kPi
Definition: constants.h:26
impeller::BlendMode::kDestinationOver
@ kDestinationOver
impeller::BlendMode::kPlus
@ kPlus
impeller::Color::CornflowerBlue
static constexpr Color CornflowerBlue()
Definition: color.h:344
impeller::BlendMode::kOverlay
@ kOverlay
impeller::Color::Yellow
static constexpr Color Yellow()
Definition: color.h:844
impeller::Radians::radians
Scalar radians
Definition: scalar.h:39
impeller::Matrix::MakeRow
static constexpr Matrix MakeRow(Scalar m0, Scalar m1, Scalar m2, Scalar m3, Scalar m4, Scalar m5, Scalar m6, Scalar m7, Scalar m8, Scalar m9, Scalar m10, Scalar m11, Scalar m12, Scalar m13, Scalar m14, Scalar m15)
Definition: matrix.h:83
impeller::Size
TSize< Scalar > Size
Definition: size.h:137
ASSERT_POINT_NEAR
#define ASSERT_POINT_NEAR(a, b)
Definition: geometry_asserts.h:193
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::Color::Clamp01
constexpr Color Clamp01() const
Definition: color.h:238
ASSERT_VECTOR4_NEAR
#define ASSERT_VECTOR4_NEAR(a, b)
Definition: geometry_asserts.h:195
impeller::Vector3::x
Scalar x
Definition: vector.h:23
impeller::TPoint::Floor
constexpr TPoint Floor() const
Definition: point.h:194
impeller::BlendMode::kModulate
@ kModulate
impeller::TPoint::Dot
constexpr Type Dot(const TPoint &p) const
Definition: point.h:220
impeller::MatrixDecomposition
Definition: matrix_decomposition.h:15
impeller::kPiOver2
constexpr float kPiOver2
Definition: constants.h:32
impeller::BlendMode::kSourceOut
@ kSourceOut
impeller::Matrix::MakeLookAt
static constexpr Matrix MakeLookAt(Vector3 position, Vector3 target, Vector3 up)
Definition: matrix.h:532
impeller::Matrix::MakePerspective
static constexpr Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:506
impeller::BlendMode::kSaturation
@ kSaturation
impeller::BlendMode::kDifference
@ kDifference
gradient.h
impeller::Vector4::Round
constexpr Vector4 Round() const
Definition: vector.h:305
impeller::TPoint::Reflect
constexpr TPoint Reflect(const TPoint &axis) const
Definition: point.h:222
impeller::BlendMode::kLighten
@ kLighten
impeller::Matrix::Basis
constexpr Matrix Basis() const
The Matrix without its w components (without translation).
Definition: matrix.h:229
impeller::TSize< Scalar >
impeller::Quaternion
Definition: quaternion.h:14
impeller::BlendMode::kSoftLight
@ kSoftLight
impeller::Point
TPoint< Scalar > Point
Definition: point.h:322
impeller::k2Pi
constexpr float k2Pi
Definition: constants.h:29
impeller::BlendMode::kColorBurn
@ kColorBurn
impeller::Half
A storage only class for half precision floating point.
Definition: half.h:41
impeller::TPoint::AngleTo
constexpr Radians AngleTo(const TPoint &p) const
Definition: point.h:232
impeller::BlendMode::kHardLight
@ kHardLight
impeller::Color::ToIColor
static constexpr uint32_t ToIColor(Color color)
Convert this color to a 32-bit representation.
Definition: color.h:161
impeller::SeparatedVector2::GetVector
Vector2 GetVector() const
Returns the vector representation of the vector.
Definition: separated_vector.cc:17
impeller::Color::SRGBToLinear
Color SRGBToLinear() const
Convert the color from sRGB space to linear space.
Definition: color.cc:399
impeller::BlendMode::kClear
@ kClear
impeller::Vector4::Lerp
constexpr Vector4 Lerp(const Vector4 &v, Scalar t) const
Definition: vector.h:309
impeller::testing::ColorBlendTestData::kExpectedResults
static const std::map< BlendMode, Color > kExpectedResults[sizeof(kSourceColors)]
Definition: geometry_unittests.cc:1526
impeller::ScalarToHalf
constexpr InternalHalf ScalarToHalf(Scalar f)
Convert a scalar to a half precision float.
Definition: half.h:32
impeller::Color::WithAlpha
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:280
impeller::TPoint::Max
constexpr TPoint Max(const TPoint &p) const
Definition: point.h:190
impeller::Vector4::Min
constexpr Vector4 Min(const Vector4 &p) const
Definition: vector.h:287
impeller::Color::ToR8G8B8A8
constexpr std::array< uint8_t, 4 > ToR8G8B8A8() const
Convert to R8G8B8A8 representation.
Definition: color.h:248
impeller::SeparatedVector2::GetAlignment
Scalar GetAlignment(const SeparatedVector2 &other) const
Definition: separated_vector.cc:21
impeller::Color::White
static constexpr Color White()
Definition: color.h:266
impeller::Matrix::MakeColumn
static constexpr Matrix MakeColumn(Scalar m0, Scalar m1, Scalar m2, Scalar m3, Scalar m4, Scalar m5, Scalar m6, Scalar m7, Scalar m8, Scalar m9, Scalar m10, Scalar m11, Scalar m12, Scalar m13, Scalar m14, Scalar m15)
Definition: matrix.h:69
impeller::Vector3::Floor
constexpr Vector3 Floor() const
Definition: vector.h:78
impeller::Vector3::Ceil
constexpr Vector3 Ceil() const
Definition: vector.h:82
impeller::Vector3::z
Scalar z
Definition: vector.h:25
impeller::Radians
Definition: scalar.h:38
impeller::MatrixDecomposition::translation
Vector3 translation
Definition: matrix_decomposition.h:16
impeller::HalfVector3
A storage only class for half precision floating point vector 3.
Definition: half.h:101
_BLEND_MODE_NAME_CHECK
#define _BLEND_MODE_NAME_CHECK(blend_mode)
Definition: geometry_unittests.cc:1673
impeller::Color::Green
static constexpr Color Green()
Definition: color.h:276
ASSERT_MATRIX_NEAR
#define ASSERT_MATRIX_NEAR(a, b)
Definition: geometry_asserts.h:189
impeller::TPoint::Cross
constexpr Type Cross(const TPoint &p) const
Definition: point.h:218
impeller::Vector3::y
Scalar y
Definition: vector.h:24
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:20
impeller::TSize::width
Type width
Definition: size.h:22
IMPELLER_FOR_EACH_BLEND_MODE
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition: color.h:19
impeller::Matrix::Invert
Matrix Invert() const
Definition: matrix.cc:97
impeller::TPoint::x
Type x
Definition: point.h:30
ASSERT_QUATERNION_NEAR
#define ASSERT_QUATERNION_NEAR(a, b)
Definition: geometry_asserts.h:190
impeller::Matrix::GetDirectionScale
constexpr Scalar GetDirectionScale(Vector3 direction) const
Definition: matrix.h:316
scalar.h
impeller::MatrixDecomposition::scale
Vector3 scale
Definition: matrix_decomposition.h:17
impeller::BlendMode::kDestinationIn
@ kDestinationIn
impeller::TPoint::Abs
constexpr TPoint Abs() const
Definition: point.h:216
impeller::BlendMode::kExclusion
@ kExclusion
impeller::BlendMode::kDestinationOut
@ kDestinationOut
half.h
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:213
constants.h
impeller::IPoint
TPoint< int64_t > IPoint
Definition: point.h:323
ASSERT_VECTOR3_NEAR
#define ASSERT_VECTOR3_NEAR(a, b)
Definition: geometry_asserts.h:194
rect.h
impeller::TPoint< Scalar >
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::Color::BlackTransparent
static constexpr Color BlackTransparent()
Definition: color.h:272
impeller::Color::Black
static constexpr Color Black()
Definition: color.h:268
impeller::testing::ColorBlendTestData::kSourceColors
static constexpr Color kSourceColors[]
Definition: geometry_unittests.cc:1521
impeller::Vector4::Floor
constexpr Vector4 Floor() const
Definition: vector.h:297
impeller::BlendMode::kSourceIn
@ kSourceIn
impeller::HalfVector4
A storage only class for half precision floating point vector 4.
Definition: half.h:60
scale
const Scalar scale
Definition: stroke_path_geometry.cc:308
impeller::BlendMode::kScreen
@ kScreen
impeller::ScalarNearlyEqual
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:30
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:497
impeller::testing::ColorBlendTestData
Definition: geometry_unittests.cc:1518
impeller::testing::ColorBlendTestData::kDestinationColor
static constexpr Color kDestinationColor
Definition: geometry_unittests.cc:1519
impeller::Vector3::Min
constexpr Vector3 Min(const Vector3 &p) const
Definition: vector.h:70
impeller::Degrees
Definition: scalar.h:46
color.h
impeller::Color::LimeGreen
static constexpr Color LimeGreen()
Definition: color.h:604
impeller::BlendMode::kLast
@ kLast
impeller::TPoint::Rotate
constexpr TPoint Rotate(const Radians &angle) const
Definition: point.h:226
impeller::TSize::height
Type height
Definition: size.h:23
impeller::BlendMode::kHue
@ kHue
impeller::SeparatedVector2::AngleTo
Radians AngleTo(const SeparatedVector2 &other) const
Returns the scalar angle between the two rays.
Definition: separated_vector.cc:25
impeller::BlendMode::kXor
@ kXor
impeller::TSize::MipCount
constexpr size_t MipCount() const
Definition: size.h:115
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:234
impeller::TPoint::Normalize
constexpr TPoint Normalize() const
Definition: point.h:208
impeller::ColorMatrix
Definition: color.h:117
impeller::Color::ApplyColorMatrix
Color ApplyColorMatrix(const ColorMatrix &color_matrix) const
A color filter that transforms colors through a 4x5 color matrix.
Definition: color.cc:378
impeller::Color::LinearToSRGB
Color LinearToSRGB() const
Convert the color from linear space to sRGB space.
Definition: color.cc:388
ASSERT_COLOR_BUFFER_NEAR
#define ASSERT_COLOR_BUFFER_NEAR(a, b)
Definition: geometry_asserts.h:198
impeller
Definition: aiks_blend_unittests.cc:18
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::BlendMode::kSourceATop
@ kSourceATop
impeller::BlendMode::kMultiply
@ kMultiply
impeller::kPiOver4
constexpr float kPiOver4
Definition: constants.h:35
impeller::Color::Premultiply
constexpr Color Premultiply() const
Definition: color.h:214
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Vector3
Definition: vector.h:20
size.h
impeller::BlendMode::kSourceOver
@ kSourceOver
impeller::Vector3::Lerp
constexpr Vector3 Lerp(const Vector3 &v, Scalar t) const
Definition: vector.h:178
ASSERT_ARRAY_4_NEAR
#define ASSERT_ARRAY_4_NEAR(a, b)
Definition: geometry_asserts.h:197
impeller::Vector3::Max
constexpr Vector3 Max(const Vector3 &p) const
Definition: vector.h:74
impeller::Vector4::Ceil
constexpr Vector4 Ceil() const
Definition: vector.h:301