Flutter Engine
 
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 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
59namespace testing {
60
61TEST(RoundSuperellipseTest, EmptyDeclaration) {
62 RoundSuperellipse rse;
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
88TEST(RoundSuperellipseTest, DefaultConstructor) {
89 RoundSuperellipse rse = RoundSuperellipse();
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
103TEST(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
119TEST(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
135TEST(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
151TEST(RoundSuperellipseTest, EmptyOvalConstruction) {
152 RoundSuperellipse rse = RoundSuperellipse::MakeRectXY(
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
167TEST(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
183TEST(RoundSuperellipseTest, InvertedOvalConstruction) {
184 RoundSuperellipse rse = RoundSuperellipse::MakeRectXY(
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
199TEST(RoundSuperellipseTest, RectRadiusConstructor) {
200 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadius(
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
215TEST(RoundSuperellipseTest, RectXYConstructor) {
216 RoundSuperellipse rse = RoundSuperellipse::MakeRectXY(
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
231TEST(RoundSuperellipseTest, RectSizeConstructor) {
232 RoundSuperellipse rse = RoundSuperellipse::MakeRectXY(
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
247TEST(RoundSuperellipseTest, RectRadiiConstructor) {
248 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
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
269TEST(RoundSuperellipseTest, RectRadiiOverflowWidthConstructor) {
270 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
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
294TEST(RoundSuperellipseTest, RectRadiiOverflowHeightConstructor) {
295 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
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
319TEST(RoundSuperellipseTest, Shift) {
320 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
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
351TEST(RoundSuperellipseTest, ExpandScalar) {
352 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
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
383TEST(RoundSuperellipseTest, ExpandTwoScalars) {
384 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
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
415TEST(RoundSuperellipseTest, ExpandFourScalars) {
416 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
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
447TEST(RoundSuperellipseTest, ContractScalar) {
448 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
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
479TEST(RoundSuperellipseTest, ContractTwoScalars) {
480 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
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
511TEST(RoundSuperellipseTest, ContractFourScalars) {
512 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
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
543TEST(RoundSuperellipseTest, ContractAndRequireRadiiAdjustment) {
544 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
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
593TEST(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
616TEST(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
629TEST(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
650TEST(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
671TEST(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
701TEST(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
738TEST(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
755TEST(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(FrameTimingsRecorderTest, RecordVsync)
TPoint< Scalar > Point
Definition point.h:327
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 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