Flutter Engine
 
Loading...
Searching...
No Matches
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 <memory>
6
9#include "gtest/gtest.h"
18
19inline ::testing::AssertionResult SolidVerticesNear(
20 std::vector<impeller::Point> a,
21 std::vector<impeller::Point> b) {
22 if (a.size() != b.size()) {
23 return ::testing::AssertionFailure() << "Colors length does not match";
24 }
25 for (auto i = 0u; i < b.size(); i++) {
26 if (!PointNear(a[i], b[i])) {
27 return ::testing::AssertionFailure() << "Positions are not equal.";
28 }
29 }
30 return ::testing::AssertionSuccess();
31}
32
33inline ::testing::AssertionResult TextureVerticesNear(
34 std::vector<impeller::TextureFillVertexShader::PerVertexData> a,
35 std::vector<impeller::TextureFillVertexShader::PerVertexData> b) {
36 if (a.size() != b.size()) {
37 return ::testing::AssertionFailure() << "Colors length does not match";
38 }
39 for (auto i = 0u; i < b.size(); i++) {
40 if (!PointNear(a[i].position, b[i].position)) {
41 return ::testing::AssertionFailure() << "Positions are not equal.";
42 }
43 if (!PointNear(a[i].texture_coords, b[i].texture_coords)) {
44 return ::testing::AssertionFailure() << "Texture coords are not equal.";
45 }
46 }
47 return ::testing::AssertionSuccess();
48}
49
50#define EXPECT_SOLID_VERTICES_NEAR(a, b) \
51 EXPECT_PRED2(&::SolidVerticesNear, a, b)
52#define EXPECT_TEXTURE_VERTICES_NEAR(a, b) \
53 EXPECT_PRED2(&::TextureVerticesNear, a, b)
54
55namespace impeller {
56
58 public:
59 static std::vector<Point> GenerateSolidStrokeVertices(
60 const PathSource& path,
61 const StrokeParameters& stroke,
62 Scalar scale) {
63 // We could create a single Tessellator instance for the whole suite,
64 // but we don't really need performance for unit tests.
65 Tessellator tessellator;
66 return StrokePathGeometry::GenerateSolidStrokeVertices( //
67 tessellator, path, stroke, scale);
68 }
69};
70
71namespace testing {
72
73TEST(EntityGeometryTest, RectGeometryCoversArea) {
74 auto geometry = Geometry::MakeRect(Rect::MakeLTRB(0, 0, 100, 100));
75 ASSERT_TRUE(geometry->CoversArea({}, Rect::MakeLTRB(0, 0, 100, 100)));
76 ASSERT_FALSE(geometry->CoversArea({}, Rect::MakeLTRB(-1, 0, 100, 100)));
77 ASSERT_TRUE(geometry->CoversArea({}, Rect::MakeLTRB(1, 1, 100, 100)));
78 ASSERT_TRUE(geometry->CoversArea({}, Rect()));
79}
80
81TEST(EntityGeometryTest, FillPathGeometryCoversArea) {
83 .AddRect(Rect::MakeLTRB(0, 0, 100, 100))
84 .TakePath();
85 auto geometry = Geometry::MakeFillPath(
86 path, /* inner rect */ Rect::MakeLTRB(0, 0, 100, 100));
87 ASSERT_TRUE(geometry->CoversArea({}, Rect::MakeLTRB(0, 0, 100, 100)));
88 ASSERT_FALSE(geometry->CoversArea({}, Rect::MakeLTRB(-1, 0, 100, 100)));
89 ASSERT_TRUE(geometry->CoversArea({}, Rect::MakeLTRB(1, 1, 100, 100)));
90 ASSERT_TRUE(geometry->CoversArea({}, Rect()));
91}
92
93TEST(EntityGeometryTest, FillPathGeometryCoversAreaNoInnerRect) {
95 .AddRect(Rect::MakeLTRB(0, 0, 100, 100))
96 .TakePath();
97 auto geometry = Geometry::MakeFillPath(path);
98 ASSERT_FALSE(geometry->CoversArea({}, Rect::MakeLTRB(0, 0, 100, 100)));
99 ASSERT_FALSE(geometry->CoversArea({}, Rect::MakeLTRB(-1, 0, 100, 100)));
100 ASSERT_FALSE(geometry->CoversArea({}, Rect::MakeLTRB(1, 1, 100, 100)));
101 ASSERT_FALSE(geometry->CoversArea({}, Rect()));
102}
103
104TEST(EntityGeometryTest, FillArcGeometryCoverage) {
105 Rect oval_bounds = Rect::MakeLTRB(100, 100, 200, 200);
106 Matrix transform45 = Matrix::MakeTranslation(oval_bounds.GetCenter()) *
108 Matrix::MakeTranslation(-oval_bounds.GetCenter());
109
110 { // Sweeps <=-360 or >=360
111 for (int start = -720; start <= 720; start += 10) {
112 for (int sweep = 360; sweep <= 720; sweep += 30) {
113 std::string label =
114 "start: " + std::to_string(start) + " + " + std::to_string(sweep);
115 auto geometry = Geometry::MakeFilledArc(oval_bounds, Degrees(start),
116 Degrees(sweep), false);
117 EXPECT_EQ(geometry->GetCoverage({}), oval_bounds)
118 << "start: " << start << ", sweep: " << sweep;
119 geometry = Geometry::MakeFilledArc(oval_bounds, Degrees(start),
120 Degrees(-sweep), false);
121 EXPECT_EQ(geometry->GetCoverage({}), oval_bounds)
122 << "start: " << start << ", sweep: " << -sweep;
123 geometry = Geometry::MakeFilledArc(oval_bounds, Degrees(start),
124 Degrees(-sweep), true);
125 EXPECT_EQ(geometry->GetCoverage({}), oval_bounds)
126 << "start: " << start << ", sweep: " << -sweep << ", with center";
127 }
128 }
129 }
130 { // Sweep from late in one quadrant to earlier in same quadrant
131 for (int start = 60; start < 360; start += 90) {
132 auto geometry = Geometry::MakeFilledArc(oval_bounds, Degrees(start),
133 Degrees(330), false);
134 EXPECT_EQ(geometry->GetCoverage({}), oval_bounds)
135 << "start: " << start << " without center";
136 geometry = Geometry::MakeFilledArc(oval_bounds, Degrees(start),
137 Degrees(330), true);
138 EXPECT_EQ(geometry->GetCoverage({}), oval_bounds)
139 << "start: " << start << " with center";
140 }
141 }
142 { // Sweep from early in one quadrant backwards to later in same quadrant
143 for (int start = 30; start < 360; start += 90) {
144 auto geometry = Geometry::MakeFilledArc(oval_bounds, Degrees(start),
145 Degrees(-330), false);
146 EXPECT_EQ(geometry->GetCoverage({}), oval_bounds)
147 << "start: " << start << " without center";
148 geometry = Geometry::MakeFilledArc(oval_bounds, Degrees(start),
149 Degrees(-330), true);
150 EXPECT_EQ(geometry->GetCoverage({}), oval_bounds)
151 << "start: " << start << " with center";
152 }
153 }
154 { // Sweep past each quadrant axis individually, no center
155 for (int start = -360; start <= 720; start += 360) {
156 { // Quadrant 0
157 auto geometry = Geometry::MakeFilledArc(
158 oval_bounds, Degrees(start - 45), Degrees(90), false);
159 Rect expected_bounds = Rect::MakeLTRB(150 + 50 * kSqrt2Over2, //
160 150 - 50 * kSqrt2Over2, //
161 200, //
162 150 + 50 * kSqrt2Over2);
163 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
164 expected_bounds)
165 << "start: " << start - 45;
166 }
167 { // Quadrant 1
168 auto geometry = Geometry::MakeFilledArc(
169 oval_bounds, Degrees(start + 45), Degrees(90), false);
170 Rect expected_bounds = Rect::MakeLTRB(150 - 50 * kSqrt2Over2, //
171 150 + 50 * kSqrt2Over2, //
172 150 + 50 * kSqrt2Over2, //
173 200);
174 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
175 expected_bounds)
176 << "start: " << start + 45;
177 }
178 { // Quadrant 2
179 auto geometry = Geometry::MakeFilledArc(
180 oval_bounds, Degrees(start + 135), Degrees(90), false);
181 Rect expected_bounds = Rect::MakeLTRB(100, //
182 150 - 50 * kSqrt2Over2, //
183 150 - 50 * kSqrt2Over2, //
184 150 + 50 * kSqrt2Over2);
185 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
186 expected_bounds)
187 << "start: " << start + 135;
188 }
189 { // Quadrant 3
190 auto geometry = Geometry::MakeFilledArc(
191 oval_bounds, Degrees(start + 225), Degrees(90), false);
192 Rect expected_bounds = Rect::MakeLTRB(150 - 50 * kSqrt2Over2, //
193 100, //
194 150 + 50 * kSqrt2Over2, //
195 150 - 50 * kSqrt2Over2);
196 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
197 expected_bounds)
198 << "start: " << start + 225;
199 }
200 }
201 }
202 { // Sweep past each quadrant axis individually, including the center
203 for (int start = -360; start <= 720; start += 360) {
204 { // Quadrant 0
205 auto geometry = Geometry::MakeFilledArc(
206 oval_bounds, Degrees(start - 45), Degrees(90), true);
207 Rect expected_bounds = Rect::MakeLTRB(150, //
208 150 - 50 * kSqrt2Over2, //
209 200, //
210 150 + 50 * kSqrt2Over2);
211 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
212 expected_bounds)
213 << "start: " << start - 45;
214 }
215 { // Quadrant 1
216 auto geometry = Geometry::MakeFilledArc(
217 oval_bounds, Degrees(start + 45), Degrees(90), true);
218 Rect expected_bounds = Rect::MakeLTRB(150 - 50 * kSqrt2Over2, //
219 150, //
220 150 + 50 * kSqrt2Over2, //
221 200);
222 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
223 expected_bounds)
224 << "start: " << start + 45;
225 }
226 { // Quadrant 2
227 auto geometry = Geometry::MakeFilledArc(
228 oval_bounds, Degrees(start + 135), Degrees(90), true);
229 Rect expected_bounds = Rect::MakeLTRB(100, //
230 150 - 50 * kSqrt2Over2, //
231 150, //
232 150 + 50 * kSqrt2Over2);
233 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
234 expected_bounds)
235 << "start: " << start + 135;
236 }
237 { // Quadrant 3
238 auto geometry = Geometry::MakeFilledArc(
239 oval_bounds, Degrees(start + 225), Degrees(90), true);
240 Rect expected_bounds = Rect::MakeLTRB(150 - 50 * kSqrt2Over2, //
241 100, //
242 150 + 50 * kSqrt2Over2, //
243 150);
244 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
245 expected_bounds)
246 << "start: " << start + 225;
247 }
248 }
249 }
250 { // 45 degree tilted full circle
251 auto geometry =
252 Geometry::MakeFilledArc(oval_bounds, Degrees(0), Degrees(360), false);
253 ASSERT_TRUE(oval_bounds.TransformBounds(transform45).Contains(oval_bounds));
254
255 EXPECT_TRUE(geometry->GetCoverage(transform45)
256 .value_or(Rect())
257 .Contains(oval_bounds));
258 }
259 { // 45 degree tilted mostly full circle
260 auto geometry =
261 Geometry::MakeFilledArc(oval_bounds, Degrees(3), Degrees(359), false);
262 ASSERT_TRUE(oval_bounds.TransformBounds(transform45).Contains(oval_bounds));
263
264 EXPECT_TRUE(geometry->GetCoverage(transform45)
265 .value_or(Rect())
266 .Contains(oval_bounds));
267 }
268}
269
270TEST(EntityGeometryTest, StrokeArcGeometryCoverage) {
271 Rect oval_bounds = Rect::MakeLTRB(100, 100, 200, 200);
272 Rect expanded_bounds = Rect::MakeLTRB(95, 95, 205, 205);
273 Rect squared_bounds = Rect::MakeLTRB(100 - 5 * kSqrt2, 100 - 5 * kSqrt2,
274 200 + 5 * kSqrt2, 200 + 5 * kSqrt2);
275 Matrix transform45 = Matrix::MakeTranslation(oval_bounds.GetCenter()) *
277 Matrix::MakeTranslation(-oval_bounds.GetCenter());
278
279 StrokeParameters butt_params = {
280 .width = 10.0f,
281 .cap = Cap::kButt,
282 };
283 StrokeParameters square_params = {
284 .width = 10.0f,
285 .cap = Cap::kSquare,
286 };
287
288 { // Sweeps <=-360 or >=360
289 for (int start = -720; start <= 720; start += 10) {
290 for (int sweep = 360; sweep <= 720; sweep += 30) {
291 std::string label =
292 "start: " + std::to_string(start) + " + " + std::to_string(sweep);
293 auto geometry = Geometry::MakeStrokedArc(oval_bounds, Degrees(start),
294 Degrees(sweep), butt_params);
295 EXPECT_EQ(geometry->GetCoverage({}), expanded_bounds)
296 << "start: " << start << ", sweep: " << sweep;
297 geometry = Geometry::MakeStrokedArc(oval_bounds, Degrees(start),
298 Degrees(-sweep), butt_params);
299 EXPECT_EQ(geometry->GetCoverage({}), expanded_bounds)
300 << "start: " << start << ", sweep: " << -sweep;
301 geometry = Geometry::MakeStrokedArc(oval_bounds, Degrees(start),
302 Degrees(-sweep), square_params);
303 EXPECT_EQ(geometry->GetCoverage({}), expanded_bounds)
304 << "start: " << start << ", sweep: " << -sweep << ", square caps";
305 }
306 }
307 }
308 { // Sweep from late in one quadrant to earlier in same quadrant
309 for (int start = 60; start < 360; start += 90) {
310 auto geometry = Geometry::MakeStrokedArc(oval_bounds, Degrees(start),
311 Degrees(330), butt_params);
312 EXPECT_EQ(geometry->GetCoverage({}), expanded_bounds)
313 << "start: " << start << ", butt caps";
314 geometry = Geometry::MakeStrokedArc(oval_bounds, Degrees(start),
315 Degrees(330), square_params);
316 EXPECT_EQ(geometry->GetCoverage({}), squared_bounds)
317 << "start: " << start << ", square caps";
318 }
319 }
320 { // Sweep from early in one quadrant backwards to later in same quadrant
321 for (int start = 30; start < 360; start += 90) {
322 auto geometry = Geometry::MakeStrokedArc(oval_bounds, Degrees(start),
323 Degrees(-330), butt_params);
324 EXPECT_EQ(geometry->GetCoverage({}), expanded_bounds)
325 << "start: " << start << " without center";
326 geometry = Geometry::MakeStrokedArc(oval_bounds, Degrees(start),
327 Degrees(-330), square_params);
328 EXPECT_EQ(geometry->GetCoverage({}), squared_bounds)
329 << "start: " << start << " with center";
330 }
331 }
332 { // Sweep past each quadrant axis individually with butt caps
333 for (int start = -360; start <= 720; start += 360) {
334 { // Quadrant 0
335 auto geometry = Geometry::MakeStrokedArc(
336 oval_bounds, Degrees(start - 45), Degrees(90), butt_params);
337 Rect expected_bounds = Rect::MakeLTRB(150 + 50 * kSqrt2Over2 - 5, //
338 150 - 50 * kSqrt2Over2 - 5, //
339 205, //
340 150 + 50 * kSqrt2Over2 + 5);
341 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
342 expected_bounds)
343 << "start: " << start - 45;
344 }
345 { // Quadrant 1
346 auto geometry = Geometry::MakeStrokedArc(
347 oval_bounds, Degrees(start + 45), Degrees(90), butt_params);
348 Rect expected_bounds = Rect::MakeLTRB(150 - 50 * kSqrt2Over2 - 5, //
349 150 + 50 * kSqrt2Over2 - 5, //
350 150 + 50 * kSqrt2Over2 + 5, //
351 205);
352 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
353 expected_bounds)
354 << "start: " << start + 45;
355 }
356 { // Quadrant 2
357 auto geometry = Geometry::MakeStrokedArc(
358 oval_bounds, Degrees(start + 135), Degrees(90), butt_params);
359 Rect expected_bounds = Rect::MakeLTRB(95, //
360 150 - 50 * kSqrt2Over2 - 5, //
361 150 - 50 * kSqrt2Over2 + 5, //
362 150 + 50 * kSqrt2Over2 + 5);
363 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
364 expected_bounds)
365 << "start: " << start + 135;
366 }
367 { // Quadrant 3
368 auto geometry = Geometry::MakeStrokedArc(
369 oval_bounds, Degrees(start + 225), Degrees(90), butt_params);
370 Rect expected_bounds = Rect::MakeLTRB(150 - 50 * kSqrt2Over2 - 5, //
371 95, //
372 150 + 50 * kSqrt2Over2 + 5, //
373 150 - 50 * kSqrt2Over2 + 5);
374 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
375 expected_bounds)
376 << "start: " << start + 225;
377 }
378 }
379 }
380 { // Sweep past each quadrant axis individually with square caps
381 Scalar pad = 5 * kSqrt2;
382 for (int start = -360; start <= 720; start += 360) {
383 { // Quadrant 0
384 auto geometry = Geometry::MakeStrokedArc(
385 oval_bounds, Degrees(start - 45), Degrees(90), square_params);
386 Rect expected_bounds = Rect::MakeLTRB(150 + 50 * kSqrt2Over2 - pad, //
387 150 - 50 * kSqrt2Over2 - pad, //
388 200 + pad, //
389 150 + 50 * kSqrt2Over2 + pad);
390 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
391 expected_bounds)
392 << "start: " << start - 45;
393 }
394 { // Quadrant 1
395 auto geometry = Geometry::MakeStrokedArc(
396 oval_bounds, Degrees(start + 45), Degrees(90), square_params);
397 Rect expected_bounds = Rect::MakeLTRB(150 - 50 * kSqrt2Over2 - pad, //
398 150 + 50 * kSqrt2Over2 - pad, //
399 150 + 50 * kSqrt2Over2 + pad, //
400 200 + pad);
401 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
402 expected_bounds)
403 << "start: " << start + 45;
404 }
405 { // Quadrant 2
406 auto geometry = Geometry::MakeStrokedArc(
407 oval_bounds, Degrees(start + 135), Degrees(90), square_params);
408 Rect expected_bounds = Rect::MakeLTRB(100 - pad, //
409 150 - 50 * kSqrt2Over2 - pad, //
410 150 - 50 * kSqrt2Over2 + pad, //
411 150 + 50 * kSqrt2Over2 + pad);
412 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
413 expected_bounds)
414 << "start: " << start + 135;
415 }
416 { // Quadrant 3
417 auto geometry = Geometry::MakeStrokedArc(
418 oval_bounds, Degrees(start + 225), Degrees(90), square_params);
419 Rect expected_bounds = Rect::MakeLTRB(150 - 50 * kSqrt2Over2 - pad, //
420 100 - pad, //
421 150 + 50 * kSqrt2Over2 + pad, //
422 150 - 50 * kSqrt2Over2 + pad);
423 EXPECT_RECT_NEAR(geometry->GetCoverage({}).value_or(Rect()),
424 expected_bounds)
425 << "start: " << start + 225;
426 }
427 }
428 }
429 { // 45 degree tilted full circle, butt caps
430 auto geometry = Geometry::MakeStrokedArc( //
431 oval_bounds, Degrees(0), Degrees(360), butt_params);
432 ASSERT_TRUE(
433 oval_bounds.TransformBounds(transform45).Contains(expanded_bounds));
434
435 EXPECT_TRUE(geometry->GetCoverage(transform45)
436 .value_or(Rect())
437 .Contains(expanded_bounds));
438 }
439 { // 45 degree tilted full circle, square caps
440 auto geometry = Geometry::MakeStrokedArc( //
441 oval_bounds, Degrees(0), Degrees(360), square_params);
442 ASSERT_TRUE(
443 oval_bounds.TransformBounds(transform45).Contains(expanded_bounds));
444
445 EXPECT_TRUE(geometry->GetCoverage(transform45)
446 .value_or(Rect())
447 .Contains(squared_bounds));
448 }
449 { // 45 degree tilted mostly full circle, butt caps
450 auto geometry = Geometry::MakeStrokedArc( //
451 oval_bounds, Degrees(3), Degrees(359), butt_params);
452 ASSERT_TRUE(
453 oval_bounds.TransformBounds(transform45).Contains(expanded_bounds));
454
455 EXPECT_TRUE(geometry->GetCoverage(transform45)
456 .value_or(Rect())
457 .Contains(expanded_bounds));
458 }
459 { // 45 degree tilted mostly full circle, square caps
460 auto geometry = Geometry::MakeStrokedArc( //
461 oval_bounds, Degrees(3), Degrees(359), square_params);
462 ASSERT_TRUE(
463 oval_bounds.TransformBounds(transform45).Contains(expanded_bounds));
464
465 EXPECT_TRUE(geometry->GetCoverage(transform45)
466 .value_or(Rect())
467 .Contains(squared_bounds));
468 }
469}
470
471TEST(EntityGeometryTest, FillRoundRectGeometryCoversArea) {
472 Rect bounds = Rect::MakeLTRB(100, 100, 200, 200);
473 RoundRect round_rect =
475 .top_left = Size(1, 11),
476 .top_right = Size(2, 12),
477 .bottom_left = Size(3, 13),
478 .bottom_right = Size(4, 14),
479 });
480 FillRoundRectGeometry geom(round_rect);
481
482 // Tall middle rect should barely be covered.
483 EXPECT_TRUE(geom.CoversArea({}, Rect::MakeLTRB(103, 100, 196, 200)));
484 EXPECT_FALSE(geom.CoversArea({}, Rect::MakeLTRB(102, 100, 196, 200)));
485 EXPECT_FALSE(geom.CoversArea({}, Rect::MakeLTRB(103, 99, 196, 200)));
486 EXPECT_FALSE(geom.CoversArea({}, Rect::MakeLTRB(103, 100, 197, 200)));
487 EXPECT_FALSE(geom.CoversArea({}, Rect::MakeLTRB(103, 100, 196, 201)));
488
489 // Wide middle rect should barely be covered.
490 EXPECT_TRUE(geom.CoversArea({}, Rect::MakeLTRB(100, 112, 200, 186)));
491 EXPECT_FALSE(geom.CoversArea({}, Rect::MakeLTRB(99, 112, 200, 186)));
492 EXPECT_FALSE(geom.CoversArea({}, Rect::MakeLTRB(100, 111, 200, 186)));
493 EXPECT_FALSE(geom.CoversArea({}, Rect::MakeLTRB(100, 112, 201, 186)));
494 EXPECT_FALSE(geom.CoversArea({}, Rect::MakeLTRB(100, 112, 200, 187)));
495}
496
497TEST(EntityGeometryTest, LineGeometryCoverage) {
498 {
499 auto geometry = Geometry::MakeLine( //
500 {10, 10}, {20, 10}, {.width = 2, .cap = Cap::kButt});
501 EXPECT_EQ(geometry->GetCoverage({}), Rect::MakeLTRB(10, 9, 20, 11));
502 EXPECT_TRUE(geometry->CoversArea({}, Rect::MakeLTRB(10, 9, 20, 11)));
503 }
504
505 {
506 auto geometry = Geometry::MakeLine( //
507 {10, 10}, {20, 10}, {.width = 2, .cap = Cap::kSquare});
508 EXPECT_EQ(geometry->GetCoverage({}), Rect::MakeLTRB(9, 9, 21, 11));
509 EXPECT_TRUE(geometry->CoversArea({}, Rect::MakeLTRB(9, 9, 21, 11)));
510 }
511
512 {
513 auto geometry = Geometry::MakeLine( //
514 {10, 10}, {10, 20}, {.width = 2, .cap = Cap::kButt});
515 EXPECT_EQ(geometry->GetCoverage({}), Rect::MakeLTRB(9, 10, 11, 20));
516 EXPECT_TRUE(geometry->CoversArea({}, Rect::MakeLTRB(9, 10, 11, 20)));
517 }
518
519 {
520 auto geometry = Geometry::MakeLine( //
521 {10, 10}, {10, 20}, {.width = 2, .cap = Cap::kSquare});
522 EXPECT_EQ(geometry->GetCoverage({}), Rect::MakeLTRB(9, 9, 11, 21));
523 EXPECT_TRUE(geometry->CoversArea({}, Rect::MakeLTRB(9, 9, 11, 21)));
524 }
525}
526
527TEST(EntityGeometryTest, RoundRectGeometryCoversArea) {
528 auto geometry =
529 Geometry::MakeRoundRect(Rect::MakeLTRB(0, 0, 100, 100), Size(20, 20));
530 EXPECT_FALSE(geometry->CoversArea({}, Rect::MakeLTRB(15, 15, 85, 85)));
531 EXPECT_TRUE(geometry->CoversArea({}, Rect::MakeLTRB(20, 20, 80, 80)));
532 EXPECT_TRUE(geometry->CoversArea({}, Rect::MakeLTRB(30, 1, 70, 99)));
533 EXPECT_TRUE(geometry->CoversArea({}, Rect::MakeLTRB(1, 30, 99, 70)));
534}
535
536TEST(EntityGeometryTest, GeometryResultHasReasonableDefaults) {
537 GeometryResult result;
538 EXPECT_EQ(result.type, PrimitiveType::kTriangleStrip);
539 EXPECT_EQ(result.transform, Matrix());
540 EXPECT_EQ(result.mode, GeometryResult::Mode::kNormal);
541}
542
543TEST(EntityGeometryTest, AlphaCoverageStrokePaths) {
544 auto matrix = Matrix::MakeScale(Vector2{3.0, 3.0});
545 EXPECT_EQ(Geometry::MakeStrokePath({}, {.width = 0.5f})
546 ->ComputeAlphaCoverage(matrix),
547 1.0f);
548 EXPECT_NEAR(Geometry::MakeStrokePath({}, {.width = 0.1f})
549 ->ComputeAlphaCoverage(matrix),
550 0.6, 0.05);
551 EXPECT_NEAR(Geometry::MakeStrokePath({}, {.width = 0.05})
552 ->ComputeAlphaCoverage(matrix),
553 0.3, 0.05);
554 EXPECT_NEAR(Geometry::MakeStrokePath({}, {.width = 0.01})
555 ->ComputeAlphaCoverage(matrix),
556 0.1, 0.1);
557 EXPECT_NEAR(Geometry::MakeStrokePath({}, {.width = 0.0000005f})
558 ->ComputeAlphaCoverage(matrix),
559 1e-05, 0.001);
560 EXPECT_EQ(Geometry::MakeStrokePath({}, {.width = 0.0f})
561 ->ComputeAlphaCoverage(matrix),
562 1.0f);
563 EXPECT_EQ(Geometry::MakeStrokePath({}, {.width = 40.0f})
564 ->ComputeAlphaCoverage(matrix),
565 1.0f);
566}
567
568TEST(EntityGeometryTest, SimpleTwoLineStrokeVerticesButtCap) {
569 flutter::DlPathBuilder path_builder;
570 path_builder.MoveTo({20, 20});
571 path_builder.LineTo({30, 20});
572 path_builder.MoveTo({120, 20});
573 path_builder.LineTo({130, 20});
574 flutter::DlPath path = path_builder.TakePath();
575
577 path,
578 {
579 .width = 10.0f,
580 .cap = Cap::kButt,
581 .join = Join::kBevel,
582 .miter_limit = 4.0f,
583 },
584 1.0f);
585
586 std::vector<Point> expected = {
587 // The points for the first segment (20, 20) -> (30, 20)
588 Point(20, 25),
589 Point(20, 15),
590 Point(30, 25),
591 Point(30, 15),
592
593 // The glue points that allow us to "pick up the pen" between segments
594 Point(30, 20),
595 Point(30, 20),
596 Point(120, 20),
597 Point(120, 20),
598
599 // The points for the second segment (120, 20) -> (130, 20)
600 Point(120, 25),
601 Point(120, 15),
602 Point(130, 25),
603 Point(130, 15),
604 };
605
606 EXPECT_EQ(points, expected);
607}
608
609TEST(EntityGeometryTest, SimpleTwoLineStrokeVerticesRoundCap) {
610 flutter::DlPathBuilder path_builder;
611 path_builder.MoveTo({20, 20});
612 path_builder.LineTo({30, 20});
613 path_builder.MoveTo({120, 20});
614 path_builder.LineTo({130, 20});
615 flutter::DlPath path = path_builder.TakePath();
616
618 path,
619 {
620 .width = 10.0f,
621 .cap = Cap::kRound,
622 .join = Join::kBevel,
623 .miter_limit = 4.0f,
624 },
625 1.0f);
626
627 size_t count = points.size();
628 ASSERT_TRUE((count & 0x1) == 0x0); // Should always be even
629
630 // For a scale factor of 1.0 and a stroke width of 10.0 we currently
631 // generate 40 total points for the 2 line segments based on the number
632 // of quadrant circle divisions for a radius of 5.0
633 //
634 // If the number of points changes because of a change in the way we
635 // compute circle divisions, we need to recompute the circular offsets
636 ASSERT_EQ(points.size(), 40u);
637
638 // Compute the indicated circular end cap offset based on the current
639 // step out of 4 divisions [1, 2, 3] (not 0 or 4) based on whether this
640 // is the left or right side of the path and whether this is a backwards
641 // (starting) cap or a forwards (ending) cap.
642 auto offset = [](int step, bool left, bool backwards) -> Point {
643 Radians angle(kPiOver2 * (step / 4.0f));
644 Point along = Point(5.0f, 0.0f) * std::cos(angle.radians);
645 Point across = Point(0.0f, 5.0f) * std::sin(angle.radians);
646 Point center = backwards ? -along : along;
647 return left ? center + across : center - across;
648 };
649
650 // The points for the first segment (20, 20) -> (30, 20)
651 EXPECT_EQ(points[0], Point(15, 20));
652 EXPECT_EQ(points[1], Point(20, 20) + offset(1, true, true));
653 EXPECT_EQ(points[2], Point(20, 20) + offset(1, false, true));
654 EXPECT_EQ(points[3], Point(20, 20) + offset(2, true, true));
655 EXPECT_EQ(points[4], Point(20, 20) + offset(2, false, true));
656 EXPECT_EQ(points[5], Point(20, 20) + offset(3, true, true));
657 EXPECT_EQ(points[6], Point(20, 20) + offset(3, false, true));
658 EXPECT_EQ(points[7], Point(20, 25));
659 EXPECT_EQ(points[8], Point(20, 15));
660 EXPECT_EQ(points[9], Point(30, 25));
661 EXPECT_EQ(points[10], Point(30, 15));
662 EXPECT_EQ(points[11], Point(30, 20) + offset(3, true, false));
663 EXPECT_EQ(points[12], Point(30, 20) + offset(3, false, false));
664 EXPECT_EQ(points[13], Point(30, 20) + offset(2, true, false));
665 EXPECT_EQ(points[14], Point(30, 20) + offset(2, false, false));
666 EXPECT_EQ(points[15], Point(30, 20) + offset(1, true, false));
667 EXPECT_EQ(points[16], Point(30, 20) + offset(1, false, false));
668 EXPECT_EQ(points[17], Point(35, 20));
669
670 // The glue points that allow us to "pick up the pen" between segments
671 EXPECT_EQ(points[18], Point(30, 20));
672 EXPECT_EQ(points[19], Point(30, 20));
673 EXPECT_EQ(points[20], Point(120, 20));
674 EXPECT_EQ(points[21], Point(120, 20));
675
676 // The points for the second segment (120, 20) -> (130, 20)
677 EXPECT_EQ(points[22], Point(115, 20));
678 EXPECT_EQ(points[23], Point(120, 20) + offset(1, true, true));
679 EXPECT_EQ(points[24], Point(120, 20) + offset(1, false, true));
680 EXPECT_EQ(points[25], Point(120, 20) + offset(2, true, true));
681 EXPECT_EQ(points[26], Point(120, 20) + offset(2, false, true));
682 EXPECT_EQ(points[27], Point(120, 20) + offset(3, true, true));
683 EXPECT_EQ(points[28], Point(120, 20) + offset(3, false, true));
684 EXPECT_EQ(points[29], Point(120, 25));
685 EXPECT_EQ(points[30], Point(120, 15));
686 EXPECT_EQ(points[31], Point(130, 25));
687 EXPECT_EQ(points[32], Point(130, 15));
688 EXPECT_EQ(points[33], Point(130, 20) + offset(3, true, false));
689 EXPECT_EQ(points[34], Point(130, 20) + offset(3, false, false));
690 EXPECT_EQ(points[35], Point(130, 20) + offset(2, true, false));
691 EXPECT_EQ(points[36], Point(130, 20) + offset(2, false, false));
692 EXPECT_EQ(points[37], Point(130, 20) + offset(1, true, false));
693 EXPECT_EQ(points[38], Point(130, 20) + offset(1, false, false));
694 EXPECT_EQ(points[39], Point(135, 20));
695}
696
697TEST(EntityGeometryTest, SimpleTwoLineStrokeVerticesSquareCap) {
698 flutter::DlPathBuilder path_builder;
699 path_builder.MoveTo({20, 20});
700 path_builder.LineTo({30, 20});
701 path_builder.MoveTo({120, 20});
702 path_builder.LineTo({130, 20});
703 flutter::DlPath path = path_builder.TakePath();
704
706 path,
707 {
708 .width = 10.0f,
709 .cap = Cap::kSquare,
710 .join = Join::kBevel,
711 .miter_limit = 4.0f,
712 },
713 1.0f);
714
715 // clang-format off
716 std::vector<Point> expected = {
717 // The points for the first segment (20, 20) -> (30, 20)
718 Point(15, 25),
719 Point(15, 15),
720 Point(20, 25),
721 Point(20, 15),
722 Point(30, 25),
723 Point(30, 15),
724 Point(35, 25),
725 Point(35, 15),
726
727 // The glue points that allow us to "pick up the pen" between segments
728 Point(30, 20),
729 Point(30, 20),
730 Point(120, 20),
731 Point(120, 20),
732
733 // The points for the second segment (120, 20) -> (130, 20)
734 Point(115, 25),
735 Point(115, 15),
736 Point(120, 25),
737 Point(120, 15),
738 Point(130, 25),
739 Point(130, 15),
740 Point(135, 25),
741 Point(135, 15),
742 };
743 // clang-format on
744
745 EXPECT_EQ(points, expected);
746}
747
748TEST(EntityGeometryTest, TwoLineSegmentsRightTurnStrokeVerticesBevelJoin) {
749 flutter::DlPathBuilder path_builder;
750 path_builder.MoveTo({20, 20});
751 path_builder.LineTo({30, 20});
752 path_builder.LineTo({30, 30});
753 flutter::DlPath path = path_builder.TakePath();
754
756 path,
757 {
758 .width = 10.0f,
759 .cap = Cap::kButt,
760 .join = Join::kBevel,
761 .miter_limit = 4.0f,
762 },
763 1.0f);
764
765 std::vector<Point> expected = {
766 // The points for the first segment (20, 20) -> (30, 20)
767 Point(20, 25),
768 Point(20, 15),
769 Point(30, 25),
770 Point(30, 15),
771
772 // The points for the second segment (120, 20) -> (130, 20)
773 Point(25, 20),
774 Point(35, 20),
775 Point(25, 30),
776 Point(35, 30),
777 };
778
779 EXPECT_EQ(points, expected);
780}
781
782TEST(EntityGeometryTest, TwoLineSegmentsLeftTurnStrokeVerticesBevelJoin) {
783 flutter::DlPathBuilder path_builder;
784 path_builder.MoveTo({20, 20});
785 path_builder.LineTo({30, 20});
786 path_builder.LineTo({30, 10});
787 flutter::DlPath path = path_builder.TakePath();
788
790 path,
791 {
792 .width = 10.0f,
793 .cap = Cap::kButt,
794 .join = Join::kBevel,
795 .miter_limit = 4.0f,
796 },
797 1.0f);
798
799 std::vector<Point> expected = {
800 // The points for the first segment (20, 20) -> (30, 20)
801 Point(20, 25),
802 Point(20, 15),
803 Point(30, 25),
804 Point(30, 15),
805
806 // The points for the second segment (120, 20) -> (130, 20)
807 Point(35, 20),
808 Point(25, 20),
809 Point(35, 10),
810 Point(25, 10),
811 };
812
813 EXPECT_EQ(points, expected);
814}
815
816TEST(EntityGeometryTest, TwoLineSegmentsRightTurnStrokeVerticesMiterJoin) {
817 flutter::DlPathBuilder path_builder;
818 path_builder.MoveTo({20, 20});
819 path_builder.LineTo({30, 20});
820 path_builder.LineTo({30, 30});
821 flutter::DlPath path = path_builder.TakePath();
822
824 path,
825 {
826 .width = 10.0f,
827 .cap = Cap::kButt,
828 .join = Join::kMiter,
829 .miter_limit = 4.0f,
830 },
831 1.0f);
832
833 std::vector<Point> expected = {
834 // The points for the first segment (20, 20) -> (30, 20)
835 Point(20, 25),
836 Point(20, 15),
837 Point(30, 25),
838 Point(30, 15),
839
840 // And one point makes a Miter
841 Point(35, 15),
842
843 // The points for the second segment (120, 20) -> (130, 20)
844 Point(25, 20),
845 Point(35, 20),
846 Point(25, 30),
847 Point(35, 30),
848 };
849
850 EXPECT_EQ(points, expected);
851}
852
853TEST(EntityGeometryTest, TwoLineSegmentsLeftTurnStrokeVerticesMiterJoin) {
854 flutter::DlPathBuilder path_builder;
855 path_builder.MoveTo({20, 20});
856 path_builder.LineTo({30, 20});
857 path_builder.LineTo({30, 10});
858 flutter::DlPath path = path_builder.TakePath();
859
861 path,
862 {
863 .width = 10.0f,
864 .cap = Cap::kButt,
865 .join = Join::kMiter,
866 .miter_limit = 4.0f,
867 },
868 1.0f);
869
870 std::vector<Point> expected = {
871 // The points for the first segment (20, 20) -> (30, 20)
872 Point(20, 25),
873 Point(20, 15),
874 Point(30, 25),
875 Point(30, 15),
876
877 // And one point makes a Miter
878 Point(35, 25),
879
880 // The points for the second segment (120, 20) -> (130, 20)
881 Point(35, 20),
882 Point(25, 20),
883 Point(35, 10),
884 Point(25, 10),
885 };
886
887 EXPECT_EQ(points, expected);
888}
889
890TEST(EntityGeometryTest, TinyQuadGeneratesCaps) {
891 flutter::DlPathBuilder path_builder;
892 path_builder.MoveTo({20, 20});
893 path_builder.QuadraticCurveTo({20.125, 20}, {20.250, 20});
894 flutter::DlPath path = path_builder.TakePath();
895
897 path,
898 {
899 .width = 4.0f,
900 .cap = Cap::kSquare,
901 .join = Join::kBevel,
902 .miter_limit = 4.0f,
903 },
904 1.0f);
905
906 std::vector<Point> expected = {
907 // The points for the opening square cap
908 Point(18, 22),
909 Point(18, 18),
910
911 // The points for the start of the curve
912 Point(20, 22),
913 Point(20, 18),
914
915 // The points for the end of the curve
916 Point(20.25, 22),
917 Point(20.25, 18),
918
919 // The points for the closing square cap
920 Point(22.25, 22),
921 Point(22.25, 18),
922 };
923
924 EXPECT_EQ(points, expected);
925}
926
927TEST(EntityGeometryTest, TinyConicGeneratesCaps) {
928 flutter::DlPathBuilder path_builder;
929 path_builder.MoveTo({20, 20});
930 path_builder.ConicCurveTo({20.125, 20}, {20.250, 20}, 0.6);
931 flutter::DlPath path = path_builder.TakePath();
932
934 path,
935 {
936 .width = 4.0f,
937 .cap = Cap::kSquare,
938 .join = Join::kBevel,
939 .miter_limit = 4.0f,
940 },
941 1.0f);
942
943 std::vector<Point> expected = {
944 // The points for the opening square cap
945 Point(18, 22),
946 Point(18, 18),
947
948 // The points for the start of the curve
949 Point(20, 22),
950 Point(20, 18),
951
952 // The points for the end of the curve
953 Point(20.25, 22),
954 Point(20.25, 18),
955
956 // The points for the closing square cap
957 Point(22.25, 22),
958 Point(22.25, 18),
959 };
960
961 EXPECT_EQ(points, expected);
962}
963
964TEST(EntityGeometryTest, TinyCubicGeneratesCaps) {
965 flutter::DlPathBuilder path_builder;
966 path_builder.MoveTo({20, 20});
967 path_builder.CubicCurveTo({20.0625, 20}, {20.125, 20}, {20.250, 20});
968 flutter::DlPath path = path_builder.TakePath();
969
971 path,
972 {
973 .width = 4.0f,
974 .cap = Cap::kSquare,
975 .join = Join::kBevel,
976 .miter_limit = 4.0f,
977 },
978 1.0f);
979
980 std::vector<Point> expected = {
981 // The points for the opening square cap
982 Point(18, 22),
983 Point(18, 18),
984
985 // The points for the start of the curve
986 Point(20, 22),
987 Point(20, 18),
988
989 // The points for the end of the curve
990 Point(20.25, 22),
991 Point(20.25, 18),
992
993 // The points for the closing square cap
994 Point(22.25, 22),
995 Point(22.25, 18),
996 };
997
998 EXPECT_EQ(points, expected);
999}
1000
1001TEST(EntityGeometryTest, TwoLineSegmentsMiterLimit) {
1002 // degrees is the angle that the line deviates from "straight ahead"
1003 for (int degrees = 10; degrees < 180; degrees += 10) {
1004 // Start with a width of 2 since line widths of 1 usually decide
1005 // that they don't need join geometry at a scale of 1.0
1006 for (int width = 2; width <= 10; width++) {
1007 Degrees d(degrees);
1008 Radians r(d);
1009 Point pixel_delta = Point(std::cos(r.radians), std::sin(r.radians));
1010
1011 if (pixel_delta.GetDistance(Point(1, 0)) * width < 1.0f) {
1012 // Some combinations of angle and width result in a join that is
1013 // less than a pixel in size. We don't care about compliance on
1014 // such a small join delta (and, in fact, the implementation may
1015 // decide to elide those small joins).
1016 continue;
1017 }
1018
1019 // Miter limits are based on angle between the vectors/segments
1020 Degrees between(180 - degrees);
1021 Radians r_between(between);
1022 Scalar limit = 1.0f / std::sin(r_between.radians / 2.0f);
1023
1024 flutter::DlPathBuilder path_builder;
1025 path_builder.MoveTo(Point(20, 20));
1026 path_builder.LineTo(Point(30, 20));
1027 path_builder.LineTo(Point(30, 20) + pixel_delta * 10.0f);
1028 flutter::DlPath path = path_builder.TakePath();
1029
1030 // Miter limit too small (99% of required) to allow a miter
1031 auto points1 =
1033 path,
1034 {
1035 .width = static_cast<Scalar>(width),
1036 .cap = Cap::kButt,
1037 .join = Join::kMiter,
1038 .miter_limit = limit * 0.99f,
1039 },
1040 1.0f);
1041 EXPECT_EQ(points1.size(), 8u)
1042 << "degrees: " << degrees << ", width: " << width << ", "
1043 << points1[4];
1044
1045 // Miter limit large enough (101% of required) to allow a miter
1046 auto points2 =
1048 path,
1049 {
1050 .width = static_cast<Scalar>(width),
1051 .cap = Cap::kButt,
1052 .join = Join::kMiter,
1053 .miter_limit = limit * 1.01f,
1054 },
1055 1.0f);
1056 EXPECT_EQ(points2.size(), 9u)
1057 << "degrees: " << degrees << ", width: " << width;
1058 EXPECT_LE(points2[4].GetDistance({30, 20}), width * limit * 1.05f)
1059 << "degrees: " << degrees << ", width: " << width << ", "
1060 << points2[4];
1061 }
1062 }
1063}
1064
1065TEST(EntityGeometryTest, TwoLineSegments180DegreeJoins) {
1066 // First, create a path that doubles back on itself.
1067 flutter::DlPathBuilder path_builder;
1068 path_builder.MoveTo(Point(10, 10));
1069 path_builder.LineTo(Point(100, 10));
1070 path_builder.LineTo(Point(10, 10));
1071 flutter::DlPath path = path_builder.TakePath();
1072
1073 auto points_bevel =
1075 path,
1076 {
1077 .width = 20.0f,
1078 .cap = Cap::kButt,
1079 .join = Join::kBevel,
1080 .miter_limit = 4.0f,
1081 },
1082 1.0f);
1083 // Generates no join - because it is a bevel join
1084 EXPECT_EQ(points_bevel.size(), 8u);
1085
1086 auto points_miter =
1088 path,
1089 {
1090 .width = 20.0f,
1091 .cap = Cap::kButt,
1092 .join = Join::kMiter,
1093 .miter_limit = 400.0f,
1094 },
1095 1.0f);
1096 // Generates no join - even with a very large miter limit
1097 EXPECT_EQ(points_miter.size(), 8u);
1098
1099 auto points_round =
1101 path,
1102 {
1103 .width = 20.0f,
1104 .cap = Cap::kButt,
1105 .join = Join::kRound,
1106 .miter_limit = 4.0f,
1107 },
1108 1.0f);
1109 // Generates lots of join points - to round off the 180 degree bend
1110 EXPECT_EQ(points_round.size(), 19u);
1111}
1112
1113TEST(EntityGeometryTest, TightQuadratic180DegreeJoins) {
1114 // First, create a mild quadratic that helps us verify how many points
1115 // should normally be on a quad with 2 legs of length 90.
1116 flutter::DlPathBuilder path_builder_refrence;
1117 path_builder_refrence.MoveTo(Point(10, 10));
1118 path_builder_refrence.QuadraticCurveTo(Point(100, 10), Point(100, 100));
1119 flutter::DlPath path_reference = path_builder_refrence.TakePath();
1120
1121 auto points_bevel_reference =
1123 path_reference,
1124 {
1125 .width = 20.0f,
1126 .cap = Cap::kButt,
1127 .join = Join::kBevel,
1128 .miter_limit = 4.0f,
1129 },
1130 1.0f);
1131 // Generates no joins because the curve is smooth
1132 EXPECT_EQ(points_bevel_reference.size(), 74u);
1133
1134 // Now create a path that doubles back on itself with a quadratic.
1135 flutter::DlPathBuilder path_builder;
1136 path_builder.MoveTo(Point(10, 10));
1137 path_builder.QuadraticCurveTo(Point(100, 10), Point(10, 10));
1138 flutter::DlPath path = path_builder.TakePath();
1139
1140 auto points_bevel =
1142 path,
1143 {
1144 .width = 20.0f,
1145 .cap = Cap::kButt,
1146 .join = Join::kBevel,
1147 .miter_limit = 4.0f,
1148 },
1149 1.0f);
1150 // Generates round join because it is in the middle of a curved segment
1151 EXPECT_GT(points_bevel.size(), points_bevel_reference.size());
1152
1153 auto points_miter =
1155 path,
1156 {
1157 .width = 20.0f,
1158 .cap = Cap::kButt,
1159 .join = Join::kMiter,
1160 .miter_limit = 400.0f,
1161 },
1162 1.0f);
1163 // Generates round join because it is in the middle of a curved segment
1164 EXPECT_GT(points_miter.size(), points_bevel_reference.size());
1165
1166 auto points_round =
1168 path,
1169 {
1170 .width = 20.0f,
1171 .cap = Cap::kButt,
1172 .join = Join::kRound,
1173 .miter_limit = 4.0f,
1174 },
1175 1.0f);
1176 // Generates round join because it is in the middle of a curved segment
1177 EXPECT_GT(points_round.size(), points_bevel_reference.size());
1178}
1179
1180TEST(EntityGeometryTest, TightConic180DegreeJoins) {
1181 // First, create a mild conic that helps us verify how many points
1182 // should normally be on a quad with 2 legs of length 90.
1183 flutter::DlPathBuilder path_builder_refrence;
1184 path_builder_refrence.MoveTo(Point(10, 10));
1185 path_builder_refrence.ConicCurveTo(Point(100, 10), Point(100, 100), 0.9f);
1186 flutter::DlPath path_reference = path_builder_refrence.TakePath();
1187
1188 auto points_bevel_reference =
1190 path_reference,
1191 {
1192 .width = 20.0f,
1193 .cap = Cap::kButt,
1194 .join = Join::kBevel,
1195 .miter_limit = 4.0f,
1196 },
1197 1.0f);
1198 // Generates no joins because the curve is smooth
1199 EXPECT_EQ(points_bevel_reference.size(), 78u);
1200
1201 // Now create a path that doubles back on itself with a conic.
1202 flutter::DlPathBuilder path_builder;
1203 path_builder.MoveTo(Point(10, 10));
1204 path_builder.QuadraticCurveTo(Point(100, 10), Point(10, 10));
1205 flutter::DlPath path = path_builder.TakePath();
1206
1207 auto points_bevel =
1209 path,
1210 {
1211 .width = 20.0f,
1212 .cap = Cap::kButt,
1213 .join = Join::kBevel,
1214 .miter_limit = 4.0f,
1215 },
1216 1.0f);
1217 // Generates round join because it is in the middle of a curved segment
1218 EXPECT_GT(points_bevel.size(), points_bevel_reference.size());
1219
1220 auto points_miter =
1222 path,
1223 {
1224 .width = 20.0f,
1225 .cap = Cap::kButt,
1226 .join = Join::kMiter,
1227 .miter_limit = 400.0f,
1228 },
1229 1.0f);
1230 // Generates round join because it is in the middle of a curved segment
1231 EXPECT_GT(points_miter.size(), points_bevel_reference.size());
1232
1233 auto points_round =
1235 path,
1236 {
1237 .width = 20.0f,
1238 .cap = Cap::kButt,
1239 .join = Join::kRound,
1240 .miter_limit = 4.0f,
1241 },
1242 1.0f);
1243 // Generates round join because it is in the middle of a curved segment
1244 EXPECT_GT(points_round.size(), points_bevel_reference.size());
1245}
1246
1247TEST(EntityGeometryTest, TightCubic180DegreeJoins) {
1248 // First, create a mild cubic that helps us verify how many points
1249 // should normally be on a quad with 3 legs of length ~50.
1250 flutter::DlPathBuilder path_builder_reference;
1251 path_builder_reference.MoveTo(Point(10, 10));
1252 path_builder_reference.CubicCurveTo(Point(60, 10), Point(100, 40),
1253 Point(100, 90));
1254 flutter::DlPath path_reference = path_builder_reference.TakePath();
1255
1256 auto points_reference =
1258 path_reference,
1259 {
1260 .width = 20.0f,
1261 .cap = Cap::kButt,
1262 .join = Join::kBevel,
1263 .miter_limit = 4.0f,
1264 },
1265 1.0f);
1266 // Generates no joins because the curve is smooth
1267 EXPECT_EQ(points_reference.size(), 76u);
1268
1269 // Now create a path that doubles back on itself with a cubic.
1270 flutter::DlPathBuilder path_builder;
1271 path_builder.MoveTo(Point(10, 10));
1272 path_builder.CubicCurveTo(Point(60, 10), Point(100, 40), Point(60, 10));
1273 flutter::DlPath path = path_builder.TakePath();
1274
1275 auto points_bevel =
1277 path,
1278 {
1279 .width = 20.0f,
1280 .cap = Cap::kButt,
1281 .join = Join::kBevel,
1282 .miter_limit = 4.0f,
1283 },
1284 1.0f);
1285 // Generates round join because it is in the middle of a curved segment
1286 EXPECT_GT(points_bevel.size(), points_reference.size());
1287
1288 auto points_miter =
1290 path,
1291 {
1292 .width = 20.0f,
1293 .cap = Cap::kButt,
1294 .join = Join::kMiter,
1295 .miter_limit = 400.0f,
1296 },
1297 1.0f);
1298 // Generates round join because it is in the middle of a curved segment
1299 EXPECT_GT(points_miter.size(), points_reference.size());
1300
1301 auto points_round =
1303 path,
1304 {
1305 .width = 20.0f,
1306 .cap = Cap::kButt,
1307 .join = Join::kRound,
1308 .miter_limit = 4.0f,
1309 },
1310 1.0f);
1311 // Generates round join because it is in the middle of a curved segment
1312 EXPECT_GT(points_round.size(), points_reference.size());
1313}
1314
1315TEST(EntityGeometryTest, RotatedFilledCircleGeometryCoverage) {
1316 Point center = Point(50, 50);
1317 auto geometry = Geometry::MakeCircle(center, 50);
1318 Rect circle_bounds = Rect::MakeLTRB(0, 0, 100, 100);
1319 ASSERT_EQ(geometry->GetCoverage({}).value_or(Rect()), circle_bounds);
1320
1321 Matrix transform45 = Matrix::MakeTranslation(center) *
1323 Matrix::MakeTranslation(-center);
1324
1325 EXPECT_TRUE(geometry->GetCoverage(transform45).has_value());
1326 Rect bounds = geometry->GetCoverage(transform45).value_or(Rect());
1327 EXPECT_TRUE(bounds.Contains(circle_bounds))
1328 << "geometry bounds: " << bounds << std::endl
1329 << " circle bounds: " << circle_bounds;
1330}
1331
1332TEST(EntityGeometryTest, RotatedStrokedCircleGeometryCoverage) {
1333 Point center = Point(50, 50);
1334 auto geometry = Geometry::MakeStrokedCircle(center, 50, 10);
1335 Rect circle_bounds = Rect::MakeLTRB(0, 0, 100, 100).Expand(5);
1336 ASSERT_EQ(geometry->GetCoverage({}).value_or(Rect()), circle_bounds);
1337
1338 Matrix transform45 = Matrix::MakeTranslation(center) *
1340 Matrix::MakeTranslation(-center);
1341
1342 EXPECT_TRUE(geometry->GetCoverage(transform45).has_value());
1343 Rect bounds = geometry->GetCoverage(transform45).value_or(Rect());
1344 EXPECT_TRUE(bounds.Contains(circle_bounds))
1345 << "geometry bounds: " << bounds << std::endl
1346 << " circle bounds: " << circle_bounds;
1347}
1348
1349} // namespace testing
1350} // namespace impeller
DlPathBuilder & LineTo(DlPoint p2)
Draw a line from the current point to the indicated point p2.
DlPathBuilder & MoveTo(DlPoint p2)
Start a new contour that will originate at the indicated point p2.
const DlPath TakePath()
Returns the path constructed by this path builder and resets its internal state to the default state ...
DlPathBuilder & ConicCurveTo(DlPoint cp, DlPoint p2, DlScalar weight)
Draw a conic curve (a rational quadratic bezier curve) from the current point to the indicated point ...
DlPathBuilder & AddRect(const DlRect &rect)
Append a closed rectangular contour to the path.
DlPathBuilder & QuadraticCurveTo(DlPoint cp, DlPoint p2)
Draw a quadratic bezier curve from the current point to the indicated point p2, using the indicated p...
DlPathBuilder & CubicCurveTo(DlPoint cp1, DlPoint cp2, DlPoint p2)
Draw a cubic bezier curve from the current point to the indicated point p2, using the indicated point...
A Geometry class that produces fillable vertices from any |RoundRect| object regardless of radii unif...
static std::unique_ptr< Geometry > MakeFillPath(const flutter::DlPath &path, std::optional< Rect > inner_rect=std::nullopt)
Definition geometry.cc:62
static std::unique_ptr< Geometry > MakeRect(const Rect &rect)
Definition geometry.cc:83
static std::unique_ptr< Geometry > MakeFilledArc(const Rect &oval_bounds, Degrees start, Degrees sweep, bool include_center)
Definition geometry.cc:108
static std::unique_ptr< Geometry > MakeStrokePath(const flutter::DlPath &path, const StrokeParameters &stroke={})
Definition geometry.cc:68
static std::unique_ptr< Geometry > MakeCircle(const Point &center, Scalar radius)
Definition geometry.cc:97
static std::unique_ptr< Geometry > MakeRoundRect(const Rect &rect, const Size &radii)
Definition geometry.cc:125
static std::unique_ptr< Geometry > MakeLine(const Point &p0, const Point &p1, const StrokeParameters &stroke)
Definition geometry.cc:91
static std::unique_ptr< Geometry > MakeStrokedCircle(const Point &center, Scalar radius, Scalar stroke_width)
Definition geometry.cc:102
static std::unique_ptr< Geometry > MakeStrokedArc(const Rect &oval_bounds, Degrees start, Degrees sweep, const StrokeParameters &stroke)
Definition geometry.cc:116
static std::vector< Point > GenerateSolidStrokeVertices(const PathSource &path, const StrokeParameters &stroke, Scalar scale)
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Definition tessellator.h:37
auto & d
Definition main.cc:28
inline ::testing::AssertionResult PointNear(impeller::Point a, impeller::Point b)
#define EXPECT_RECT_NEAR(a, b)
inline ::testing::AssertionResult SolidVerticesNear(std::vector< impeller::Point > a, std::vector< impeller::Point > b)
inline ::testing::AssertionResult TextureVerticesNear(std::vector< impeller::TextureFillVertexShader::PerVertexData > a, std::vector< impeller::TextureFillVertexShader::PerVertexData > b)
TEST(FrameTimingsRecorderTest, RecordVsync)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switch_defs.h:52
float Scalar
Definition scalar.h:19
constexpr float kSqrt2Over2
Definition constants.h:51
constexpr float kPiOver2
Definition constants.h:32
constexpr float kSqrt2
Definition constants.h:47
int32_t width
PrimitiveType type
Definition geometry.h:37
@ kNormal
The geometry has no overlapping triangles.
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
static Matrix MakeRotationZ(Radians r)
Definition matrix.h:223
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
Scalar radians
Definition scalar.h:45
static RoundRect MakeRectRadii(const Rect &rect, const RoundingRadii &radii)
Definition round_rect.cc:9
A structure to store all of the parameters related to stroking a path or basic geometry object.
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
Definition rect.h:618
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
const size_t start
std::vector< Point > points