Flutter Impeller
shadow_path_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 
6 
7 #include "flutter/display_list/geometry/dl_path.h"
8 #include "flutter/display_list/geometry/dl_path_builder.h"
9 #include "gtest/gtest.h"
10 
11 #include "flutter/third_party/skia/src/core/SkVerticesPriv.h" // nogncheck
12 #include "flutter/third_party/skia/src/utils/SkShadowTessellator.h" // nogncheck
13 
14 #define SHADOW_UNITTEST_SHOW_VERTICES false
15 
16 namespace impeller {
17 namespace testing {
18 
19 using flutter::DlPath;
20 using flutter::DlPathBuilder;
21 using flutter::DlPoint;
22 using flutter::DlRect;
23 
24 namespace {
25 
26 #if SHADOW_UNITTEST_SHOW_VERTICES
27 void ShowVertices(const std::string& label,
28  const std::shared_ptr<ShadowVertices>& shadow_vertices) {
29  auto vertices = shadow_vertices->GetVertices();
30  auto alphas = shadow_vertices->GetGaussians();
31  auto indices = shadow_vertices->GetIndices();
32  FML_LOG(ERROR) << label << "[" << indices.size() / 3 << "] = {";
33  for (size_t i = 0u; i < indices.size(); i += 3) {
34  // clang-format off
35  FML_LOG(ERROR)
36  << " (" << vertices[indices[i + 0]] << ", " << alphas[indices[i + 0]] << "), "
37  << " (" << vertices[indices[i + 1]] << ", " << alphas[indices[i + 1]] << "), "
38  << " (" << vertices[indices[i + 2]] << ", " << alphas[indices[i + 2]] << ")";
39  // clang-format on
40  }
41  FML_LOG(ERROR) << "} // " << label;
42 }
43 #endif
44 
45 constexpr Scalar kEpsilonSquared =
47 
48 bool SimilarPoint(Point p1, Point p2) {
49  return p1.GetDistanceSquared(p2) < kEpsilonSquared;
50 }
51 
52 bool SimilarPointPair(std::array<Point, 2> pair1, std::array<Point, 2> pair2) {
53  if (SimilarPoint(pair1[1], pair2[1]) && SimilarPoint(pair1[2], pair2[2])) {
54  return true;
55  }
56  if (SimilarPoint(pair1[1], pair2[2]) && SimilarPoint(pair1[2], pair2[1])) {
57  return true;
58  }
59  return false;
60 }
61 
62 bool SimilarPointTrio(std::array<Point, 3> trio1, std::array<Point, 3> trio2) {
63  if (SimilarPoint(trio1[1], trio2[1]) &&
64  SimilarPointPair({trio1[2], trio1[3]}, {trio2[2], trio2[3]})) {
65  return true;
66  }
67  if (SimilarPoint(trio1[1], trio2[2]) &&
68  SimilarPointPair({trio1[2], trio1[3]}, {trio2[1], trio2[3]})) {
69  return true;
70  }
71  if (SimilarPoint(trio1[1], trio2[3]) &&
72  SimilarPointPair({trio1[2], trio1[3]}, {trio2[1], trio2[2]})) {
73  return true;
74  }
75  return false;
76 }
77 
78 size_t CountDuplicateVertices(
79  const std::shared_ptr<ShadowVertices>& shadow_vertices) {
80  size_t duplicate_vertices = 0u;
81  auto vertices = shadow_vertices->GetVertices();
82  size_t vertex_count = vertices.size();
83 
84  for (size_t i = 1u; i < vertex_count; i++) {
85  Point& vertex = vertices[i];
86  for (size_t j = 0u; j < i; j++) {
87  if (SimilarPoint(vertex, vertices[j])) {
88  duplicate_vertices++;
89  }
90  }
91  }
92 
93  return duplicate_vertices;
94 }
95 
96 size_t CountDuplicateTriangles(
97  const std::shared_ptr<ShadowVertices>& shadow_vertices) {
98  size_t duplicate_triangles = 0u;
99  auto vertices = shadow_vertices->GetVertices();
100  auto indices = shadow_vertices->GetIndices();
101  size_t index_count = indices.size();
102 
103  for (size_t i = 3u; i < index_count; i += 3) {
104  std::array trio1 = {
105  vertices[indices[i + 0]],
106  vertices[indices[i + 1]],
107  vertices[indices[i + 2]],
108  };
109  for (size_t j = 0; j < i; j += 3) {
110  std::array trio2 = {
111  vertices[indices[j + 0]],
112  vertices[indices[j + 1]],
113  vertices[indices[j + 2]],
114  };
115  if (SimilarPointTrio(trio1, trio2)) {
116  duplicate_triangles++;
117  }
118  }
119  }
120 
121  return duplicate_triangles;
122 }
123 
124 bool IsPointInsideTriangle(Point p, std::array<Point, 3> triangle) {
125  if (SimilarPoint(p, triangle[0]) || //
126  SimilarPoint(p, triangle[1]) || //
127  SimilarPoint(p, triangle[2])) {
128  return false;
129  }
130  Scalar direction = Point::Cross(p, triangle[0], triangle[1]);
131  // All 3 cross products must be non-zero and have the same sign.
132  return direction * Point::Cross(p, triangle[1], triangle[2]) > 0 &&
133  direction * Point::Cross(p, triangle[2], triangle[0]) > 0;
134 };
135 
136 // This test verifies a condition that doesn't invalidate the process
137 // per se, but we'd have to use overlap prevention to render the mesh
138 // if this test returned true. We've carefully planned our meshes to
139 // avoid that condition, though, so we're just making sure.
140 bool DoTrianglesOverlap(
141  const std::shared_ptr<ShadowVertices>& shadow_vertices) {
142  auto vertices = shadow_vertices->GetVertices();
143  auto indices = shadow_vertices->GetIndices();
144  size_t index_count = indices.size();
145  size_t vertex_count = vertices.size();
146 
147  for (size_t i = 0u; i < index_count; i += 3) {
148  std::array triangle = {
149  vertices[indices[i + 0]],
150  vertices[indices[i + 1]],
151  vertices[indices[i + 2]],
152  };
153  // Rather than check each pair of triangles to see if any of their
154  // vertices is inside the other, we just check the list of all vertices
155  // to see if that vertex is inside any triangle in the mesh.
156  for (size_t j = 0; j < vertex_count; j++) {
157  if (IsPointInsideTriangle(vertices[j], triangle)) {
158  FML_LOG(ERROR) << "Point " << vertices[j] << " inside triangle ["
159  << triangle[0] << ", " //
160  << triangle[1] << ", " //
161  << triangle[2] << "]";
162  FML_LOG(ERROR) << "Point - corner[0] == " << vertices[j] - triangle[0];
163  FML_LOG(ERROR) << "Point - corner[1] == " << vertices[j] - triangle[1];
164  FML_LOG(ERROR) << "Point - corner[2] == " << vertices[j] - triangle[2];
165  return true;
166  }
167  }
168  }
169 
170  return false;
171 }
172 
173 } // namespace
174 
175 TEST(ShadowPathGeometryTest, EmptyPathTest) {
176  DlPathBuilder path_builder;
177  const DlPath path = path_builder.TakePath();
178  const Matrix matrix;
179  const Scalar height = 10.0f;
180 
181  Tessellator tessellator;
182  std::shared_ptr<ShadowVertices> shadow_vertices =
183  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
184  matrix);
185 
186  ASSERT_NE(shadow_vertices, nullptr);
187  EXPECT_TRUE(shadow_vertices->IsEmpty());
188 }
189 
190 TEST(ShadowPathGeometryTest, MoveToOnlyTest) {
191  DlPathBuilder path_builder;
192  path_builder.MoveTo(DlPoint(100, 100));
193  const DlPath path = path_builder.TakePath();
194  const Matrix matrix;
195  const Scalar height = 10.0f;
196 
197  Tessellator tessellator;
198  std::shared_ptr<ShadowVertices> shadow_vertices =
199  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
200  matrix);
201 
202  ASSERT_NE(shadow_vertices, nullptr);
203  EXPECT_TRUE(shadow_vertices->IsEmpty());
204 }
205 
206 TEST(ShadowPathGeometryTest, OnePathSegmentTest) {
207  DlPathBuilder path_builder;
208  path_builder.MoveTo(DlPoint(100, 100));
209  path_builder.LineTo(DlPoint(200, 100));
210  const DlPath path = path_builder.TakePath();
211  const Matrix matrix;
212  const Scalar height = 10.0f;
213 
214  Tessellator tessellator;
215  std::shared_ptr<ShadowVertices> shadow_vertices =
216  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
217  matrix);
218 
219  ASSERT_NE(shadow_vertices, nullptr);
220  EXPECT_TRUE(shadow_vertices->IsEmpty());
221 }
222 
223 TEST(ShadowPathGeometryTest, TwoColinearSegmentsTest) {
224  DlPathBuilder path_builder;
225  path_builder.MoveTo(DlPoint(100, 100));
226  path_builder.LineTo(DlPoint(200, 100));
227  path_builder.LineTo(DlPoint(300, 100));
228  const DlPath path = path_builder.TakePath();
229  const Matrix matrix;
230  const Scalar height = 10.0f;
231 
232  Tessellator tessellator;
233  std::shared_ptr<ShadowVertices> shadow_vertices =
234  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
235  matrix);
236 
237  ASSERT_NE(shadow_vertices, nullptr);
238  EXPECT_TRUE(shadow_vertices->IsEmpty());
239 }
240 
241 TEST(ShadowPathGeometryTest, EmptyRectTest) {
242  DlPathBuilder path_builder;
243  path_builder.MoveTo(DlPoint(100, 100));
244  path_builder.LineTo(DlPoint(200, 100));
245  path_builder.LineTo(DlPoint(200, 100));
246  path_builder.LineTo(DlPoint(100, 100));
247  path_builder.Close();
248  const DlPath path = path_builder.TakePath();
249  const Matrix matrix;
250  const Scalar height = 10.0f;
251 
252  Tessellator tessellator;
253  std::shared_ptr<ShadowVertices> shadow_vertices =
254  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
255  matrix);
256 
257  ASSERT_NE(shadow_vertices, nullptr);
258  EXPECT_TRUE(shadow_vertices->IsEmpty());
259 }
260 
261 TEST(ShadowPathGeometryTest, GetAndTakeVertices) {
262  DlPath path = DlPath::MakeRectLTRB(100, 100, 200, 200);
263  const Scalar height = 10.0f;
264 
265  Tessellator tessellator;
266  ShadowPathGeometry geometry(tessellator, {}, path, height);
267 
268  // Can call Get as many times as you want.
269  for (int i = 0; i < 10; i++) {
270  EXPECT_TRUE(geometry.GetShadowVertices());
271  }
272 
273  // Can only call Take once.
274  EXPECT_TRUE(geometry.TakeShadowVertices());
275 
276  // Further access wll then fail.
277  EXPECT_FALSE(geometry.GetShadowVertices());
278  EXPECT_FALSE(geometry.TakeShadowVertices());
279 }
280 
281 TEST(ShadowPathGeometryTest, ClockwiseTriangleTest) {
282  DlPathBuilder path_builder;
283  path_builder.MoveTo(DlPoint(100, 0));
284  path_builder.LineTo(DlPoint(200, 110));
285  path_builder.LineTo(DlPoint(0, 110));
286  path_builder.Close();
287  const DlPath path = path_builder.TakePath();
288  const Matrix matrix;
289  const Scalar height = 10.0f;
290 
291  Tessellator tessellator;
292  std::shared_ptr<ShadowVertices> shadow_vertices =
293  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
294  matrix);
295 
296  ASSERT_NE(shadow_vertices, nullptr);
297  EXPECT_FALSE(shadow_vertices->IsEmpty());
298  EXPECT_EQ(shadow_vertices->GetVertexCount(), 33u);
299  EXPECT_EQ(shadow_vertices->GetIndexCount(), 102u);
300  EXPECT_EQ(shadow_vertices->GetVertices().size(), 33u);
301  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 33u);
302  EXPECT_EQ(shadow_vertices->GetIndices().size(), 102u);
303  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
304  // We repeat the first and last vertex that is on the outer umbra.
305  // There is another duplicate vertex from somewhere else not yet realized.
306  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 2u);
307  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
308  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
309 
310 #if SHADOW_UNITTEST_SHOW_VERTICES
311  ShowVertices("Impeller Vertices", shadow_vertices);
312 #endif
313 }
314 
315 TEST(ShadowPathGeometryTest, CounterClockwiseTriangleTest) {
316  DlPathBuilder path_builder;
317  path_builder.MoveTo(DlPoint(100, 0));
318  path_builder.LineTo(DlPoint(0, 110));
319  path_builder.LineTo(DlPoint(200, 110));
320  path_builder.Close();
321  const DlPath path = path_builder.TakePath();
322  const Matrix matrix;
323  const Scalar height = 10.0f;
324 
325  Tessellator tessellator;
326  std::shared_ptr<ShadowVertices> shadow_vertices =
327  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
328  matrix);
329 
330  ASSERT_NE(shadow_vertices, nullptr);
331  EXPECT_FALSE(shadow_vertices->IsEmpty());
332  EXPECT_EQ(shadow_vertices->GetVertexCount(), 33u);
333  EXPECT_EQ(shadow_vertices->GetIndexCount(), 102u);
334  EXPECT_EQ(shadow_vertices->GetVertices().size(), 33u);
335  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 33u);
336  EXPECT_EQ(shadow_vertices->GetIndices().size(), 102u);
337  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
338  // We repeat the first and last vertex that is on the outer umbra.
339  // There is another duplicate vertex from somewhere else not yet realized.
340  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 2u);
341  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
342  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
343 
344 #if SHADOW_UNITTEST_SHOW_VERTICES
345  ShowVertices("Impeller Vertices", shadow_vertices);
346 #endif
347 }
348 
349 TEST(ShadowPathGeometryTest, ClockwiseRectTest) {
350  DlPathBuilder path_builder;
351  path_builder.MoveTo(DlPoint(0, 0));
352  path_builder.LineTo(DlPoint(100, 0));
353  path_builder.LineTo(DlPoint(100, 80));
354  path_builder.LineTo(DlPoint(0, 80));
355  path_builder.Close();
356  const DlPath path = path_builder.TakePath();
357  const Matrix matrix;
358  const Scalar height = 10.0f;
359 
360  Tessellator tessellator;
361  std::shared_ptr<ShadowVertices> shadow_vertices =
362  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
363  matrix);
364 
365  ASSERT_NE(shadow_vertices, nullptr);
366  EXPECT_FALSE(shadow_vertices->IsEmpty());
367  EXPECT_EQ(shadow_vertices->GetVertexCount(), 34u);
368  EXPECT_EQ(shadow_vertices->GetIndexCount(), 108u);
369  EXPECT_EQ(shadow_vertices->GetVertices().size(), 34u);
370  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 34u);
371  EXPECT_EQ(shadow_vertices->GetIndices().size(), 108u);
372  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
373  // We repeat the first and last vertex that is on the outer umbra.
374  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 1u);
375  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
376  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
377 
378 #if SHADOW_UNITTEST_SHOW_VERTICES
379  ShowVertices("Impeller Vertices", shadow_vertices);
380 #endif
381 }
382 
383 TEST(ShadowPathGeometryTest, CounterClockwiseRectTest) {
384  DlPathBuilder path_builder;
385  path_builder.MoveTo(DlPoint(0, 0));
386  path_builder.LineTo(DlPoint(0, 80));
387  path_builder.LineTo(DlPoint(100, 80));
388  path_builder.LineTo(DlPoint(100, 0));
389  path_builder.Close();
390  DlPath path = path_builder.TakePath();
391  Matrix matrix;
392  const Scalar height = 10.0f;
393 
394  Tessellator tessellator;
395  std::shared_ptr<ShadowVertices> shadow_vertices =
396  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
397  matrix);
398 
399  ASSERT_NE(shadow_vertices, nullptr);
400  EXPECT_FALSE(shadow_vertices->IsEmpty());
401  EXPECT_EQ(shadow_vertices->GetVertexCount(), 34u);
402  EXPECT_EQ(shadow_vertices->GetIndexCount(), 108u);
403  EXPECT_EQ(shadow_vertices->GetVertices().size(), 34u);
404  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 34u);
405  EXPECT_EQ(shadow_vertices->GetIndices().size(), 108u);
406  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
407  // We repeat the first and last vertex that is on the outer umbra.
408  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 1u);
409  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
410  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
411 
412 #if SHADOW_UNITTEST_SHOW_VERTICES
413  ShowVertices("Impeller Vertices", shadow_vertices);
414 #endif
415 }
416 
417 TEST(ShadowPathGeometryTest, ClockwiseRectExtraColinearPointsTest) {
418  // This path includes a colinear point to each edge of the rectangle
419  // which should be trimmed out and ignored when generating the mesh
420  // resulting in the same number of vertices and triangles as the mesh
421  // above.
422  DlPathBuilder path_builder;
423  path_builder.MoveTo(DlPoint(0, 0));
424  path_builder.LineTo(DlPoint(50, 0));
425  path_builder.LineTo(DlPoint(100, 0));
426  path_builder.LineTo(DlPoint(100, 40));
427  path_builder.LineTo(DlPoint(100, 80));
428  path_builder.LineTo(DlPoint(50, 80));
429  path_builder.LineTo(DlPoint(0, 80));
430  path_builder.LineTo(DlPoint(0, 40));
431  path_builder.Close();
432  const DlPath path = path_builder.TakePath();
433  const Matrix matrix;
434  const Scalar height = 10.0f;
435 
436  Tessellator tessellator;
437  std::shared_ptr<ShadowVertices> shadow_vertices =
438  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
439  matrix);
440 
441  ASSERT_NE(shadow_vertices, nullptr);
442  EXPECT_FALSE(shadow_vertices->IsEmpty());
443  EXPECT_EQ(shadow_vertices->GetVertexCount(), 34u);
444  EXPECT_EQ(shadow_vertices->GetIndexCount(), 108u);
445  EXPECT_EQ(shadow_vertices->GetVertices().size(), 34u);
446  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 34u);
447  EXPECT_EQ(shadow_vertices->GetIndices().size(), 108u);
448  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
449  // We repeat the first and last vertex that is on the outer umbra.
450  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 1u);
451  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
452  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
453 
454 #if SHADOW_UNITTEST_SHOW_VERTICES
455  ShowVertices("Impeller Vertices", shadow_vertices);
456 #endif
457 }
458 
459 TEST(ShadowPathGeometryTest, CounterClockwiseRectExtraColinearPointsTest) {
460  // This path includes a colinear point to each edge of the rectangle
461  // which should be trimmed out and ignored when generating the mesh
462  // resulting in the same number of vertices and triangles as the mesh
463  // above.
464  DlPathBuilder path_builder;
465  path_builder.MoveTo(DlPoint(0, 0));
466  path_builder.LineTo(DlPoint(0, 40));
467  path_builder.LineTo(DlPoint(0, 80));
468  path_builder.LineTo(DlPoint(50, 80));
469  path_builder.LineTo(DlPoint(100, 80));
470  path_builder.LineTo(DlPoint(100, 40));
471  path_builder.LineTo(DlPoint(100, 0));
472  path_builder.LineTo(DlPoint(50, 0));
473  path_builder.Close();
474  DlPath path = path_builder.TakePath();
475  Matrix matrix;
476  const Scalar height = 10.0f;
477 
478  Tessellator tessellator;
479  std::shared_ptr<ShadowVertices> shadow_vertices =
480  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
481  matrix);
482 
483  ASSERT_NE(shadow_vertices, nullptr);
484  EXPECT_FALSE(shadow_vertices->IsEmpty());
485  EXPECT_EQ(shadow_vertices->GetVertexCount(), 34u);
486  EXPECT_EQ(shadow_vertices->GetIndexCount(), 108u);
487  EXPECT_EQ(shadow_vertices->GetVertices().size(), 34u);
488  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 34u);
489  EXPECT_EQ(shadow_vertices->GetIndices().size(), 108u);
490  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
491  // We repeat the first and last vertex that is on the outer umbra.
492  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 1u);
493  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
494  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
495 
496 #if SHADOW_UNITTEST_SHOW_VERTICES
497  ShowVertices("Impeller Vertices", shadow_vertices);
498 #endif
499 }
500 
501 TEST(ShadowPathGeometryTest, ClockwiseRectTrickyColinearPointsTest) {
502  // This path includes a colinear point added to each edge of the rectangle
503  // which seems to violate convexity, but is eliminated as not contributing
504  // to the path. We should be able to process the path anyway.
505  DlPathBuilder path_builder;
506  path_builder.MoveTo(DlPoint(0, 0));
507  path_builder.LineTo(DlPoint(-10, 0));
508  path_builder.LineTo(DlPoint(100, 0));
509  path_builder.LineTo(DlPoint(100, -10));
510  path_builder.LineTo(DlPoint(100, 80));
511  path_builder.LineTo(DlPoint(110, 80));
512  path_builder.LineTo(DlPoint(0, 80));
513  path_builder.LineTo(DlPoint(0, 90));
514  path_builder.Close();
515  const DlPath path = path_builder.TakePath();
516  const Matrix matrix;
517  const Scalar height = 10.0f;
518 
519  Tessellator tessellator;
520  std::shared_ptr<ShadowVertices> shadow_vertices =
521  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
522  matrix);
523 
524  ASSERT_NE(shadow_vertices, nullptr);
525  EXPECT_FALSE(shadow_vertices->IsEmpty());
526  EXPECT_EQ(shadow_vertices->GetVertexCount(), 34u);
527  EXPECT_EQ(shadow_vertices->GetIndexCount(), 108u);
528  EXPECT_EQ(shadow_vertices->GetVertices().size(), 34u);
529  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 34u);
530  EXPECT_EQ(shadow_vertices->GetIndices().size(), 108u);
531  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
532  // We repeat the first and last vertex that is on the outer umbra.
533  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 1u);
534  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
535  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
536 
537 #if SHADOW_UNITTEST_SHOW_VERTICES
538  ShowVertices("Impeller Vertices", shadow_vertices);
539 #endif
540 }
541 
542 TEST(ShadowPathGeometryTest, CounterClockwiseRectTrickyColinearPointsTest) {
543  // This path includes a colinear point added to each edge of the rectangle
544  // which seems to violate convexity, but is eliminated as not contributing
545  // to the path. We should be able to process the path anyway.
546  DlPathBuilder path_builder;
547  path_builder.MoveTo(DlPoint(0, 0));
548  path_builder.LineTo(DlPoint(0, -10));
549  path_builder.LineTo(DlPoint(0, 80));
550  path_builder.LineTo(DlPoint(-10, 80));
551  path_builder.LineTo(DlPoint(100, 80));
552  path_builder.LineTo(DlPoint(100, 90));
553  path_builder.LineTo(DlPoint(100, 0));
554  path_builder.LineTo(DlPoint(110, 0));
555  path_builder.Close();
556  DlPath path = path_builder.TakePath();
557  Matrix matrix;
558  const Scalar height = 10.0f;
559 
560  Tessellator tessellator;
561  std::shared_ptr<ShadowVertices> shadow_vertices =
562  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
563  matrix);
564 
565  ASSERT_NE(shadow_vertices, nullptr);
566  EXPECT_FALSE(shadow_vertices->IsEmpty());
567  EXPECT_EQ(shadow_vertices->GetVertexCount(), 34u);
568  EXPECT_EQ(shadow_vertices->GetIndexCount(), 108u);
569  EXPECT_EQ(shadow_vertices->GetVertices().size(), 34u);
570  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 34u);
571  EXPECT_EQ(shadow_vertices->GetIndices().size(), 108u);
572  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
573  // We repeat the first and last vertex that is on the outer umbra.
574  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 1u);
575  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
576  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
577 
578 #if SHADOW_UNITTEST_SHOW_VERTICES
579  ShowVertices("Impeller Vertices", shadow_vertices);
580 #endif
581 }
582 
583 TEST(ShadowPathGeometryTest, ClockwiseRectTrickyDupColinearPointsTest) {
584  // This path includes a colinear point added to each edge of the rectangle
585  // which seems to violate convexity, but is eliminated as not contributing
586  // to the path. We should be able to process the path anyway.
587  // It also includes multiple collinear points on the first and last points
588  // that end up back where we started to make sure that in that case we
589  // eliminate all of the collinear points and the duplicate, rather than
590  // just the intermediate collinear points.
591  DlPathBuilder path_builder;
592  path_builder.MoveTo(DlPoint(0, 0));
593  path_builder.LineTo(DlPoint(-10, 0));
594  path_builder.LineTo(DlPoint(0, 0));
595  path_builder.LineTo(DlPoint(100, 0));
596  path_builder.LineTo(DlPoint(100, -10));
597  path_builder.LineTo(DlPoint(100, 80));
598  path_builder.LineTo(DlPoint(110, 80));
599  path_builder.LineTo(DlPoint(0, 80));
600  path_builder.LineTo(DlPoint(0, 90));
601  path_builder.LineTo(DlPoint(0, 80));
602  path_builder.Close();
603  const DlPath path = path_builder.TakePath();
604  const Matrix matrix;
605  const Scalar height = 10.0f;
606 
607  Tessellator tessellator;
608  std::shared_ptr<ShadowVertices> shadow_vertices =
609  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
610  matrix);
611 
612  ASSERT_NE(shadow_vertices, nullptr);
613  EXPECT_FALSE(shadow_vertices->IsEmpty());
614  EXPECT_EQ(shadow_vertices->GetVertexCount(), 34u);
615  EXPECT_EQ(shadow_vertices->GetIndexCount(), 108u);
616  EXPECT_EQ(shadow_vertices->GetVertices().size(), 34u);
617  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 34u);
618  EXPECT_EQ(shadow_vertices->GetIndices().size(), 108u);
619  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
620  // We repeat the first and last vertex that is on the outer umbra.
621  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 1u);
622  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
623  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
624 
625 #if SHADOW_UNITTEST_SHOW_VERTICES
626  ShowVertices("Impeller Vertices", shadow_vertices);
627 #endif
628 }
629 
630 TEST(ShadowPathGeometryTest, CounterClockwiseRectTrickyDupColinearPointsTest) {
631  // This path includes a colinear point added to each edge of the rectangle
632  // which seems to violate convexity, but is eliminated as not contributing
633  // to the path. We should be able to process the path anyway.
634  // It also includes multiple collinear points on the first and last points
635  // that end up back where we started to make sure that in that case we
636  // eliminate all of the collinear points and the duplicate, rather than
637  // just the intermediate collinear points.
638  DlPathBuilder path_builder;
639  path_builder.MoveTo(DlPoint(0, 0));
640  path_builder.LineTo(DlPoint(0, -10));
641  path_builder.LineTo(DlPoint(0, 0));
642  path_builder.LineTo(DlPoint(0, 80));
643  path_builder.LineTo(DlPoint(-10, 80));
644  path_builder.LineTo(DlPoint(100, 80));
645  path_builder.LineTo(DlPoint(100, 90));
646  path_builder.LineTo(DlPoint(100, 0));
647  path_builder.LineTo(DlPoint(110, 0));
648  path_builder.LineTo(DlPoint(100, 0));
649  path_builder.Close();
650  DlPath path = path_builder.TakePath();
651  Matrix matrix;
652  const Scalar height = 10.0f;
653 
654  Tessellator tessellator;
655  std::shared_ptr<ShadowVertices> shadow_vertices =
656  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
657  matrix);
658 
659  ASSERT_NE(shadow_vertices, nullptr);
660  EXPECT_FALSE(shadow_vertices->IsEmpty());
661  EXPECT_EQ(shadow_vertices->GetVertexCount(), 34u);
662  EXPECT_EQ(shadow_vertices->GetIndexCount(), 108u);
663  EXPECT_EQ(shadow_vertices->GetVertices().size(), 34u);
664  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 34u);
665  EXPECT_EQ(shadow_vertices->GetIndices().size(), 108u);
666  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
667  // We repeat the first and last vertex that is on the outer umbra.
668  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 1u);
669  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
670  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
671 
672 #if SHADOW_UNITTEST_SHOW_VERTICES
673  ShowVertices("Impeller Vertices", shadow_vertices);
674 #endif
675 }
676 
677 TEST(ShadowPathGeometryTest, ClockwiseRectNearlyColinearPointsTest) {
678  // This path includes a bunch of colinear points and one point that
679  // is barely non-colinear but still convex. It should add exactly
680  // one extra set of vertices to the mesh (3 points and 3 triangles)
681  // compared to the regular rects.
682  DlPathBuilder path_builder;
683  path_builder.MoveTo(DlPoint(0, 0));
684  path_builder.LineTo(DlPoint(50, -0.065));
685  path_builder.LineTo(DlPoint(100, 0));
686  path_builder.LineTo(DlPoint(100, 40));
687  path_builder.LineTo(DlPoint(100, 80));
688  path_builder.LineTo(DlPoint(50, 80));
689  path_builder.LineTo(DlPoint(0, 80));
690  path_builder.LineTo(DlPoint(0, 40));
691  path_builder.Close();
692  const DlPath path = path_builder.TakePath();
693  const Matrix matrix;
694  const Scalar height = 10.0f;
695 
696  Tessellator tessellator;
697  std::shared_ptr<ShadowVertices> shadow_vertices =
698  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
699  matrix);
700 
701  ASSERT_NE(shadow_vertices, nullptr);
702  EXPECT_FALSE(shadow_vertices->IsEmpty());
703  EXPECT_EQ(shadow_vertices->GetVertexCount(), 37u);
704  EXPECT_EQ(shadow_vertices->GetIndexCount(), 120u);
705  EXPECT_EQ(shadow_vertices->GetVertices().size(), 37u);
706  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 37u);
707  EXPECT_EQ(shadow_vertices->GetIndices().size(), 120u);
708  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
709  // We repeat the first and last vertex that is on the outer umbra.
710  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 1u);
711  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
712  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
713 
714 #if SHADOW_UNITTEST_SHOW_VERTICES
715  ShowVertices("Impeller Vertices", shadow_vertices);
716 #endif
717 }
718 
719 TEST(ShadowPathGeometryTest, CounterClockwiseRectNearlyColinearPointsTest) {
720  // This path includes a bunch of colinear points and one point that
721  // is barely non-colinear but still convex. It should add exactly
722  // one extra set of vertices to the mesh (3 points and 3 triangles)
723  // compared to the regular rects.
724  DlPathBuilder path_builder;
725  path_builder.MoveTo(DlPoint(0, 0));
726  path_builder.LineTo(DlPoint(-0.065, 40));
727  path_builder.LineTo(DlPoint(0, 80));
728  path_builder.LineTo(DlPoint(50, 80));
729  path_builder.LineTo(DlPoint(100, 80));
730  path_builder.LineTo(DlPoint(100, 40));
731  path_builder.LineTo(DlPoint(100, 0));
732  path_builder.LineTo(DlPoint(50, 0));
733  path_builder.Close();
734  DlPath path = path_builder.TakePath();
735  Matrix matrix;
736  const Scalar height = 10.0f;
737 
738  Tessellator tessellator;
739  std::shared_ptr<ShadowVertices> shadow_vertices =
740  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
741  matrix);
742 
743  ASSERT_NE(shadow_vertices, nullptr);
744  EXPECT_FALSE(shadow_vertices->IsEmpty());
745  EXPECT_EQ(shadow_vertices->GetVertexCount(), 37u);
746  EXPECT_EQ(shadow_vertices->GetIndexCount(), 120u);
747  EXPECT_EQ(shadow_vertices->GetVertices().size(), 37u);
748  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 37u);
749  EXPECT_EQ(shadow_vertices->GetIndices().size(), 120u);
750  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
751  // We repeat the first and last vertex that is on the outer umbra.
752  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 1u);
753  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
754  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
755 
756 #if SHADOW_UNITTEST_SHOW_VERTICES
757  ShowVertices("Impeller Vertices", shadow_vertices);
758 #endif
759 }
760 
761 TEST(ShadowPathGeometryTest, ScaledRectTest) {
762  Tessellator tessellator;
763  DlPath path = DlPath::MakeRect(DlRect::MakeLTRB(0, 0, 100, 80));
764  Matrix matrix = Matrix::MakeScale({2, 3, 1});
765  const Scalar height = 10.0f;
766 
767  std::shared_ptr<ShadowVertices> shadow_vertices =
768  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
769  matrix);
770 
771  ASSERT_NE(shadow_vertices, nullptr);
772  EXPECT_FALSE(shadow_vertices->IsEmpty());
773  EXPECT_EQ(shadow_vertices->GetVertexCount(), 34u);
774  EXPECT_EQ(shadow_vertices->GetIndexCount(), 108u);
775  EXPECT_EQ(shadow_vertices->GetVertices().size(), 34u);
776  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 34u);
777  EXPECT_EQ(shadow_vertices->GetIndices().size(), 108u);
778  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
779  // We repeat the first and last vertex that is on the outer umbra.
780  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 1u);
781  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
782  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
783 
784 #if SHADOW_UNITTEST_SHOW_VERTICES
785  ShowVertices("Impeller Vertices", shadow_vertices);
786 #endif
787 }
788 
789 TEST(ShadowPathGeometryTest, EllipseTest) {
790  Tessellator tessellator;
791  DlPath path = DlPath::MakeOval(DlRect::MakeLTRB(0, 0, 100, 80));
792  Matrix matrix;
793  const Scalar height = 10.0f;
794 
795  std::shared_ptr<ShadowVertices> shadow_vertices =
796  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
797  matrix);
798 
799  ASSERT_NE(shadow_vertices, nullptr);
800  EXPECT_FALSE(shadow_vertices->IsEmpty());
801  EXPECT_EQ(shadow_vertices->GetVertexCount(), 122u);
802  EXPECT_EQ(shadow_vertices->GetIndexCount(), 480u);
803  EXPECT_EQ(shadow_vertices->GetVertices().size(), 122u);
804  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 122u);
805  EXPECT_EQ(shadow_vertices->GetIndices().size(), 480u);
806  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
807  // We repeat the first and last vertex that is on the outer umbra.
808  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 1u);
809  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
810  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
811 }
812 
813 TEST(ShadowPathGeometryTest, RoundRectTest) {
814  Tessellator tessellator;
815  DlPath path = DlPath::MakeRoundRectXY(DlRect::MakeLTRB(0, 0, 100, 80), 5, 4);
816  Matrix matrix;
817  const Scalar height = 10.0f;
818 
819  std::shared_ptr<ShadowVertices> shadow_vertices =
820  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
821  matrix);
822 
823  ASSERT_NE(shadow_vertices, nullptr);
824  EXPECT_FALSE(shadow_vertices->IsEmpty());
825  EXPECT_EQ(shadow_vertices->GetVertexCount(), 55u);
826  EXPECT_EQ(shadow_vertices->GetIndexCount(), 168u);
827  EXPECT_EQ(shadow_vertices->GetVertices().size(), 55u);
828  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 55u);
829  EXPECT_EQ(shadow_vertices->GetIndices().size(), 168u);
830  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
831  // We repeat the first and last vertex that is on the outer umbra.
832  // There is another duplicate vertex from somewhere else not yet realized.
833  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 2u);
834  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
835  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
836 }
837 
838 TEST(ShadowPathGeometryTest, HourglassSelfIntersectingTest) {
839  DlPathBuilder path_builder;
840  path_builder.MoveTo(DlPoint(0, 0));
841  path_builder.LineTo(DlPoint(100, 80));
842  path_builder.LineTo(DlPoint(100, 0));
843  path_builder.LineTo(DlPoint(0, 80));
844  path_builder.Close();
845  const DlPath path = path_builder.TakePath();
846  const Matrix matrix;
847  const Scalar height = 10.0f;
848 
849  Tessellator tessellator;
850  std::shared_ptr<ShadowVertices> shadow_vertices =
851  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
852  matrix);
853 
854  EXPECT_EQ(shadow_vertices, nullptr);
855 }
856 
857 TEST(ShadowPathGeometryTest, ReverseHourglassSelfIntersectingTest) {
858  DlPathBuilder path_builder;
859  path_builder.MoveTo(DlPoint(0, 0));
860  path_builder.LineTo(DlPoint(100, 80));
861  path_builder.LineTo(DlPoint(0, 80));
862  path_builder.LineTo(DlPoint(100, 0));
863  path_builder.Close();
864  const DlPath path = path_builder.TakePath();
865  const Matrix matrix;
866  const Scalar height = 10.0f;
867 
868  Tessellator tessellator;
869  std::shared_ptr<ShadowVertices> shadow_vertices =
870  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
871  matrix);
872 
873  EXPECT_EQ(shadow_vertices, nullptr);
874 }
875 
876 TEST(ShadowPathGeometryTest, InnerToOuterOverturningSpiralTest) {
877  const Matrix matrix;
878  const Scalar height = 10.0f;
879  int step_count = 20;
880 
881  DlPathBuilder path_builder;
882  path_builder.MoveTo(DlPoint(300, 200));
883  for (int i = 1; i < step_count * 2; i++) {
884  Scalar angle = (k2Pi * i) / step_count;
885  Scalar radius = 80.0f + std::abs(i - step_count);
886  path_builder.LineTo(DlPoint(200, 200) + DlPoint(std::cos(angle) * radius,
887  std::sin(angle) * radius));
888  }
889  path_builder.Close();
890  DlPath path = path_builder.TakePath();
891 
892  Tessellator tessellator;
893  std::shared_ptr<ShadowVertices> shadow_vertices =
894  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
895  matrix);
896 
897  EXPECT_EQ(shadow_vertices, nullptr);
898 }
899 
900 TEST(ShadowPathGeometryTest, ReverseInnerToOuterOverturningSpiralTest) {
901  const Matrix matrix;
902  const Scalar height = 10.0f;
903  int step_count = 20;
904 
905  DlPathBuilder path_builder;
906  path_builder.MoveTo(DlPoint(300, 200));
907  for (int i = 1; i < step_count * 2; i++) {
908  Scalar angle = -(k2Pi * i) / step_count;
909  Scalar radius = 80.0f + std::abs(i - step_count);
910  path_builder.LineTo(DlPoint(200, 200) + DlPoint(std::cos(angle) * radius,
911  std::sin(angle) * radius));
912  }
913  path_builder.Close();
914  DlPath path = path_builder.TakePath();
915 
916  Tessellator tessellator;
917  std::shared_ptr<ShadowVertices> shadow_vertices =
918  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
919  matrix);
920 
921  EXPECT_EQ(shadow_vertices, nullptr);
922 }
923 
924 TEST(ShadowPathGeometryTest, OuterToInnerOverturningSpiralTest) {
925  const Matrix matrix;
926  const Scalar height = 10.0f;
927  int step_count = 20;
928 
929  DlPathBuilder path_builder;
930  path_builder.MoveTo(DlPoint(280, 200));
931  for (int i = 1; i < step_count * 2; i++) {
932  Scalar angle = (k2Pi * i) / step_count;
933  Scalar radius = 100.0f - std::abs(i - step_count);
934  path_builder.LineTo(DlPoint(200, 200) + DlPoint(std::cos(angle) * radius,
935  std::sin(angle) * radius));
936  }
937  path_builder.Close();
938  DlPath path = path_builder.TakePath();
939 
940  Tessellator tessellator;
941  std::shared_ptr<ShadowVertices> shadow_vertices =
942  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
943  matrix);
944 
945  EXPECT_EQ(shadow_vertices, nullptr);
946 }
947 
948 TEST(ShadowPathGeometryTest, ReverseOuterToInnerOverturningSpiralTest) {
949  const Matrix matrix;
950  const Scalar height = 10.0f;
951  int step_count = 20;
952 
953  DlPathBuilder path_builder;
954  path_builder.MoveTo(DlPoint(280, 200));
955  for (int i = 1; i < step_count * 2; i++) {
956  Scalar angle = -(k2Pi * i) / step_count;
957  Scalar radius = 100.0f - std::abs(i - step_count);
958  path_builder.LineTo(DlPoint(200, 200) + DlPoint(std::cos(angle) * radius,
959  std::sin(angle) * radius));
960  }
961  path_builder.Close();
962  DlPath path = path_builder.TakePath();
963 
964  Tessellator tessellator;
965  std::shared_ptr<ShadowVertices> shadow_vertices =
966  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
967  matrix);
968 
969  EXPECT_EQ(shadow_vertices, nullptr);
970 }
971 
972 TEST(ShadowPathGeometryTest, ClockwiseOctagonCollapsedUmbraPolygonTest) {
973  const Matrix matrix = Matrix::MakeScale({2, 2, 1});
974  const Scalar height = 100.0f;
975 
976  DlPathBuilder path_builder;
977  path_builder.MoveTo(DlPoint(100, 125));
978  path_builder.LineTo(DlPoint(125, 100));
979  path_builder.LineTo(DlPoint(275, 100));
980  path_builder.LineTo(DlPoint(300, 125));
981  path_builder.LineTo(DlPoint(300, 275));
982  path_builder.LineTo(DlPoint(275, 300));
983  path_builder.LineTo(DlPoint(125, 300));
984  path_builder.LineTo(DlPoint(100, 275));
985  path_builder.Close();
986  DlPath path = path_builder.TakePath();
987 
988  Tessellator tessellator;
989  std::shared_ptr<ShadowVertices> shadow_vertices =
990  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
991  matrix);
992 
993  ASSERT_NE(shadow_vertices, nullptr);
994  EXPECT_FALSE(shadow_vertices->IsEmpty());
995  EXPECT_EQ(shadow_vertices->GetVertexCount(), 87u);
996  EXPECT_EQ(shadow_vertices->GetIndexCount(), 267u);
997  EXPECT_EQ(shadow_vertices->GetVertices().size(), 87u);
998  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 87u);
999  EXPECT_EQ(shadow_vertices->GetIndices().size(), 267u);
1000  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
1001  // We repeat the first and last vertex that is on the outer umbra.
1002  // There are a couple additional duplicate vertices in this case.
1003  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 3u);
1004  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
1005  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
1006 }
1007 
1008 TEST(ShadowPathGeometryTest, CounterClockwiseOctagonCollapsedUmbraPolygonTest) {
1009  const Matrix matrix = Matrix::MakeScale({2, 2, 1});
1010  const Scalar height = 100.0f;
1011 
1012  DlPathBuilder path_builder;
1013  path_builder.MoveTo(DlPoint(100, 125));
1014  path_builder.LineTo(DlPoint(100, 275));
1015  path_builder.LineTo(DlPoint(125, 300));
1016  path_builder.LineTo(DlPoint(275, 300));
1017  path_builder.LineTo(DlPoint(300, 275));
1018  path_builder.LineTo(DlPoint(300, 125));
1019  path_builder.LineTo(DlPoint(275, 100));
1020  path_builder.LineTo(DlPoint(125, 100));
1021  path_builder.Close();
1022  DlPath path = path_builder.TakePath();
1023 
1024  Tessellator tessellator;
1025  std::shared_ptr<ShadowVertices> shadow_vertices =
1026  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
1027  matrix);
1028 
1029  ASSERT_NE(shadow_vertices, nullptr);
1030  EXPECT_FALSE(shadow_vertices->IsEmpty());
1031  EXPECT_EQ(shadow_vertices->GetVertexCount(), 88u);
1032  EXPECT_EQ(shadow_vertices->GetIndexCount(), 267u);
1033  EXPECT_EQ(shadow_vertices->GetVertices().size(), 88u);
1034  EXPECT_EQ(shadow_vertices->GetGaussians().size(), 88u);
1035  EXPECT_EQ(shadow_vertices->GetIndices().size(), 267u);
1036  EXPECT_EQ((shadow_vertices->GetIndices().size() % 3u), 0u);
1037  // We repeat the first and last vertex that is on the outer umbra.
1038  // There are a couple additional duplicate vertices in this case.
1039  EXPECT_LE(CountDuplicateVertices(shadow_vertices), 3u);
1040  EXPECT_EQ(CountDuplicateTriangles(shadow_vertices), 0u);
1041  EXPECT_FALSE(DoTrianglesOverlap(shadow_vertices));
1042 }
1043 
1044 TEST(ShadowPathGeometryTest, MultipleContoursTest) {
1045  const Matrix matrix;
1046  const Scalar height = 10.0f;
1047 
1048  DlPathBuilder path_builder;
1049  path_builder.MoveTo(DlPoint(150, 100));
1050  path_builder.LineTo(DlPoint(200, 300));
1051  path_builder.LineTo(DlPoint(100, 300));
1052  path_builder.Close();
1053  path_builder.MoveTo(DlPoint(250, 100));
1054  path_builder.LineTo(DlPoint(300, 300));
1055  path_builder.LineTo(DlPoint(200, 300));
1056  path_builder.Close();
1057  DlPath path = path_builder.TakePath();
1058 
1059  Tessellator tessellator;
1060  std::shared_ptr<ShadowVertices> shadow_vertices =
1061  ShadowPathGeometry::MakeAmbientShadowVertices(tessellator, path, height,
1062  matrix);
1063 
1064  EXPECT_EQ(shadow_vertices, nullptr);
1065 }
1066 
1067 } // namespace testing
1068 } // namespace impeller
static std::shared_ptr< ShadowVertices > MakeAmbientShadowVertices(Tessellator &tessellator, const PathSource &source, Scalar occluder_height, const Matrix &matrix)
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Definition: tessellator.h:37
TEST(AllocationSizeTest, CanCreateTypedAllocations)
constexpr float k2Pi
Definition: constants.h:29
flutter::DlRect DlRect
Definition: dl_dispatcher.h:25
float Scalar
Definition: scalar.h:19
constexpr float kEhCloseEnough
Definition: constants.h:57
TPoint< Scalar > Point
Definition: point.h:425
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
flutter::DlPath DlPath
Definition: dl_dispatcher.h:29
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
constexpr Type Cross(const TPoint &p) const
Definition: point.h:295