Flutter Impeller
geometry_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "gtest/gtest.h"
7 
8 #include <limits>
9 #include <map>
10 #include <sstream>
11 #include <type_traits>
12 
13 #include "flutter/fml/build_config.h"
14 #include "flutter/testing/testing.h"
18 #include "impeller/geometry/half.h"
20 #include "impeller/geometry/rect.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}) *
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}) *
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}) *
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}) *
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}) *
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}) *
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}) *
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  {
422  Matrix::MakeScale(Vector3(2, 3, 4));
423  Vector3 x = m.GetBasisX();
424  Vector3 y = m.GetBasisY();
425  Vector3 z = m.GetBasisZ();
426  ASSERT_VECTOR3_NEAR(x, Vector3(0, 2, 0));
427  ASSERT_VECTOR3_NEAR(y, Vector3(0, 0, 3));
428  ASSERT_VECTOR3_NEAR(z, Vector3(4, 0, 0));
429  }
430 }
431 
432 TEST(GeometryTest, MatrixGetDirectionScale) {
433  {
434  auto m = Matrix();
435  Scalar result = m.GetDirectionScale(Vector3{1, 0, 0});
436  ASSERT_FLOAT_EQ(result, 1);
437  }
438 
439  {
440  auto m = Matrix::MakeRotationX(Degrees{10}) *
443  Scalar result = m.GetDirectionScale(Vector3{0, 1, 0});
444  ASSERT_FLOAT_EQ(result, 1);
445  }
446 
447  {
449  Matrix::MakeScale(Vector3(3, 4, 5));
450  Scalar result = m.GetDirectionScale(Vector3{2, 0, 0});
451  ASSERT_FLOAT_EQ(result, 8);
452  }
453 }
454 
455 TEST(GeometryTest, 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  EXPECT_EQ((Size{128, 128}.MipCount()), 7u);
582  EXPECT_EQ((Size{128, 256}.MipCount()), 7u);
583  EXPECT_EQ((Size{128, 130}.MipCount()), 7u);
584  EXPECT_EQ((Size{128, 257}.MipCount()), 7u);
585  EXPECT_EQ((Size{257, 128}.MipCount()), 7u);
586  EXPECT_EQ((Size{128, 0}.MipCount()), 1u);
587  EXPECT_EQ((Size{128, -25}.MipCount()), 1u);
588  EXPECT_EQ((Size{-128, 25}.MipCount()), 1u);
589  EXPECT_EQ((Size{1, 1}.MipCount()), 1u);
590  EXPECT_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, Vector4IsFinite) {
1012  {
1013  Vector4 v;
1014  ASSERT_TRUE(v.IsFinite());
1015  v.x = std::numeric_limits<Scalar>::infinity();
1016  ASSERT_FALSE(v.IsFinite());
1017  v.x = -std::numeric_limits<Scalar>::infinity();
1018  ASSERT_FALSE(v.IsFinite());
1019  v.x = -std::numeric_limits<Scalar>::quiet_NaN();
1020  ASSERT_FALSE(v.IsFinite());
1021  }
1022 
1023  {
1024  Vector4 v;
1025  ASSERT_TRUE(v.IsFinite());
1026  v.y = std::numeric_limits<Scalar>::infinity();
1027  ASSERT_FALSE(v.IsFinite());
1028  v.y = -std::numeric_limits<Scalar>::infinity();
1029  ASSERT_FALSE(v.IsFinite());
1030  v.y = -std::numeric_limits<Scalar>::quiet_NaN();
1031  ASSERT_FALSE(v.IsFinite());
1032  }
1033 
1034  {
1035  Vector4 v;
1036  ASSERT_TRUE(v.IsFinite());
1037  v.z = std::numeric_limits<Scalar>::infinity();
1038  ASSERT_FALSE(v.IsFinite());
1039  v.z = -std::numeric_limits<Scalar>::infinity();
1040  ASSERT_FALSE(v.IsFinite());
1041  v.z = -std::numeric_limits<Scalar>::quiet_NaN();
1042  ASSERT_FALSE(v.IsFinite());
1043  }
1044 
1045  {
1046  Vector4 v;
1047  ASSERT_TRUE(v.IsFinite());
1048  v.w = std::numeric_limits<Scalar>::infinity();
1049  ASSERT_FALSE(v.IsFinite());
1050  v.w = -std::numeric_limits<Scalar>::infinity();
1051  ASSERT_FALSE(v.IsFinite());
1052  v.w = -std::numeric_limits<Scalar>::quiet_NaN();
1053  ASSERT_FALSE(v.IsFinite());
1054  }
1055 }
1056 
1057 TEST(GeometryTest, Vector3Min) {
1058  Vector3 p(1, 2, 3);
1059  Vector3 result = p.Min({0, 10, 2});
1060  Vector3 expected(0, 2, 2);
1061  ASSERT_VECTOR3_NEAR(result, expected);
1062 }
1063 
1064 TEST(GeometryTest, Vector4Min) {
1065  Vector4 p(1, 2, 3, 4);
1066  Vector4 result = p.Min({0, 10, 2, 1});
1067  Vector4 expected(0, 2, 2, 1);
1068  ASSERT_VECTOR4_NEAR(result, expected);
1069 }
1070 
1071 TEST(GeometryTest, PointMax) {
1072  Point p(1, 2);
1073  Point result = p.Max({0, 10});
1074  Point expected(1, 10);
1075  ASSERT_POINT_NEAR(result, expected);
1076 }
1077 
1078 TEST(GeometryTest, Vector3Max) {
1079  Vector3 p(1, 2, 3);
1080  Vector3 result = p.Max({0, 10, 2});
1081  Vector3 expected(1, 10, 3);
1082  ASSERT_VECTOR3_NEAR(result, expected);
1083 }
1084 
1085 TEST(GeometryTest, Vector4Max) {
1086  Vector4 p(1, 2, 3, 4);
1087  Vector4 result = p.Max({0, 10, 2, 1});
1088  Vector4 expected(1, 10, 3, 4);
1089  ASSERT_VECTOR4_NEAR(result, expected);
1090 }
1091 
1092 TEST(GeometryTest, PointFloor) {
1093  Point p(1.5, 2.3);
1094  Point result = p.Floor();
1095  Point expected(1, 2);
1096  ASSERT_POINT_NEAR(result, expected);
1097 }
1098 
1099 TEST(GeometryTest, Vector3Floor) {
1100  Vector3 p(1.5, 2.3, 3.9);
1101  Vector3 result = p.Floor();
1102  Vector3 expected(1, 2, 3);
1103  ASSERT_VECTOR3_NEAR(result, expected);
1104 }
1105 
1106 TEST(GeometryTest, Vector4Floor) {
1107  Vector4 p(1.5, 2.3, 3.9, 4.0);
1108  Vector4 result = p.Floor();
1109  Vector4 expected(1, 2, 3, 4);
1110  ASSERT_VECTOR4_NEAR(result, expected);
1111 }
1112 
1113 TEST(GeometryTest, PointCeil) {
1114  Point p(1.5, 2.3);
1115  Point result = p.Ceil();
1116  Point expected(2, 3);
1117  ASSERT_POINT_NEAR(result, expected);
1118 }
1119 
1120 TEST(GeometryTest, Vector3Ceil) {
1121  Vector3 p(1.5, 2.3, 3.9);
1122  Vector3 result = p.Ceil();
1123  Vector3 expected(2, 3, 4);
1124  ASSERT_VECTOR3_NEAR(result, expected);
1125 }
1126 
1127 TEST(GeometryTest, Vector4Ceil) {
1128  Vector4 p(1.5, 2.3, 3.9, 4.0);
1129  Vector4 result = p.Ceil();
1130  Vector4 expected(2, 3, 4, 4);
1131  ASSERT_VECTOR4_NEAR(result, expected);
1132 }
1133 
1134 TEST(GeometryTest, PointRound) {
1135  Point p(1.5, 2.3);
1136  Point result = p.Round();
1137  Point expected(2, 2);
1138  ASSERT_POINT_NEAR(result, expected);
1139 }
1140 
1141 TEST(GeometryTest, Vector3Round) {
1142  Vector3 p(1.5, 2.3, 3.9);
1143  Vector3 result = p.Round();
1144  Vector3 expected(2, 2, 4);
1145  ASSERT_VECTOR3_NEAR(result, expected);
1146 }
1147 
1148 TEST(GeometryTest, Vector4Round) {
1149  Vector4 p(1.5, 2.3, 3.9, 4.0);
1150  Vector4 result = p.Round();
1151  Vector4 expected(2, 2, 4, 4);
1152  ASSERT_VECTOR4_NEAR(result, expected);
1153 }
1154 
1155 TEST(GeometryTest, PointLerp) {
1156  Point p(1, 2);
1157  Point result = p.Lerp({5, 10}, 0.75);
1158  Point expected(4, 8);
1159  ASSERT_POINT_NEAR(result, expected);
1160 }
1161 
1162 TEST(GeometryTest, Vector3Lerp) {
1163  Vector3 p(1, 2, 3);
1164  Vector3 result = p.Lerp({5, 10, 15}, 0.75);
1165  Vector3 expected(4, 8, 12);
1166  ASSERT_VECTOR3_NEAR(result, expected);
1167 }
1168 
1169 TEST(GeometryTest, Vector4Lerp) {
1170  Vector4 p(1, 2, 3, 4);
1171  Vector4 result = p.Lerp({5, 10, 15, 20}, 0.75);
1172  Vector4 expected(4, 8, 12, 16);
1173  ASSERT_VECTOR4_NEAR(result, expected);
1174 }
1175 
1176 TEST(GeometryTest, SeparatedVector2NormalizesWithConstructor) {
1177  SeparatedVector2 v(Vector2(10, 0));
1179  ASSERT_NEAR(v.magnitude, 10, kEhCloseEnough);
1180 }
1181 
1182 TEST(GeometryTest, SeparatedVector2GetVector) {
1183  SeparatedVector2 v(Vector2(10, 0));
1184  ASSERT_POINT_NEAR(v.GetVector(), Vector2(10, 0));
1185 }
1186 
1187 TEST(GeometryTest, SeparatedVector2GetAlignment) {
1188  // Parallel
1189  {
1190  SeparatedVector2 v(Vector2(10, 0));
1191  Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(5, 0)));
1192  ASSERT_NEAR(actual, 1, kEhCloseEnough);
1193  }
1194 
1195  // Perpendicular
1196  {
1197  SeparatedVector2 v(Vector2(10, 0));
1198  Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(0, 5)));
1199  ASSERT_NEAR(actual, 0, kEhCloseEnough);
1200  }
1201 
1202  // Opposite parallel
1203  {
1204  SeparatedVector2 v(Vector2(0, 10));
1205  Scalar actual = v.GetAlignment(SeparatedVector2(Vector2(0, -5)));
1206  ASSERT_NEAR(actual, -1, kEhCloseEnough);
1207  }
1208 }
1209 
1210 TEST(GeometryTest, SeparatedVector2AngleTo) {
1211  {
1212  SeparatedVector2 v(Vector2(10, 0));
1213  Radians actual = v.AngleTo(SeparatedVector2(Vector2(5, 0)));
1214  Radians expected = Radians{0};
1215  ASSERT_NEAR(actual.radians, expected.radians, kEhCloseEnough);
1216  }
1217 
1218  {
1219  SeparatedVector2 v(Vector2(10, 0));
1220  Radians actual = v.AngleTo(SeparatedVector2(Vector2(0, -5)));
1221  Radians expected = Radians{-kPi / 2};
1222  ASSERT_NEAR(actual.radians, expected.radians, kEhCloseEnough);
1223  }
1224 }
1225 
1226 TEST(GeometryTest, CanUseVector3AssignmentOperators) {
1227  {
1228  Vector3 p(1, 2, 4);
1229  p += Vector3(1, 2, 4);
1230  ASSERT_EQ(p.x, 2u);
1231  ASSERT_EQ(p.y, 4u);
1232  ASSERT_EQ(p.z, 8u);
1233  }
1234 
1235  {
1236  Vector3 p(3, 6, 8);
1237  p -= Vector3(1, 2, 3);
1238  ASSERT_EQ(p.x, 2u);
1239  ASSERT_EQ(p.y, 4u);
1240  ASSERT_EQ(p.z, 5u);
1241  }
1242 
1243  {
1244  Vector3 p(1, 2, 3);
1245  p *= Vector3(2, 3, 4);
1246  ASSERT_EQ(p.x, 2u);
1247  ASSERT_EQ(p.y, 6u);
1248  ASSERT_EQ(p.z, 12u);
1249  }
1250 
1251  {
1252  Vector3 p(1, 2, 3);
1253  p *= 2;
1254  ASSERT_EQ(p.x, 2u);
1255  ASSERT_EQ(p.y, 4u);
1256  ASSERT_EQ(p.z, 6u);
1257  }
1258 
1259  {
1260  Vector3 p(2, 6, 12);
1261  p /= Vector3(2, 3, 4);
1262  ASSERT_EQ(p.x, 1u);
1263  ASSERT_EQ(p.y, 2u);
1264  ASSERT_EQ(p.z, 3u);
1265  }
1266 
1267  {
1268  Vector3 p(2, 6, 12);
1269  p /= 2;
1270  ASSERT_EQ(p.x, 1u);
1271  ASSERT_EQ(p.y, 3u);
1272  ASSERT_EQ(p.z, 6u);
1273  }
1274 }
1275 
1276 TEST(GeometryTest, CanPerformAlgebraicVector3Ops) {
1277  {
1278  Vector3 p1(1, 2, 3);
1279  Vector3 p2 = p1 + Vector3(1, 2, 3);
1280  ASSERT_EQ(p2.x, 2u);
1281  ASSERT_EQ(p2.y, 4u);
1282  ASSERT_EQ(p2.z, 6u);
1283  }
1284 
1285  {
1286  Vector3 p1(3, 6, 9);
1287  Vector3 p2 = p1 - Vector3(1, 2, 3);
1288  ASSERT_EQ(p2.x, 2u);
1289  ASSERT_EQ(p2.y, 4u);
1290  ASSERT_EQ(p2.z, 6u);
1291  }
1292 
1293  {
1294  Vector3 p1(1, 2, 3);
1295  Vector3 p2 = p1 * Vector3(2, 3, 4);
1296  ASSERT_EQ(p2.x, 2u);
1297  ASSERT_EQ(p2.y, 6u);
1298  ASSERT_EQ(p2.z, 12u);
1299  }
1300 
1301  {
1302  Vector3 p1(2, 6, 12);
1303  Vector3 p2 = p1 / Vector3(2, 3, 4);
1304  ASSERT_EQ(p2.x, 1u);
1305  ASSERT_EQ(p2.y, 2u);
1306  ASSERT_EQ(p2.z, 3u);
1307  }
1308 }
1309 
1310 TEST(GeometryTest, CanPerformAlgebraicVector3OpsWithArithmeticTypes) {
1311  // LHS
1312  {
1313  Vector3 p1(1, 2, 3);
1314  Vector3 p2 = p1 + 2.0f;
1315  ASSERT_EQ(p2.x, 3);
1316  ASSERT_EQ(p2.y, 4);
1317  ASSERT_EQ(p2.z, 5);
1318  }
1319 
1320  {
1321  Vector3 p1(1, 2, 3);
1322  Vector3 p2 = p1 - 2.0f;
1323  ASSERT_EQ(p2.x, -1);
1324  ASSERT_EQ(p2.y, 0);
1325  ASSERT_EQ(p2.z, 1);
1326  }
1327 
1328  {
1329  Vector3 p1(1, 2, 3);
1330  Vector3 p2 = p1 * 2.0f;
1331  ASSERT_EQ(p2.x, 2);
1332  ASSERT_EQ(p2.y, 4);
1333  ASSERT_EQ(p2.z, 6);
1334  }
1335 
1336  {
1337  Vector3 p1(2, 6, 12);
1338  Vector3 p2 = p1 / 2.0f;
1339  ASSERT_EQ(p2.x, 1);
1340  ASSERT_EQ(p2.y, 3);
1341  ASSERT_EQ(p2.z, 6);
1342  }
1343 
1344  // RHS
1345  {
1346  Vector3 p1(1, 2, 3);
1347  Vector3 p2 = 2.0f + p1;
1348  ASSERT_EQ(p2.x, 3);
1349  ASSERT_EQ(p2.y, 4);
1350  ASSERT_EQ(p2.z, 5);
1351  }
1352 
1353  {
1354  Vector3 p1(1, 2, 3);
1355  Vector3 p2 = 2.0f - p1;
1356  ASSERT_EQ(p2.x, 1);
1357  ASSERT_EQ(p2.y, 0);
1358  ASSERT_EQ(p2.z, -1);
1359  }
1360 
1361  {
1362  Vector3 p1(1, 2, 3);
1363  Vector3 p2 = 2.0f * p1;
1364  ASSERT_EQ(p2.x, 2);
1365  ASSERT_EQ(p2.y, 4);
1366  ASSERT_EQ(p2.z, 6);
1367  }
1368 
1369  {
1370  Vector3 p1(2, 6, 12);
1371  Vector3 p2 = 12.0f / p1;
1372  ASSERT_EQ(p2.x, 6);
1373  ASSERT_EQ(p2.y, 2);
1374  ASSERT_EQ(p2.z, 1);
1375  }
1376 }
1377 
1378 TEST(GeometryTest, ColorPremultiply) {
1379  {
1380  Color a(1.0, 0.5, 0.2, 0.5);
1381  Color premultiplied = a.Premultiply();
1382  Color expected = Color(0.5, 0.25, 0.1, 0.5);
1383  ASSERT_COLOR_NEAR(premultiplied, expected);
1384  }
1385 
1386  {
1387  Color a(0.5, 0.25, 0.1, 0.5);
1388  Color unpremultiplied = a.Unpremultiply();
1389  Color expected = Color(1.0, 0.5, 0.2, 0.5);
1390  ASSERT_COLOR_NEAR(unpremultiplied, expected);
1391  }
1392 
1393  {
1394  Color a(0.5, 0.25, 0.1, 0.0);
1395  Color unpremultiplied = a.Unpremultiply();
1396  Color expected = Color(0.0, 0.0, 0.0, 0.0);
1397  ASSERT_COLOR_NEAR(unpremultiplied, expected);
1398  }
1399 }
1400 
1401 TEST(GeometryTest, ColorR8G8B8A8) {
1402  {
1403  Color a(1.0, 0.5, 0.2, 0.5);
1404  std::array<uint8_t, 4> expected = {255, 128, 51, 128};
1405  ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1406  }
1407 
1408  {
1409  Color a(0.0, 0.0, 0.0, 0.0);
1410  std::array<uint8_t, 4> expected = {0, 0, 0, 0};
1411  ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1412  }
1413 
1414  {
1415  Color a(1.0, 1.0, 1.0, 1.0);
1416  std::array<uint8_t, 4> expected = {255, 255, 255, 255};
1417  ASSERT_ARRAY_4_NEAR(a.ToR8G8B8A8(), expected);
1418  }
1419 }
1420 
1421 TEST(GeometryTest, ColorLerp) {
1422  {
1423  Color a(0.0, 0.0, 0.0, 0.0);
1424  Color b(1.0, 1.0, 1.0, 1.0);
1425 
1426  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.5), Color(0.5, 0.5, 0.5, 0.5));
1427  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.0), a);
1428  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 1.0), b);
1429  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.2), Color(0.2, 0.2, 0.2, 0.2));
1430  }
1431 
1432  {
1433  Color a(0.2, 0.4, 1.0, 0.5);
1434  Color b(0.4, 1.0, 0.2, 0.3);
1435 
1436  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.5), Color(0.3, 0.7, 0.6, 0.4));
1437  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.0), a);
1438  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 1.0), b);
1439  ASSERT_COLOR_NEAR(Color::Lerp(a, b, 0.2), Color(0.24, 0.52, 0.84, 0.46));
1440  }
1441 }
1442 
1443 TEST(GeometryTest, ColorClamp01) {
1444  {
1445  Color result = Color(0.5, 0.5, 0.5, 0.5).Clamp01();
1446  Color expected = Color(0.5, 0.5, 0.5, 0.5);
1447  ASSERT_COLOR_NEAR(result, expected);
1448  }
1449 
1450  {
1451  Color result = Color(-1, -1, -1, -1).Clamp01();
1452  Color expected = Color(0, 0, 0, 0);
1453  ASSERT_COLOR_NEAR(result, expected);
1454  }
1455 
1456  {
1457  Color result = Color(2, 2, 2, 2).Clamp01();
1458  Color expected = Color(1, 1, 1, 1);
1459  ASSERT_COLOR_NEAR(result, expected);
1460  }
1461 }
1462 
1463 TEST(GeometryTest, ColorMakeRGBA8) {
1464  {
1465  Color a = Color::MakeRGBA8(0, 0, 0, 0);
1467  ASSERT_COLOR_NEAR(a, b);
1468  }
1469 
1470  {
1471  Color a = Color::MakeRGBA8(255, 255, 255, 255);
1472  Color b = Color::White();
1473  ASSERT_COLOR_NEAR(a, b);
1474  }
1475 
1476  {
1477  Color a = Color::MakeRGBA8(63, 127, 191, 127);
1478  Color b(0.247059, 0.498039, 0.74902, 0.498039);
1479  ASSERT_COLOR_NEAR(a, b);
1480  }
1481 }
1482 
1483 TEST(GeometryTest, ColorApplyColorMatrix) {
1484  {
1485  ColorMatrix color_matrix = {
1486  1, 1, 1, 1, 1, //
1487  1, 1, 1, 1, 1, //
1488  1, 1, 1, 1, 1, //
1489  1, 1, 1, 1, 1, //
1490  };
1491  auto result = Color::White().ApplyColorMatrix(color_matrix);
1492  auto expected = Color(1, 1, 1, 1);
1493  ASSERT_COLOR_NEAR(result, expected);
1494  }
1495 
1496  {
1497  ColorMatrix color_matrix = {
1498  0.1, 0, 0, 0, 0.01, //
1499  0, 0.2, 0, 0, 0.02, //
1500  0, 0, 0.3, 0, 0.03, //
1501  0, 0, 0, 0.4, 0.04, //
1502  };
1503  auto result = Color::White().ApplyColorMatrix(color_matrix);
1504  auto expected = Color(0.11, 0.22, 0.33, 0.44);
1505  ASSERT_COLOR_NEAR(result, expected);
1506  }
1507 }
1508 
1509 TEST(GeometryTest, ColorLinearToSRGB) {
1510  {
1511  auto result = Color::White().LinearToSRGB();
1512  auto expected = Color(1, 1, 1, 1);
1513  ASSERT_COLOR_NEAR(result, expected);
1514  }
1515 
1516  {
1517  auto result = Color::BlackTransparent().LinearToSRGB();
1518  auto expected = Color(0, 0, 0, 0);
1519  ASSERT_COLOR_NEAR(result, expected);
1520  }
1521 
1522  {
1523  auto result = Color(0.2, 0.4, 0.6, 0.8).LinearToSRGB();
1524  auto expected = Color(0.484529, 0.665185, 0.797738, 0.8);
1525  ASSERT_COLOR_NEAR(result, expected);
1526  }
1527 }
1528 
1529 TEST(GeometryTest, ColorSRGBToLinear) {
1530  {
1531  auto result = Color::White().SRGBToLinear();
1532  auto expected = Color(1, 1, 1, 1);
1533  ASSERT_COLOR_NEAR(result, expected);
1534  }
1535 
1536  {
1537  auto result = Color::BlackTransparent().SRGBToLinear();
1538  auto expected = Color(0, 0, 0, 0);
1539  ASSERT_COLOR_NEAR(result, expected);
1540  }
1541 
1542  {
1543  auto result = Color(0.2, 0.4, 0.6, 0.8).SRGBToLinear();
1544  auto expected = Color(0.0331048, 0.132868, 0.318547, 0.8);
1545  ASSERT_COLOR_NEAR(result, expected);
1546  }
1547 }
1548 
1550  static constexpr Color kDestinationColor =
1552  static constexpr Color kSourceColors[] = {Color::White().WithAlpha(0.75),
1553  Color::LimeGreen().WithAlpha(0.75),
1554  Color::Black().WithAlpha(0.75)};
1555 
1556  static const std::map<BlendMode, Color>
1558 };
1559 
1560 // THIS RESULT TABLE IS GENERATED!
1561 //
1562 // Uncomment the `GenerateColorBlendResults` test below to print a new table
1563 // after making changes to `Color::Blend`.
1564 const std::map<BlendMode, Color> ColorBlendTestData::kExpectedResults[sizeof(
1566  {
1567  {BlendMode::kClear, {0, 0, 0, 0}},
1568  {BlendMode::kSrc, {1, 1, 1, 0.75}},
1569  {BlendMode::kDst, {0.392157, 0.584314, 0.929412, 0.75}},
1570  {BlendMode::kSrcOver, {0.878431, 0.916863, 0.985882, 0.9375}},
1571  {BlendMode::kDstOver, {0.513726, 0.667451, 0.943529, 0.9375}},
1572  {BlendMode::kSrcIn, {1, 1, 1, 0.5625}},
1573  {BlendMode::kDstIn, {0.392157, 0.584314, 0.929412, 0.5625}},
1574  {BlendMode::kSrcOut, {1, 1, 1, 0.1875}},
1575  {BlendMode::kDstOut, {0.392157, 0.584314, 0.929412, 0.1875}},
1576  {BlendMode::kSrcATop, {0.848039, 0.896078, 0.982353, 0.75}},
1577  {BlendMode::kDstATop, {0.544118, 0.688235, 0.947059, 0.75}},
1578  {BlendMode::kXor, {0.696078, 0.792157, 0.964706, 0.375}},
1579  {BlendMode::kPlus, {1, 1, 1, 1}},
1580  {BlendMode::kModulate, {0.392157, 0.584314, 0.929412, 0.5625}},
1581  {BlendMode::kScreen, {0.878431, 0.916863, 0.985882, 0.9375}},
1582  {BlendMode::kOverlay, {0.74902, 0.916863, 0.985882, 0.9375}},
1583  {BlendMode::kDarken, {0.513726, 0.667451, 0.943529, 0.9375}},
1584  {BlendMode::kLighten, {0.878431, 0.916863, 0.985882, 0.9375}},
1585  {BlendMode::kColorDodge, {0.878431, 0.916863, 0.985882, 0.9375}},
1586  {BlendMode::kColorBurn, {0.513725, 0.667451, 0.943529, 0.9375}},
1587  {BlendMode::kHardLight, {0.878431, 0.916863, 0.985882, 0.9375}},
1588  {BlendMode::kSoftLight, {0.654166, 0.775505, 0.964318, 0.9375}},
1589  {BlendMode::kDifference, {0.643137, 0.566275, 0.428235, 0.9375}},
1590  {BlendMode::kExclusion, {0.643137, 0.566275, 0.428235, 0.9375}},
1591  {BlendMode::kMultiply, {0.513726, 0.667451, 0.943529, 0.9375}},
1592  {BlendMode::kHue, {0.617208, 0.655639, 0.724659, 0.9375}},
1593  {BlendMode::kSaturation, {0.617208, 0.655639, 0.724659, 0.9375}},
1594  {BlendMode::kColor, {0.617208, 0.655639, 0.724659, 0.9375}},
1595  {BlendMode::kLuminosity, {0.878431, 0.916863, 0.985882, 0.9375}},
1596  },
1597  {
1598  {BlendMode::kClear, {0, 0, 0, 0}},
1599  {BlendMode::kSrc, {0.196078, 0.803922, 0.196078, 0.75}},
1600  {BlendMode::kDst, {0.392157, 0.584314, 0.929412, 0.75}},
1601  {BlendMode::kSrcOver, {0.235294, 0.76, 0.342745, 0.9375}},
1602  {BlendMode::kDstOver, {0.352941, 0.628235, 0.782745, 0.9375}},
1603  {BlendMode::kSrcIn, {0.196078, 0.803922, 0.196078, 0.5625}},
1604  {BlendMode::kDstIn, {0.392157, 0.584314, 0.929412, 0.5625}},
1605  {BlendMode::kSrcOut, {0.196078, 0.803922, 0.196078, 0.1875}},
1606  {BlendMode::kDstOut, {0.392157, 0.584314, 0.929412, 0.1875}},
1607  {BlendMode::kSrcATop, {0.245098, 0.74902, 0.379412, 0.75}},
1608  {BlendMode::kDstATop, {0.343137, 0.639216, 0.746078, 0.75}},
1609  {BlendMode::kXor, {0.294118, 0.694118, 0.562745, 0.375}},
1610  {BlendMode::kPlus, {0.441176, 1, 0.844118, 1}},
1611  {BlendMode::kModulate, {0.0768935, 0.469742, 0.182238, 0.5625}},
1612  {BlendMode::kScreen, {0.424452, 0.828743, 0.79105, 0.9375}},
1613  {BlendMode::kOverlay, {0.209919, 0.779839, 0.757001, 0.9375}},
1614  {BlendMode::kDarken, {0.235294, 0.628235, 0.342745, 0.9375}},
1615  {BlendMode::kLighten, {0.352941, 0.76, 0.782745, 0.9375}},
1616  {BlendMode::kColorDodge, {0.41033, 0.877647, 0.825098, 0.9375}},
1617  {BlendMode::kColorBurn, {0.117647, 0.567403, 0.609098, 0.9375}},
1618  {BlendMode::kHardLight, {0.209919, 0.779839, 0.443783, 0.9375}},
1619  {BlendMode::kSoftLight, {0.266006, 0.693915, 0.758818, 0.9375}},
1620  {BlendMode::kDifference, {0.235294, 0.409412, 0.665098, 0.9375}},
1621  {BlendMode::kExclusion, {0.378316, 0.546897, 0.681707, 0.9375}},
1622  {BlendMode::kMultiply, {0.163783, 0.559493, 0.334441, 0.9375}},
1623  {BlendMode::kHue, {0.266235, 0.748588, 0.373686, 0.9375}},
1624  {BlendMode::kSaturation, {0.339345, 0.629787, 0.811502, 0.9375}},
1625  {BlendMode::kColor, {0.241247, 0.765953, 0.348698, 0.9375}},
1626  {BlendMode::kLuminosity, {0.346988, 0.622282, 0.776792, 0.9375}},
1627  },
1628  {
1629  {BlendMode::kClear, {0, 0, 0, 0}},
1630  {BlendMode::kSrc, {0, 0, 0, 0.75}},
1631  {BlendMode::kDst, {0.392157, 0.584314, 0.929412, 0.75}},
1632  {BlendMode::kSrcOver, {0.0784314, 0.116863, 0.185882, 0.9375}},
1633  {BlendMode::kDstOver, {0.313726, 0.467451, 0.743529, 0.9375}},
1634  {BlendMode::kSrcIn, {0, 0, 0, 0.5625}},
1635  {BlendMode::kDstIn, {0.392157, 0.584314, 0.929412, 0.5625}},
1636  {BlendMode::kSrcOut, {0, 0, 0, 0.1875}},
1637  {BlendMode::kDstOut, {0.392157, 0.584314, 0.929412, 0.1875}},
1638  {BlendMode::kSrcATop, {0.0980392, 0.146078, 0.232353, 0.75}},
1639  {BlendMode::kDstATop, {0.294118, 0.438235, 0.697059, 0.75}},
1640  {BlendMode::kXor, {0.196078, 0.292157, 0.464706, 0.375}},
1641  {BlendMode::kPlus, {0.294118, 0.438235, 0.697059, 1}},
1642  {BlendMode::kModulate, {0, 0, 0, 0.5625}},
1643  {BlendMode::kScreen, {0.313726, 0.467451, 0.743529, 0.9375}},
1644  {BlendMode::kOverlay, {0.0784314, 0.218039, 0.701176, 0.9375}},
1645  {BlendMode::kDarken, {0.0784314, 0.116863, 0.185882, 0.9375}},
1646  {BlendMode::kLighten, {0.313726, 0.467451, 0.743529, 0.9375}},
1647  {BlendMode::kColorDodge, {0.313726, 0.467451, 0.743529, 0.9375}},
1648  {BlendMode::kColorBurn, {0.0784314, 0.116863, 0.185882, 0.9375}},
1649  {BlendMode::kHardLight, {0.0784314, 0.116863, 0.185882, 0.9375}},
1650  {BlendMode::kSoftLight, {0.170704, 0.321716, 0.704166, 0.9375}},
1651  {BlendMode::kDifference, {0.313726, 0.467451, 0.743529, 0.9375}},
1652  {BlendMode::kExclusion, {0.313726, 0.467451, 0.743529, 0.9375}},
1653  {BlendMode::kMultiply, {0.0784314, 0.116863, 0.185882, 0.9375}},
1654  {BlendMode::kHue, {0.417208, 0.455639, 0.524659, 0.9375}},
1655  {BlendMode::kSaturation, {0.417208, 0.455639, 0.524659, 0.9375}},
1656  {BlendMode::kColor, {0.417208, 0.455639, 0.524659, 0.9375}},
1657  {BlendMode::kLuminosity, {0.0784314, 0.116863, 0.185882, 0.9375}},
1658  },
1659 };
1660 
1661 /// To print a new ColorBlendTestData::kExpectedResults table, uncomment this
1662 /// test and run with:
1663 /// --gtest_filter="GeometryTest.GenerateColorBlendResults"
1664 /*
1665 TEST(GeometryTest, GenerateColorBlendResults) {
1666  auto& o = std::cout;
1667  using BlendT = std::underlying_type_t<BlendMode>;
1668  o << "{";
1669  for (const auto& source : ColorBlendTestData::kSourceColors) {
1670  o << "{";
1671  for (BlendT blend_i = 0;
1672  blend_i < static_cast<BlendT>(BlendMode::kLast) + 1; blend_i++) {
1673  auto blend = static_cast<BlendMode>(blend_i);
1674  Color c = ColorBlendTestData::kDestinationColor.Blend(source, blend);
1675  o << "{ BlendMode::k" << BlendModeToString(blend) << ", ";
1676  o << "{" << c.red << "," << c.green << "," << c.blue << "," << c.alpha
1677  << "}";
1678  o << "}," << std::endl;
1679  }
1680  o << "},";
1681  }
1682  o << "};" << std::endl;
1683 }
1684 */
1685 
1686 #define _BLEND_MODE_RESULT_CHECK(blend_mode) \
1687  expected = ColorBlendTestData::kExpectedResults[source_i] \
1688  .find(BlendMode::k##blend_mode) \
1689  ->second; \
1690  EXPECT_COLOR_NEAR(dst.Blend(src, BlendMode::k##blend_mode), expected);
1691 
1692 TEST(GeometryTest, ColorBlendReturnsExpectedResults) {
1694  for (size_t source_i = 0;
1695  source_i < sizeof(ColorBlendTestData::kSourceColors) / sizeof(Color);
1696  source_i++) {
1697  Color src = ColorBlendTestData::kSourceColors[source_i];
1698 
1699  Color expected;
1701  }
1702 }
1703 
1704 #define _BLEND_MODE_NAME_CHECK(blend_mode) \
1705  case BlendMode::k##blend_mode: \
1706  ASSERT_STREQ(result, #blend_mode); \
1707  break;
1708 
1709 TEST(GeometryTest, BlendModeToString) {
1710  using BlendT = std::underlying_type_t<BlendMode>;
1711  for (BlendT i = 0; i <= static_cast<BlendT>(BlendMode::kLastMode); i++) {
1712  auto mode = static_cast<BlendMode>(i);
1713  auto result = BlendModeToString(mode);
1715  }
1716 }
1717 
1718 TEST(GeometryTest, CanConvertBetweenDegressAndRadians) {
1719  {
1720  auto deg = Degrees{90.0};
1721  Radians rad = deg;
1722  ASSERT_FLOAT_EQ(rad.radians, kPiOver2);
1723  }
1724 }
1725 
1726 TEST(GeometryTest, MatrixPrinting) {
1727  {
1728  std::stringstream stream;
1729  Matrix m;
1730  stream << m;
1731  ASSERT_EQ(stream.str(), R"((
1732  1.000000, 0.000000, 0.000000, 0.000000,
1733  0.000000, 1.000000, 0.000000, 0.000000,
1734  0.000000, 0.000000, 1.000000, 0.000000,
1735  0.000000, 0.000000, 0.000000, 1.000000,
1736 ))");
1737  }
1738 
1739  {
1740  std::stringstream stream;
1741  Matrix m = Matrix::MakeTranslation(Vector3(10, 20, 30));
1742  stream << m;
1743 
1744  ASSERT_EQ(stream.str(), R"((
1745  1.000000, 0.000000, 0.000000, 10.000000,
1746  0.000000, 1.000000, 0.000000, 20.000000,
1747  0.000000, 0.000000, 1.000000, 30.000000,
1748  0.000000, 0.000000, 0.000000, 1.000000,
1749 ))");
1750  }
1751 }
1752 
1753 TEST(GeometryTest, PointPrinting) {
1754  {
1755  std::stringstream stream;
1756  Point m;
1757  stream << m;
1758  ASSERT_EQ(stream.str(), "(0, 0)");
1759  }
1760 
1761  {
1762  std::stringstream stream;
1763  Point m(13, 37);
1764  stream << m;
1765  ASSERT_EQ(stream.str(), "(13, 37)");
1766  }
1767 }
1768 
1769 TEST(GeometryTest, Vector3Printing) {
1770  {
1771  std::stringstream stream;
1772  Vector3 m;
1773  stream << m;
1774  ASSERT_EQ(stream.str(), "(0, 0, 0)");
1775  }
1776 
1777  {
1778  std::stringstream stream;
1779  Vector3 m(1, 2, 3);
1780  stream << m;
1781  ASSERT_EQ(stream.str(), "(1, 2, 3)");
1782  }
1783 }
1784 
1785 TEST(GeometryTest, Vector4Printing) {
1786  {
1787  std::stringstream stream;
1788  Vector4 m;
1789  stream << m;
1790  ASSERT_EQ(stream.str(), "(0, 0, 0, 1)");
1791  }
1792 
1793  {
1794  std::stringstream stream;
1795  Vector4 m(1, 2, 3, 4);
1796  stream << m;
1797  ASSERT_EQ(stream.str(), "(1, 2, 3, 4)");
1798  }
1799 }
1800 
1801 TEST(GeometryTest, ColorPrinting) {
1802  {
1803  std::stringstream stream;
1804  Color m;
1805  stream << m;
1806  ASSERT_EQ(stream.str(), "(0, 0, 0, 0)");
1807  }
1808 
1809  {
1810  std::stringstream stream;
1811  Color m(1, 2, 3, 4);
1812  stream << m;
1813  ASSERT_EQ(stream.str(), "(1, 2, 3, 4)");
1814  }
1815 }
1816 
1817 TEST(GeometryTest, ToIColor) {
1818  ASSERT_EQ(Color::ToIColor(Color(0, 0, 0, 0)), 0u);
1819  ASSERT_EQ(Color::ToIColor(Color(1.0, 1.0, 1.0, 1.0)), 0xFFFFFFFF);
1820  ASSERT_EQ(Color::ToIColor(Color(0.5, 0.5, 1.0, 1.0)), 0xFF8080FF);
1821 }
1822 
1823 TEST(GeometryTest, Gradient) {
1824  {
1825  // Simple 2 color gradient produces color buffer containing exactly those
1826  // values.
1827  std::vector<Color> colors = {Color::Red(), Color::Blue()};
1828  std::vector<Scalar> stops = {0.0, 1.0};
1829 
1830  auto gradient = CreateGradientBuffer(colors, stops);
1831 
1832  ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, colors);
1833  ASSERT_EQ(gradient.texture_size, 2u);
1834  }
1835 
1836  {
1837  // Gradient with duplicate stops does not create an empty texture.
1838  std::vector<Color> colors = {Color::Red(), Color::Yellow(), Color::Black(),
1839  Color::Blue()};
1840  std::vector<Scalar> stops = {0.0, 0.25, 0.25, 1.0};
1841 
1842  auto gradient = CreateGradientBuffer(colors, stops);
1843  ASSERT_EQ(gradient.texture_size, 5u);
1844  }
1845 
1846  {
1847  // Simple N color gradient produces color buffer containing exactly those
1848  // values.
1849  std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green(),
1850  Color::White()};
1851  std::vector<Scalar> stops = {0.0, 0.33, 0.66, 1.0};
1852 
1853  auto gradient = CreateGradientBuffer(colors, stops);
1854 
1855  ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, colors);
1856  ASSERT_EQ(gradient.texture_size, 4u);
1857  }
1858 
1859  {
1860  // Gradient with color stops will lerp and scale buffer.
1861  std::vector<Color> colors = {Color::Red(), Color::Blue(), Color::Green()};
1862  std::vector<Scalar> stops = {0.0, 0.25, 1.0};
1863 
1864  auto gradient = CreateGradientBuffer(colors, stops);
1865 
1866  std::vector<Color> lerped_colors = {
1867  Color::Red(),
1868  Color::Blue(),
1869  Color::Lerp(Color::Blue(), Color::Green(), 0.3333),
1870  Color::Lerp(Color::Blue(), Color::Green(), 0.6666),
1871  Color::Green(),
1872  };
1873  ASSERT_COLOR_BUFFER_NEAR(gradient.color_bytes, lerped_colors);
1874  ASSERT_EQ(gradient.texture_size, 5u);
1875  }
1876 
1877  {
1878  // Gradient size is capped at 1024.
1879  std::vector<Color> colors = {};
1880  std::vector<Scalar> stops = {};
1881  for (auto i = 0u; i < 1025; i++) {
1882  colors.push_back(Color::Blue());
1883  stops.push_back(i / 1025.0);
1884  }
1885 
1886  auto gradient = CreateGradientBuffer(colors, stops);
1887 
1888  ASSERT_EQ(gradient.texture_size, 1024u);
1889  ASSERT_EQ(gradient.color_bytes.size(), 1024u * 4);
1890  }
1891 }
1892 
1893 TEST(GeometryTest, HalfConversions) {
1894 #if defined(FML_OS_MACOSX) || defined(FML_OS_IOS) || \
1895  defined(FML_OS_IOS_SIMULATOR)
1896  ASSERT_EQ(ScalarToHalf(0.0), 0.0f16);
1897  ASSERT_EQ(ScalarToHalf(0.05), 0.05f16);
1898  ASSERT_EQ(ScalarToHalf(2.43), 2.43f16);
1899  ASSERT_EQ(ScalarToHalf(-1.45), -1.45f16);
1900 
1901  // 65504 is the largest possible half.
1902  ASSERT_EQ(ScalarToHalf(65504.0f), 65504.0f16);
1903  ASSERT_EQ(ScalarToHalf(65504.0f + 1), 65504.0f16);
1904 
1905  // Colors
1906  ASSERT_EQ(HalfVector4(Color::Red()),
1907  HalfVector4(1.0f16, 0.0f16, 0.0f16, 1.0f16));
1908  ASSERT_EQ(HalfVector4(Color::Green()),
1909  HalfVector4(0.0f16, 1.0f16, 0.0f16, 1.0f16));
1910  ASSERT_EQ(HalfVector4(Color::Blue()),
1911  HalfVector4(0.0f16, 0.0f16, 1.0f16, 1.0f16));
1912  ASSERT_EQ(HalfVector4(Color::Black().WithAlpha(0)),
1913  HalfVector4(0.0f16, 0.0f16, 0.0f16, 0.0f16));
1914 
1915  ASSERT_EQ(HalfVector3(Vector3(4.0, 6.0, -1.0)),
1916  HalfVector3(4.0f16, 6.0f16, -1.0f16));
1917  ASSERT_EQ(HalfVector2(Vector2(4.0, 6.0)), HalfVector2(4.0f16, 6.0f16));
1918 
1919  ASSERT_EQ(Half(0.5f), Half(0.5f16));
1920  ASSERT_EQ(Half(0.5), Half(0.5f16));
1921  ASSERT_EQ(Half(5), Half(5.0f16));
1922 #else
1923  GTEST_SKIP() << "Half-precision floats (IEEE 754) are not portable and "
1924  "only used on Apple platforms.";
1925 #endif // FML_OS_MACOSX || FML_OS_IOS || FML_OS_IOS_SIMULATOR
1926 }
1927 
1928 } // namespace testing
1929 } // namespace impeller
1930 
1931 // NOLINTEND(bugprone-unchecked-optional-access)
#define IMPELLER_FOR_EACH_BLEND_MODE(V)
Definition: color.h:19
int32_t x
#define _BLEND_MODE_RESULT_CHECK(blend_mode)
#define _BLEND_MODE_NAME_CHECK(blend_mode)
#define ASSERT_VECTOR4_NEAR(a, b)
#define ASSERT_MATRIX_NEAR(a, b)
#define ASSERT_QUATERNION_NEAR(a, b)
#define ASSERT_COLOR_NEAR(a, b)
#define ASSERT_POINT_NEAR(a, b)
#define ASSERT_ARRAY_4_NEAR(a, b)
#define ASSERT_COLOR_BUFFER_NEAR(a, b)
#define ASSERT_VECTOR3_NEAR(a, b)
TEST(AllocationSizeTest, CanCreateTypedAllocations)
constexpr float k2Pi
Definition: constants.h:29
Point Vector2
Definition: point.h:331
constexpr float kPi
Definition: constants.h:26
float Scalar
Definition: scalar.h:19
constexpr float kEhCloseEnough
Definition: constants.h:57
constexpr InternalHalf ScalarToHalf(Scalar f)
Convert a scalar to a half precision float.
Definition: half.h:32
TPoint< Scalar > Point
Definition: point.h:327
const char * BlendModeToString(BlendMode blend_mode)
Definition: color.cc:47
constexpr float kPiOver2
Definition: constants.h:32
BlendMode
Definition: color.h:58
TPoint< int64_t > IPoint
Definition: point.h:328
constexpr float kPiOver4
Definition: constants.h:35
TSize< Scalar > Size
Definition: size.h:159
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
ISize64 ISize
Definition: size.h:162
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:36
constexpr float k1OverSqrt2
Definition: constants.h:50
std::array< uint8_t, 4 > ToR8G8B8A8() const
Convert to R8G8B8A8 representation.
Definition: color.h:246
static uint32_t ToIColor(Color color)
Convert this color to a 32-bit representation.
Definition: color.h:159
static constexpr Color LimeGreen()
Definition: color.h:602
static constexpr Color BlackTransparent()
Definition: color.h:270
Color LinearToSRGB() const
Convert the color from linear space to sRGB space.
Definition: color.cc:311
static constexpr Color Black()
Definition: color.h:266
static constexpr Color CornflowerBlue()
Definition: color.h:342
static constexpr Color White()
Definition: color.h:264
constexpr Color WithAlpha(Scalar new_alpha) const
Definition: color.h:278
Color ApplyColorMatrix(const ColorMatrix &color_matrix) const
A color filter that transforms colors through a 4x5 color matrix.
Definition: color.cc:301
static constexpr Color Red()
Definition: color.h:272
constexpr Color Unpremultiply() const
Definition: color.h:216
constexpr Color Premultiply() const
Definition: color.h:212
static constexpr Color MakeRGBA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Definition: color.h:152
constexpr Color Clamp01() const
Definition: color.h:236
constexpr static Color Lerp(Color a, Color b, Scalar t)
Return a color that is linearly interpolated between colors a and b, according to the value of t.
Definition: color.h:232
static constexpr Color Yellow()
Definition: color.h:842
Color SRGBToLinear() const
Convert the color from sRGB space to linear space.
Definition: color.cc:322
static constexpr Color Blue()
Definition: color.h:276
static constexpr Color Green()
Definition: color.h:274
A storage only class for half precision floating point.
Definition: half.h:41
A storage only class for half precision floating point vector 2.
Definition: half.h:129
A storage only class for half precision floating point vector 3.
Definition: half.h:101
A storage only class for half precision floating point vector 4.
Definition: half.h:60
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:566
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
Scalar GetDirectionScale(Vector3 direction) const
Definition: matrix.h:346
constexpr Matrix Basis() const
The Matrix without its w components (without translation).
Definition: matrix.h:239
Matrix Invert() const
Definition: matrix.cc:97
static constexpr Matrix MakeColumn(Scalar m0, Scalar m1, Scalar m2, Scalar m3, Scalar m4, Scalar m5, Scalar m6, Scalar m7, Scalar m8, Scalar m9, Scalar m10, Scalar m11, Scalar m12, Scalar m13, Scalar m14, Scalar m15)
Definition: matrix.h:69
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:208
std::optional< MatrixDecomposition > Decompose() const
Definition: matrix.cc:200
static Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:575
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
static constexpr Matrix MakeSkew(Scalar sx, Scalar sy)
Definition: matrix.h:127
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:223
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
static Matrix MakeLookAt(Vector3 position, Vector3 target, Vector3 up)
Definition: matrix.h:601
static Matrix MakeRotation(Quaternion q)
Definition: matrix.h:136
static Matrix MakeRotationX(Radians r)
Definition: matrix.h:193
Scalar radians
Definition: scalar.h:45
A Vector2, broken down as a separate magnitude and direction. Assumes that the direction given is nor...
Radians AngleTo(const SeparatedVector2 &other) const
Returns the scalar angle between the two rays.
Scalar GetAlignment(const SeparatedVector2 &other) const
Vector2 direction
The normalized direction of the vector.
Vector2 GetVector() const
Returns the vector representation of the vector.
Scalar magnitude
The magnitude of the vector.
constexpr TPoint Abs() const
Definition: point.h:216
static constexpr TPoint Round(const TPoint< U > &other)
Definition: point.h:49
constexpr TPoint Max(const TPoint &p) const
Definition: point.h:190
constexpr TPoint Ceil() const
Definition: point.h:196
constexpr TPoint Normalize() const
Definition: point.h:208
constexpr Type Cross(const TPoint &p) const
Definition: point.h:218
constexpr TPoint Lerp(const TPoint &p, Scalar t) const
Definition: point.h:236
constexpr TPoint Floor() const
Definition: point.h:194
constexpr TPoint Rotate(const Radians &angle) const
Definition: point.h:226
constexpr TPoint Reflect(const TPoint &axis) const
Definition: point.h:222
constexpr TPoint Min(const TPoint &p) const
Definition: point.h:186
constexpr Type Dot(const TPoint &p) const
Definition: point.h:220
constexpr Radians AngleTo(const TPoint &p) const
Definition: point.h:232
Type height
Definition: size.h:29
Type width
Definition: size.h:28
constexpr size_t MipCount() const
Return the mip count of the texture.
Definition: size.h:137
Vector3 Min(const Vector3 &p) const
Definition: vector.h:70
constexpr Vector3 Lerp(const Vector3 &v, Scalar t) const
Definition: vector.h:178
Vector3 Round() const
Definition: vector.h:86
Vector3 Ceil() const
Definition: vector.h:82
Vector3 Floor() const
Definition: vector.h:78
Vector3 Max(const Vector3 &p) const
Definition: vector.h:74
constexpr Vector4 Min(const Vector4 &p) const
Definition: vector.h:292
constexpr Vector4 Lerp(const Vector4 &v, Scalar t) const
Definition: vector.h:314
Vector4 Floor() const
Definition: vector.h:302
bool IsFinite() const
Definition: vector.h:258
Vector4 Round() const
Definition: vector.h:310
constexpr Vector4 Max(const Vector4 &p) const
Definition: vector.h:297
Vector4 Ceil() const
Definition: vector.h:306
static const std::map< BlendMode, Color > kExpectedResults[sizeof(kSourceColors)]