| 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
5 #include "flutter/impeller/entity/geometry/shadow_path_geometry.h"
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 =
46 flutter::kEhCloseEnough * flutter::kEhCloseEnough;
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
impeller::ShadowPathGeometry
Definition: shadow_path_geometry.h:80
impeller::ShadowPathGeometry::MakeAmbientShadowVertices
static std::shared_ptr< ShadowVertices > MakeAmbientShadowVertices(Tessellator &tessellator, const PathSource &source, Scalar occluder_height, const Matrix &matrix)
Definition: shadow_path_geometry.cc:1454
impeller::Tessellator
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Definition: tessellator.h:37
impeller::testing::TEST
TEST(AllocationSizeTest, CanCreateTypedAllocations)
Definition: allocation_size_unittests.cc:10
impeller
Definition: allocation.cc:12
impeller::k2Pi
constexpr float k2Pi
Definition: constants.h:29
impeller::DlRect
flutter::DlRect DlRect
Definition: dl_dispatcher.h:25
impeller::Scalar
float Scalar
Definition: scalar.h:19
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:57
impeller::Point
TPoint< Scalar > Point
Definition: point.h:425
impeller::DlPoint
flutter::DlPoint DlPoint
Definition: dl_dispatcher.h:24
impeller::DlPath
flutter::DlPath DlPath
Definition: dl_dispatcher.h:29
shadow_path_geometry.h
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::TPoint< Scalar >::Cross
constexpr Type Cross(const TPoint &p) const
Definition: point.h:295