| 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, MatrixGetMaxBasisLengthXY) {
334 {
335 auto m = Matrix::MakeScale({3, 1, 1});
336 ASSERT_EQ(m.GetMaxBasisLengthXY(), 3);
337
338 m = m * Matrix::MakeSkew(0, 4);
339 ASSERT_EQ(m.GetMaxBasisLengthXY(), 5);
340 }
341
342 {
343 auto m = Matrix::MakeScale({-3, 4, 7});
344 ASSERT_EQ(m.GetMaxBasisLengthXY(), 4);
345 }
346
347 {
348 // clang-format off
349 auto m = Matrix::MakeColumn(
350 1.0f, 0.0f, 0.0f, 0.0f,
351 0.0f, 1.0f, 0.0f, 0.0f,
352 4.0f, 0.0f, 1.0f, 0.0f,
353 0.0f, 0.0f, 0.0f, 1.0f
354 );
355 // clang-format on
356 ASSERT_EQ(m.GetMaxBasisLengthXY(), 1.0f);
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, MatrixTranslationScaleOnly) {
456 {
457 auto m = Matrix();
458 bool result = m.IsTranslationScaleOnly();
459 ASSERT_TRUE(result);
460 }
461
462 {
463 auto m = Matrix::MakeScale(Vector3(2, 3, 4));
464 bool result = m.IsTranslationScaleOnly();
465 ASSERT_TRUE(result);
466 }
467
468 {
469 auto m = Matrix::MakeTranslation(Vector3(2, 3, 4));
470 bool result = m.IsTranslationScaleOnly();
471 ASSERT_TRUE(result);
472 }
473
474 {
475 auto m = Matrix::MakeRotationZ(Degrees(10));
476 bool result = m.IsTranslationScaleOnly();
477 ASSERT_FALSE(result);
478 }
479 }
480
481 TEST(GeometryTest, MatrixLookAt) {
482 {
483 auto m = Matrix::MakeLookAt(Vector3(0, 0, -1), Vector3(0, 0, 1),
484 Vector3(0, 1, 0));
485 auto expected = Matrix{
486 1, 0, 0, 0, //
487 0, 1, 0, 0, //
488 0, 0, 1, 0, //
489 0, 0, 1, 1, //
490 };
491 ASSERT_MATRIX_NEAR(m, expected);
492 }
493
494 // Sideways tilt.
495 {
496 auto m = Matrix::MakeLookAt(Vector3(0, 0, -1), Vector3(0, 0, 1),
497 Vector3(1, 1, 0).Normalize());
498
499 // clang-format off
500 auto expected = Matrix{
501 k1OverSqrt2, k1OverSqrt2, 0, 0,
502 -k1OverSqrt2, k1OverSqrt2, 0, 0,
503 0, 0, 1, 0,
504 0, 0, 1, 1,
505 };
506 // clang-format on
507 ASSERT_MATRIX_NEAR(m, expected);
508 }
509
510 // Half way between +x and -y, yaw 90
511 {
512 auto m =
513 Matrix::MakeLookAt(Vector3(), Vector3(10, -10, 0), Vector3(0, 0, -1));
514
515 // clang-format off
516 auto expected = Matrix{
517 -k1OverSqrt2, 0, k1OverSqrt2, 0,
518 -k1OverSqrt2, 0, -k1OverSqrt2, 0,
519 0, -1, 0, 0,
520 0, 0, 0, 1,
521 };
522 // clang-format on
523 ASSERT_MATRIX_NEAR(m, expected);
524 }
525 }
526
527 TEST(GeometryTest, QuaternionLerp) {
528 auto q1 = Quaternion{{0.0, 0.0, 1.0}, 0.0};
529 auto q2 = Quaternion{{0.0, 0.0, 1.0}, kPiOver4};
530
531 auto q3 = q1.Slerp(q2, 0.5);
532
533 auto expected = Quaternion{{0.0, 0.0, 1.0}, kPiOver4 / 2.0};
534
535 ASSERT_QUATERNION_NEAR(q3, expected);
536 }
537
538 TEST(GeometryTest, QuaternionVectorMultiply) {
539 {
540 Quaternion q({0, 0, 1}, 0);
541 Vector3 v(0, 1, 0);
542
543 Vector3 result = q * v;
544 Vector3 expected(0, 1, 0);
545
546 ASSERT_VECTOR3_NEAR(result, expected);
547 }
548
549 {
550 Quaternion q({0, 0, 1}, k2Pi);
551 Vector3 v(1, 0, 0);
552
553 Vector3 result = q * v;
554 Vector3 expected(1, 0, 0);
555
556 ASSERT_VECTOR3_NEAR(result, expected);
557 }
558
559 {
560 Quaternion q({0, 0, 1}, kPiOver4);
561 Vector3 v(0, 1, 0);
562
563 Vector3 result = q * v;
564 Vector3 expected(-k1OverSqrt2, k1OverSqrt2, 0);
565
566 ASSERT_VECTOR3_NEAR(result, expected);
567 }
568
569 {
570 Quaternion q(Vector3(1, 0, 1).Normalize(), kPi);
571 Vector3 v(0, 0, -1);
572
573 Vector3 result = q * v;
574 Vector3 expected(-1, 0, 0);
575
576 ASSERT_VECTOR3_NEAR(result, expected);
577 }
578 }
579
580 TEST(GeometryTest, CanGenerateMipCounts) {
581 ASSERT_EQ((Size{128, 128}.MipCount()), 7u);
582 ASSERT_EQ((Size{128, 256}.MipCount()), 8u);
583 ASSERT_EQ((Size{128, 130}.MipCount()), 8u);
584 ASSERT_EQ((Size{128, 257}.MipCount()), 9u);
585 ASSERT_EQ((Size{257, 128}.MipCount()), 9u);
586 ASSERT_EQ((Size{128, 0}.MipCount()), 1u);
587 ASSERT_EQ((Size{128, -25}.MipCount()), 1u);
588 ASSERT_EQ((Size{-128, 25}.MipCount()), 1u);
589 ASSERT_EQ((Size{1, 1}.MipCount()), 1u);
590 ASSERT_EQ((Size{0, 0}.MipCount()), 1u);
591 }
592
593 TEST(GeometryTest, CanConvertTTypesExplicitly) {
594 {
595 Point p1(1.0, 2.0);
596 IPoint p2 = static_cast<IPoint>(p1);
597 ASSERT_EQ(p2.x, 1u);
598 ASSERT_EQ(p2.y, 2u);
599 }
600
601 {
602 Size s1(1.0, 2.0);
603 ISize s2 = static_cast<ISize>(s1);
604 ASSERT_EQ(s2.width, 1u);
605 ASSERT_EQ(s2.height, 2u);
606 }
607
608 {
609 Size s1(1.0, 2.0);
610 Point p1 = static_cast<Point>(s1);
611 ASSERT_EQ(p1.x, 1u);
612 ASSERT_EQ(p1.y, 2u);
613 }
614 }
615
616 TEST(GeometryTest, CanPerformAlgebraicPointOps) {
617 {
618 IPoint p1(1, 2);
619 IPoint p2 = p1 + IPoint(1, 2);
620 ASSERT_EQ(p2.x, 2u);
621 ASSERT_EQ(p2.y, 4u);
622 }
623
624 {
625 IPoint p1(3, 6);
626 IPoint p2 = p1 - IPoint(1, 2);
627 ASSERT_EQ(p2.x, 2u);
628 ASSERT_EQ(p2.y, 4u);
629 }
630
631 {
632 IPoint p1(1, 2);
633 IPoint p2 = p1 * IPoint(2, 3);
634 ASSERT_EQ(p2.x, 2u);
635 ASSERT_EQ(p2.y, 6u);
636 }
637
638 {
639 IPoint p1(2, 6);
640 IPoint p2 = p1 / IPoint(2, 3);
641 ASSERT_EQ(p2.x, 1u);
642 ASSERT_EQ(p2.y, 2u);
643 }
644 }
645
646 TEST(GeometryTest, CanPerformAlgebraicPointOpsWithArithmeticTypes) {
647 // LHS
648 {
649 IPoint p1(1, 2);
650 IPoint p2 = p1 * 2.0f;
651 ASSERT_EQ(p2.x, 2u);
652 ASSERT_EQ(p2.y, 4u);
653 }
654
655 {
656 IPoint p1(2, 6);
657 IPoint p2 = p1 / 2.0f;
658 ASSERT_EQ(p2.x, 1u);
659 ASSERT_EQ(p2.y, 3u);
660 }
661
662 // RHS
663 {
664 IPoint p1(1, 2);
665 IPoint p2 = 2.0f * p1;
666 ASSERT_EQ(p2.x, 2u);
667 ASSERT_EQ(p2.y, 4u);
668 }
669
670 {
671 IPoint p1(2, 6);
672 IPoint p2 = 12.0f / p1;
673 ASSERT_EQ(p2.x, 6u);
674 ASSERT_EQ(p2.y, 2u);
675 }
676 }
677
678 TEST(GeometryTest, PointIntegerCoercesToFloat) {
679 // Integer on LHS, float on RHS
680 {
681 IPoint p1(1, 2);
682 Point p2 = p1 + Point(1, 2);
683 ASSERT_FLOAT_EQ(p2.x, 2u);
684 ASSERT_FLOAT_EQ(p2.y, 4u);
685 }
686
687 {
688 IPoint p1(3, 6);
689 Point p2 = p1 - Point(1, 2);
690 ASSERT_FLOAT_EQ(p2.x, 2u);
691 ASSERT_FLOAT_EQ(p2.y, 4u);
692 }
693
694 {
695 IPoint p1(1, 2);
696 Point p2 = p1 * Point(2, 3);
697 ASSERT_FLOAT_EQ(p2.x, 2u);
698 ASSERT_FLOAT_EQ(p2.y, 6u);
699 }
700
701 {
702 IPoint p1(2, 6);
703 Point p2 = p1 / Point(2, 3);
704 ASSERT_FLOAT_EQ(p2.x, 1u);
705 ASSERT_FLOAT_EQ(p2.y, 2u);
706 }
707
708 // Float on LHS, integer on RHS
709 {
710 Point p1(1, 2);
711 Point p2 = p1 + IPoint(1, 2);
712 ASSERT_FLOAT_EQ(p2.x, 2u);
713 ASSERT_FLOAT_EQ(p2.y, 4u);
714 }
715
716 {
717 Point p1(3, 6);
718 Point p2 = p1 - IPoint(1, 2);
719 ASSERT_FLOAT_EQ(p2.x, 2u);
720 ASSERT_FLOAT_EQ(p2.y, 4u);
721 }
722
723 {
724 Point p1(1, 2);
725 Point p2 = p1 * IPoint(2, 3);
726 ASSERT_FLOAT_EQ(p2.x, 2u);
727 ASSERT_FLOAT_EQ(p2.y, 6u);
728 }
729
730 {
731 Point p1(2, 6);
732 Point p2 = p1 / IPoint(2, 3);
733 ASSERT_FLOAT_EQ(p2.x, 1u);
734 ASSERT_FLOAT_EQ(p2.y, 2u);
735 }
736 }
737
738 TEST(GeometryTest, SizeCoercesToPoint) {
739 // Point on LHS, Size on RHS
740 {
741 IPoint p1(1, 2);
742 IPoint p2 = p1 + ISize(1, 2);
743 ASSERT_EQ(p2.x, 2u);
744 ASSERT_EQ(p2.y, 4u);
745 }
746
747 {
748 IPoint p1(3, 6);
749 IPoint p2 = p1 - ISize(1, 2);
750 ASSERT_EQ(p2.x, 2u);
751 ASSERT_EQ(p2.y, 4u);
752 }
753
754 {
755 IPoint p1(1, 2);
756 IPoint p2 = p1 * ISize(2, 3);
757 ASSERT_EQ(p2.x, 2u);
758 ASSERT_EQ(p2.y, 6u);
759 }
760
761 {
762 IPoint p1(2, 6);
763 IPoint p2 = p1 / ISize(2, 3);
764 ASSERT_EQ(p2.x, 1u);
765 ASSERT_EQ(p2.y, 2u);
766 }
767
768 // Size on LHS, Point on RHS
769 {
770 ISize p1(1, 2);
771 IPoint p2 = p1 + IPoint(1, 2);
772 ASSERT_EQ(p2.x, 2u);
773 ASSERT_EQ(p2.y, 4u);
774 }
775
776 {
777 ISize p1(3, 6);
778 IPoint p2 = p1 - IPoint(1, 2);
779 ASSERT_EQ(p2.x, 2u);
780 ASSERT_EQ(p2.y, 4u);
781 }
782
783 {
784 ISize p1(1, 2);
785 IPoint p2 = p1 * IPoint(2, 3);
786 ASSERT_EQ(p2.x, 2u);
787 ASSERT_EQ(p2.y, 6u);
788 }
789
790 {
791 ISize p1(2, 6);
792 IPoint p2 = p1 / IPoint(2, 3);
793 ASSERT_EQ(p2.x, 1u);
794 ASSERT_EQ(p2.y, 2u);
795 }
796 }
797
798 TEST(GeometryTest, CanUsePointAssignmentOperators) {
799 // Point on RHS
800 {
801 IPoint p(1, 2);
802 p += IPoint(1, 2);
803 ASSERT_EQ(p.x, 2u);
804 ASSERT_EQ(p.y, 4u);
805 }
806
807 {
808 IPoint p(3, 6);
809 p -= IPoint(1, 2);
810 ASSERT_EQ(p.x, 2u);
811 ASSERT_EQ(p.y, 4u);
812 }
813
814 {
815 IPoint p(1, 2);
816 p *= IPoint(2, 3);
817 ASSERT_EQ(p.x, 2u);
818 ASSERT_EQ(p.y, 6u);
819 }
820
821 {
822 IPoint p(2, 6);
823 p /= IPoint(2, 3);
824 ASSERT_EQ(p.x, 1u);
825 ASSERT_EQ(p.y, 2u);
826 }
827
828 // Size on RHS
829 {
830 IPoint p(1, 2);
831 p += ISize(1, 2);
832 ASSERT_EQ(p.x, 2u);
833 ASSERT_EQ(p.y, 4u);
834 }
835
836 {
837 IPoint p(3, 6);
838 p -= ISize(1, 2);
839 ASSERT_EQ(p.x, 2u);
840 ASSERT_EQ(p.y, 4u);
841 }
842
843 {
844 IPoint p(1, 2);
845 p *= ISize(2, 3);
846 ASSERT_EQ(p.x, 2u);
847 ASSERT_EQ(p.y, 6u);
848 }
849
850 {
851 IPoint p(2, 6);
852 p /= ISize(2, 3);
853 ASSERT_EQ(p.x, 1u);
854 ASSERT_EQ(p.y, 2u);
855 }
856
857 // Arithmetic type on RHS
858 {
859 IPoint p(1, 2);
860 p *= 3;
861 ASSERT_EQ(p.x, 3u);
862 ASSERT_EQ(p.y, 6u);
863 }
864
865 {
866 IPoint p(3, 6);
867 p /= 3;
868 ASSERT_EQ(p.x, 1u);
869 ASSERT_EQ(p.y, 2u);
870 }
871 }
872
873 TEST(GeometryTest, PointDotProduct) {
874 {
875 Point p(1, 0);
876 Scalar s = p.Dot(Point(-1, 0));
877 ASSERT_FLOAT_EQ(s, -1);
878 }
879
880 {
881 Point p(0, -1);
882 Scalar s = p.Dot(Point(-1, 0));
883 ASSERT_FLOAT_EQ(s, 0);
884 }
885
886 {
887 Point p(1, 2);
888 Scalar s = p.Dot(Point(3, -4));
889 ASSERT_FLOAT_EQ(s, -5);
890 }
891 }
892
893 TEST(GeometryTest, PointCrossProduct) {
894 {
895 Point p(1, 0);
896 Scalar s = p.Cross(Point(-1, 0));
897 ASSERT_FLOAT_EQ(s, 0);
898 }
899
900 {
901 Point p(0, -1);
902 Scalar s = p.Cross(Point(-1, 0));
903 ASSERT_FLOAT_EQ(s, -1);
904 }
905
906 {
907 Point p(1, 2);
908 Scalar s = p.Cross(Point(3, -4));
909 ASSERT_FLOAT_EQ(s, -10);
910 }
911 }
912
913 TEST(GeometryTest, PointReflect) {
914 {
915 Point axis = Point(0, 1);
916 Point a(2, 3);
917 auto reflected = a.Reflect(axis);
918 auto expected = Point(2, -3);
919 ASSERT_POINT_NEAR(reflected, expected);
920 }
921
922 {
923 Point axis = Point(1, 1).Normalize();
924 Point a(1, 0);
925 auto reflected = a.Reflect(axis);
926 auto expected = Point(0, -1);
927 ASSERT_POINT_NEAR(reflected, expected);
928 }
929
930 {
931 Point axis = Point(1, 1).Normalize();
932 Point a(-1, -1);
933 auto reflected = a.Reflect(axis);
934 ASSERT_POINT_NEAR(reflected, -a);
935 }
936 }
937
938 TEST(GeometryTest, PointAbs) {
939 Point a(-1, -2);
940 auto a_abs = a.Abs();
941 auto expected = Point(1, 2);
942 ASSERT_POINT_NEAR(a_abs, expected);
943 }
944
945 TEST(GeometryTest, PointRotate) {
946 {
947 Point a(1, 0);
948 auto rotated = a.Rotate(Radians{kPiOver2});
949 auto expected = Point(0, 1);
950 ASSERT_POINT_NEAR(rotated, expected);
951 }
952
953 {
954 Point a(1, 0);
955 auto rotated = a.Rotate(Radians{-kPiOver2});
956 auto expected = Point(0, -1);
957 ASSERT_POINT_NEAR(rotated, expected);
958 }
959
960 {
961 Point a(1, 0);
962 auto rotated = a.Rotate(Radians{kPi});
963 auto expected = Point(-1, 0);
964 ASSERT_POINT_NEAR(rotated, expected);
965 }
966
967 {
968 Point a(1, 0);
969 auto rotated = a.Rotate(Radians{kPi * 1.5});
970 auto expected = Point(0, -1);
971 ASSERT_POINT_NEAR(rotated, expected);
972 }
973 }
974
975 TEST(GeometryTest, PointAngleTo) {
976 // Negative result in the CCW (with up = -Y) direction.
977 {
978 Point a(1, 1);
979 Point b(1, -1);
980 Radians actual = a.AngleTo(b);
981 Radians expected = Radians{-kPi / 2};
982 ASSERT_FLOAT_EQ(actual.radians, expected.radians);
983 }
984
985 // Check the other direction to ensure the result is signed correctly.
986 {
987 Point a(1, -1);
988 Point b(1, 1);
989 Radians actual = a.AngleTo(b);
990 Radians expected = Radians{kPi / 2};
991 ASSERT_FLOAT_EQ(actual.radians, expected.radians);
992 }
993
994 // Differences in magnitude should have no impact on the result.
995 {
996 Point a(100, -100);
997 Point b(0.01, 0.01);
998 Radians actual = a.AngleTo(b);
999 Radians expected = Radians{kPi / 2};
1000 ASSERT_FLOAT_EQ(actual.radians, expected.radians);
1001 }
1002 }
1003
1004 TEST(GeometryTest, PointMin) {
1005 Point p(1, 2);
1006 Point result = p.Min({0, 10});
1007 Point expected(0, 2);
1008 ASSERT_POINT_NEAR(result, expected);
1009 }
1010
1011 TEST(GeometryTest, Vector3Min) {
1012 Vector3 p(1, 2, 3);
1013 Vector3 result = p.Min({0, 10, 2});
1014 Vector3 expected(0, 2, 2);
1015 ASSERT_VECTOR3_NEAR(result, expected);
1016 }
1017
1018 TEST(GeometryTest, Vector4Min) {
1019 Vector4 p(1, 2, 3, 4);
1020 Vector4 result = p.Min({0, 10, 2, 1});
1021 Vector4 expected(0, 2, 2, 1);
1022 ASSERT_VECTOR4_NEAR(result, expected);
1023 }
1024
1025 TEST(GeometryTest, PointMax) {
1026 Point p(1, 2);
1027 Point result = p.Max({0, 10});
1028 Point expected(1, 10);
1029 ASSERT_POINT_NEAR(result, expected);
1030 }
1031
1032 TEST(GeometryTest, Vector3Max) {
1033 Vector3 p(1, 2, 3);
1034 Vector3 result = p.Max({0, 10, 2});
1035 Vector3 expected(1, 10, 3);
1036 ASSERT_VECTOR3_NEAR(result, expected);
1037 }
1038
1039 TEST(GeometryTest, Vector4Max) {
1040 Vector4 p(1, 2, 3, 4);
1041 Vector4 result = p.Max({0, 10, 2, 1});
1042 Vector4 expected(1, 10, 3, 4);
1043 ASSERT_VECTOR4_NEAR(result, expected);
1044 }
1045
1046 TEST(GeometryTest, PointFloor) {
1047 Point p(1.5, 2.3);
1048 Point result = p.Floor();
1049 Point expected(1, 2);
1050 ASSERT_POINT_NEAR(result, expected);
1051 }
1052
1053 TEST(GeometryTest, Vector3Floor) {
1054 Vector3 p(1.5, 2.3, 3.9);
1055 Vector3 result = p.Floor();
1056 Vector3 expected(1, 2, 3);
1057 ASSERT_VECTOR3_NEAR(result, expected);
1058 }
1059
1060 TEST(GeometryTest, Vector4Floor) {
1061 Vector4 p(1.5, 2.3, 3.9, 4.0);
1062 Vector4 result = p.Floor();
1063 Vector4 expected(1, 2, 3, 4);
1064 ASSERT_VECTOR4_NEAR(result, expected);
1065 }
1066
1067 TEST(GeometryTest, PointCeil) {
1068 Point p(1.5, 2.3);
1069 Point result = p.Ceil();
1070 Point expected(2, 3);
1071 ASSERT_POINT_NEAR(result, expected);
1072 }
1073
1074 TEST(GeometryTest, Vector3Ceil) {
1075 Vector3 p(1.5, 2.3, 3.9);
1076 Vector3 result = p.Ceil();
1077 Vector3 expected(2, 3, 4);
1078 ASSERT_VECTOR3_NEAR(result, expected);
1079 }
1080
1081 TEST(GeometryTest, Vector4Ceil) {
1082 Vector4 p(1.5, 2.3, 3.9, 4.0);
1083 Vector4 result = p.Ceil();
1084 Vector4 expected(2, 3, 4, 4);
1085 ASSERT_VECTOR4_NEAR(result, expected);
1086 }
1087
1088 TEST(GeometryTest, PointRound) {
1089 Point p(1.5, 2.3);
1090 Point result = p.Round();
1091 Point expected(2, 2);
1092 ASSERT_POINT_NEAR(result, expected);
1093 }
1094
1095 TEST(GeometryTest, Vector3Round) {
1096 Vector3 p(1.5, 2.3, 3.9);
1097 Vector3 result = p.Round();
1098 Vector3 expected(2, 2, 4);
1099 ASSERT_VECTOR3_NEAR(result, expected);
1100 }
1101
1102 TEST(GeometryTest, Vector4Round) {
1103 Vector4 p(1.5, 2.3, 3.9, 4.0);
1104 Vector4 result = p.Round();
1105 Vector4 expected(2, 2, 4, 4);
1106 ASSERT_VECTOR4_NEAR(result, expected);
1107 }
1108
1109 TEST(GeometryTest, PointLerp) {
1110 Point p(1, 2);
1111 Point result = p.Lerp({5, 10}, 0.75);
1112 Point expected(4, 8);
1113 ASSERT_POINT_NEAR(result, expected);
1114 }
1115
1116 TEST(GeometryTest, Vector3Lerp) {
1117 Vector3 p(1, 2, 3);
1118 Vector3 result = p.Lerp({5, 10, 15}, 0.75);
1119 Vector3 expected(4, 8, 12);
1120 ASSERT_VECTOR3_NEAR(result, expected);
1121 }
1122
1123 TEST(GeometryTest, Vector4Lerp) {
1124 Vector4 p(1, 2, 3, 4);
1125 Vector4 result = p.Lerp({5, 10, 15, 20}, 0.75);
1126 Vector4 expected(4, 8, 12, 16);
1127 ASSERT_VECTOR4_NEAR(result, expected);
1128 }
1129
1130 TEST(GeometryTest, SeparatedVector2NormalizesWithConstructor) {
1131 SeparatedVector2 v(Vector2(10, 0));
1132 ASSERT_POINT_NEAR(v.direction, Vector2(1, 0));
1133 ASSERT_NEAR(v.magnitude, 10, kEhCloseEnough);
1134 }
1135
1136 TEST(GeometryTest, SeparatedVector2GetVector) {
1137 SeparatedVector2 v(Vector2(10, 0));
1138 ASSERT_POINT_NEAR(v.GetVector(), Vector2(10, 0));
1139 }
1140
1141 TEST(GeometryTest, SeparatedVector2GetAlignment) {
1142 // Parallel
1143 {
1144 SeparatedVector2 v(Vector2(10, 0));
1145 Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(5, 0)));
1146 ASSERT_NEAR(actual, 1, kEhCloseEnough);
1147 }
1148
1149 // Perpendicular
1150 {
1151 SeparatedVector2 v(Vector2(10, 0));
1152 Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(0, 5)));
1153 ASSERT_NEAR(actual, 0, kEhCloseEnough);
1154 }
1155
1156 // Opposite parallel
1157 {
1158 SeparatedVector2 v(Vector2(0, 10));
1159 Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(0, -5)));
1160 ASSERT_NEAR(actual, -1, kEhCloseEnough);
1161 }
1162 }
1163
1164 TEST(GeometryTest, SeparatedVector2AngleTo) {
1165 {
1166 SeparatedVector2 v(Vector2(10, 0));
1167 Radians actual = v.AngleTo(SeparatedVector2(Vector2(5, 0)));
1168 Radians expected = Radians{0};
1169 ASSERT_NEAR(actual.radians, expected.radians, kEhCloseEnough);
1170 }
1171
1172 {
1173 SeparatedVector2 v(Vector2(10, 0));
1174 Radians actual = v.AngleTo(SeparatedVector2(Vector2(0, -5)));
1175 Radians expected = Radians{-kPi / 2};
1176 ASSERT_NEAR(actual.radians, expected.radians, kEhCloseEnough);
1177 }
1178 }
1179
1180 TEST(GeometryTest, CanUseVector3AssignmentOperators) {
1181 {
1182 Vector3 p(1, 2, 4);
1183 p += Vector3(1, 2, 4);
1184 ASSERT_EQ(p.x, 2u);
1185 ASSERT_EQ(p.y, 4u);
1186 ASSERT_EQ(p.z, 8u);
1187 }
1188
1189 {
1190 Vector3 p(3, 6, 8);
1191 p -= Vector3(1, 2, 3);
1192 ASSERT_EQ(p.x, 2u);
1193 ASSERT_EQ(p.y, 4u);
1194 ASSERT_EQ(p.z, 5u);
1195 }
1196
1197 {
1198 Vector3 p(1, 2, 3);
1199 p *= Vector3(2, 3, 4);
1200 ASSERT_EQ(p.x, 2u);
1201 ASSERT_EQ(p.y, 6u);
1202 ASSERT_EQ(p.z, 12u);
1203 }
1204
1205 {
1206 Vector3 p(1, 2, 3);
1207 p *= 2;
1208 ASSERT_EQ(p.x, 2u);
1209 ASSERT_EQ(p.y, 4u);
1210 ASSERT_EQ(p.z, 6u);
1211 }
1212
1213 {
1214 Vector3 p(2, 6, 12);
1215 p /= Vector3(2, 3, 4);
1216 ASSERT_EQ(p.x, 1u);
1217 ASSERT_EQ(p.y, 2u);
1218 ASSERT_EQ(p.z, 3u);
1219 }
1220
1221 {
1222 Vector3 p(2, 6, 12);
1223 p /= 2;
1224 ASSERT_EQ(p.x, 1u);
1225 ASSERT_EQ(p.y, 3u);
1226 ASSERT_EQ(p.z, 6u);
1227 }
1228 }
1229
1230 TEST(GeometryTest, CanPerformAlgebraicVector3Ops) {
1231 {
1232 Vector3 p1(1, 2, 3);
1233 Vector3 p2 = p1 + Vector3(1, 2, 3);
1234 ASSERT_EQ(p2.x, 2u);
1235 ASSERT_EQ(p2.y, 4u);
1236 ASSERT_EQ(p2.z, 6u);
1237 }
1238
1239 {
1240 Vector3 p1(3, 6, 9);
1241 Vector3 p2 = p1 - Vector3(1, 2, 3);
1242 ASSERT_EQ(p2.x, 2u);
1243 ASSERT_EQ(p2.y, 4u);
1244 ASSERT_EQ(p2.z, 6u);
1245 }
1246
1247 {
1248 Vector3 p1(1, 2, 3);
1249 Vector3 p2 = p1 * Vector3(2, 3, 4);
1250 ASSERT_EQ(p2.x, 2u);
1251 ASSERT_EQ(p2.y, 6u);
1252 ASSERT_EQ(p2.z, 12u);
1253 }
1254
1255 {
1256 Vector3 p1(2, 6, 12);
1257 Vector3 p2 = p1 / Vector3(2, 3, 4);
1258 ASSERT_EQ(p2.x, 1u);
1259 ASSERT_EQ(p2.y, 2u);
1260 ASSERT_EQ(p2.z, 3u);
1261 }
1262 }
1263
1264 TEST(GeometryTest, CanPerformAlgebraicVector3OpsWithArithmeticTypes) {
1265 // LHS
1266 {
1267 Vector3 p1(1, 2, 3);
1268 Vector3 p2 = p1 + 2.0f;
1269 ASSERT_EQ(p2.x, 3);
1270 ASSERT_EQ(p2.y, 4);
1271 ASSERT_EQ(p2.z, 5);
1272 }
1273
1274 {
1275 Vector3 p1(1, 2, 3);
1276 Vector3 p2 = p1 - 2.0f;
1277 ASSERT_EQ(p2.x, -1);
1278 ASSERT_EQ(p2.y, 0);
1279 ASSERT_EQ(p2.z, 1);
1280 }
1281
1282 {
1283 Vector3 p1(1, 2, 3);
1284 Vector3 p2 = p1 * 2.0f;
1285 ASSERT_EQ(p2.x, 2);
1286 ASSERT_EQ(p2.y, 4);
1287 ASSERT_EQ(p2.z, 6);
1288 }
1289
1290 {
1291 Vector3 p1(2, 6, 12);
1292 Vector3 p2 = p1 / 2.0f;
1293 ASSERT_EQ(p2.x, 1);
1294 ASSERT_EQ(p2.y, 3);
1295 ASSERT_EQ(p2.z, 6);
1296 }
1297
1298 // RHS
1299 {
1300 Vector3 p1(1, 2, 3);
1301 Vector3 p2 = 2.0f + p1;
1302 ASSERT_EQ(p2.x, 3);
1303 ASSERT_EQ(p2.y, 4);
1304 ASSERT_EQ(p2.z, 5);
1305 }
1306
1307 {
1308 Vector3 p1(1, 2, 3);
1309 Vector3 p2 = 2.0f - p1;
1310 ASSERT_EQ(p2.x, 1);
1311 ASSERT_EQ(p2.y, 0);
1312 ASSERT_EQ(p2.z, -1);
1313 }
1314
1315 {
1316 Vector3 p1(1, 2, 3);
1317 Vector3 p2 = 2.0f * p1;
1318 ASSERT_EQ(p2.x, 2);
1319 ASSERT_EQ(p2.y, 4);
1320 ASSERT_EQ(p2.z, 6);
1321 }
1322
1323 {
1324 Vector3 p1(2, 6, 12);
1325 Vector3 p2 = 12.0f / p1;
1326 ASSERT_EQ(p2.x, 6);
1327 ASSERT_EQ(p2.y, 2);
1328 ASSERT_EQ(p2.z, 1);
1329 }
1330 }
1331
1332 TEST(GeometryTest, ColorPremultiply) {
1333 {
1334 Color a(1.0, 0.5, 0.2, 0.5);
1335 Color premultiplied = a.Premultiply();
1336 Color expected = Color(0.5, 0.25, 0.1, 0.5);
1337 ASSERT_COLOR_NEAR(premultiplied, expected);
1338 }
1339
1340 {
1341 Color a(0.5, 0.25, 0.1, 0.5);
1342 Color unpremultiplied = a.Unpremultiply();
1343 Color expected = Color(1.0, 0.5, 0.2, 0.5);
1344 ASSERT_COLOR_NEAR(unpremultiplied, expected);
1345 }
1346
1347 {
1348 Color a(0.5, 0.25, 0.1, 0.0);
1349 Color unpremultiplied = a.Unpremultiply();
1350 Color expected = Color(0.0, 0.0, 0.0, 0.0);
1351 ASSERT_COLOR_NEAR(unpremultiplied, expected);
1352 }
1353 }
1354
1355 TEST(GeometryTest, ColorR8G8B8A8) {
1356 {
1357 Color a(1.0, 0.5, 0.2, 0.5);
1358 std::array<uint8_t, 4> expected = {255, 128, 51, 128};
1359 ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1360 }
1361
1362 {
1363 Color a(0.0, 0.0, 0.0, 0.0);
1364 std::array<uint8_t, 4> expected = {0, 0, 0, 0};
1365 ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1366 }
1367
1368 {
1369 Color a(1.0, 1.0, 1.0, 1.0);
1370 std::array<uint8_t, 4> expected = {255, 255, 255, 255};
1371 ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1372 }
1373 }
1374
1375 TEST(GeometryTest, ColorLerp) {
1376 {
1377 Color a(0.0, 0.0, 0.0, 0.0);
1378 Color b(1.0, 1.0, 1.0, 1.0);
1379
1380 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.5), Color(0.5, 0.5, 0.5, 0.5));
1381 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.0), a);
1382 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 1.0), b);
1383 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.2), Color(0.2, 0.2, 0.2, 0.2));
1384 }
1385
1386 {
1387 Color a(0.2, 0.4, 1.0, 0.5);
1388 Color b(0.4, 1.0, 0.2, 0.3);
1389
1390 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.5), Color(0.3, 0.7, 0.6, 0.4));
1391 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.0), a);
1392 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 1.0), b);
1393 ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.2), Color(0.24, 0.52, 0.84, 0.46));
1394 }
1395 }
1396
1397 TEST(GeometryTest, ColorClamp01) {
1398 {
1399 Color result = Color(0.5, 0.5, 0.5, 0.5).Clamp01();
1400 Color expected = Color(0.5, 0.5, 0.5, 0.5);
1401 ASSERT_COLOR_NEAR(result, expected);
1402 }
1403
1404 {
1405 Color result = Color(-1, -1, -1, -1).Clamp01();
1406 Color expected = Color(0, 0, 0, 0);
1407 ASSERT_COLOR_NEAR(result, expected);
1408 }
1409
1410 {
1411 Color result = Color(2, 2, 2, 2).Clamp01();
1412 Color expected = Color(1, 1, 1, 1);
1413 ASSERT_COLOR_NEAR(result, expected);
1414 }
1415 }
1416
1417 TEST(GeometryTest, ColorMakeRGBA8) {
1418 {
1419 Color a = Color::MakeRGBA8(0, 0, 0, 0);
1420 Color b = Color::BlackTransparent();
1421 ASSERT_COLOR_NEAR(a, b);
1422 }
1423
1424 {
1425 Color a = Color::MakeRGBA8(255, 255, 255, 255);
1426 Color b = Color::White();
1427 ASSERT_COLOR_NEAR(a, b);
1428 }
1429
1430 {
1431 Color a = Color::MakeRGBA8(63, 127, 191, 127);
1432 Color b(0.247059, 0.498039, 0.74902, 0.498039);
1433 ASSERT_COLOR_NEAR(a, b);
1434 }
1435 }
1436
1437 TEST(GeometryTest, ColorApplyColorMatrix) {
1438 {
1439 ColorMatrix color_matrix = {
1440 1, 1, 1, 1, 1, //
1441 1, 1, 1, 1, 1, //
1442 1, 1, 1, 1, 1, //
1443 1, 1, 1, 1, 1, //
1444 };
1445 auto result = Color::White().ApplyColorMatrix(color_matrix);
1446 auto expected = Color(1, 1, 1, 1);
1447 ASSERT_COLOR_NEAR(result, expected);
1448 }
1449
1450 {
1451 ColorMatrix color_matrix = {
1452 0.1, 0, 0, 0, 0.01, //
1453 0, 0.2, 0, 0, 0.02, //
1454 0, 0, 0.3, 0, 0.03, //
1455 0, 0, 0, 0.4, 0.04, //
1456 };
1457 auto result = Color::White().ApplyColorMatrix(color_matrix);
1458 auto expected = Color(0.11, 0.22, 0.33, 0.44);
1459 ASSERT_COLOR_NEAR(result, expected);
1460 }
1461 }
1462
1463 TEST(GeometryTest, ColorLinearToSRGB) {
1464 {
1465 auto result = Color::White().LinearToSRGB();
1466 auto expected = Color(1, 1, 1, 1);
1467 ASSERT_COLOR_NEAR(result, expected);
1468 }
1469
1470 {
1471 auto result = Color::BlackTransparent().LinearToSRGB();
1472 auto expected = Color(0, 0, 0, 0);
1473 ASSERT_COLOR_NEAR(result, expected);
1474 }
1475
1476 {
1477 auto result = Color(0.2, 0.4, 0.6, 0.8).LinearToSRGB();
1478 auto expected = Color(0.484529, 0.665185, 0.797738, 0.8);
1479 ASSERT_COLOR_NEAR(result, expected);
1480 }
1481 }
1482
1483 TEST(GeometryTest, ColorSRGBToLinear) {
1484 {
1485 auto result = Color::White().SRGBToLinear();
1486 auto expected = Color(1, 1, 1, 1);
1487 ASSERT_COLOR_NEAR(result, expected);
1488 }
1489
1490 {
1491 auto result = Color::BlackTransparent().SRGBToLinear();
1492 auto expected = Color(0, 0, 0, 0);
1493 ASSERT_COLOR_NEAR(result, expected);
1494 }
1495
1496 {
1497 auto result = Color(0.2, 0.4, 0.6, 0.8).SRGBToLinear();
1498 auto expected = Color(0.0331048, 0.132868, 0.318547, 0.8);
1499 ASSERT_COLOR_NEAR(result, expected);
1500 }
1501 }
1502
1503 struct ColorBlendTestData {
1504 static constexpr Color kDestinationColor =
1505 Color::CornflowerBlue().WithAlpha(0.75);
1506 static constexpr Color kSourceColors[] = {Color::White().WithAlpha(0.75),
1507 Color::LimeGreen().WithAlpha(0.75),
1508 Color::Black().WithAlpha(0.75)};
1509
1510 static const std::map<BlendMode, Color>
1511 kExpectedResults[sizeof(kSourceColors)];
1512 };
1513
1514 // THIS RESULT TABLE IS GENERATED!
1515 //
1516 // Uncomment the `GenerateColorBlendResults` test below to print a new table
1517 // after making changes to `Color::Blend`.
1518 const std::map<BlendMode, Color> ColorBlendTestData::kExpectedResults[sizeof(
1519 ColorBlendTestData::kSourceColors)] = {
1520 {
1521 {BlendMode::kClear, {0, 0, 0, 0}},
1522 {BlendMode::kSource, {1, 1, 1, 0.75}},
1523 {BlendMode::kDestination, {0.392157, 0.584314, 0.929412, 0.75}},
1524 {BlendMode::kSourceOver, {0.878431, 0.916863, 0.985882, 0.9375}},
1525 {BlendMode::kDestinationOver, {0.513726, 0.667451, 0.943529, 0.9375}},
1526 {BlendMode::kSourceIn, {1, 1, 1, 0.5625}},
1527 {BlendMode::kDestinationIn, {0.392157, 0.584314, 0.929412, 0.5625}},
1528 {BlendMode::kSourceOut, {1, 1, 1, 0.1875}},
1529 {BlendMode::kDestinationOut, {0.392157, 0.584314, 0.929412, 0.1875}},
1530 {BlendMode::kSourceATop, {0.848039, 0.896078, 0.982353, 0.75}},
1531 {BlendMode::kDestinationATop, {0.544118, 0.688235, 0.947059, 0.75}},
1532 {BlendMode::kXor, {0.696078, 0.792157, 0.964706, 0.375}},
1533 {BlendMode::kPlus, {1, 1, 1, 1}},
1534 {BlendMode::kModulate, {0.392157, 0.584314, 0.929412, 0.5625}},
1535 {BlendMode::kScreen, {0.878431, 0.916863, 0.985882, 0.9375}},
1536 {BlendMode::kOverlay, {0.74902, 0.916863, 0.985882, 0.9375}},
1537 {BlendMode::kDarken, {0.513726, 0.667451, 0.943529, 0.9375}},
1538 {BlendMode::kLighten, {0.878431, 0.916863, 0.985882, 0.9375}},
1539 {BlendMode::kColorDodge, {0.878431, 0.916863, 0.985882, 0.9375}},
1540 {BlendMode::kColorBurn, {0.513725, 0.667451, 0.943529, 0.9375}},
1541 {BlendMode::kHardLight, {0.878431, 0.916863, 0.985882, 0.9375}},
1542 {BlendMode::kSoftLight, {0.654166, 0.775505, 0.964318, 0.9375}},
1543 {BlendMode::kDifference, {0.643137, 0.566275, 0.428235, 0.9375}},
1544 {BlendMode::kExclusion, {0.643137, 0.566275, 0.428235, 0.9375}},
1545 {BlendMode::kMultiply, {0.513726, 0.667451, 0.943529, 0.9375}},
1546 {BlendMode::kHue, {0.617208, 0.655639, 0.724659, 0.9375}},
1547 {BlendMode::kSaturation, {0.617208, 0.655639, 0.724659, 0.9375}},
1548 {BlendMode::kColor, {0.617208, 0.655639, 0.724659, 0.9375}},
1549 {BlendMode::kLuminosity, {0.878431, 0.916863, 0.985882, 0.9375}},
1550 },
1551 {
1552 {BlendMode::kClear, {0, 0, 0, 0}},
1553 {BlendMode::kSource, {0.196078, 0.803922, 0.196078, 0.75}},
1554 {BlendMode::kDestination, {0.392157, 0.584314, 0.929412, 0.75}},
1555 {BlendMode::kSourceOver, {0.235294, 0.76, 0.342745, 0.9375}},
1556 {BlendMode::kDestinationOver, {0.352941, 0.628235, 0.782745, 0.9375}},
1557 {BlendMode::kSourceIn, {0.196078, 0.803922, 0.196078, 0.5625}},
1558 {BlendMode::kDestinationIn, {0.392157, 0.584314, 0.929412, 0.5625}},
1559 {BlendMode::kSourceOut, {0.196078, 0.803922, 0.196078, 0.1875}},
1560 {BlendMode::kDestinationOut, {0.392157, 0.584314, 0.929412, 0.1875}},
1561 {BlendMode::kSourceATop, {0.245098, 0.74902, 0.379412, 0.75}},
1562 {BlendMode::kDestinationATop, {0.343137, 0.639216, 0.746078, 0.75}},
1563 {BlendMode::kXor, {0.294118, 0.694118, 0.562745, 0.375}},
1564 {BlendMode::kPlus, {0.441176, 1, 0.844118, 1}},
1565 {BlendMode::kModulate, {0.0768935, 0.469742, 0.182238, 0.5625}},
1566 {BlendMode::kScreen, {0.424452, 0.828743, 0.79105, 0.9375}},
1567 {BlendMode::kOverlay, {0.209919, 0.779839, 0.757001, 0.9375}},
1568 {BlendMode::kDarken, {0.235294, 0.628235, 0.342745, 0.9375}},
1569 {BlendMode::kLighten, {0.352941, 0.76, 0.782745, 0.9375}},
1570 {BlendMode::kColorDodge, {0.41033, 0.877647, 0.825098, 0.9375}},
1571 {BlendMode::kColorBurn, {0.117647, 0.567403, 0.609098, 0.9375}},
1572 {BlendMode::kHardLight, {0.209919, 0.779839, 0.443783, 0.9375}},
1573 {BlendMode::kSoftLight, {0.266006, 0.693915, 0.758818, 0.9375}},
1574 {BlendMode::kDifference, {0.235294, 0.409412, 0.665098, 0.9375}},
1575 {BlendMode::kExclusion, {0.378316, 0.546897, 0.681707, 0.9375}},
1576 {BlendMode::kMultiply, {0.163783, 0.559493, 0.334441, 0.9375}},
1577 {BlendMode::kHue, {0.266235, 0.748588, 0.373686, 0.9375}},
1578 {BlendMode::kSaturation, {0.339345, 0.629787, 0.811502, 0.9375}},
1579 {BlendMode::kColor, {0.241247, 0.765953, 0.348698, 0.9375}},
1580 {BlendMode::kLuminosity, {0.346988, 0.622282, 0.776792, 0.9375}},
1581 },
1582 {
1583 {BlendMode::kClear, {0, 0, 0, 0}},
1584 {BlendMode::kSource, {0, 0, 0, 0.75}},
1585 {BlendMode::kDestination, {0.392157, 0.584314, 0.929412, 0.75}},
1586 {BlendMode::kSourceOver, {0.0784314, 0.116863, 0.185882, 0.9375}},
1587 {BlendMode::kDestinationOver, {0.313726, 0.467451, 0.743529, 0.9375}},
1588 {BlendMode::kSourceIn, {0, 0, 0, 0.5625}},
1589 {BlendMode::kDestinationIn, {0.392157, 0.584314, 0.929412, 0.5625}},
1590 {BlendMode::kSourceOut, {0, 0, 0, 0.1875}},
1591 {BlendMode::kDestinationOut, {0.392157, 0.584314, 0.929412, 0.1875}},
1592 {BlendMode::kSourceATop, {0.0980392, 0.146078, 0.232353, 0.75}},
1593 {BlendMode::kDestinationATop, {0.294118, 0.438235, 0.697059, 0.75}},
1594 {BlendMode::kXor, {0.196078, 0.292157, 0.464706, 0.375}},
1595 {BlendMode::kPlus, {0.294118, 0.438235, 0.697059, 1}},
1596 {BlendMode::kModulate, {0, 0, 0, 0.5625}},
1597 {BlendMode::kScreen, {0.313726, 0.467451, 0.743529, 0.9375}},
1598 {BlendMode::kOverlay, {0.0784314, 0.218039, 0.701176, 0.9375}},
1599 {BlendMode::kDarken, {0.0784314, 0.116863, 0.185882, 0.9375}},
1600 {BlendMode::kLighten, {0.313726, 0.467451, 0.743529, 0.9375}},
1601 {BlendMode::kColorDodge, {0.313726, 0.467451, 0.743529, 0.9375}},
1602 {BlendMode::kColorBurn, {0.0784314, 0.116863, 0.185882, 0.9375}},
1603 {BlendMode::kHardLight, {0.0784314, 0.116863, 0.185882, 0.9375}},
1604 {BlendMode::kSoftLight, {0.170704, 0.321716, 0.704166, 0.9375}},
1605 {BlendMode::kDifference, {0.313726, 0.467451, 0.743529, 0.9375}},
1606 {BlendMode::kExclusion, {0.313726, 0.467451, 0.743529, 0.9375}},
1607 {BlendMode::kMultiply, {0.0784314, 0.116863, 0.185882, 0.9375}},
1608 {BlendMode::kHue, {0.417208, 0.455639, 0.524659, 0.9375}},
1609 {BlendMode::kSaturation, {0.417208, 0.455639, 0.524659, 0.9375}},
1610 {BlendMode::kColor, {0.417208, 0.455639, 0.524659, 0.9375}},
1611 {BlendMode::kLuminosity, {0.0784314, 0.116863, 0.185882, 0.9375}},
1612 },
1613 };
1614
1615 /// To print a new ColorBlendTestData::kExpectedResults table, uncomment this
1616 /// test and run with:
1617 /// --gtest_filter="GeometryTest.GenerateColorBlendResults"
1618 /*
1619 TEST(GeometryTest, GenerateColorBlendResults) {
1620 auto& o = std::cout;
1621 using BlendT = std::underlying_type_t<BlendMode>;
1622 o << "{";
1623 for (const auto& source : ColorBlendTestData::kSourceColors) {
1624 o << "{";
1625 for (BlendT blend_i = 0;
1626 blend_i < static_cast<BlendT>(BlendMode::kLast) + 1; blend_i++) {
1627 auto blend = static_cast<BlendMode>(blend_i);
1628 Color c = ColorBlendTestData::kDestinationColor.Blend(source, blend);
1629 o << "{ BlendMode::k" << BlendModeToString(blend) << ", ";
1630 o << "{" << c.red << "," << c.green << "," << c.blue << "," << c.alpha
1631 << "}";
1632 o << "}," << std::endl;
1633 }
1634 o << "},";
1635 }
1636 o << "};" << std::endl;
1637 }
1638 */
1639
1640 #define _BLEND_MODE_RESULT_CHECK(blend_mode) \
1641 expected = ColorBlendTestData::kExpectedResults[source_i] \
1642 .find(BlendMode::k##blend_mode) \
1643 ->second; \
1644 EXPECT_COLOR_NEAR(dst.Blend(src, BlendMode::k##blend_mode), expected);
1645
1646 TEST(GeometryTest, ColorBlendReturnsExpectedResults) {
1647 Color dst = ColorBlendTestData::kDestinationColor;
1648 for (size_t source_i = 0;
1649 source_i < sizeof(ColorBlendTestData::kSourceColors) / sizeof(Color);
1650 source_i++) {
1651 Color src = ColorBlendTestData::kSourceColors[source_i];
1652
1653 Color expected;
1654 IMPELLER_FOR_EACH_BLEND_MODE(_BLEND_MODE_RESULT_CHECK)
1655 }
1656 }
1657
1658 #define _BLEND_MODE_NAME_CHECK(blend_mode) \
1659 case BlendMode::k##blend_mode: \
1660 ASSERT_STREQ(result, #blend_mode); \
1661 break;
1662
1663 TEST(GeometryTest, BlendModeToString) {
1664 using BlendT = std::underlying_type_t<BlendMode>;
1665 for (BlendT i = 0; i <= static_cast<BlendT>(BlendMode::kLast); i++) {
1666 auto mode = static_cast<BlendMode>(i);
1667 auto result = BlendModeToString(mode);
1668 switch (mode) { IMPELLER_FOR_EACH_BLEND_MODE(_BLEND_MODE_NAME_CHECK) }
1669 }
1670 }
1671
1672 TEST(GeometryTest, CanConvertBetweenDegressAndRadians) {
1673 {
1674 auto deg = Degrees{90.0};
1675 Radians rad = deg;
1676 ASSERT_FLOAT_EQ(rad.radians, kPiOver2);
1677 }
1678 }
1679
1680 TEST(GeometryTest, MatrixPrinting) {
1681 {
1682 std::stringstream stream;
1683 Matrix m;
1684 stream << m;
1685 ASSERT_EQ(stream.str(), R"((
1686 1.000000, 0.000000, 0.000000, 0.000000,
1687 0.000000, 1.000000, 0.000000, 0.000000,
1688 0.000000, 0.000000, 1.000000, 0.000000,
1689 0.000000, 0.000000, 0.000000, 1.000000,
1690 ))");
1691 }
1692
1693 {
1694 std::stringstream stream;
1695 Matrix m = Matrix::MakeTranslation(Vector3(10, 20, 30));
1696 stream << m;
1697
1698 ASSERT_EQ(stream.str(), R"((
1699 1.000000, 0.000000, 0.000000, 10.000000,
1700 0.000000, 1.000000, 0.000000, 20.000000,
1701 0.000000, 0.000000, 1.000000, 30.000000,
1702 0.000000, 0.000000, 0.000000, 1.000000,
1703 ))");
1704 }
1705 }
1706
1707 TEST(GeometryTest, PointPrinting) {
1708 {
1709 std::stringstream stream;
1710 Point m;
1711 stream << m;
1712 ASSERT_EQ(stream.str(), "(0, 0)");
1713 }
1714
1715 {
1716 std::stringstream stream;
1717 Point m(13, 37);
1718 stream << m;
1719 ASSERT_EQ(stream.str(), "(13, 37)");
1720 }
1721 }
1722
1723 TEST(GeometryTest, Vector3Printing) {
1724 {
1725 std::stringstream stream;
1726 Vector3 m;
1727 stream << m;
1728 ASSERT_EQ(stream.str(), "(0, 0, 0)");
1729 }
1730
1731 {
1732 std::stringstream stream;
1733 Vector3 m(1, 2, 3);
1734 stream << m;
1735 ASSERT_EQ(stream.str(), "(1, 2, 3)");
1736 }
1737 }
1738
1739 TEST(GeometryTest, Vector4Printing) {
1740 {
1741 std::stringstream stream;
1742 Vector4 m;
1743 stream << m;
1744 ASSERT_EQ(stream.str(), "(0, 0, 0, 1)");
1745 }
1746
1747 {
1748 std::stringstream stream;
1749 Vector4 m(1, 2, 3, 4);
1750 stream << m;
1751 ASSERT_EQ(stream.str(), "(1, 2, 3, 4)");
1752 }
1753 }
1754
1755 TEST(GeometryTest, ColorPrinting) {
1756 {
1757 std::stringstream stream;
1758 Color m;
1759 stream << m;
1760 ASSERT_EQ(stream.str(), "(0, 0, 0, 0)");
1761 }
1762
1763 {
1764 std::stringstream stream;
1765 Color m(1, 2, 3, 4);
1766 stream << m;
1767 ASSERT_EQ(stream.str(), "(1, 2, 3, 4)");
1768 }
1769 }
1770
1771 TEST(GeometryTest, ToIColor) {
1772 ASSERT_EQ(Color::ToIColor(Color(0, 0, 0, 0)), 0u);
1773 ASSERT_EQ(Color::ToIColor(Color(1.0, 1.0, 1.0, 1.0)), 0xFFFFFFFF);
1774 ASSERT_EQ(Color::ToIColor(Color(0.5, 0.5, 1.0, 1.0)), 0xFF8080FF);
1775 }
1776
1777 TEST(GeometryTest, Gradient) {
1778 {
1779 // Simple 2 color gradient produces color buffer containing exactly those
1780 // values.
1781 std::vector<Color> colors = {Color::Red(), Color::Blue()};
1782 std::vector<Scalar> stops = {0.0, 1.0};
1783
1784 auto gradient = CreateGradientBuffer(colors, stops);
1785
1786 ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, colors);
1787 ASSERT_EQ(gradient.texture_size, 2u);
1788 }
1789
1790 {
1791 // Gradient with duplicate stops does not create an empty texture.
1792 std::vector<Color> colors = {Color::Red(), Color::Yellow(), Color::Black(),
1793 Color::Blue()};
1794 std::vector<Scalar> stops = {0.0, 0.25, 0.25, 1.0};
1795
1796 auto gradient = CreateGradientBuffer(colors, stops);
1797 ASSERT_EQ(gradient.texture_size, 5u);
1798 }
1799
1800 {
1801 // Simple N color gradient produces color buffer containing exactly those
1802 // values.
1803 std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green(),
1804 Color::White()};
1805 std::vector<Scalar> stops = {0.0, 0.33, 0.66, 1.0};
1806
1807 auto gradient = CreateGradientBuffer(colors, stops);
1808
1809 ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, colors);
1810 ASSERT_EQ(gradient.texture_size, 4u);
1811 }
1812
1813 {
1814 // Gradient with color stops will lerp and scale buffer.
1815 std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green()};
1816 std::vector<Scalar> stops = {0.0, 0.25, 1.0};
1817
1818 auto gradient = CreateGradientBuffer(colors, stops);
1819
1820 std::vector<Color> lerped_colors = {
1821 Color::Red(),
1822 Color::Blue(),
1823 Color::Lerp(Color::Blue(), Color::Green(), 0.3333),
1824 Color::Lerp(Color::Blue(), Color::Green(), 0.6666),
1825 Color::Green(),
1826 };
1827 ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, lerped_colors);
1828 ASSERT_EQ(gradient.texture_size, 5u);
1829 }
1830
1831 {
1832 // Gradient size is capped at 1024.
1833 std::vector<Color> colors = {};
1834 std::vector<Scalar> stops = {};
1835 for (auto i = 0u; i < 1025; i++) {
1836 colors.push_back(Color::Blue());
1837 stops.push_back(i / 1025.0);
1838 }
1839
1840 auto gradient = CreateGradientBuffer(colors, stops);
1841
1842 ASSERT_EQ(gradient.texture_size, 1024u);
1843 ASSERT_EQ(gradient.color_bytes.size(), 1024u * 4);
1844 }
1845 }
1846
1847 TEST(GeometryTest, HalfConversions) {
1848 #if defined(FML_OS_MACOSX) || defined(FML_OS_IOS) || \
1849 defined(FML_OS_IOS_SIMULATOR)
1850 ASSERT_EQ(ScalarToHalf(0.0), 0.0f16);
1851 ASSERT_EQ(ScalarToHalf(0.05), 0.05f16);
1852 ASSERT_EQ(ScalarToHalf(2.43), 2.43f16);
1853 ASSERT_EQ(ScalarToHalf(-1.45), -1.45f16);
1854
1855 // 65504 is the largest possible half.
1856 ASSERT_EQ(ScalarToHalf(65504.0f), 65504.0f16);
1857 ASSERT_EQ(ScalarToHalf(65504.0f + 1), 65504.0f16);
1858
1859 // Colors
1860 ASSERT_EQ(HalfVector4(Color::Red()),
1861 HalfVector4(1.0f16, 0.0f16, 0.0f16, 1.0f16));
1862 ASSERT_EQ(HalfVector4(Color::Green()),
1863 HalfVector4(0.0f16, 1.0f16, 0.0f16, 1.0f16));
1864 ASSERT_EQ(HalfVector4(Color::Blue()),
1865 HalfVector4(0.0f16, 0.0f16, 1.0f16, 1.0f16));
1866 ASSERT_EQ(HalfVector4(Color::Black().WithAlpha(0)),
1867 HalfVector4(0.0f16, 0.0f16, 0.0f16, 0.0f16));
1868
1869 ASSERT_EQ(HalfVector3(Vector3(4.0, 6.0, -1.0)),
1870 HalfVector3(4.0f16, 6.0f16, -1.0f16));
1871 ASSERT_EQ(HalfVector2(Vector2(4.0, 6.0)), HalfVector2(4.0f16, 6.0f16));
1872
1873 ASSERT_EQ(Half(0.5f), Half(0.5f16));
1874 ASSERT_EQ(Half(0.5), Half(0.5f16));
1875 ASSERT_EQ(Half(5), Half(5.0f16));
1876 #else
1877 GTEST_SKIP() << "Half-precision floats (IEEE 754) are not portable and "
1878 "only used on Apple platforms.";
1879 #endif // FML_OS_MACOSX || FML_OS_IOS || FML_OS_IOS_SIMULATOR
1880 }
1881
1882 } // namespace testing
1883 } // namespace impeller
1884
1885 // 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:1640
impeller::Color::Blue
static constexpr Color Blue()
Definition: color.h:275
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:200
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:271
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:58
impeller::Color::MakeRGBA8
static constexpr Color MakeRGBA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Definition: color.h:151
impeller::Color
Definition: color.h:123
impeller::Color::Unpremultiply
constexpr Color Unpremultiply() const
Definition: color.h:215
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::TPoint::Round
static constexpr TPoint Round(const TPoint< U > &other)
Definition: point.h:49
impeller::Vector2
Point Vector2
Definition: point.h:331
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:341
impeller::BlendMode::kOverlay
@ kOverlay
impeller::Color::Yellow
static constexpr Color Yellow()
Definition: color.h:841
impeller::Radians::radians
Scalar radians
Definition: scalar.h:44
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:235
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:558
impeller::Matrix::MakePerspective
static constexpr Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:532
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:327
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:158
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:322
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:1511
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:277
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:245
impeller::SeparatedVector2::GetAlignment
Scalar GetAlignment(const SeparatedVector2 &other) const
Definition: separated_vector.cc:21
impeller::Color::White
static constexpr Color White()
Definition: color.h:263
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:43
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:1658
impeller::Color::Green
static constexpr Color Green()
Definition: color.h:273
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:321
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:328
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:269
impeller::Color::Black
static constexpr Color Black()
Definition: color.h:265
impeller::testing::ColorBlendTestData::kSourceColors
static constexpr Color kSourceColors[]
Definition: geometry_unittests.cc:1506
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:301
impeller::BlendMode::kScreen
@ kScreen
impeller::ScalarNearlyEqual
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:35
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:523
impeller::testing::ColorBlendTestData
Definition: geometry_unittests.cc:1503
impeller::testing::ColorBlendTestData::kDestinationColor
static constexpr Color kDestinationColor
Definition: geometry_unittests.cc:1504
impeller::Vector3::Min
constexpr Vector3 Min(const Vector3 &p) const
Definition: vector.h:70
impeller::Degrees
Definition: scalar.h:51
color.h
impeller::Color::LimeGreen
static constexpr Color LimeGreen()
Definition: color.h:601
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:231
impeller::TPoint::Normalize
constexpr TPoint Normalize() const
Definition: point.h:208
impeller::ColorMatrix
Definition: color.h:116
impeller::Color::ApplyColorMatrix
Color ApplyColorMatrix(const ColorMatrix &color_matrix) const
A color filter that transforms colors through a 4x5 color matrix.
Definition: color.cc:301
impeller::Color::LinearToSRGB
Color LinearToSRGB() const
Convert the color from linear space to sRGB space.
Definition: color.cc:311
ASSERT_COLOR_BUFFER_NEAR
#define ASSERT_COLOR_BUFFER_NEAR(a, b)
Definition: geometry_asserts.h:198
impeller
Definition: allocation.cc:12
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::testing::TEST
TEST(AllocationSizeTest, CanCreateTypedAllocations)
Definition: allocation_size_unittests.cc:10
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:211
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