| Flutter Impeller |

round_superellipse_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "gtest/gtest.h"
6
7 #include "flutter/impeller/geometry/round_superellipse.h"
8
9 #include "flutter/impeller/geometry/geometry_asserts.h"
10
11 #define CHECK_POINT_WITH_OFFSET(rr, p, outward_offset) \
12 EXPECT_TRUE(rr.Contains(p)); \
13 EXPECT_FALSE(rr.Contains(p + outward_offset));
14
15 namespace impeller {
16 namespace testing {
17
18 TEST(RoundSuperellipseTest, EmptyDeclaration) {
19 RoundSuperellipse rse;
20
21 EXPECT_TRUE(rse.IsEmpty());
22 EXPECT_FALSE(rse.IsRect());
23 EXPECT_FALSE(rse.IsOval());
24 EXPECT_TRUE(rse.IsFinite());
25 EXPECT_TRUE(rse.GetBounds().IsEmpty());
26 EXPECT_EQ(rse.GetBounds(), Rect());
27 EXPECT_EQ(rse.GetBounds().GetLeft(), 0.0f);
28 EXPECT_EQ(rse.GetBounds().GetTop(), 0.0f);
29 EXPECT_EQ(rse.GetBounds().GetRight(), 0.0f);
30 EXPECT_EQ(rse.GetBounds().GetBottom(), 0.0f);
31 EXPECT_EQ(rse.GetRadii().top_left, Size());
32 EXPECT_EQ(rse.GetRadii().top_right, Size());
33 EXPECT_EQ(rse.GetRadii().bottom_left, Size());
34 EXPECT_EQ(rse.GetRadii().bottom_right, Size());
35 EXPECT_EQ(rse.GetRadii().top_left.width, 0.0f);
36 EXPECT_EQ(rse.GetRadii().top_left.height, 0.0f);
37 EXPECT_EQ(rse.GetRadii().top_right.width, 0.0f);
38 EXPECT_EQ(rse.GetRadii().top_right.height, 0.0f);
39 EXPECT_EQ(rse.GetRadii().bottom_left.width, 0.0f);
40 EXPECT_EQ(rse.GetRadii().bottom_left.height, 0.0f);
41 EXPECT_EQ(rse.GetRadii().bottom_right.width, 0.0f);
42 EXPECT_EQ(rse.GetRadii().bottom_right.height, 0.0f);
43 }
44
45 TEST(RoundSuperellipseTest, DefaultConstructor) {
46 RoundSuperellipse rse = RoundSuperellipse();
47
48 EXPECT_TRUE(rse.IsEmpty());
49 EXPECT_FALSE(rse.IsRect());
50 EXPECT_FALSE(rse.IsOval());
51 EXPECT_TRUE(rse.IsFinite());
52 EXPECT_TRUE(rse.GetBounds().IsEmpty());
53 EXPECT_EQ(rse.GetBounds(), Rect());
54 EXPECT_EQ(rse.GetRadii().top_left, Size());
55 EXPECT_EQ(rse.GetRadii().top_right, Size());
56 EXPECT_EQ(rse.GetRadii().bottom_left, Size());
57 EXPECT_EQ(rse.GetRadii().bottom_right, Size());
58 }
59
60 TEST(RoundSuperellipseTest, EmptyRectConstruction) {
61 RoundSuperellipse rse =
62 RoundSuperellipse::MakeRect(Rect::MakeLTRB(20.0f, 20.0f, 20.0f, 20.0f));
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::MakeLTRB(20.0f, 20.0f, 20.0f, 20.0f));
70 EXPECT_EQ(rse.GetRadii().top_left, Size());
71 EXPECT_EQ(rse.GetRadii().top_right, Size());
72 EXPECT_EQ(rse.GetRadii().bottom_left, Size());
73 EXPECT_EQ(rse.GetRadii().bottom_right, Size());
74 }
75
76 TEST(RoundSuperellipseTest, RectConstructor) {
77 RoundSuperellipse rse =
78 RoundSuperellipse::MakeRect(Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
79
80 EXPECT_FALSE(rse.IsEmpty());
81 EXPECT_TRUE(rse.IsRect());
82 EXPECT_FALSE(rse.IsOval());
83 EXPECT_TRUE(rse.IsFinite());
84 EXPECT_FALSE(rse.GetBounds().IsEmpty());
85 EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.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 }
91
92 TEST(RoundSuperellipseTest, InvertedRectConstruction) {
93 RoundSuperellipse rse =
94 RoundSuperellipse::MakeRect(Rect::MakeLTRB(20.0f, 20.0f, 10.0f, 10.0f));
95
96 EXPECT_FALSE(rse.IsEmpty());
97 EXPECT_TRUE(rse.IsRect());
98 EXPECT_FALSE(rse.IsOval());
99 EXPECT_TRUE(rse.IsFinite());
100 EXPECT_FALSE(rse.GetBounds().IsEmpty());
101 EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
102 EXPECT_EQ(rse.GetRadii().top_left, Size());
103 EXPECT_EQ(rse.GetRadii().top_right, Size());
104 EXPECT_EQ(rse.GetRadii().bottom_left, Size());
105 EXPECT_EQ(rse.GetRadii().bottom_right, Size());
106 }
107
108 TEST(RoundSuperellipseTest, EmptyOvalConstruction) {
109 RoundSuperellipse rse = RoundSuperellipse::MakeRectXY(
110 Rect::MakeLTRB(20.0f, 20.0f, 20.0f, 20.0f), 10.0f, 10.0f);
111
112 EXPECT_TRUE(rse.IsEmpty());
113 EXPECT_FALSE(rse.IsRect());
114 EXPECT_FALSE(rse.IsOval());
115 EXPECT_TRUE(rse.IsFinite());
116 EXPECT_TRUE(rse.GetBounds().IsEmpty());
117 EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(20.0f, 20.0f, 20.0f, 20.0f));
118 EXPECT_EQ(rse.GetRadii().top_left, Size());
119 EXPECT_EQ(rse.GetRadii().top_right, Size());
120 EXPECT_EQ(rse.GetRadii().bottom_left, Size());
121 EXPECT_EQ(rse.GetRadii().bottom_right, Size());
122 }
123
124 TEST(RoundSuperellipseTest, OvalConstructor) {
125 RoundSuperellipse rse =
126 RoundSuperellipse::MakeOval(Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
127
128 EXPECT_FALSE(rse.IsEmpty());
129 EXPECT_FALSE(rse.IsRect());
130 EXPECT_TRUE(rse.IsOval());
131 EXPECT_TRUE(rse.IsFinite());
132 EXPECT_FALSE(rse.GetBounds().IsEmpty());
133 EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
134 EXPECT_EQ(rse.GetRadii().top_left, Size(5.0f, 5.0f));
135 EXPECT_EQ(rse.GetRadii().top_right, Size(5.0f, 5.0f));
136 EXPECT_EQ(rse.GetRadii().bottom_left, Size(5.0f, 5.0f));
137 EXPECT_EQ(rse.GetRadii().bottom_right, Size(5.0f, 5.0f));
138 }
139
140 TEST(RoundSuperellipseTest, InvertedOvalConstruction) {
141 RoundSuperellipse rse = RoundSuperellipse::MakeRectXY(
142 Rect::MakeLTRB(20.0f, 20.0f, 10.0f, 10.0f), 10.0f, 10.0f);
143
144 EXPECT_FALSE(rse.IsEmpty());
145 EXPECT_FALSE(rse.IsRect());
146 EXPECT_TRUE(rse.IsOval());
147 EXPECT_TRUE(rse.IsFinite());
148 EXPECT_FALSE(rse.GetBounds().IsEmpty());
149 EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
150 EXPECT_EQ(rse.GetRadii().top_left, Size(5.0f, 5.0f));
151 EXPECT_EQ(rse.GetRadii().top_right, Size(5.0f, 5.0f));
152 EXPECT_EQ(rse.GetRadii().bottom_left, Size(5.0f, 5.0f));
153 EXPECT_EQ(rse.GetRadii().bottom_right, Size(5.0f, 5.0f));
154 }
155
156 TEST(RoundSuperellipseTest, RectRadiusConstructor) {
157 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadius(
158 Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), 2.0f);
159
160 EXPECT_FALSE(rse.IsEmpty());
161 EXPECT_FALSE(rse.IsRect());
162 EXPECT_FALSE(rse.IsOval());
163 EXPECT_TRUE(rse.IsFinite());
164 EXPECT_FALSE(rse.GetBounds().IsEmpty());
165 EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
166 EXPECT_EQ(rse.GetRadii().top_left, Size(2.0f, 2.0f));
167 EXPECT_EQ(rse.GetRadii().top_right, Size(2.0f, 2.0f));
168 EXPECT_EQ(rse.GetRadii().bottom_left, Size(2.0f, 2.0f));
169 EXPECT_EQ(rse.GetRadii().bottom_right, Size(2.0f, 2.0f));
170 }
171
172 TEST(RoundSuperellipseTest, RectXYConstructor) {
173 RoundSuperellipse rse = RoundSuperellipse::MakeRectXY(
174 Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), 2.0f, 3.0f);
175
176 EXPECT_FALSE(rse.IsEmpty());
177 EXPECT_FALSE(rse.IsRect());
178 EXPECT_FALSE(rse.IsOval());
179 EXPECT_TRUE(rse.IsFinite());
180 EXPECT_FALSE(rse.GetBounds().IsEmpty());
181 EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
182 EXPECT_EQ(rse.GetRadii().top_left, Size(2.0f, 3.0f));
183 EXPECT_EQ(rse.GetRadii().top_right, Size(2.0f, 3.0f));
184 EXPECT_EQ(rse.GetRadii().bottom_left, Size(2.0f, 3.0f));
185 EXPECT_EQ(rse.GetRadii().bottom_right, Size(2.0f, 3.0f));
186 }
187
188 TEST(RoundSuperellipseTest, RectSizeConstructor) {
189 RoundSuperellipse rse = RoundSuperellipse::MakeRectXY(
190 Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f), Size(2.0f, 3.0f));
191
192 EXPECT_FALSE(rse.IsEmpty());
193 EXPECT_FALSE(rse.IsRect());
194 EXPECT_FALSE(rse.IsOval());
195 EXPECT_TRUE(rse.IsFinite());
196 EXPECT_FALSE(rse.GetBounds().IsEmpty());
197 EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
198 EXPECT_EQ(rse.GetRadii().top_left, Size(2.0f, 3.0f));
199 EXPECT_EQ(rse.GetRadii().top_right, Size(2.0f, 3.0f));
200 EXPECT_EQ(rse.GetRadii().bottom_left, Size(2.0f, 3.0f));
201 EXPECT_EQ(rse.GetRadii().bottom_right, Size(2.0f, 3.0f));
202 }
203
204 TEST(RoundSuperellipseTest, RectRadiiConstructor) {
205 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
206 Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f),
207 {
208 .top_left = Size(1.0, 1.5),
209 .top_right = Size(2.0, 2.5f),
210 .bottom_left = Size(3.0, 3.5f),
211 .bottom_right = Size(4.0, 4.5f),
212 });
213
214 EXPECT_FALSE(rse.IsEmpty());
215 EXPECT_FALSE(rse.IsRect());
216 EXPECT_FALSE(rse.IsOval());
217 EXPECT_TRUE(rse.IsFinite());
218 EXPECT_FALSE(rse.GetBounds().IsEmpty());
219 EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 20.0f, 20.0f));
220 EXPECT_EQ(rse.GetRadii().top_left, Size(1.0f, 1.5f));
221 EXPECT_EQ(rse.GetRadii().top_right, Size(2.0f, 2.5f));
222 EXPECT_EQ(rse.GetRadii().bottom_left, Size(3.0f, 3.5f));
223 EXPECT_EQ(rse.GetRadii().bottom_right, Size(4.0f, 4.5f));
224 }
225
226 TEST(RoundSuperellipseTest, RectRadiiOverflowWidthConstructor) {
227 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
228 Rect::MakeXYWH(10.0f, 10.0f, 6.0f, 30.0f),
229 {
230 .top_left = Size(1.0f, 2.0f),
231 .top_right = Size(3.0f, 4.0f),
232 .bottom_left = Size(5.0f, 6.0f),
233 .bottom_right = Size(7.0f, 8.0f),
234 });
235 // Largest sum of paired radii widths is the bottom edge which sums to 12
236 // Rect is only 6 wide so all radii are scaled by half
237 // Rect is 30 tall so no scaling should happen due to radii heights
238
239 EXPECT_FALSE(rse.IsEmpty());
240 EXPECT_FALSE(rse.IsRect());
241 EXPECT_FALSE(rse.IsOval());
242 EXPECT_TRUE(rse.IsFinite());
243 EXPECT_FALSE(rse.GetBounds().IsEmpty());
244 EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 16.0f, 40.0f));
245 EXPECT_EQ(rse.GetRadii().top_left, Size(0.5f, 1.0f));
246 EXPECT_EQ(rse.GetRadii().top_right, Size(1.5f, 2.0f));
247 EXPECT_EQ(rse.GetRadii().bottom_left, Size(2.5f, 3.0f));
248 EXPECT_EQ(rse.GetRadii().bottom_right, Size(3.5f, 4.0f));
249 }
250
251 TEST(RoundSuperellipseTest, RectRadiiOverflowHeightConstructor) {
252 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
253 Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 6.0f),
254 {
255 .top_left = Size(1.0f, 2.0f),
256 .top_right = Size(3.0f, 4.0f),
257 .bottom_left = Size(5.0f, 6.0f),
258 .bottom_right = Size(7.0f, 8.0f),
259 });
260 // Largest sum of paired radii heights is the right edge which sums to 12
261 // Rect is only 6 tall so all radii are scaled by half
262 // Rect is 30 wide so no scaling should happen due to radii widths
263
264 EXPECT_FALSE(rse.IsEmpty());
265 EXPECT_FALSE(rse.IsRect());
266 EXPECT_FALSE(rse.IsOval());
267 EXPECT_TRUE(rse.IsFinite());
268 EXPECT_FALSE(rse.GetBounds().IsEmpty());
269 EXPECT_EQ(rse.GetBounds(), Rect::MakeLTRB(10.0f, 10.0f, 40.0f, 16.0f));
270 EXPECT_EQ(rse.GetRadii().top_left, Size(0.5f, 1.0f));
271 EXPECT_EQ(rse.GetRadii().top_right, Size(1.5f, 2.0f));
272 EXPECT_EQ(rse.GetRadii().bottom_left, Size(2.5f, 3.0f));
273 EXPECT_EQ(rse.GetRadii().bottom_right, Size(3.5f, 4.0f));
274 }
275
276 TEST(RoundSuperellipseTest, Shift) {
277 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
278 Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f),
279 {
280 .top_left = Size(1.0f, 2.0f),
281 .top_right = Size(3.0f, 4.0f),
282 .bottom_left = Size(5.0f, 6.0f),
283 .bottom_right = Size(7.0f, 8.0f),
284 });
285 RoundSuperellipse shifted = rse.Shift(5.0, 6.0);
286
287 EXPECT_FALSE(shifted.IsEmpty());
288 EXPECT_FALSE(shifted.IsRect());
289 EXPECT_FALSE(shifted.IsOval());
290 EXPECT_TRUE(shifted.IsFinite());
291 EXPECT_FALSE(shifted.GetBounds().IsEmpty());
292 EXPECT_EQ(shifted.GetBounds(), Rect::MakeLTRB(15.0f, 16.0f, 45.0f, 46.0f));
293 EXPECT_EQ(shifted.GetRadii().top_left, Size(1.0f, 2.0f));
294 EXPECT_EQ(shifted.GetRadii().top_right, Size(3.0f, 4.0f));
295 EXPECT_EQ(shifted.GetRadii().bottom_left, Size(5.0f, 6.0f));
296 EXPECT_EQ(shifted.GetRadii().bottom_right, Size(7.0f, 8.0f));
297
298 EXPECT_EQ(shifted, RoundSuperellipse::MakeRectRadii(
299 Rect::MakeXYWH(15.0f, 16.0f, 30.0f, 30.0f),
300 {
301 .top_left = Size(1.0f, 2.0f),
302 .top_right = Size(3.0f, 4.0f),
303 .bottom_left = Size(5.0f, 6.0f),
304 .bottom_right = Size(7.0f, 8.0f),
305 }));
306 }
307
308 TEST(RoundSuperellipseTest, ExpandScalar) {
309 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
310 Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.0f),
311 {
312 .top_left = Size(1.0f, 2.0f),
313 .top_right = Size(3.0f, 4.0f),
314 .bottom_left = Size(5.0f, 6.0f),
315 .bottom_right = Size(7.0f, 8.0f),
316 });
317 RoundSuperellipse expanded = rse.Expand(5.0);
318
319 EXPECT_FALSE(expanded.IsEmpty());
320 EXPECT_FALSE(expanded.IsRect());
321 EXPECT_FALSE(expanded.IsOval());
322 EXPECT_TRUE(expanded.IsFinite());
323 EXPECT_FALSE(expanded.GetBounds().IsEmpty());
324 EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(5.0f, 5.0f, 45.0f, 45.0f));
325 EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f));
326 EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f));
327 EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f));
328 EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f));
329
330 EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
331 Rect::MakeXYWH(5.0f, 5.0f, 40.0f, 40.0f),
332 {
333 .top_left = Size(1.0f, 2.0f),
334 .top_right = Size(3.0f, 4.0f),
335 .bottom_left = Size(5.0f, 6.0f),
336 .bottom_right = Size(7.0f, 8.0f),
337 }));
338 }
339
340 TEST(RoundSuperellipseTest, ExpandTwoScalars) {
341 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
342 Rect::MakeXYWH(10.0f, 10.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 RoundSuperellipse expanded = rse.Expand(5.0, 6.0);
350
351 EXPECT_FALSE(expanded.IsEmpty());
352 EXPECT_FALSE(expanded.IsRect());
353 EXPECT_FALSE(expanded.IsOval());
354 EXPECT_TRUE(expanded.IsFinite());
355 EXPECT_FALSE(expanded.GetBounds().IsEmpty());
356 EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(5.0f, 4.0f, 45.0f, 46.0f));
357 EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f));
358 EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f));
359 EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f));
360 EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f));
361
362 EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
363 Rect::MakeXYWH(5.0f, 4.0f, 40.0f, 42.0f),
364 {
365 .top_left = Size(1.0f, 2.0f),
366 .top_right = Size(3.0f, 4.0f),
367 .bottom_left = Size(5.0f, 6.0f),
368 .bottom_right = Size(7.0f, 8.0f),
369 }));
370 }
371
372 TEST(RoundSuperellipseTest, ExpandFourScalars) {
373 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
374 Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.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 RoundSuperellipse expanded = rse.Expand(5.0, 6.0, 7.0, 8.0);
382
383 EXPECT_FALSE(expanded.IsEmpty());
384 EXPECT_FALSE(expanded.IsRect());
385 EXPECT_FALSE(expanded.IsOval());
386 EXPECT_TRUE(expanded.IsFinite());
387 EXPECT_FALSE(expanded.GetBounds().IsEmpty());
388 EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(5.0f, 4.0f, 47.0f, 48.0f));
389 EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f));
390 EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f));
391 EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f));
392 EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f));
393
394 EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
395 Rect::MakeXYWH(5.0f, 4.0f, 42.0f, 44.0f),
396 {
397 .top_left = Size(1.0f, 2.0f),
398 .top_right = Size(3.0f, 4.0f),
399 .bottom_left = Size(5.0f, 6.0f),
400 .bottom_right = Size(7.0f, 8.0f),
401 }));
402 }
403
404 TEST(RoundSuperellipseTest, ContractScalar) {
405 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
406 Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.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 RoundSuperellipse expanded = rse.Expand(-2.0);
414
415 EXPECT_FALSE(expanded.IsEmpty());
416 EXPECT_FALSE(expanded.IsRect());
417 EXPECT_FALSE(expanded.IsOval());
418 EXPECT_TRUE(expanded.IsFinite());
419 EXPECT_FALSE(expanded.GetBounds().IsEmpty());
420 EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(12.0f, 12.0f, 38.0f, 38.0f));
421 EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f));
422 EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f));
423 EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f));
424 EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f));
425
426 EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
427 Rect::MakeXYWH(12.0f, 12.0f, 26.0f, 26.0f),
428 {
429 .top_left = Size(1.0f, 2.0f),
430 .top_right = Size(3.0f, 4.0f),
431 .bottom_left = Size(5.0f, 6.0f),
432 .bottom_right = Size(7.0f, 8.0f),
433 }));
434 }
435
436 TEST(RoundSuperellipseTest, ContractTwoScalars) {
437 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
438 Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.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 RoundSuperellipse expanded = rse.Expand(-1.0, -2.0);
446
447 EXPECT_FALSE(expanded.IsEmpty());
448 EXPECT_FALSE(expanded.IsRect());
449 EXPECT_FALSE(expanded.IsOval());
450 EXPECT_TRUE(expanded.IsFinite());
451 EXPECT_FALSE(expanded.GetBounds().IsEmpty());
452 EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(11.0f, 12.0f, 39.0f, 38.0f));
453 EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f));
454 EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f));
455 EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f));
456 EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f));
457
458 EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
459 Rect::MakeXYWH(11.0f, 12.0f, 28.0f, 26.0f),
460 {
461 .top_left = Size(1.0f, 2.0f),
462 .top_right = Size(3.0f, 4.0f),
463 .bottom_left = Size(5.0f, 6.0f),
464 .bottom_right = Size(7.0f, 8.0f),
465 }));
466 }
467
468 TEST(RoundSuperellipseTest, ContractFourScalars) {
469 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
470 Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.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 RoundSuperellipse expanded = rse.Expand(-1.0, -1.5, -2.0, -2.5);
478
479 EXPECT_FALSE(expanded.IsEmpty());
480 EXPECT_FALSE(expanded.IsRect());
481 EXPECT_FALSE(expanded.IsOval());
482 EXPECT_TRUE(expanded.IsFinite());
483 EXPECT_FALSE(expanded.GetBounds().IsEmpty());
484 EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(11.0f, 11.5f, 38.0f, 37.5f));
485 EXPECT_EQ(expanded.GetRadii().top_left, Size(1.0f, 2.0f));
486 EXPECT_EQ(expanded.GetRadii().top_right, Size(3.0f, 4.0f));
487 EXPECT_EQ(expanded.GetRadii().bottom_left, Size(5.0f, 6.0f));
488 EXPECT_EQ(expanded.GetRadii().bottom_right, Size(7.0f, 8.0f));
489
490 EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
491 Rect::MakeXYWH(11.0f, 11.5f, 27.0f, 26.0f),
492 {
493 .top_left = Size(1.0f, 2.0f),
494 .top_right = Size(3.0f, 4.0f),
495 .bottom_left = Size(5.0f, 6.0f),
496 .bottom_right = Size(7.0f, 8.0f),
497 }));
498 }
499
500 TEST(RoundSuperellipseTest, ContractAndRequireRadiiAdjustment) {
501 RoundSuperellipse rse = RoundSuperellipse::MakeRectRadii(
502 Rect::MakeXYWH(10.0f, 10.0f, 30.0f, 30.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 RoundSuperellipse expanded = rse.Expand(-12.0);
510 // Largest sum of paired radii sizes are the bottom and right edges
511 // both of which sum to 12
512 // Rect was 30x30 reduced by 12 on all sides leaving only 6x6, so all
513 // radii are scaled by half to avoid overflowing the contracted rect
514
515 EXPECT_FALSE(expanded.IsEmpty());
516 EXPECT_FALSE(expanded.IsRect());
517 EXPECT_FALSE(expanded.IsOval());
518 EXPECT_TRUE(expanded.IsFinite());
519 EXPECT_FALSE(expanded.GetBounds().IsEmpty());
520 EXPECT_EQ(expanded.GetBounds(), Rect::MakeLTRB(22.0f, 22.0f, 28.0f, 28.0f));
521 EXPECT_EQ(expanded.GetRadii().top_left, Size(0.5f, 1.0f));
522 EXPECT_EQ(expanded.GetRadii().top_right, Size(1.5f, 2.0f));
523 EXPECT_EQ(expanded.GetRadii().bottom_left, Size(2.5f, 3.0f));
524 EXPECT_EQ(expanded.GetRadii().bottom_right, Size(3.5f, 4.0f));
525
526 // In this test, the MakeRectRadii constructor will make the same
527 // adjustment to the radii that the Expand method applied.
528 EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
529 Rect::MakeXYWH(22.0f, 22.0f, 6.0f, 6.0f),
530 {
531 .top_left = Size(1.0f, 2.0f),
532 .top_right = Size(3.0f, 4.0f),
533 .bottom_left = Size(5.0f, 6.0f),
534 .bottom_right = Size(7.0f, 8.0f),
535 }));
536
537 // In this test, the arguments to the constructor supply the correctly
538 // adjusted radii (though there is no real way to tell other than
539 // the result is the same).
540 EXPECT_EQ(expanded, RoundSuperellipse::MakeRectRadii(
541 Rect::MakeXYWH(22.0f, 22.0f, 6.0f, 6.0f),
542 {
543 .top_left = Size(0.5f, 1.0f),
544 .top_right = Size(1.5f, 2.0f),
545 .bottom_left = Size(2.5f, 3.0f),
546 .bottom_right = Size(3.5f, 4.0f),
547 }));
548 }
549
550 TEST(RoundSuperellipseTest, NoCornerRoundSuperellipseContains) {
551 Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
552 // Rounded superellipses of bounds with no corners contains corners just
553 // barely.
554 auto no_corners = RoundSuperellipse::MakeRectRadii(
555 bounds, RoundingRadii::MakeRadii({0.0f, 0.0f}));
556
557 EXPECT_TRUE(no_corners.Contains({-50, -50}));
558 // Rectangles have half-in, half-out containment so we need
559 // to be careful about testing containment of right/bottom corners.
560 EXPECT_TRUE(no_corners.Contains({-50, 49.99}));
561 EXPECT_TRUE(no_corners.Contains({49.99, -50}));
562 EXPECT_TRUE(no_corners.Contains({49.99, 49.99}));
563 EXPECT_FALSE(no_corners.Contains({-50.01, -50}));
564 EXPECT_FALSE(no_corners.Contains({-50, -50.01}));
565 EXPECT_FALSE(no_corners.Contains({-50.01, 50}));
566 EXPECT_FALSE(no_corners.Contains({-50, 50.01}));
567 EXPECT_FALSE(no_corners.Contains({50.01, -50}));
568 EXPECT_FALSE(no_corners.Contains({50, -50.01}));
569 EXPECT_FALSE(no_corners.Contains({50.01, 50}));
570 EXPECT_FALSE(no_corners.Contains({50, 50.01}));
571 }
572
573 TEST(RoundSuperellipseTest, TinyCornerContains) {
574 Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
575 // Rounded superellipses of bounds with even the tiniest corners does not
576 // contain corners.
577 auto tiny_corners = RoundSuperellipse::MakeRectRadii(
578 bounds, RoundingRadii::MakeRadii({0.01f, 0.01f}));
579
580 EXPECT_FALSE(tiny_corners.Contains({-50, -50}));
581 EXPECT_FALSE(tiny_corners.Contains({-50, 50}));
582 EXPECT_FALSE(tiny_corners.Contains({50, -50}));
583 EXPECT_FALSE(tiny_corners.Contains({50, 50}));
584 }
585
586 TEST(RoundSuperellipseTest, UniformSquareContains) {
587 Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
588 auto rr = RoundSuperellipse::MakeRectRadii(
589 bounds, RoundingRadii::MakeRadii({5.0f, 5.0f}));
590
591 #define CHECK_POINT_AND_MIRRORS(p) \
592 CHECK_POINT_WITH_OFFSET(rr, (p), Point(0.02, 0.02)); \
593 CHECK_POINT_WITH_OFFSET(rr, (p) * Point(1, -1), Point(0.02, -0.02)); \
594 CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, 1), Point(-0.02, 0.02)); \
595 CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, -1), Point(-0.02, -0.02));
596
597 CHECK_POINT_AND_MIRRORS(Point(0, 49.995)); // Top
598 CHECK_POINT_AND_MIRRORS(Point(44.245, 49.95)); // Top curve start
599 CHECK_POINT_AND_MIRRORS(Point(45.72, 49.87)); // Top joint
600 CHECK_POINT_AND_MIRRORS(Point(48.53, 48.53)); // Circular arc mid
601 CHECK_POINT_AND_MIRRORS(Point(49.87, 45.72)); // Right joint
602 CHECK_POINT_AND_MIRRORS(Point(49.95, 44.245)); // Right curve start
603 CHECK_POINT_AND_MIRRORS(Point(49.995, 0)); // Right
604 #undef CHECK_POINT_AND_MIRRORS
605 }
606
607 TEST(RoundSuperellipseTest, UniformEllipticalContains) {
608 Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
609 auto rr = RoundSuperellipse::MakeRectRadii(
610 bounds, RoundingRadii::MakeRadii({5.0f, 10.0f}));
611
612 #define CHECK_POINT_AND_MIRRORS(p) \
613 CHECK_POINT_WITH_OFFSET(rr, (p), Point(0.02, 0.02)); \
614 CHECK_POINT_WITH_OFFSET(rr, (p) * Point(1, -1), Point(0.02, -0.02)); \
615 CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, 1), Point(-0.02, 0.02)); \
616 CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, -1), Point(-0.02, -0.02));
617
618 CHECK_POINT_AND_MIRRORS(Point(0, 49.995)); // Top
619 CHECK_POINT_AND_MIRRORS(Point(44.245, 49.911)); // Top curve start
620 CHECK_POINT_AND_MIRRORS(Point(45.72, 49.75)); // Top joint
621 CHECK_POINT_AND_MIRRORS(Point(48.51, 47.07)); // Circular arc mid
622 CHECK_POINT_AND_MIRRORS(Point(49.87, 41.44)); // Right joint
623 CHECK_POINT_AND_MIRRORS(Point(49.95, 38.49)); // Right curve start
624 CHECK_POINT_AND_MIRRORS(Point(49.995, 0)); // Right
625 #undef CHECK_POINT_AND_MIRRORS
626 }
627
628 TEST(RoundSuperellipseTest, UniformRectangularContains) {
629 // The bounds is not centered at the origin and has unequal height and width.
630 Rect bounds = Rect::MakeLTRB(0.0f, 0.0f, 50.0f, 100.0f);
631 auto rr = RoundSuperellipse::MakeRectRadii(
632 bounds, RoundingRadii::MakeRadii({23.0f, 30.0f}));
633
634 Point center = bounds.GetCenter();
635 #define CHECK_POINT_AND_MIRRORS(p) \
636 CHECK_POINT_WITH_OFFSET(rr, (p - center) * Point(1, 1) + center, \
637 Point(0.02, 0.02)); \
638 CHECK_POINT_WITH_OFFSET(rr, (p - center) * Point(1, -1) + center, \
639 Point(0.02, -0.02)); \
640 CHECK_POINT_WITH_OFFSET(rr, (p - center) * Point(-1, 1) + center, \
641 Point(-0.02, 0.02)); \
642 CHECK_POINT_WITH_OFFSET(rr, (p - center) * Point(-1, -1) + center, \
643 Point(-0.02, -0.02));
644
645 CHECK_POINT_AND_MIRRORS(Point(24.99, 99.99)); // Bottom mid edge
646 CHECK_POINT_AND_MIRRORS(Point(29.99, 99.64));
647 CHECK_POINT_AND_MIRRORS(Point(34.99, 98.06));
648 CHECK_POINT_AND_MIRRORS(Point(39.99, 94.73));
649 CHECK_POINT_AND_MIRRORS(Point(44.13, 89.99));
650 CHECK_POINT_AND_MIRRORS(Point(48.46, 79.99));
651 CHECK_POINT_AND_MIRRORS(Point(49.70, 69.99));
652 CHECK_POINT_AND_MIRRORS(Point(49.97, 59.99));
653 CHECK_POINT_AND_MIRRORS(Point(49.99, 49.99)); // Right mid edge
654
655 #undef CHECK_POINT_AND_MIRRORS
656 }
657
658 TEST(RoundSuperellipseTest, SlimDiagnalContains) {
659 // This shape has large radii on one diagnal and tiny radii on the other,
660 // resulting in a almond-like shape placed diagnally (NW to SE).
661 Rect bounds = Rect::MakeLTRB(-50.0f, -50.0f, 50.0f, 50.0f);
662 auto rr = RoundSuperellipse::MakeRectRadii(
663 bounds, {
664 .top_left = Size(1.0, 1.0),
665 .top_right = Size(99.0, 99.0),
666 .bottom_left = Size(99.0, 99.0),
667 .bottom_right = Size(1.0, 1.0),
668 });
669
670 EXPECT_TRUE(rr.Contains(Point{0, 0}));
671 EXPECT_FALSE(rr.Contains(Point{-49.999, -49.999}));
672 EXPECT_FALSE(rr.Contains(Point{-49.999, 49.999}));
673 EXPECT_FALSE(rr.Contains(Point{49.999, 49.999}));
674 EXPECT_FALSE(rr.Contains(Point{49.999, -49.999}));
675
676 // The pointy ends at the NE and SW corners
677 CHECK_POINT_WITH_OFFSET(rr, Point(-49.70, -49.70), Point(-0.02, -0.02));
678 CHECK_POINT_WITH_OFFSET(rr, Point(49.70, 49.70), Point(0.02, 0.02));
679
680 // Checks two points symmetrical to the origin.
681 #define CHECK_DIAGNAL_POINTS(p) \
682 CHECK_POINT_WITH_OFFSET(rr, (p), Point(0.02, -0.02)); \
683 CHECK_POINT_WITH_OFFSET(rr, (p) * Point(-1, -1), Point(-0.02, 0.02));
684
685 // A few other points along the edge
686 CHECK_DIAGNAL_POINTS(Point(-40.0, -49.59));
687 CHECK_DIAGNAL_POINTS(Point(-20.0, -45.64));
688 CHECK_DIAGNAL_POINTS(Point(0.0, -37.01));
689 CHECK_DIAGNAL_POINTS(Point(20.0, -21.96));
690 CHECK_DIAGNAL_POINTS(Point(21.05, -20.92));
691 CHECK_DIAGNAL_POINTS(Point(40.0, 5.68));
692 #undef CHECK_POINT_AND_MIRRORS
693 }
694
695 } // namespace testing
696 } // namespace impeller
geometry_asserts.h
impeller::testing::TEST
TEST(AllocationSizeTest, CanCreateTypedAllocations)
Definition: allocation_size_unittests.cc:10
impeller
Definition: allocation.cc:12
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
impeller::Size
TSize< Scalar > Size
Definition: size.h:159
round_superellipse.h
CHECK_DIAGNAL_POINTS
#define CHECK_DIAGNAL_POINTS(p)
CHECK_POINT_WITH_OFFSET
#define CHECK_POINT_WITH_OFFSET(rr, p, outward_offset)
Definition: round_superellipse_unittests.cc:11
CHECK_POINT_AND_MIRRORS
#define CHECK_POINT_AND_MIRRORS(p)
impeller::RoundSuperellipse
Definition: round_superellipse.h:17
impeller::RoundSuperellipse::Expand
constexpr RoundSuperellipse Expand(Scalar left, Scalar top, Scalar right, Scalar bottom) const
Returns a round rectangle with expanded edges. Negative expansion results in shrinking.
Definition: round_superellipse.h:92
impeller::RoundSuperellipse::MakeOval
constexpr static RoundSuperellipse MakeOval(const Rect &rect)
Definition: round_superellipse.h:24
impeller::RoundSuperellipse::GetRadii
constexpr const RoundingRadii & GetRadii() const
Definition: round_superellipse.h:49
impeller::RoundSuperellipse::MakeRectRadii
static RoundSuperellipse MakeRectRadii(const Rect &rect, const RoundingRadii &radii)
Definition: round_superellipse.cc:12
impeller::RoundSuperellipse::IsOval
constexpr bool IsOval() const
Definition: round_superellipse.h:65
impeller::RoundSuperellipse::MakeRect
constexpr static RoundSuperellipse MakeRect(const Rect &rect)
Definition: round_superellipse.h:20
impeller::RoundSuperellipse::IsFinite
constexpr bool IsFinite() const
Definition: round_superellipse.h:51
impeller::RoundSuperellipse::IsEmpty
constexpr bool IsEmpty() const
Definition: round_superellipse.h:59
impeller::RoundSuperellipse::IsRect
constexpr bool IsRect() const
Definition: round_superellipse.h:61
impeller::RoundSuperellipse::Shift
constexpr RoundSuperellipse Shift(Scalar dx, Scalar dy) const
Returns a new round rectangle translated by the given offset.
Definition: round_superellipse.h:83
impeller::RoundSuperellipse::MakeRectRadius
constexpr static RoundSuperellipse MakeRectRadius(const Rect &rect, Scalar radius)
Definition: round_superellipse.h:28
impeller::RoundSuperellipse::GetBounds
constexpr const Rect & GetBounds() const
Definition: round_superellipse.h:48
impeller::RoundSuperellipse::MakeRectXY
constexpr static RoundSuperellipse MakeRectXY(const Rect &rect, Scalar x_radius, Scalar y_radius)
Definition: round_superellipse.h:33
impeller::RoundingRadii::top_right
Size top_right
Definition: rounding_radii.h:16
impeller::RoundingRadii::bottom_left
Size bottom_left
Definition: rounding_radii.h:17
impeller::RoundingRadii::bottom_right
Size bottom_right
Definition: rounding_radii.h:18
impeller::RoundingRadii::top_left
Size top_left
Definition: rounding_radii.h:15
impeller::RoundingRadii::MakeRadii
constexpr static RoundingRadii MakeRadii(Size radii)
Definition: rounding_radii.h:24
impeller::TPoint< Scalar >
impeller::TRect< Scalar >
impeller::TRect::GetBottom
constexpr auto GetBottom() const
Definition: rect.h:361
impeller::TRect::GetTop
constexpr auto GetTop() const
Definition: rect.h:357
impeller::TRect::IsEmpty
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition: rect.h:301
impeller::TRect::GetLeft
constexpr auto GetLeft() const
Definition: rect.h:355
impeller::TRect::GetRight
constexpr auto GetRight() const
Definition: rect.h:359
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
impeller::TRect::GetCenter
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition: rect.h:386
impeller::TRect< Scalar >::MakeLTRB
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
impeller::TSize< Scalar >
impeller::TSize::height
Type height
Definition: size.h:29
impeller::TSize::width
Type width
Definition: size.h:28