Flutter Impeller
tessellator_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/testing/testing.h"
6 #include "gtest/gtest.h"
7 
8 #include "flutter/display_list/geometry/dl_path_builder.h"
13 
14 namespace impeller {
15 namespace testing {
16 
17 TEST(TessellatorTest, TessellatorBuilderReturnsCorrectResultStatus) {
18  // Zero points.
19  {
21  auto path = flutter::DlPathBuilder{} //
22  .SetFillType(FillType::kOdd)
23  .TakePath();
25  path, 1.0f,
26  [](const float* vertices, size_t vertices_count,
27  const uint16_t* indices, size_t indices_count) { return true; });
28 
29  ASSERT_EQ(result, TessellatorLibtess::Result::kInputError);
30  }
31 
32  // One point.
33  {
35  auto path = flutter::DlPathBuilder{}
36  .LineTo({0, 0})
37  .SetFillType(FillType::kOdd)
38  .TakePath();
40  path, 1.0f,
41  [](const float* vertices, size_t vertices_count,
42  const uint16_t* indices, size_t indices_count) { return true; });
43 
44  ASSERT_EQ(result, TessellatorLibtess::Result::kSuccess);
45  }
46 
47  // Two points.
48  {
50  auto path = flutter::DlPathBuilder{}
51  .MoveTo({0, 0})
52  .LineTo({0, 1})
53  .SetFillType(FillType::kOdd)
54  .TakePath();
56  path, 1.0f,
57  [](const float* vertices, size_t vertices_count,
58  const uint16_t* indices, size_t indices_count) { return true; });
59 
60  ASSERT_EQ(result, TessellatorLibtess::Result::kSuccess);
61  }
62 
63  // Many points.
64  {
66  flutter::DlPathBuilder builder;
67  for (int i = 0; i < 1000; i++) {
68  auto coord = i * 1.0f;
69  builder.MoveTo({coord, coord}).LineTo({coord + 1, coord + 1});
70  }
71  auto path = builder.SetFillType(FillType::kOdd).TakePath();
73  path, 1.0f,
74  [](const float* vertices, size_t vertices_count,
75  const uint16_t* indices, size_t indices_count) { return true; });
76 
77  ASSERT_EQ(result, TessellatorLibtess::Result::kSuccess);
78  }
79 
80  // Closure fails.
81  {
83  auto path = flutter::DlPathBuilder{}
84  .MoveTo({0, 0})
85  .LineTo({0, 1})
86  .SetFillType(FillType::kOdd)
87  .TakePath();
89  path, 1.0f,
90  [](const float* vertices, size_t vertices_count,
91  const uint16_t* indices, size_t indices_count) { return false; });
92 
93  ASSERT_EQ(result, TessellatorLibtess::Result::kInputError);
94  }
95 }
96 
97 TEST(TessellatorTest, TessellateConvex) {
98  {
99  std::vector<Point> points;
100  std::vector<uint16_t> indices;
101  // Sanity check simple rectangle.
103  flutter::DlPath::MakeRect(Rect::MakeLTRB(0, 0, 10, 10)), points,
104  indices, 1.0);
105 
106  // Note: the origin point is repeated but not referenced in the indices
107  // below
108  std::vector<Point> expected = {{0, 0}, {10, 0}, {10, 10}, {0, 10}, {0, 0}};
109  std::vector<uint16_t> expected_indices = {0, 1, 3, 2};
110  EXPECT_EQ(points, expected);
111  EXPECT_EQ(indices, expected_indices);
112  }
113 
114  {
115  std::vector<Point> points;
116  std::vector<uint16_t> indices;
118  flutter::DlPath(flutter::DlPathBuilder{}
119  .AddRect(Rect::MakeLTRB(0, 0, 10, 10))
120  .AddRect(Rect::MakeLTRB(20, 20, 30, 30))
121  .TakePath()),
122  points, indices, 1.0);
123 
124  std::vector<Point> expected = {{0, 0}, {10, 0}, {10, 10}, {0, 10},
125  {0, 0}, {20, 20}, {30, 20}, {30, 30},
126  {20, 30}, {20, 20}};
127  std::vector<uint16_t> expected_indices = {0, 1, 3, 2, 2, 5, 5, 6, 8, 7};
128  EXPECT_EQ(points, expected);
129  EXPECT_EQ(indices, expected_indices);
130  }
131 }
132 
133 // Filled Paths without an explicit close should still be closed implicitly
134 TEST(TessellatorTest, TessellateConvexUnclosedPath) {
135  std::vector<Point> points;
136  std::vector<uint16_t> indices;
137 
138  // Create a rectangle that lacks an explicit close.
139  flutter::DlPath path = flutter::DlPathBuilder{}
140  .LineTo({100, 0})
141  .LineTo({100, 100})
142  .LineTo({0, 100})
143  .TakePath();
145  1.0);
146 
147  std::vector<Point> expected = {
148  {0, 0}, {100, 0}, {100, 100}, {0, 100}, {0, 0}};
149  std::vector<uint16_t> expected_indices = {0, 1, 3, 2};
150  EXPECT_EQ(points, expected);
151  EXPECT_EQ(indices, expected_indices);
152 }
153 
154 TEST(TessellatorTest, CircleVertexCounts) {
155  Tessellator tessellator;
156 
157  auto test = [&tessellator](const Matrix& transform, Scalar radius) {
158  auto generator = tessellator.FilledCircle(transform, {}, radius);
159  size_t quadrant_divisions = generator.GetVertexCount() / 4;
160 
161  // Confirm the approximation error is within the currently accepted
162  // |kCircleTolerance| value advertised by |CircleTessellator|.
163  // (With an additional 1% tolerance for floating point rounding.)
164  double angle = kPiOver2 / quadrant_divisions;
165  Point first = {radius, 0};
166  Point next = {static_cast<Scalar>(cos(angle) * radius),
167  static_cast<Scalar>(sin(angle) * radius)};
168  Point midpoint = (first + next) * 0.5;
169  EXPECT_GE(midpoint.GetLength(),
170  radius - Tessellator::kCircleTolerance * 1.01)
171  << ", transform = " << transform << ", radius = " << radius
172  << ", divisions = " << quadrant_divisions;
173  };
174 
175  test({}, 0.0);
176  test({}, 0.9);
177  test({}, 1.0);
178  test({}, 1.9);
179  test(Matrix::MakeScale(Vector2(2.0, 2.0)), 0.95);
180  test({}, 2.0);
181  test(Matrix::MakeScale(Vector2(2.0, 2.0)), 1.0);
182  test({}, 11.9);
183  test({}, 12.0);
184  test({}, 35.9);
185  for (int i = 36; i < 10000; i += 4) {
186  test({}, i);
187  }
188 }
189 
190 TEST(TessellatorTest, FilledCircleTessellationVertices) {
191  Tessellator tessellator;
192 
193  auto test = [&tessellator](const Matrix& transform, const Point& center,
194  Scalar radius) {
195  auto generator = tessellator.FilledCircle(transform, center, radius);
196  EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
197 
198  auto vertex_count = generator.GetVertexCount();
199  auto vertices = std::vector<Point>();
200  generator.GenerateVertices([&vertices](const Point& p) { //
201  vertices.push_back(p);
202  });
203  EXPECT_EQ(vertices.size(), vertex_count);
204  ASSERT_EQ(vertex_count % 4, 0u);
205 
206  auto quadrant_count = vertex_count / 4;
207  for (size_t i = 0; i < quadrant_count; i++) {
208  double angle = kPiOver2 * i / (quadrant_count - 1);
209  double degrees = angle * 180.0 / kPi;
210  double rsin = sin(angle) * radius;
211  // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
212  double rcos = (i == quadrant_count - 1) ? 0.0f : cos(angle) * radius;
213  EXPECT_POINT_NEAR(vertices[i * 2],
214  Point(center.x - rcos, center.y + rsin))
215  << "vertex " << i << ", angle = " << degrees << std::endl;
216  EXPECT_POINT_NEAR(vertices[i * 2 + 1],
217  Point(center.x - rcos, center.y - rsin))
218  << "vertex " << i << ", angle = " << degrees << std::endl;
219  EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 1],
220  Point(center.x + rcos, center.y - rsin))
221  << "vertex " << i << ", angle = " << degrees << std::endl;
222  EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 2],
223  Point(center.x + rcos, center.y + rsin))
224  << "vertex " << i << ", angle = " << degrees << std::endl;
225  }
226  };
227 
228  test({}, {}, 2.0);
229  test({}, {10, 10}, 2.0);
230  test(Matrix::MakeScale({500.0, 500.0, 0.0}), {}, 2.0);
231  test(Matrix::MakeScale({0.002, 0.002, 0.0}), {}, 1000.0);
232 }
233 
234 TEST(TessellatorTest, StrokedCircleTessellationVertices) {
235  Tessellator tessellator;
236 
237  auto test = [&tessellator](const Matrix& transform, const Point& center,
238  Scalar radius, Scalar half_width) {
239  ASSERT_GT(radius, half_width);
240  auto generator =
241  tessellator.StrokedCircle(transform, center, radius, half_width);
242  EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
243 
244  auto vertex_count = generator.GetVertexCount();
245  auto vertices = std::vector<Point>();
246  generator.GenerateVertices([&vertices](const Point& p) { //
247  vertices.push_back(p);
248  });
249  EXPECT_EQ(vertices.size(), vertex_count);
250  ASSERT_EQ(vertex_count % 4, 0u);
251 
252  auto quadrant_count = vertex_count / 8;
253 
254  // Test outer points first
255  for (size_t i = 0; i < quadrant_count; i++) {
256  double angle = kPiOver2 * i / (quadrant_count - 1);
257  double degrees = angle * 180.0 / kPi;
258  double rsin = sin(angle) * (radius + half_width);
259  // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
260  double rcos =
261  (i == quadrant_count - 1) ? 0.0f : cos(angle) * (radius + half_width);
262  EXPECT_POINT_NEAR(vertices[i * 2],
263  Point(center.x - rcos, center.y - rsin))
264  << "vertex " << i << ", angle = " << degrees << std::endl;
265  EXPECT_POINT_NEAR(vertices[quadrant_count * 2 + i * 2],
266  Point(center.x + rsin, center.y - rcos))
267  << "vertex " << i << ", angle = " << degrees << std::endl;
268  EXPECT_POINT_NEAR(vertices[quadrant_count * 4 + i * 2],
269  Point(center.x + rcos, center.y + rsin))
270  << "vertex " << i << ", angle = " << degrees << std::endl;
271  EXPECT_POINT_NEAR(vertices[quadrant_count * 6 + i * 2],
272  Point(center.x - rsin, center.y + rcos))
273  << "vertex " << i << ", angle = " << degrees << std::endl;
274  }
275 
276  // Then test innerer points
277  for (size_t i = 0; i < quadrant_count; i++) {
278  double angle = kPiOver2 * i / (quadrant_count - 1);
279  double degrees = angle * 180.0 / kPi;
280  double rsin = sin(angle) * (radius - half_width);
281  // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
282  double rcos =
283  (i == quadrant_count - 1) ? 0.0f : cos(angle) * (radius - half_width);
284  EXPECT_POINT_NEAR(vertices[i * 2 + 1],
285  Point(center.x - rcos, center.y - rsin))
286  << "vertex " << i << ", angle = " << degrees << std::endl;
287  EXPECT_POINT_NEAR(vertices[quadrant_count * 2 + i * 2 + 1],
288  Point(center.x + rsin, center.y - rcos))
289  << "vertex " << i << ", angle = " << degrees << std::endl;
290  EXPECT_POINT_NEAR(vertices[quadrant_count * 4 + i * 2 + 1],
291  Point(center.x + rcos, center.y + rsin))
292  << "vertex " << i << ", angle = " << degrees << std::endl;
293  EXPECT_POINT_NEAR(vertices[quadrant_count * 6 + i * 2 + 1],
294  Point(center.x - rsin, center.y + rcos))
295  << "vertex " << i << ", angle = " << degrees << std::endl;
296  }
297  };
298 
299  test({}, {}, 2.0, 1.0);
300  test({}, {}, 2.0, 0.5);
301  test({}, {10, 10}, 2.0, 1.0);
302  test(Matrix::MakeScale({500.0, 500.0, 0.0}), {}, 2.0, 1.0);
303  test(Matrix::MakeScale({0.002, 0.002, 0.0}), {}, 1000.0, 10.0);
304 }
305 
306 TEST(TessellatorTest, FilledArcStripTessellationVertices) {
307  Tessellator tessellator;
308 
309  auto test = [&tessellator](const Matrix& transform, const Arc& arc) {
310  auto generator = tessellator.FilledArc(transform, arc,
311  /*supports_triangle_fans=*/false);
312  EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
313 
314  auto vertex_count = generator.GetVertexCount();
315  auto vertices = std::vector<Point>();
316  generator.GenerateVertices([&vertices](const Point& p) { //
317  vertices.push_back(p);
318  });
319  EXPECT_EQ(vertices.size(), vertex_count);
320 
321  auto center = arc.GetOvalBounds().GetCenter();
322  auto radius = arc.GetOvalSize().width * 0.5;
323 
324  // Test position of first point
326  vertices[0],
327  Point(center.x + cos(Radians(arc.GetStart()).radians) * radius,
328  center.y + sin(Radians(arc.GetStart()).radians) * radius));
329 
330  // Test position of last point
331  auto last_angle = arc.GetStart() + arc.GetSweep();
333  vertices[vertex_count - 1],
334  Point(center.x + cos(Radians(last_angle).radians) * radius,
335  center.y + sin(Radians(last_angle).radians) * radius));
336 
337  // Test odd-indexed points. These are all the origin.
338  Point origin = arc.IncludeCenter()
339  ? center
340  : (vertices[0] + vertices[vertex_count - 1]) * 0.5f;
341  for (size_t i = 1; i < vertex_count; i += 2) {
342  EXPECT_POINT_NEAR(vertices[i], origin);
343  }
344 
345  // Test even-indexed points. These are points on the outer edge of the arc.
346  auto previous_outer_point = vertices[0];
347  auto outer_increment_distance = (vertices[4] - vertices[2]).GetLength();
348  for (size_t i = 2; i < vertex_count; i += 2) {
349  // Each is |radius| from the center.
350  EXPECT_NEAR((vertices[i] - center).GetLength(), radius, kEhCloseEnough);
351 
352  // Each is within |outer_increment_distance| from the previous
353  if (i == 2 || i == vertex_count - 1) {
354  // The very first and last points may be closer than
355  // |outer_increment_distance| to their adjacent outer points
356  EXPECT_LE((vertices[i] - previous_outer_point).GetLength(),
357  outer_increment_distance + kEhCloseEnough);
358  } else {
359  // Other outer points are |outer_increment_distance| apart
360  EXPECT_NEAR((vertices[i] - previous_outer_point).GetLength(),
361  outer_increment_distance, kEhCloseEnough);
362  }
363 
364  previous_outer_point = vertices[i];
365  }
366  };
367 
368  test({}, Arc(Rect::MakeXYWH(0, 0, 100, 100), Degrees(0), Degrees(90), false));
369  test({}, Arc(Rect::MakeXYWH(0, 0, 100, 100), Degrees(0), Degrees(90), true));
370 
371  test({},
372  Arc(Rect::MakeXYWH(0, 0, 100, 100), Degrees(0), Degrees(-270), false));
373  test({},
374  Arc(Rect::MakeXYWH(0, 0, 100, 100), Degrees(0), Degrees(-270), true));
375 
376  test({},
377  Arc(Rect::MakeXYWH(0, 0, 100, 100), Degrees(94), Degrees(322), false));
378  test({},
379  Arc(Rect::MakeXYWH(0, 0, 100, 100), Degrees(94), Degrees(322), true));
380 }
381 
382 TEST(TessellatorTest, RoundCapLineTessellationVertices) {
383  Tessellator tessellator;
384 
385  auto test = [&tessellator](const Matrix& transform, const Point& p0,
386  const Point& p1, Scalar radius) {
387  auto generator = tessellator.RoundCapLine(transform, p0, p1, radius);
388  EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
389 
390  auto vertex_count = generator.GetVertexCount();
391  auto vertices = std::vector<Point>();
392  generator.GenerateVertices([&vertices](const Point& p) { //
393  vertices.push_back(p);
394  });
395  EXPECT_EQ(vertices.size(), vertex_count);
396  ASSERT_EQ(vertex_count % 4, 0u);
397 
398  Point along = p1 - p0;
399  Scalar length = along.GetLength();
400  if (length > 0) {
401  along *= radius / length;
402  } else {
403  along = {radius, 0};
404  }
405  Point across = {-along.y, along.x};
406 
407  auto quadrant_count = vertex_count / 4;
408  for (size_t i = 0; i < quadrant_count; i++) {
409  double angle = kPiOver2 * i / (quadrant_count - 1);
410  double degrees = angle * 180.0 / kPi;
411  // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
412  Point relative_along =
413  along * ((i == quadrant_count - 1) ? 0.0f : cos(angle));
414  Point relative_across = across * sin(angle);
415  EXPECT_POINT_NEAR(vertices[i * 2], //
416  p0 - relative_along + relative_across)
417  << "vertex " << i << ", angle = " << degrees << ", " //
418  << "line = " << p0 << " => " << p1 << ", " //
419  << "radius = " << radius << std::endl;
420  EXPECT_POINT_NEAR(vertices[i * 2 + 1], //
421  p0 - relative_along - relative_across)
422  << "vertex " << i << ", angle = " << degrees << ", " //
423  << "line = " << p0 << " => " << p1 << ", " //
424  << "radius = " << radius << std::endl;
425  EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 1], //
426  p1 + relative_along - relative_across)
427  << "vertex " << i << ", angle = " << degrees << ", " //
428  << "line = " << p0 << " => " << p1 << ", " //
429  << "radius = " << radius << std::endl;
430  EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 2], //
431  p1 + relative_along + relative_across)
432  << "vertex " << i << ", angle = " << degrees << ", " //
433  << "line = " << p0 << " => " << p1 << ", " //
434  << "radius = " << radius << std::endl;
435  }
436  };
437 
438  // Empty line should actually use the circle generator, but its
439  // results should match the same math as the round cap generator.
440  test({}, {0, 0}, {0, 0}, 10);
441 
442  test({}, {0, 0}, {10, 0}, 2);
443  test({}, {10, 0}, {0, 0}, 2);
444  test({}, {0, 0}, {10, 10}, 2);
445 
446  test(Matrix::MakeScale({500.0, 500.0, 0.0}), {0, 0}, {10, 0}, 2);
447  test(Matrix::MakeScale({500.0, 500.0, 0.0}), {10, 0}, {0, 0}, 2);
448  test(Matrix::MakeScale({500.0, 500.0, 0.0}), {0, 0}, {10, 10}, 2);
449 
450  test(Matrix::MakeScale({0.002, 0.002, 0.0}), {0, 0}, {10, 0}, 2);
451  test(Matrix::MakeScale({0.002, 0.002, 0.0}), {10, 0}, {0, 0}, 2);
452  test(Matrix::MakeScale({0.002, 0.002, 0.0}), {0, 0}, {10, 10}, 2);
453 }
454 
455 TEST(TessellatorTest, FilledEllipseTessellationVertices) {
456  Tessellator tessellator;
457 
458  auto test = [&tessellator](const Matrix& transform, const Rect& bounds) {
459  auto center = bounds.GetCenter();
460  auto half_size = bounds.GetSize() * 0.5f;
461 
462  auto generator = tessellator.FilledEllipse(transform, bounds);
463  EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
464 
465  auto vertex_count = generator.GetVertexCount();
466  auto vertices = std::vector<Point>();
467  generator.GenerateVertices([&vertices](const Point& p) { //
468  vertices.push_back(p);
469  });
470  EXPECT_EQ(vertices.size(), vertex_count);
471  ASSERT_EQ(vertex_count % 4, 0u);
472 
473  auto quadrant_count = vertex_count / 4;
474  for (size_t i = 0; i < quadrant_count; i++) {
475  double angle = kPiOver2 * i / (quadrant_count - 1);
476  double degrees = angle * 180.0 / kPi;
477  // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
478  double rcos =
479  (i == quadrant_count - 1) ? 0.0f : cos(angle) * half_size.width;
480  double rsin = sin(angle) * half_size.height;
481  EXPECT_POINT_NEAR(vertices[i * 2],
482  Point(center.x - rcos, center.y + rsin))
483  << "vertex " << i << ", angle = " << degrees << ", " //
484  << "bounds = " << bounds << std::endl;
485  EXPECT_POINT_NEAR(vertices[i * 2 + 1],
486  Point(center.x - rcos, center.y - rsin))
487  << "vertex " << i << ", angle = " << degrees << ", " //
488  << "bounds = " << bounds << std::endl;
489  EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 1],
490  Point(center.x + rcos, center.y - rsin))
491  << "vertex " << i << ", angle = " << degrees << ", " //
492  << "bounds = " << bounds << std::endl;
493  EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 2],
494  Point(center.x + rcos, center.y + rsin))
495  << "vertex " << i << ", angle = " << degrees << ", " //
496  << "bounds = " << bounds << std::endl;
497  }
498  };
499 
500  // Square bounds should actually use the circle generator, but its
501  // results should match the same math as the ellipse generator.
502  test({}, Rect::MakeXYWH(0, 0, 2, 2));
503 
504  test({}, Rect::MakeXYWH(0, 0, 2, 3));
505  test({}, Rect::MakeXYWH(0, 0, 3, 2));
506  test({}, Rect::MakeXYWH(5, 10, 2, 3));
507  test({}, Rect::MakeXYWH(16, 7, 3, 2));
508  test(Matrix::MakeScale({500.0, 500.0, 0.0}), Rect::MakeXYWH(5, 10, 3, 2));
509  test(Matrix::MakeScale({500.0, 500.0, 0.0}), Rect::MakeXYWH(5, 10, 2, 3));
510  test(Matrix::MakeScale({0.002, 0.002, 0.0}),
511  Rect::MakeXYWH(5000, 10000, 3000, 2000));
512  test(Matrix::MakeScale({0.002, 0.002, 0.0}),
513  Rect::MakeXYWH(5000, 10000, 2000, 3000));
514 }
515 
516 TEST(TessellatorTest, FilledRoundRectTessellationVertices) {
517  Tessellator tessellator;
518 
519  auto test = [&tessellator](const Matrix& transform, const Rect& bounds,
520  const Size& radii) {
521  FML_DCHECK(radii.width * 2 <= bounds.GetWidth()) << radii << bounds;
522  FML_DCHECK(radii.height * 2 <= bounds.GetHeight()) << radii << bounds;
523 
524  Scalar middle_left = bounds.GetX() + radii.width;
525  Scalar middle_top = bounds.GetY() + radii.height;
526  Scalar middle_right = bounds.GetX() + bounds.GetWidth() - radii.width;
527  Scalar middle_bottom = bounds.GetY() + bounds.GetHeight() - radii.height;
528 
529  auto generator = tessellator.FilledRoundRect(transform, bounds, radii);
530  EXPECT_EQ(generator.GetTriangleType(), PrimitiveType::kTriangleStrip);
531 
532  auto vertex_count = generator.GetVertexCount();
533  auto vertices = std::vector<Point>();
534  generator.GenerateVertices([&vertices](const Point& p) { //
535  vertices.push_back(p);
536  });
537  EXPECT_EQ(vertices.size(), vertex_count);
538  ASSERT_EQ(vertex_count % 4, 0u);
539 
540  auto quadrant_count = vertex_count / 4;
541  for (size_t i = 0; i < quadrant_count; i++) {
542  double angle = kPiOver2 * i / (quadrant_count - 1);
543  double degrees = angle * 180.0 / kPi;
544  // Note that cos(radians(90 degrees)) isn't exactly 0.0 like it should be
545  double rcos = (i == quadrant_count - 1) ? 0.0f : cos(angle) * radii.width;
546  double rsin = sin(angle) * radii.height;
547  EXPECT_POINT_NEAR(vertices[i * 2],
548  Point(middle_left - rcos, middle_bottom + rsin))
549  << "vertex " << i << ", angle = " << degrees << ", " //
550  << "bounds = " << bounds << std::endl;
551  EXPECT_POINT_NEAR(vertices[i * 2 + 1],
552  Point(middle_left - rcos, middle_top - rsin))
553  << "vertex " << i << ", angle = " << degrees << ", " //
554  << "bounds = " << bounds << std::endl;
555  EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 1],
556  Point(middle_right + rcos, middle_top - rsin))
557  << "vertex " << i << ", angle = " << degrees << ", " //
558  << "bounds = " << bounds << std::endl;
559  EXPECT_POINT_NEAR(vertices[vertex_count - i * 2 - 2],
560  Point(middle_right + rcos, middle_bottom + rsin))
561  << "vertex " << i << ", angle = " << degrees << ", " //
562  << "bounds = " << bounds << std::endl;
563  }
564  };
565 
566  // Both radii spanning the bounds should actually use the circle/ellipse
567  // generator, but their results should match the same math as the round
568  // rect generator.
569  test({}, Rect::MakeXYWH(0, 0, 20, 20), {10, 10});
570 
571  // One radius spanning the bounds, but not the other will not match the
572  // round rect math if the generator transfers to circle/ellipse
573  test({}, Rect::MakeXYWH(0, 0, 20, 20), {10, 5});
574  test({}, Rect::MakeXYWH(0, 0, 20, 20), {5, 10});
575 
576  test({}, Rect::MakeXYWH(0, 0, 20, 30), {2, 2});
577  test({}, Rect::MakeXYWH(0, 0, 30, 20), {2, 2});
578  test({}, Rect::MakeXYWH(5, 10, 20, 30), {2, 3});
579  test({}, Rect::MakeXYWH(16, 7, 30, 20), {2, 3});
580  test(Matrix::MakeScale({500.0, 500.0, 0.0}), Rect::MakeXYWH(5, 10, 30, 20),
581  {2, 3});
582  test(Matrix::MakeScale({500.0, 500.0, 0.0}), Rect::MakeXYWH(5, 10, 20, 30),
583  {2, 3});
584  test(Matrix::MakeScale({0.002, 0.002, 0.0}),
585  Rect::MakeXYWH(5000, 10000, 3000, 2000), {50, 70});
586  test(Matrix::MakeScale({0.002, 0.002, 0.0}),
587  Rect::MakeXYWH(5000, 10000, 2000, 3000), {50, 70});
588 }
589 
590 TEST(TessellatorTest, EarlyReturnEmptyConvexShape) {
591  // This path is not technically empty (it has a size in one dimension), but
592  // it contains only move commands and no actual path segment definitions.
593  flutter::DlPathBuilder builder;
594  builder.MoveTo({0, 0});
595  builder.MoveTo({10, 10});
596 
597  std::vector<Point> points;
598  std::vector<uint16_t> indices;
599  Tessellator::TessellateConvexInternal(builder.TakePath(), points, indices,
600  3.0f);
601 
602  EXPECT_TRUE(points.empty());
603  EXPECT_TRUE(indices.empty());
604 }
605 
606 } // namespace testing
607 } // namespace impeller
size_t GetVertexCount() const override
|VertexGenerator|
Definition: tessellator.h:148
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Definition: tessellator.h:37
EllipticalVertexGenerator RoundCapLine(const Matrix &view_transform, const Point &p0, const Point &p1, Scalar radius)
Create a |VertexGenerator| that can produce vertices for a line with round end caps of the given radi...
Definition: tessellator.cc:645
EllipticalVertexGenerator FilledRoundRect(const Matrix &view_transform, const Rect &bounds, const Size &radii)
Create a |VertexGenerator| that can produce vertices for a filled round rect within the given bounds ...
Definition: tessellator.cc:689
static constexpr Scalar kCircleTolerance
The pixel tolerance used by the algorighm to determine how many divisions to create for a circle.
Definition: tessellator.h:280
EllipticalVertexGenerator FilledCircle(const Matrix &view_transform, const Point &center, Scalar radius)
Create a |VertexGenerator| that can produce vertices for a filled circle of the given radius around t...
Definition: tessellator.cc:488
static void TessellateConvexInternal(const PathSource &path, std::vector< Point > &point_buffer, std::vector< uint16_t > &index_buffer, Scalar tolerance)
Definition: tessellator.cc:440
EllipticalVertexGenerator StrokedCircle(const Matrix &view_transform, const Point &center, Scalar radius, Scalar half_width)
Create a |VertexGenerator| that can produce vertices for a stroked circle of the given radius and hal...
Definition: tessellator.cc:504
EllipticalVertexGenerator FilledEllipse(const Matrix &view_transform, const Rect &bounds)
Create a |VertexGenerator| that can produce vertices for a filled ellipse inscribed within the given ...
Definition: tessellator.cc:668
ArcVertexGenerator FilledArc(const Matrix &view_transform, const Arc &arc, bool supports_triangle_fans)
Create a |VertexGenerator| that can produce vertices for a stroked arc inscribed within the given ova...
Definition: tessellator.cc:612
An extended tessellator that offers arbitrary/concave tessellation via the libtess2 library.
TessellatorLibtess::Result Tessellate(const PathSource &source, Scalar tolerance, const BuilderCallback &callback)
Generates filled triangles from the path. A callback is invoked once for the entire tessellation.
#define EXPECT_POINT_NEAR(a, b)
TEST(AllocationSizeTest, CanCreateTypedAllocations)
Point Vector2
Definition: point.h:429
constexpr float kPi
Definition: constants.h:26
float Scalar
Definition: scalar.h:19
constexpr float kEhCloseEnough
Definition: constants.h:57
TPoint< Scalar > Point
Definition: point.h:425
constexpr float kPiOver2
Definition: constants.h:32
flutter::DlPath DlPath
Definition: dl_dispatcher.h:29
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:24
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
Scalar radians
Definition: scalar.h:45
constexpr Type GetLength() const
Definition: point.h:209
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
std::vector< Point > points