Flutter Engine
 
Loading...
Searching...
No Matches
round_superellipse_unittests.cc File Reference

Go to the source code of this file.

Namespaces

namespace  impeller
 
namespace  impeller::testing
 

Macros

#define CHECK_POINT_WITH_OFFSET(rr, p, outward_offset)
 
#define CHECK_POINT_AND_MIRRORS(p)
 
#define CHECK_POINT_AND_MIRRORS(p)
 
#define CHECK_POINT_AND_MIRRORS(p)
 
#define CHECK_DIAGONAL_POINTS(p)
 

Macro Definition Documentation

◆ CHECK_DIAGONAL_POINTS

#define CHECK_DIAGONAL_POINTS (   p)
Value:
CHECK_POINT_WITH_OFFSET(rr, (p), Point(0.02, -0.02)); \
CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, -1), Point(-0.02, 0.02));
#define CHECK_POINT_WITH_OFFSET(rr, p, outward_offset)

◆ CHECK_POINT_AND_MIRRORS [1/3]

#define CHECK_POINT_AND_MIRRORS (   p)
Value:
CHECK_POINT_WITH_OFFSET(rr, (p), Point(0.02, 0.02)); \
CHECK_POINT_WITH_OFFSET(rr, (p) * Point(1, -1), Point(0.02, -0.02)); \
CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, 1), Point(-0.02, 0.02)); \
CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, -1), Point(-0.02, -0.02));

◆ CHECK_POINT_AND_MIRRORS [2/3]

#define CHECK_POINT_AND_MIRRORS (   p)
Value:
CHECK_POINT_WITH_OFFSET(rr, (p), Point(0.02, 0.02)); \
CHECK_POINT_WITH_OFFSET(rr, (p) * Point(1, -1), Point(0.02, -0.02)); \
CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, 1), Point(-0.02, 0.02)); \
CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, -1), Point(-0.02, -0.02));

◆ CHECK_POINT_AND_MIRRORS [3/3]

#define CHECK_POINT_AND_MIRRORS (   p)
Value:
CHECK_POINT_WITH_OFFSET(rr, (p - center) * Point(1, 1) + center, \
Point(0.02, 0.02)); \
CHECK_POINT_WITH_OFFSET(rr, (p - center) * Point(1, -1) + center, \
Point(0.02, -0.02)); \
CHECK_POINT_WITH_OFFSET(rr, (p - center) * Point(-1, 1) + center, \
Point(-0.02, 0.02)); \
CHECK_POINT_WITH_OFFSET(rr, (p - center) * Point(-1, -1) + center, \
Point(-0.02, -0.02));

◆ CHECK_POINT_WITH_OFFSET

#define CHECK_POINT_WITH_OFFSET (   rr,
  p,
  outward_offset 
)
Value:
EXPECT_TRUE(rr.Contains(p)); \
EXPECT_FALSE(rr.Contains(p + outward_offset));

Definition at line 12 of file round_superellipse_unittests.cc.

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