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