Flutter Impeller
round_superellipse_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "gtest/gtest.h"
6 
9 
11 
12 #define CHECK_POINT_WITH_OFFSET(rr, p, outward_offset) \
13  EXPECT_TRUE(rr.Contains(p)); \
14  EXPECT_FALSE(rr.Contains(p + outward_offset));
15 
16 namespace impeller {
17 
18 namespace {
19 
20 // A `PathReceiver` that allows setting callbacks for each kind of path
21 // segments.
22 class SpyPathReceiver : public PathReceiver {
23  public:
24  // For now not all segment types are defined since they're not all used.
25  using LineSegment = std::function<void(const Point&)>;
26  using CubicSegment =
27  std::function<void(const Point&, const Point&, const Point&)>;
28 
29  void SpyLineTo(LineSegment line_to) { line_to_ = std::move(line_to); }
30 
31  void SpyCubicTo(CubicSegment cubic_to) { cubic_to_ = std::move(cubic_to); }
32 
33  // |PathReceiver|
34  void MoveTo(const Point& p2, bool will_be_closed) override {}
35  // |PathReceiver|
36  void LineTo(const Point& p2) override {
37  if (line_to_) {
38  line_to_(p2);
39  }
40  }
41  // |PathReceiver|
42  void QuadTo(const Point& cp, const Point& p2) override {}
43  // |PathReceiver|
44  void CubicTo(const Point& cp1, const Point& cp2, const Point& p2) override {
45  if (cubic_to_) {
46  cubic_to_(cp1, cp2, p2);
47  }
48  }
49  // |PathReceiver|
50  void Close() override {}
51 
52  private:
53  LineSegment line_to_;
54  CubicSegment cubic_to_;
55 };
56 
57 } // namespace
58 
59 namespace testing {
60 
61 TEST(RoundSuperellipseTest, EmptyDeclaration) {
63 
64  EXPECT_TRUE(rse.IsEmpty());
65  EXPECT_FALSE(rse.IsRect());
66  EXPECT_FALSE(rse.IsOval());
67  EXPECT_TRUE(rse.IsFinite());
68  EXPECT_TRUE(rse.GetBounds().IsEmpty());
69  EXPECT_EQ(rse.GetBounds(), Rect());
70  EXPECT_EQ(rse.GetBounds().GetLeft(), 0.0f);
71  EXPECT_EQ(rse.GetBounds().GetTop(), 0.0f);
72  EXPECT_EQ(rse.GetBounds().GetRight(), 0.0f);
73  EXPECT_EQ(rse.GetBounds().GetBottom(), 0.0f);
74  EXPECT_EQ(rse.GetRadii().top_left, Size());
75  EXPECT_EQ(rse.GetRadii().top_right, Size());
76  EXPECT_EQ(rse.GetRadii().bottom_left, Size());
77  EXPECT_EQ(rse.GetRadii().bottom_right, Size());
78  EXPECT_EQ(rse.GetRadii().top_left.width, 0.0f);
79  EXPECT_EQ(rse.GetRadii().top_left.height, 0.0f);
80  EXPECT_EQ(rse.GetRadii().top_right.width, 0.0f);
81  EXPECT_EQ(rse.GetRadii().top_right.height, 0.0f);
82  EXPECT_EQ(rse.GetRadii().bottom_left.width, 0.0f);
83  EXPECT_EQ(rse.GetRadii().bottom_left.height, 0.0f);
84  EXPECT_EQ(rse.GetRadii().bottom_right.width, 0.0f);
85  EXPECT_EQ(rse.GetRadii().bottom_right.height, 0.0f);
86 }
87 
88 TEST(RoundSuperellipseTest, DefaultConstructor) {
90 
91  EXPECT_TRUE(rse.IsEmpty());
92  EXPECT_FALSE(rse.IsRect());
93  EXPECT_FALSE(rse.IsOval());
94  EXPECT_TRUE(rse.IsFinite());
95  EXPECT_TRUE(rse.GetBounds().IsEmpty());
96  EXPECT_EQ(rse.GetBounds(), Rect());
97  EXPECT_EQ(rse.GetRadii().top_left, Size());
98  EXPECT_EQ(rse.GetRadii().top_right, Size());
99  EXPECT_EQ(rse.GetRadii().bottom_left, Size());
100  EXPECT_EQ(rse.GetRadii().bottom_right, Size());
101 }
102 
103 TEST(RoundSuperellipseTest, EmptyRectConstruction) {
104  RoundSuperellipse rse =
105  RoundSuperellipse::MakeRect(Rect::MakeLTRB(20.0f, 20.0f, 20.0f, 20.0f));
106 
107  EXPECT_TRUE(rse.IsEmpty());
108  EXPECT_FALSE(rse.IsRect());
109  EXPECT_FALSE(rse.IsOval());
110  EXPECT_TRUE(rse.IsFinite());
111  EXPECT_TRUE(rse.GetBounds().IsEmpty());
112  EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(20.0f, 20.0f, 20.0f, 20.0f));
113  EXPECT_EQ(rse.GetRadii().top_left, Size());
114  EXPECT_EQ(rse.GetRadii().top_right, Size());
115  EXPECT_EQ(rse.GetRadii().bottom_left, Size());
116  EXPECT_EQ(rse.GetRadii().bottom_right, Size());
117 }
118 
119 TEST(RoundSuperellipseTest, RectConstructor) {
120  RoundSuperellipse rse =
121  RoundSuperellipse::MakeRect(Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
122 
123  EXPECT_FALSE(rse.IsEmpty());
124  EXPECT_TRUE(rse.IsRect());
125  EXPECT_FALSE(rse.IsOval());
126  EXPECT_TRUE(rse.IsFinite());
127  EXPECT_FALSE(rse.GetBounds().IsEmpty());
128  EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
129  EXPECT_EQ(rse.GetRadii().top_left, Size());
130  EXPECT_EQ(rse.GetRadii().top_right, Size());
131  EXPECT_EQ(rse.GetRadii().bottom_left, Size());
132  EXPECT_EQ(rse.GetRadii().bottom_right, Size());
133 }
134 
135 TEST(RoundSuperellipseTest, InvertedRectConstruction) {
136  RoundSuperellipse rse =
137  RoundSuperellipse::MakeRect(Rect::MakeLTRB(20.0f, 20.0f, 10.0f, 10.0f));
138 
139  EXPECT_FALSE(rse.IsEmpty());
140  EXPECT_TRUE(rse.IsRect());
141  EXPECT_FALSE(rse.IsOval());
142  EXPECT_TRUE(rse.IsFinite());
143  EXPECT_FALSE(rse.GetBounds().IsEmpty());
144  EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
145  EXPECT_EQ(rse.GetRadii().top_left, Size());
146  EXPECT_EQ(rse.GetRadii().top_right, Size());
147  EXPECT_EQ(rse.GetRadii().bottom_left, Size());
148  EXPECT_EQ(rse.GetRadii().bottom_right, Size());
149 }
150 
151 TEST(RoundSuperellipseTest, EmptyOvalConstruction) {
153  Rect::MakeLTRB(20.0f, 20.0f, 20.0f, 20.0f), 10.0f, 10.0f);
154 
155  EXPECT_TRUE(rse.IsEmpty());
156  EXPECT_FALSE(rse.IsRect());
157  EXPECT_FALSE(rse.IsOval());
158  EXPECT_TRUE(rse.IsFinite());
159  EXPECT_TRUE(rse.GetBounds().IsEmpty());
160  EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(20.0f, 20.0f, 20.0f, 20.0f));
161  EXPECT_EQ(rse.GetRadii().top_left, Size());
162  EXPECT_EQ(rse.GetRadii().top_right, Size());
163  EXPECT_EQ(rse.GetRadii().bottom_left, Size());
164  EXPECT_EQ(rse.GetRadii().bottom_right, Size());
165 }
166 
167 TEST(RoundSuperellipseTest, OvalConstructor) {
168  RoundSuperellipse rse =
169  RoundSuperellipse::MakeOval(Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
170 
171  EXPECT_FALSE(rse.IsEmpty());
172  EXPECT_FALSE(rse.IsRect());
173  EXPECT_TRUE(rse.IsOval());
174  EXPECT_TRUE(rse.IsFinite());
175  EXPECT_FALSE(rse.GetBounds().IsEmpty());
176  EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
177  EXPECT_EQ(rse.GetRadii().top_left, Size(5.0f, 5.0f));
178  EXPECT_EQ(rse.GetRadii().top_right, Size(5.0f, 5.0f));
179  EXPECT_EQ(rse.GetRadii().bottom_left, Size(5.0f, 5.0f));
180  EXPECT_EQ(rse.GetRadii().bottom_right, Size(5.0f, 5.0f));
181 }
182 
183 TEST(RoundSuperellipseTest, InvertedOvalConstruction) {
185  Rect::MakeLTRB(20.0f, 20.0f, 10.0f, 10.0f), 10.0f, 10.0f);
186 
187  EXPECT_FALSE(rse.IsEmpty());
188  EXPECT_FALSE(rse.IsRect());
189  EXPECT_TRUE(rse.IsOval());
190  EXPECT_TRUE(rse.IsFinite());
191  EXPECT_FALSE(rse.GetBounds().IsEmpty());
192  EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
193  EXPECT_EQ(rse.GetRadii().top_left, Size(5.0f, 5.0f));
194  EXPECT_EQ(rse.GetRadii().top_right, Size(5.0f, 5.0f));
195  EXPECT_EQ(rse.GetRadii().bottom_left, Size(5.0f, 5.0f));
196  EXPECT_EQ(rse.GetRadii().bottom_right, Size(5.0f, 5.0f));
197 }
198 
199 TEST(RoundSuperellipseTest, RectRadiusConstructor) {
201  Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), 2.0f);
202 
203  EXPECT_FALSE(rse.IsEmpty());
204  EXPECT_FALSE(rse.IsRect());
205  EXPECT_FALSE(rse.IsOval());
206  EXPECT_TRUE(rse.IsFinite());
207  EXPECT_FALSE(rse.GetBounds().IsEmpty());
208  EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
209  EXPECT_EQ(rse.GetRadii().top_left, Size(2.0f, 2.0f));
210  EXPECT_EQ(rse.GetRadii().top_right, Size(2.0f, 2.0f));
211  EXPECT_EQ(rse.GetRadii().bottom_left, Size(2.0f, 2.0f));
212  EXPECT_EQ(rse.GetRadii().bottom_right, Size(2.0f, 2.0f));
213 }
214 
215 TEST(RoundSuperellipseTest, RectXYConstructor) {
217  Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), 2.0f, 3.0f);
218 
219  EXPECT_FALSE(rse.IsEmpty());
220  EXPECT_FALSE(rse.IsRect());
221  EXPECT_FALSE(rse.IsOval());
222  EXPECT_TRUE(rse.IsFinite());
223  EXPECT_FALSE(rse.GetBounds().IsEmpty());
224  EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
225  EXPECT_EQ(rse.GetRadii().top_left, Size(2.0f, 3.0f));
226  EXPECT_EQ(rse.GetRadii().top_right, Size(2.0f, 3.0f));
227  EXPECT_EQ(rse.GetRadii().bottom_left, Size(2.0f, 3.0f));
228  EXPECT_EQ(rse.GetRadii().bottom_right, Size(2.0f, 3.0f));
229 }
230 
231 TEST(RoundSuperellipseTest, RectSizeConstructor) {
233  Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), Size(2.0f, 3.0f));
234 
235  EXPECT_FALSE(rse.IsEmpty());
236  EXPECT_FALSE(rse.IsRect());
237  EXPECT_FALSE(rse.IsOval());
238  EXPECT_TRUE(rse.IsFinite());
239  EXPECT_FALSE(rse.GetBounds().IsEmpty());
240  EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
241  EXPECT_EQ(rse.GetRadii().top_left, Size(2.0f, 3.0f));
242  EXPECT_EQ(rse.GetRadii().top_right, Size(2.0f, 3.0f));
243  EXPECT_EQ(rse.GetRadii().bottom_left, Size(2.0f, 3.0f));
244  EXPECT_EQ(rse.GetRadii().bottom_right, Size(2.0f, 3.0f));
245 }
246 
247 TEST(RoundSuperellipseTest, RectRadiiConstructor) {
249  Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f),
250  {
251  .top_left = Size(1.0, 1.5),
252  .top_right = Size(2.0, 2.5f),
253  .bottom_left = Size(3.0, 3.5f),
254  .bottom_right = Size(4.0, 4.5f),
255  });
256 
257  EXPECT_FALSE(rse.IsEmpty());
258  EXPECT_FALSE(rse.IsRect());
259  EXPECT_FALSE(rse.IsOval());
260  EXPECT_TRUE(rse.IsFinite());
261  EXPECT_FALSE(rse.GetBounds().IsEmpty());
262  EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
263  EXPECT_EQ(rse.GetRadii().top_left, Size(1.0f, 1.5f));
264  EXPECT_EQ(rse.GetRadii().top_right, Size(2.0f, 2.5f));
265  EXPECT_EQ(rse.GetRadii().bottom_left, Size(3.0f, 3.5f));
266  EXPECT_EQ(rse.GetRadii().bottom_right, Size(4.0f, 4.5f));
267 }
268 
269 TEST(RoundSuperellipseTest, RectRadiiOverflowWidthConstructor) {
271  Rect::MakeXYWH(10.0f, 10.0f, 6.0f, 30.0f),
272  {
273  .top_left = Size(1.0f, 2.0f),
274  .top_right = Size(3.0f, 4.0f),
275  .bottom_left = Size(5.0f, 6.0f),
276  .bottom_right = Size(7.0f, 8.0f),
277  });
278  // Largest sum of paired radii widths is the bottom edge which sums to 12
279  // Rect is only 6 wide so all radii are scaled by half
280  // Rect is 30 tall so no scaling should happen due to radii heights
281 
282  EXPECT_FALSE(rse.IsEmpty());
283  EXPECT_FALSE(rse.IsRect());
284  EXPECT_FALSE(rse.IsOval());
285  EXPECT_TRUE(rse.IsFinite());
286  EXPECT_FALSE(rse.GetBounds().IsEmpty());
287  EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 16.0f, 40.0f));
288  EXPECT_EQ(rse.GetRadii().top_left, Size(0.5f, 1.0f));
289  EXPECT_EQ(rse.GetRadii().top_right, Size(1.5f, 2.0f));
290  EXPECT_EQ(rse.GetRadii().bottom_left, Size(2.5f, 3.0f));
291  EXPECT_EQ(rse.GetRadii().bottom_right, Size(3.5f, 4.0f));
292 }
293 
294 TEST(RoundSuperellipseTest, RectRadiiOverflowHeightConstructor) {
296  Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 6.0f),
297  {
298  .top_left = Size(1.0f, 2.0f),
299  .top_right = Size(3.0f, 4.0f),
300  .bottom_left = Size(5.0f, 6.0f),
301  .bottom_right = Size(7.0f, 8.0f),
302  });
303  // Largest sum of paired radii heights is the right edge which sums to 12
304  // Rect is only 6 tall so all radii are scaled by half
305  // Rect is 30 wide so no scaling should happen due to radii widths
306 
307  EXPECT_FALSE(rse.IsEmpty());
308  EXPECT_FALSE(rse.IsRect());
309  EXPECT_FALSE(rse.IsOval());
310  EXPECT_TRUE(rse.IsFinite());
311  EXPECT_FALSE(rse.GetBounds().IsEmpty());
312  EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 40.0f, 16.0f));
313  EXPECT_EQ(rse.GetRadii().top_left, Size(0.5f, 1.0f));
314  EXPECT_EQ(rse.GetRadii().top_right, Size(1.5f, 2.0f));
315  EXPECT_EQ(rse.GetRadii().bottom_left, Size(2.5f, 3.0f));
316  EXPECT_EQ(rse.GetRadii().bottom_right, Size(3.5f, 4.0f));
317 }
318 
319 TEST(RoundSuperellipseTest, Shift) {
321  Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f),
322  {
323  .top_left = Size(1.0f, 2.0f),
324  .top_right = Size(3.0f, 4.0f),
325  .bottom_left = Size(5.0f, 6.0f),
326  .bottom_right = Size(7.0f, 8.0f),
327  });
328  RoundSuperellipse shifted = rse.Shift(5.0, 6.0);
329 
330  EXPECT_FALSE(shifted.IsEmpty());
331  EXPECT_FALSE(shifted.IsRect());
332  EXPECT_FALSE(shifted.IsOval());
333  EXPECT_TRUE(shifted.IsFinite());
334  EXPECT_FALSE(shifted.GetBounds().IsEmpty());
335  EXPECT_EQ(shifted.GetBounds(), Rect::MakeLTRB(15.0f, 16.0f, 45.0f, 46.0f));
336  EXPECT_EQ(shifted.GetRadii().top_left, Size(1.0f, 2.0f));
337  EXPECT_EQ(shifted.GetRadii().top_right, Size(3.0f, 4.0f));
338  EXPECT_EQ(shifted.GetRadii().bottom_left, Size(5.0f, 6.0f));
339  EXPECT_EQ(shifted.GetRadii().bottom_right, Size(7.0f, 8.0f));
340 
341  EXPECT_EQ(shifted, RoundSuperellipse::MakeRectRadii(
342  Rect::MakeXYWH(15.0f, 16.0f, 30.0f, 30.0f),
343  {
344  .top_left = Size(1.0f, 2.0f),
345  .top_right = Size(3.0f, 4.0f),
346  .bottom_left = Size(5.0f, 6.0f),
347  .bottom_right = Size(7.0f, 8.0f),
348  }));
349 }
350 
351 TEST(RoundSuperellipseTest, ExpandScalar) {
353  Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f),
354  {
355  .top_left = Size(1.0f, 2.0f),
356  .top_right = Size(3.0f, 4.0f),
357  .bottom_left = Size(5.0f, 6.0f),
358  .bottom_right = Size(7.0f, 8.0f),
359  });
360  RoundSuperellipse expanded = rse.Expand(5.0);
361 
362  EXPECT_FALSE(expanded.IsEmpty());
363  EXPECT_FALSE(expanded.IsRect());
364  EXPECT_FALSE(expanded.IsOval());
365  EXPECT_TRUE(expanded.IsFinite());
366  EXPECT_FALSE(expanded.GetBounds().IsEmpty());
367  EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(5.0f, 5.0f, 45.0f, 45.0f));
368  EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f));
369  EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f));
370  EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f));
371  EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f));
372 
373  EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
374  Rect::MakeXYWH(5.0f, 5.0f, 40.0f, 40.0f),
375  {
376  .top_left = Size(1.0f, 2.0f),
377  .top_right = Size(3.0f, 4.0f),
378  .bottom_left = Size(5.0f, 6.0f),
379  .bottom_right = Size(7.0f, 8.0f),
380  }));
381 }
382 
383 TEST(RoundSuperellipseTest, ExpandTwoScalars) {
385  Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f),
386  {
387  .top_left = Size(1.0f, 2.0f),
388  .top_right = Size(3.0f, 4.0f),
389  .bottom_left = Size(5.0f, 6.0f),
390  .bottom_right = Size(7.0f, 8.0f),
391  });
392  RoundSuperellipse expanded = rse.Expand(5.0, 6.0);
393 
394  EXPECT_FALSE(expanded.IsEmpty());
395  EXPECT_FALSE(expanded.IsRect());
396  EXPECT_FALSE(expanded.IsOval());
397  EXPECT_TRUE(expanded.IsFinite());
398  EXPECT_FALSE(expanded.GetBounds().IsEmpty());
399  EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(5.0f, 4.0f, 45.0f, 46.0f));
400  EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f));
401  EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f));
402  EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f));
403  EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f));
404 
405  EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
406  Rect::MakeXYWH(5.0f, 4.0f, 40.0f, 42.0f),
407  {
408  .top_left = Size(1.0f, 2.0f),
409  .top_right = Size(3.0f, 4.0f),
410  .bottom_left = Size(5.0f, 6.0f),
411  .bottom_right = Size(7.0f, 8.0f),
412  }));
413 }
414 
415 TEST(RoundSuperellipseTest, ExpandFourScalars) {
417  Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f),
418  {
419  .top_left = Size(1.0f, 2.0f),
420  .top_right = Size(3.0f, 4.0f),
421  .bottom_left = Size(5.0f, 6.0f),
422  .bottom_right = Size(7.0f, 8.0f),
423  });
424  RoundSuperellipse expanded = rse.Expand(5.0, 6.0, 7.0, 8.0);
425 
426  EXPECT_FALSE(expanded.IsEmpty());
427  EXPECT_FALSE(expanded.IsRect());
428  EXPECT_FALSE(expanded.IsOval());
429  EXPECT_TRUE(expanded.IsFinite());
430  EXPECT_FALSE(expanded.GetBounds().IsEmpty());
431  EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(5.0f, 4.0f, 47.0f, 48.0f));
432  EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f));
433  EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f));
434  EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f));
435  EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f));
436 
437  EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
438  Rect::MakeXYWH(5.0f, 4.0f, 42.0f, 44.0f),
439  {
440  .top_left = Size(1.0f, 2.0f),
441  .top_right = Size(3.0f, 4.0f),
442  .bottom_left = Size(5.0f, 6.0f),
443  .bottom_right = Size(7.0f, 8.0f),
444  }));
445 }
446 
447 TEST(RoundSuperellipseTest, ContractScalar) {
449  Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f),
450  {
451  .top_left = Size(1.0f, 2.0f),
452  .top_right = Size(3.0f, 4.0f),
453  .bottom_left = Size(5.0f, 6.0f),
454  .bottom_right = Size(7.0f, 8.0f),
455  });
456  RoundSuperellipse expanded = rse.Expand(-2.0);
457 
458  EXPECT_FALSE(expanded.IsEmpty());
459  EXPECT_FALSE(expanded.IsRect());
460  EXPECT_FALSE(expanded.IsOval());
461  EXPECT_TRUE(expanded.IsFinite());
462  EXPECT_FALSE(expanded.GetBounds().IsEmpty());
463  EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(12.0f, 12.0f, 38.0f, 38.0f));
464  EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f));
465  EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f));
466  EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f));
467  EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f));
468 
469  EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
470  Rect::MakeXYWH(12.0f, 12.0f, 26.0f, 26.0f),
471  {
472  .top_left = Size(1.0f, 2.0f),
473  .top_right = Size(3.0f, 4.0f),
474  .bottom_left = Size(5.0f, 6.0f),
475  .bottom_right = Size(7.0f, 8.0f),
476  }));
477 }
478 
479 TEST(RoundSuperellipseTest, ContractTwoScalars) {
481  Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f),
482  {
483  .top_left = Size(1.0f, 2.0f),
484  .top_right = Size(3.0f, 4.0f),
485  .bottom_left = Size(5.0f, 6.0f),
486  .bottom_right = Size(7.0f, 8.0f),
487  });
488  RoundSuperellipse expanded = rse.Expand(-1.0, -2.0);
489 
490  EXPECT_FALSE(expanded.IsEmpty());
491  EXPECT_FALSE(expanded.IsRect());
492  EXPECT_FALSE(expanded.IsOval());
493  EXPECT_TRUE(expanded.IsFinite());
494  EXPECT_FALSE(expanded.GetBounds().IsEmpty());
495  EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(11.0f, 12.0f, 39.0f, 38.0f));
496  EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f));
497  EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f));
498  EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f));
499  EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f));
500 
501  EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
502  Rect::MakeXYWH(11.0f, 12.0f, 28.0f, 26.0f),
503  {
504  .top_left = Size(1.0f, 2.0f),
505  .top_right = Size(3.0f, 4.0f),
506  .bottom_left = Size(5.0f, 6.0f),
507  .bottom_right = Size(7.0f, 8.0f),
508  }));
509 }
510 
511 TEST(RoundSuperellipseTest, ContractFourScalars) {
513  Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f),
514  {
515  .top_left = Size(1.0f, 2.0f),
516  .top_right = Size(3.0f, 4.0f),
517  .bottom_left = Size(5.0f, 6.0f),
518  .bottom_right = Size(7.0f, 8.0f),
519  });
520  RoundSuperellipse expanded = rse.Expand(-1.0, -1.5, -2.0, -2.5);
521 
522  EXPECT_FALSE(expanded.IsEmpty());
523  EXPECT_FALSE(expanded.IsRect());
524  EXPECT_FALSE(expanded.IsOval());
525  EXPECT_TRUE(expanded.IsFinite());
526  EXPECT_FALSE(expanded.GetBounds().IsEmpty());
527  EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(11.0f, 11.5f, 38.0f, 37.5f));
528  EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f));
529  EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f));
530  EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f));
531  EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f));
532 
533  EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
534  Rect::MakeXYWH(11.0f, 11.5f, 27.0f, 26.0f),
535  {
536  .top_left = Size(1.0f, 2.0f),
537  .top_right = Size(3.0f, 4.0f),
538  .bottom_left = Size(5.0f, 6.0f),
539  .bottom_right = Size(7.0f, 8.0f),
540  }));
541 }
542 
543 TEST(RoundSuperellipseTest, ContractAndRequireRadiiAdjustment) {
545  Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f),
546  {
547  .top_left = Size(1.0f, 2.0f),
548  .top_right = Size(3.0f, 4.0f),
549  .bottom_left = Size(5.0f, 6.0f),
550  .bottom_right = Size(7.0f, 8.0f),
551  });
552  RoundSuperellipse expanded = rse.Expand(-12.0);
553  // Largest sum of paired radii sizes are the bottom and right edges
554  // both of which sum to 12
555  // Rect was 30x30 reduced by 12 on all sides leaving only 6x6, so all
556  // radii are scaled by half to avoid overflowing the contracted rect
557 
558  EXPECT_FALSE(expanded.IsEmpty());
559  EXPECT_FALSE(expanded.IsRect());
560  EXPECT_FALSE(expanded.IsOval());
561  EXPECT_TRUE(expanded.IsFinite());
562  EXPECT_FALSE(expanded.GetBounds().IsEmpty());
563  EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(22.0f, 22.0f, 28.0f, 28.0f));
564  EXPECT_EQ(expanded.GetRadii().top_left, Size(0.5f, 1.0f));
565  EXPECT_EQ(expanded.GetRadii().top_right, Size(1.5f, 2.0f));
566  EXPECT_EQ(expanded.GetRadii().bottom_left, Size(2.5f, 3.0f));
567  EXPECT_EQ(expanded.GetRadii().bottom_right, Size(3.5f, 4.0f));
568 
569  // In this test, the MakeRectRadii constructor will make the same
570  // adjustment to the radii that the Expand method applied.
571  EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
572  Rect::MakeXYWH(22.0f, 22.0f, 6.0f, 6.0f),
573  {
574  .top_left = Size(1.0f, 2.0f),
575  .top_right = Size(3.0f, 4.0f),
576  .bottom_left = Size(5.0f, 6.0f),
577  .bottom_right = Size(7.0f, 8.0f),
578  }));
579 
580  // In this test, the arguments to the constructor supply the correctly
581  // adjusted radii (though there is no real way to tell other than
582  // the result is the same).
583  EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
584  Rect::MakeXYWH(22.0f, 22.0f, 6.0f, 6.0f),
585  {
586  .top_left = Size(0.5f, 1.0f),
587  .top_right = Size(1.5f, 2.0f),
588  .bottom_left = Size(2.5f, 3.0f),
589  .bottom_right = Size(3.5f, 4.0f),
590  }));
591 }
592 
593 TEST(RoundSuperellipseTest, NoCornerRoundSuperellipseContains) {
594  Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
595  // Rounded superellipses of bounds with no corners contains corners just
596  // barely.
597  auto no_corners = RoundSuperellipse::MakeRectRadii(
598  bounds, RoundingRadii::MakeRadii({0.0f, 0.0f}));
599 
600  EXPECT_TRUE(no_corners.Contains({-50, -50}));
601  // Rectangles have half-in, half-out containment so we need
602  // to be careful about testing containment of right/bottom corners.
603  EXPECT_TRUE(no_corners.Contains({-50, 49.99}));
604  EXPECT_TRUE(no_corners.Contains({49.99, -50}));
605  EXPECT_TRUE(no_corners.Contains({49.99, 49.99}));
606  EXPECT_FALSE(no_corners.Contains({-50.01, -50}));
607  EXPECT_FALSE(no_corners.Contains({-50, -50.01}));
608  EXPECT_FALSE(no_corners.Contains({-50.01, 50}));
609  EXPECT_FALSE(no_corners.Contains({-50, 50.01}));
610  EXPECT_FALSE(no_corners.Contains({50.01, -50}));
611  EXPECT_FALSE(no_corners.Contains({50, -50.01}));
612  EXPECT_FALSE(no_corners.Contains({50.01, 50}));
613  EXPECT_FALSE(no_corners.Contains({50, 50.01}));
614 }
615 
616 TEST(RoundSuperellipseTest, TinyCornerContains) {
617  Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
618  // Rounded superellipses of bounds with even the tiniest corners does not
619  // contain corners.
620  auto tiny_corners = RoundSuperellipse::MakeRectRadii(
621  bounds, RoundingRadii::MakeRadii({0.01f, 0.01f}));
622 
623  EXPECT_FALSE(tiny_corners.Contains({-50, -50}));
624  EXPECT_FALSE(tiny_corners.Contains({-50, 50}));
625  EXPECT_FALSE(tiny_corners.Contains({50, -50}));
626  EXPECT_FALSE(tiny_corners.Contains({50, 50}));
627 }
628 
629 TEST(RoundSuperellipseTest, UniformSquareContains) {
630  Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
632  bounds, RoundingRadii::MakeRadii({5.0f, 5.0f}));
633 
634 #define CHECK_POINT_AND_MIRRORS(p) \
635  CHECK_POINT_WITH_OFFSET(rr, (p), Point(0.02, 0.02)); \
636  CHECK_POINT_WITH_OFFSET(rr, (p) * Point(1, -1), Point(0.02, -0.02)); \
637  CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, 1), Point(-0.02, 0.02)); \
638  CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, -1), Point(-0.02, -0.02));
639 
640  CHECK_POINT_AND_MIRRORS(Point(0, 49.995)); // Top
641  CHECK_POINT_AND_MIRRORS(Point(44.245, 49.95)); // Top curve start
642  CHECK_POINT_AND_MIRRORS(Point(45.72, 49.87)); // Top joint
643  CHECK_POINT_AND_MIRRORS(Point(48.53, 48.53)); // Circular arc mid
644  CHECK_POINT_AND_MIRRORS(Point(49.87, 45.72)); // Right joint
645  CHECK_POINT_AND_MIRRORS(Point(49.95, 44.245)); // Right curve start
646  CHECK_POINT_AND_MIRRORS(Point(49.995, 0)); // Right
647 #undef CHECK_POINT_AND_MIRRORS
648 }
649 
650 TEST(RoundSuperellipseTest, UniformEllipticalContains) {
651  Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
653  bounds, RoundingRadii::MakeRadii({5.0f, 10.0f}));
654 
655 #define CHECK_POINT_AND_MIRRORS(p) \
656  CHECK_POINT_WITH_OFFSET(rr, (p), Point(0.02, 0.02)); \
657  CHECK_POINT_WITH_OFFSET(rr, (p) * Point(1, -1), Point(0.02, -0.02)); \
658  CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, 1), Point(-0.02, 0.02)); \
659  CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, -1), Point(-0.02, -0.02));
660 
661  CHECK_POINT_AND_MIRRORS(Point(0, 49.995)); // Top
662  CHECK_POINT_AND_MIRRORS(Point(44.245, 49.911)); // Top curve start
663  CHECK_POINT_AND_MIRRORS(Point(45.72, 49.75)); // Top joint
664  CHECK_POINT_AND_MIRRORS(Point(48.51, 47.07)); // Circular arc mid
665  CHECK_POINT_AND_MIRRORS(Point(49.87, 41.44)); // Right joint
666  CHECK_POINT_AND_MIRRORS(Point(49.95, 38.49)); // Right curve start
667  CHECK_POINT_AND_MIRRORS(Point(49.995, 0)); // Right
668 #undef CHECK_POINT_AND_MIRRORS
669 }
670 
671 TEST(RoundSuperellipseTest, UniformRectangularContains) {
672  // The bounds is not centered at the origin and has unequal height and width.
673  Rect bounds = Rect::MakeLTRB(0.0f, 0.0f, 50.0f, 100.0f);
675  bounds, RoundingRadii::MakeRadii({23.0f, 30.0f}));
676 
677  Point center = bounds.GetCenter();
678 #define CHECK_POINT_AND_MIRRORS(p) \
679  CHECK_POINT_WITH_OFFSET(rr, (p - center) * Point(1, 1) + center, \
680  Point(0.02, 0.02)); \
681  CHECK_POINT_WITH_OFFSET(rr, (p - center) * Point(1, -1) + center, \
682  Point(0.02, -0.02)); \
683  CHECK_POINT_WITH_OFFSET(rr, (p - center) * Point(-1, 1) + center, \
684  Point(-0.02, 0.02)); \
685  CHECK_POINT_WITH_OFFSET(rr, (p - center) * Point(-1, -1) + center, \
686  Point(-0.02, -0.02));
687 
688  CHECK_POINT_AND_MIRRORS(Point(24.99, 99.99)); // Bottom mid edge
689  CHECK_POINT_AND_MIRRORS(Point(29.99, 99.64));
690  CHECK_POINT_AND_MIRRORS(Point(34.99, 98.06));
691  CHECK_POINT_AND_MIRRORS(Point(39.99, 94.73));
692  CHECK_POINT_AND_MIRRORS(Point(44.13, 89.99));
693  CHECK_POINT_AND_MIRRORS(Point(48.46, 79.99));
694  CHECK_POINT_AND_MIRRORS(Point(49.70, 69.99));
695  CHECK_POINT_AND_MIRRORS(Point(49.97, 59.99));
696  CHECK_POINT_AND_MIRRORS(Point(49.99, 49.99)); // Right mid edge
697 
698 #undef CHECK_POINT_AND_MIRRORS
699 }
700 
701 TEST(RoundSuperellipseTest, SlimDiagonalContains) {
702  // This shape has large radii on one diagonal and tiny radii on the other,
703  // resulting in a almond-like shape placed diagonally (NW to SE).
704  Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
706  bounds, {
707  .top_left = Size(1.0, 1.0),
708  .top_right = Size(99.0, 99.0),
709  .bottom_left = Size(99.0, 99.0),
710  .bottom_right = Size(1.0, 1.0),
711  });
712 
713  EXPECT_TRUE(rr.Contains(Point{0, 0}));
714  EXPECT_FALSE(rr.Contains(Point{-49.999, -49.999}));
715  EXPECT_FALSE(rr.Contains(Point{-49.999, 49.999}));
716  EXPECT_FALSE(rr.Contains(Point{49.999, 49.999}));
717  EXPECT_FALSE(rr.Contains(Point{49.999, -49.999}));
718 
719  // The pointy ends at the NE and SW corners
720  CHECK_POINT_WITH_OFFSET(rr, Point(-49.70, -49.70), Point(-0.02, -0.02));
721  CHECK_POINT_WITH_OFFSET(rr, Point(49.70, 49.70), Point(0.02, 0.02));
722 
723 // Checks two points symmetrical to the origin.
724 #define CHECK_DIAGONAL_POINTS(p) \
725  CHECK_POINT_WITH_OFFSET(rr, (p), Point(0.02, -0.02)); \
726  CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, -1), Point(-0.02, 0.02));
727 
728  // A few other points along the edge
729  CHECK_DIAGONAL_POINTS(Point(-40.0, -49.59));
730  CHECK_DIAGONAL_POINTS(Point(-20.0, -45.64));
731  CHECK_DIAGONAL_POINTS(Point(0.0, -37.01));
732  CHECK_DIAGONAL_POINTS(Point(20.0, -21.96));
733  CHECK_DIAGONAL_POINTS(Point(21.05, -20.92));
734  CHECK_DIAGONAL_POINTS(Point(40.0, 5.68));
735 #undef CHECK_POINT_AND_MIRRORS
736 }
737 
738 TEST(RoundSuperellipseTest, PointsOutsideOfSharpCorner) {
739  Rect bounds = Rect::MakeLTRB(196.0f, 0.0f, 294.0f, 28.0f);
740  // Regression test for a case where RoundSuperellipseParam::Contains
741  // previously failed. Although the bounding rect filter of
742  // `RoundSuperellipse::Contains` would reject this point, this test ensures
743  // the internal logic of RoundSuperellipseParam::Contains is now correct.
745  bounds, {
746  .top_left = Size(0.0, 0.0),
747  .top_right = Size(3.0, 3.0),
748  .bottom_left = Size(0.0, 0.0),
749  .bottom_right = Size(3.0, 3.0),
750  });
751 
752  EXPECT_FALSE(rr.Contains(Point{147.0, 14.0}));
753 }
754 
755 TEST(RoundSuperellipseTest,
756  PathForRectangularRseWithShapeCornersShouldBeWithinBounds) {
757  Rect bounds = Rect::MakeLTRB(34.0f, 242.0f, 766.0f, 358.0f);
758  // Regression test for https://github.com/flutter/flutter/issues/170593.
759  // The issue was caused by incorrect calculation when building paths for
760  // rounded superellipses with sharp corners and unequal width and height.
761  // Since the most obvious symptom of the issue is some points being
762  // incorrectly placed out of bounds, this test case simply verifies that all
763  // points are within the bounds.
764 
766  bounds, {
767  .top_left = Size(14.0, 14.0),
768  .top_right = Size(14.0, 14.0),
769  .bottom_left = Size(0.0, 0.0),
770  .bottom_right = Size(0.0, 0.0),
771  });
772  SpyPathReceiver receiver;
773  receiver.SpyLineTo(
774  [&](const Point& p2) { EXPECT_TRUE(bounds.ContainsInclusive(p2)); });
775  receiver.SpyCubicTo([&](const Point& cp1, const Point& cp2, const Point& p2) {
776  EXPECT_TRUE(bounds.ContainsInclusive(p2));
777  });
778 
779  rr.Dispatch(receiver);
780 }
781 
782 } // namespace testing
783 } // namespace impeller
TEST(AllocationSizeTest, CanCreateTypedAllocations)
TPoint< Scalar > Point
Definition: point.h:327
TSize< Scalar > Size
Definition: size.h:159
void MoveTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:20
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:24
void CubicTo(PathBuilder *builder, Scalar x1, Scalar y1, Scalar x2, Scalar y2, Scalar x3, Scalar y3)
Definition: tessellator.cc:28
void Close(PathBuilder *builder)
Definition: tessellator.cc:38
#define CHECK_DIAGONAL_POINTS(p)
#define CHECK_POINT_WITH_OFFSET(rr, p, outward_offset)
#define CHECK_POINT_AND_MIRRORS(p)
constexpr const RoundingRadii & GetRadii() const
static RoundSuperellipse MakeRectRadii(const Rect &rect, const RoundingRadii &radii)
constexpr bool IsOval() const
constexpr bool IsFinite() const
constexpr bool IsEmpty() const
RoundSuperellipse Expand(Scalar left, Scalar top, Scalar right, Scalar bottom) const
Returns a round rectangle with expanded edges. Negative expansion results in shrinking.
constexpr bool IsRect() const
static RoundSuperellipse MakeOval(const Rect &rect)
RoundSuperellipse Shift(Scalar dx, Scalar dy) const
Returns a new round rectangle translated by the given offset.
static RoundSuperellipse MakeRect(const Rect &rect)
static RoundSuperellipse MakeRectRadius(const Rect &rect, Scalar radius)
constexpr const Rect & GetBounds() const
static RoundSuperellipse MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
static RoundSuperellipseParam MakeBoundsRadii(const Rect &bounds, const RoundingRadii &radii)
void Dispatch(PathReceiver &receiver) const
constexpr static RoundingRadii MakeRadii(Size radii)
constexpr auto GetBottom() const
Definition: rect.h:361
constexpr bool ContainsInclusive(const TPoint< Type > &p) const
Returns true iff the provided point |p| is inside the closed-range interior of this rectangle.
Definition: rect.h:254
constexpr auto GetTop() const
Definition: rect.h:357
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition: rect.h:301
constexpr auto GetLeft() const
Definition: rect.h:355
constexpr auto GetRight() const
Definition: rect.h:359
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition: rect.h:386
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
Type height
Definition: size.h:29
Type width
Definition: size.h:28