Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
rect_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
8
10
11namespace impeller {
12namespace testing {
13
14TEST(RectTest, RectEmptyDeclaration) {
15 Rect rect;
16
17 EXPECT_EQ(rect.GetLeft(), 0.0f);
18 EXPECT_EQ(rect.GetTop(), 0.0f);
19 EXPECT_EQ(rect.GetRight(), 0.0f);
20 EXPECT_EQ(rect.GetBottom(), 0.0f);
21 EXPECT_EQ(rect.GetX(), 0.0f);
22 EXPECT_EQ(rect.GetY(), 0.0f);
23 EXPECT_EQ(rect.GetWidth(), 0.0f);
24 EXPECT_EQ(rect.GetHeight(), 0.0f);
25 EXPECT_TRUE(rect.IsEmpty());
26 EXPECT_TRUE(rect.IsFinite());
27}
28
29TEST(RectTest, IRectEmptyDeclaration) {
30 IRect rect;
31
32 EXPECT_EQ(rect.GetLeft(), 0);
33 EXPECT_EQ(rect.GetTop(), 0);
34 EXPECT_EQ(rect.GetRight(), 0);
35 EXPECT_EQ(rect.GetBottom(), 0);
36 EXPECT_EQ(rect.GetX(), 0);
37 EXPECT_EQ(rect.GetY(), 0);
38 EXPECT_EQ(rect.GetWidth(), 0);
39 EXPECT_EQ(rect.GetHeight(), 0);
40 EXPECT_TRUE(rect.IsEmpty());
41 // EXPECT_TRUE(rect.IsFinite()); // should fail to compile
42}
43
44TEST(RectTest, RectDefaultConstructor) {
45 Rect rect = Rect();
46
47 EXPECT_EQ(rect.GetLeft(), 0.0f);
48 EXPECT_EQ(rect.GetTop(), 0.0f);
49 EXPECT_EQ(rect.GetRight(), 0.0f);
50 EXPECT_EQ(rect.GetBottom(), 0.0f);
51 EXPECT_EQ(rect.GetX(), 0.0f);
52 EXPECT_EQ(rect.GetY(), 0.0f);
53 EXPECT_EQ(rect.GetWidth(), 0.0f);
54 EXPECT_EQ(rect.GetHeight(), 0.0f);
55 EXPECT_TRUE(rect.IsEmpty());
56 EXPECT_TRUE(rect.IsFinite());
57}
58
59TEST(RectTest, IRectDefaultConstructor) {
60 IRect rect = IRect();
61
62 EXPECT_EQ(rect.GetLeft(), 0);
63 EXPECT_EQ(rect.GetTop(), 0);
64 EXPECT_EQ(rect.GetRight(), 0);
65 EXPECT_EQ(rect.GetBottom(), 0);
66 EXPECT_EQ(rect.GetX(), 0);
67 EXPECT_EQ(rect.GetY(), 0);
68 EXPECT_EQ(rect.GetWidth(), 0);
69 EXPECT_EQ(rect.GetHeight(), 0);
70 EXPECT_TRUE(rect.IsEmpty());
71}
72
73TEST(RectTest, RectSimpleLTRB) {
74 // Using fractional-power-of-2 friendly values for equality tests
75 Rect rect = Rect::MakeLTRB(5.125f, 10.25f, 20.625f, 25.375f);
76
77 EXPECT_EQ(rect.GetLeft(), 5.125f);
78 EXPECT_EQ(rect.GetTop(), 10.25f);
79 EXPECT_EQ(rect.GetRight(), 20.625f);
80 EXPECT_EQ(rect.GetBottom(), 25.375f);
81 EXPECT_EQ(rect.GetX(), 5.125f);
82 EXPECT_EQ(rect.GetY(), 10.25f);
83 EXPECT_EQ(rect.GetWidth(), 15.5f);
84 EXPECT_EQ(rect.GetHeight(), 15.125f);
85 EXPECT_FALSE(rect.IsEmpty());
86 EXPECT_TRUE(rect.IsFinite());
87}
88
89TEST(RectTest, IRectSimpleLTRB) {
90 IRect rect = IRect::MakeLTRB(5, 10, 20, 25);
91
92 EXPECT_EQ(rect.GetLeft(), 5);
93 EXPECT_EQ(rect.GetTop(), 10);
94 EXPECT_EQ(rect.GetRight(), 20);
95 EXPECT_EQ(rect.GetBottom(), 25);
96 EXPECT_EQ(rect.GetX(), 5);
97 EXPECT_EQ(rect.GetY(), 10);
98 EXPECT_EQ(rect.GetWidth(), 15);
99 EXPECT_EQ(rect.GetHeight(), 15);
100 EXPECT_FALSE(rect.IsEmpty());
101}
102
103TEST(RectTest, RectSimpleXYWH) {
104 // Using fractional-power-of-2 friendly values for equality tests
105 Rect rect = Rect::MakeXYWH(5.125f, 10.25f, 15.5f, 15.125f);
106
107 EXPECT_EQ(rect.GetLeft(), 5.125f);
108 EXPECT_EQ(rect.GetTop(), 10.25f);
109 EXPECT_EQ(rect.GetRight(), 20.625f);
110 EXPECT_EQ(rect.GetBottom(), 25.375f);
111 EXPECT_EQ(rect.GetX(), 5.125f);
112 EXPECT_EQ(rect.GetY(), 10.25f);
113 EXPECT_EQ(rect.GetWidth(), 15.5f);
114 EXPECT_EQ(rect.GetHeight(), 15.125f);
115 EXPECT_FALSE(rect.IsEmpty());
116 EXPECT_TRUE(rect.IsFinite());
117}
118
119TEST(RectTest, IRectSimpleXYWH) {
120 IRect rect = IRect::MakeXYWH(5, 10, 15, 16);
121
122 EXPECT_EQ(rect.GetLeft(), 5);
123 EXPECT_EQ(rect.GetTop(), 10);
124 EXPECT_EQ(rect.GetRight(), 20);
125 EXPECT_EQ(rect.GetBottom(), 26);
126 EXPECT_EQ(rect.GetX(), 5);
127 EXPECT_EQ(rect.GetY(), 10);
128 EXPECT_EQ(rect.GetWidth(), 15);
129 EXPECT_EQ(rect.GetHeight(), 16);
130 EXPECT_FALSE(rect.IsEmpty());
131}
132
133TEST(RectTest, RectSimpleWH) {
134 // Using fractional-power-of-2 friendly values for equality tests
135 Rect rect = Rect::MakeWH(15.5f, 15.125f);
136
137 EXPECT_EQ(rect.GetLeft(), 0.0f);
138 EXPECT_EQ(rect.GetTop(), 0.0f);
139 EXPECT_EQ(rect.GetRight(), 15.5f);
140 EXPECT_EQ(rect.GetBottom(), 15.125f);
141 EXPECT_EQ(rect.GetX(), 0.0f);
142 EXPECT_EQ(rect.GetY(), 0.0f);
143 EXPECT_EQ(rect.GetWidth(), 15.5f);
144 EXPECT_EQ(rect.GetHeight(), 15.125f);
145 EXPECT_FALSE(rect.IsEmpty());
146 EXPECT_TRUE(rect.IsFinite());
147}
148
149TEST(RectTest, IRectSimpleWH) {
150 // Using fractional-power-of-2 friendly values for equality tests
151 IRect rect = IRect::MakeWH(15, 25);
152
153 EXPECT_EQ(rect.GetLeft(), 0);
154 EXPECT_EQ(rect.GetTop(), 0);
155 EXPECT_EQ(rect.GetRight(), 15);
156 EXPECT_EQ(rect.GetBottom(), 25);
157 EXPECT_EQ(rect.GetX(), 0);
158 EXPECT_EQ(rect.GetY(), 0);
159 EXPECT_EQ(rect.GetWidth(), 15);
160 EXPECT_EQ(rect.GetHeight(), 25);
161 EXPECT_FALSE(rect.IsEmpty());
162}
163
164TEST(RectTest, RectFromIRect) {
165 IRect irect = IRect::MakeLTRB(10, 20, 30, 40);
166 Rect rect = Rect::Make(irect);
167
168 EXPECT_EQ(rect.GetLeft(), 10);
169 EXPECT_EQ(rect.GetTop(), 20);
170 EXPECT_EQ(rect.GetRight(), 30);
171 EXPECT_EQ(rect.GetBottom(), 40);
172
173 // The following do not compile
174 // IRect irect2 = IRect::Make(rect);
175 // IRect irect2 = IRect::Make(irect);
176}
177
178TEST(RectTest, RectMakeCircleBounds) {
179 Rect rect = Rect::MakeCircleBounds(Point(100.0f, 200.0f), 20.0f);
180
181 EXPECT_FALSE(rect.IsEmpty());
182 EXPECT_EQ(rect.GetLeft(), 80.0f);
183 EXPECT_EQ(rect.GetRight(), 120.0f);
184 EXPECT_EQ(rect.GetTop(), 180.0f);
185 EXPECT_EQ(rect.GetBottom(), 220.0f);
186}
187
188TEST(RectTest, RectMakeCircleBoundsNegativeRadius) {
189 Rect rect = Rect::MakeCircleBounds(Point(100.0f, 200.0f), -20.0f);
190
191 EXPECT_TRUE(rect.IsEmpty());
192 EXPECT_EQ(rect.GetLeft(), 120.0f);
193 EXPECT_EQ(rect.GetRight(), 80.0f);
194 EXPECT_EQ(rect.GetTop(), 220.0f);
195 EXPECT_EQ(rect.GetBottom(), 180.0f);
196}
197
198TEST(RectTest, IRectMakeCircleBounds) {
199 IRect rect = IRect::MakeCircleBounds(IPoint(100, 200), 20);
200
201 EXPECT_FALSE(rect.IsEmpty());
202 EXPECT_EQ(rect.GetLeft(), 80);
203 EXPECT_EQ(rect.GetRight(), 120);
204 EXPECT_EQ(rect.GetTop(), 180);
205 EXPECT_EQ(rect.GetBottom(), 220);
206}
207
208TEST(RectTest, IRectMakeCircleBoundsNegativeRadius) {
209 IRect rect = IRect::MakeCircleBounds(IPoint(100, 200), -20);
210
211 EXPECT_TRUE(rect.IsEmpty());
212 EXPECT_EQ(rect.GetLeft(), 120);
213 EXPECT_EQ(rect.GetRight(), 80);
214 EXPECT_EQ(rect.GetTop(), 220);
215 EXPECT_EQ(rect.GetBottom(), 180);
216}
217
218TEST(RectTest, RectMakeEllipseBoundsSize) {
219 Rect rect =
220 Rect::MakeEllipseBounds(Point(100.0f, 200.0f), Size(20.0f, 30.0f));
221
222 EXPECT_FALSE(rect.IsEmpty());
223 EXPECT_EQ(rect.GetLeft(), 80.0f);
224 EXPECT_EQ(rect.GetRight(), 120.0f);
225 EXPECT_EQ(rect.GetTop(), 170.0f);
226 EXPECT_EQ(rect.GetBottom(), 230.0f);
227}
228
229TEST(RectTest, RectMakeEllipseBoundsNegativeSize) {
230 Rect rect =
231 Rect::MakeEllipseBounds(Point(100.0f, 200.0f), Size(-20.0f, -30.0f));
232
233 EXPECT_TRUE(rect.IsEmpty());
234 EXPECT_EQ(rect.GetLeft(), 120.0f);
235 EXPECT_EQ(rect.GetRight(), 80.0f);
236 EXPECT_EQ(rect.GetTop(), 230.0f);
237 EXPECT_EQ(rect.GetBottom(), 170.0f);
238}
239
240TEST(RectTest, RectMakeEllipseBoundsPoint) {
241 Rect rect =
242 Rect::MakeEllipseBounds(Point(100.0f, 200.0f), Point(20.0f, 30.0f));
243
244 EXPECT_FALSE(rect.IsEmpty());
245 EXPECT_EQ(rect.GetLeft(), 80.0f);
246 EXPECT_EQ(rect.GetRight(), 120.0f);
247 EXPECT_EQ(rect.GetTop(), 170.0f);
248 EXPECT_EQ(rect.GetBottom(), 230.0f);
249}
250
251TEST(RectTest, RectMakeEllipseBoundsNegativePoint) {
252 Rect rect =
253 Rect::MakeEllipseBounds(Point(100.0f, 200.0f), Point(-20.0f, -30.0f));
254
255 EXPECT_TRUE(rect.IsEmpty());
256 EXPECT_EQ(rect.GetLeft(), 120.0f);
257 EXPECT_EQ(rect.GetRight(), 80.0f);
258 EXPECT_EQ(rect.GetTop(), 230.0f);
259 EXPECT_EQ(rect.GetBottom(), 170.0f);
260}
261
262TEST(RectTest, IRectMakeEllipseBoundsSize) {
263 IRect rect = IRect::MakeEllipseBounds(IPoint(100, 200), ISize(20, 30));
264
265 EXPECT_FALSE(rect.IsEmpty());
266 EXPECT_EQ(rect.GetLeft(), 80);
267 EXPECT_EQ(rect.GetRight(), 120);
268 EXPECT_EQ(rect.GetTop(), 170);
269 EXPECT_EQ(rect.GetBottom(), 230);
270}
271
272TEST(RectTest, IRectMakeEllipseBoundsNegativeSize) {
273 IRect rect = IRect::MakeEllipseBounds(IPoint(100, 200), ISize(-20, -30));
274
275 EXPECT_TRUE(rect.IsEmpty());
276 EXPECT_EQ(rect.GetLeft(), 120);
277 EXPECT_EQ(rect.GetRight(), 80);
278 EXPECT_EQ(rect.GetTop(), 230);
279 EXPECT_EQ(rect.GetBottom(), 170);
280}
281
282TEST(RectTest, IRectMakeEllipseBoundsPoint) {
283 IRect rect = IRect::MakeEllipseBounds(IPoint(100, 200), IPoint(20, 30));
284
285 EXPECT_FALSE(rect.IsEmpty());
286 EXPECT_EQ(rect.GetLeft(), 80);
287 EXPECT_EQ(rect.GetRight(), 120);
288 EXPECT_EQ(rect.GetTop(), 170);
289 EXPECT_EQ(rect.GetBottom(), 230);
290}
291
292TEST(RectTest, IRectMakeEllipseBoundsNegativePoint) {
293 IRect rect = IRect::MakeEllipseBounds(IPoint(100, 200), IPoint(-20, -30));
294
295 EXPECT_TRUE(rect.IsEmpty());
296 EXPECT_EQ(rect.GetLeft(), 120);
297 EXPECT_EQ(rect.GetRight(), 80);
298 EXPECT_EQ(rect.GetTop(), 230);
299 EXPECT_EQ(rect.GetBottom(), 170);
300}
301
302TEST(RectTest, RectOverflowXYWH) {
303 auto min = std::numeric_limits<Scalar>::lowest();
304 auto max = std::numeric_limits<Scalar>::max();
305 auto inf = std::numeric_limits<Scalar>::infinity();
306
307 // 8 cases:
308 // finite X, max W
309 // max X, max W
310 // finite Y, max H
311 // max Y, max H
312 // finite X, min W
313 // min X, min W
314 // finite Y, min H
315 // min Y, min H
316
317 // a small finite value added to a max value will remain max
318 // a very large finite value (like max) added to max will go to infinity
319
320 {
321 Rect rect = Rect::MakeXYWH(5.0, 10.0f, max, 15.0f);
322
323 EXPECT_EQ(rect.GetLeft(), 5.0f);
324 EXPECT_EQ(rect.GetTop(), 10.0f);
325 EXPECT_EQ(rect.GetRight(), max);
326 EXPECT_EQ(rect.GetBottom(), 25.0f);
327 EXPECT_EQ(rect.GetX(), 5.0f);
328 EXPECT_EQ(rect.GetY(), 10.0f);
329 EXPECT_EQ(rect.GetWidth(), max);
330 EXPECT_EQ(rect.GetHeight(), 15.0f);
331 EXPECT_FALSE(rect.IsEmpty());
332 EXPECT_TRUE(rect.IsFinite());
333 }
334
335 {
336 Rect rect = Rect::MakeXYWH(max, 10.0f, max, 15.0f);
337
338 EXPECT_EQ(rect.GetLeft(), max);
339 EXPECT_EQ(rect.GetTop(), 10.0f);
340 EXPECT_EQ(rect.GetRight(), inf);
341 EXPECT_EQ(rect.GetBottom(), 25.0f);
342 EXPECT_EQ(rect.GetX(), max);
343 EXPECT_EQ(rect.GetY(), 10.0f);
344 EXPECT_EQ(rect.GetWidth(), inf);
345 EXPECT_EQ(rect.GetHeight(), 15.0f);
346 EXPECT_FALSE(rect.IsEmpty());
347 EXPECT_FALSE(rect.IsFinite());
348 }
349
350 {
351 Rect rect = Rect::MakeXYWH(5.0f, 10.0f, 20.0f, max);
352
353 EXPECT_EQ(rect.GetLeft(), 5.0f);
354 EXPECT_EQ(rect.GetTop(), 10.0f);
355 EXPECT_EQ(rect.GetRight(), 25.0f);
356 EXPECT_EQ(rect.GetBottom(), max);
357 EXPECT_EQ(rect.GetX(), 5.0f);
358 EXPECT_EQ(rect.GetY(), 10.0f);
359 EXPECT_EQ(rect.GetWidth(), 20.0f);
360 EXPECT_EQ(rect.GetHeight(), max);
361 EXPECT_FALSE(rect.IsEmpty());
362 EXPECT_TRUE(rect.IsFinite());
363 }
364
365 {
366 Rect rect = Rect::MakeXYWH(5.0f, max, 20.0f, max);
367
368 EXPECT_EQ(rect.GetLeft(), 5.0f);
369 EXPECT_EQ(rect.GetTop(), max);
370 EXPECT_EQ(rect.GetRight(), 25.0f);
371 EXPECT_EQ(rect.GetBottom(), inf);
372 EXPECT_EQ(rect.GetX(), 5.0f);
373 EXPECT_EQ(rect.GetY(), max);
374 EXPECT_EQ(rect.GetWidth(), 20.0f);
375 EXPECT_EQ(rect.GetHeight(), inf);
376 EXPECT_FALSE(rect.IsEmpty());
377 EXPECT_FALSE(rect.IsFinite());
378 }
379
380 {
381 Rect rect = Rect::MakeXYWH(5.0, 10.0f, min, 15.0f);
382
383 EXPECT_EQ(rect.GetLeft(), 5.0f);
384 EXPECT_EQ(rect.GetTop(), 10.0f);
385 EXPECT_EQ(rect.GetRight(), min);
386 EXPECT_EQ(rect.GetBottom(), 25.0f);
387 EXPECT_EQ(rect.GetX(), 5.0f);
388 EXPECT_EQ(rect.GetY(), 10.0f);
389 EXPECT_EQ(rect.GetWidth(), min);
390 EXPECT_EQ(rect.GetHeight(), 15.0f);
391 EXPECT_TRUE(rect.IsEmpty());
392 EXPECT_TRUE(rect.IsFinite());
393 }
394
395 {
396 Rect rect = Rect::MakeXYWH(min, 10.0f, min, 15.0f);
397
398 EXPECT_EQ(rect.GetLeft(), min);
399 EXPECT_EQ(rect.GetTop(), 10.0f);
400 EXPECT_EQ(rect.GetRight(), -inf);
401 EXPECT_EQ(rect.GetBottom(), 25.0f);
402 EXPECT_EQ(rect.GetX(), min);
403 EXPECT_EQ(rect.GetY(), 10.0f);
404 EXPECT_EQ(rect.GetWidth(), -inf);
405 EXPECT_EQ(rect.GetHeight(), 15.0f);
406 EXPECT_TRUE(rect.IsEmpty());
407 EXPECT_FALSE(rect.IsFinite());
408 }
409
410 {
411 Rect rect = Rect::MakeXYWH(5.0f, 10.0f, 20.0f, min);
412
413 EXPECT_EQ(rect.GetLeft(), 5.0f);
414 EXPECT_EQ(rect.GetTop(), 10.0f);
415 EXPECT_EQ(rect.GetRight(), 25.0f);
416 EXPECT_EQ(rect.GetBottom(), min);
417 EXPECT_EQ(rect.GetX(), 5.0f);
418 EXPECT_EQ(rect.GetY(), 10.0f);
419 EXPECT_EQ(rect.GetWidth(), 20.0f);
420 EXPECT_EQ(rect.GetHeight(), min);
421 EXPECT_TRUE(rect.IsEmpty());
422 EXPECT_TRUE(rect.IsFinite());
423 }
424
425 {
426 Rect rect = Rect::MakeXYWH(5.0f, min, 20.0f, min);
427
428 EXPECT_EQ(rect.GetLeft(), 5.0f);
429 EXPECT_EQ(rect.GetTop(), min);
430 EXPECT_EQ(rect.GetRight(), 25.0f);
431 EXPECT_EQ(rect.GetBottom(), -inf);
432 EXPECT_EQ(rect.GetX(), 5.0f);
433 EXPECT_EQ(rect.GetY(), min);
434 EXPECT_EQ(rect.GetWidth(), 20.0f);
435 EXPECT_EQ(rect.GetHeight(), -inf);
436 EXPECT_TRUE(rect.IsEmpty());
437 EXPECT_FALSE(rect.IsFinite());
438 }
439}
440
441TEST(RectTest, IRectOverflowXYWH) {
442 auto min = std::numeric_limits<int64_t>::min();
443 auto max = std::numeric_limits<int64_t>::max();
444
445 // 4 cases
446 // x near max, positive w takes it past max
447 // x near min, negative w takes it below min
448 // y near max, positive h takes it past max
449 // y near min, negative h takes it below min
450
451 {
452 IRect rect = IRect::MakeXYWH(max - 5, 10, 10, 16);
453
454 EXPECT_EQ(rect.GetLeft(), max - 5);
455 EXPECT_EQ(rect.GetTop(), 10);
456 EXPECT_EQ(rect.GetRight(), max);
457 EXPECT_EQ(rect.GetBottom(), 26);
458 EXPECT_EQ(rect.GetX(), max - 5);
459 EXPECT_EQ(rect.GetY(), 10);
460 EXPECT_EQ(rect.GetWidth(), 5);
461 EXPECT_EQ(rect.GetHeight(), 16);
462 EXPECT_FALSE(rect.IsEmpty());
463 }
464
465 {
466 IRect rect = IRect::MakeXYWH(min + 5, 10, -10, 16);
467
468 EXPECT_EQ(rect.GetLeft(), min + 5);
469 EXPECT_EQ(rect.GetTop(), 10);
470 EXPECT_EQ(rect.GetRight(), min);
471 EXPECT_EQ(rect.GetBottom(), 26);
472 EXPECT_EQ(rect.GetX(), min + 5);
473 EXPECT_EQ(rect.GetY(), 10);
474 EXPECT_EQ(rect.GetWidth(), -5);
475 EXPECT_EQ(rect.GetHeight(), 16);
476 EXPECT_TRUE(rect.IsEmpty());
477 }
478
479 {
480 IRect rect = IRect::MakeXYWH(5, max - 10, 10, 16);
481
482 EXPECT_EQ(rect.GetLeft(), 5);
483 EXPECT_EQ(rect.GetTop(), max - 10);
484 EXPECT_EQ(rect.GetRight(), 15);
485 EXPECT_EQ(rect.GetBottom(), max);
486 EXPECT_EQ(rect.GetX(), 5);
487 EXPECT_EQ(rect.GetY(), max - 10);
488 EXPECT_EQ(rect.GetWidth(), 10);
489 EXPECT_EQ(rect.GetHeight(), 10);
490 EXPECT_FALSE(rect.IsEmpty());
491 }
492
493 {
494 IRect rect = IRect::MakeXYWH(5, min + 10, 10, -16);
495
496 EXPECT_EQ(rect.GetLeft(), 5);
497 EXPECT_EQ(rect.GetTop(), min + 10);
498 EXPECT_EQ(rect.GetRight(), 15);
499 EXPECT_EQ(rect.GetBottom(), min);
500 EXPECT_EQ(rect.GetX(), 5);
501 EXPECT_EQ(rect.GetY(), min + 10);
502 EXPECT_EQ(rect.GetWidth(), 10);
503 EXPECT_EQ(rect.GetHeight(), -10);
504 EXPECT_TRUE(rect.IsEmpty());
505 }
506}
507
508TEST(RectTest, RectOverflowLTRB) {
509 auto min = std::numeric_limits<Scalar>::lowest();
510 auto max = std::numeric_limits<Scalar>::max();
511 auto inf = std::numeric_limits<Scalar>::infinity();
512
513 // 8 cases:
514 // finite negative X, max W
515 // ~min X, ~max W
516 // finite negative Y, max H
517 // ~min Y, ~max H
518 // finite positive X, min W
519 // ~min X, ~min W
520 // finite positive Y, min H
521 // ~min Y, ~min H
522
523 // a small finite value subtracted from a max value will remain max
524 // a very large finite value (like min) subtracted from max will go to inf
525
526 {
527 Rect rect = Rect::MakeLTRB(-5.0f, 10.0f, max, 25.0f);
528
529 EXPECT_EQ(rect.GetLeft(), -5.0f);
530 EXPECT_EQ(rect.GetTop(), 10.0f);
531 EXPECT_EQ(rect.GetRight(), max);
532 EXPECT_EQ(rect.GetBottom(), 25.0f);
533 EXPECT_EQ(rect.GetX(), -5.0f);
534 EXPECT_EQ(rect.GetY(), 10.0f);
535 EXPECT_EQ(rect.GetWidth(), max);
536 EXPECT_EQ(rect.GetHeight(), 15.0f);
537 EXPECT_FALSE(rect.IsEmpty());
538 EXPECT_TRUE(rect.IsFinite());
539 }
540
541 {
542 Rect rect = Rect::MakeLTRB(min + 5.0f, 10.0f, max - 5.0f, 25.0f);
543
544 EXPECT_EQ(rect.GetLeft(), min + 5.0f);
545 EXPECT_EQ(rect.GetTop(), 10.0f);
546 EXPECT_EQ(rect.GetRight(), max - 5.0f);
547 EXPECT_EQ(rect.GetBottom(), 25.0f);
548 EXPECT_EQ(rect.GetX(), min + 5.0f);
549 EXPECT_EQ(rect.GetY(), 10.0f);
550 EXPECT_EQ(rect.GetWidth(), inf);
551 EXPECT_EQ(rect.GetHeight(), 15.0f);
552 EXPECT_FALSE(rect.IsEmpty());
553 EXPECT_TRUE(rect.IsFinite());
554 }
555
556 {
557 Rect rect = Rect::MakeLTRB(5.0f, -10.0f, 20.0f, max);
558
559 EXPECT_EQ(rect.GetLeft(), 5.0f);
560 EXPECT_EQ(rect.GetTop(), -10.0f);
561 EXPECT_EQ(rect.GetRight(), 20.0f);
562 EXPECT_EQ(rect.GetBottom(), max);
563 EXPECT_EQ(rect.GetX(), 5.0f);
564 EXPECT_EQ(rect.GetY(), -10.0f);
565 EXPECT_EQ(rect.GetWidth(), 15.0f);
566 EXPECT_EQ(rect.GetHeight(), max);
567 EXPECT_FALSE(rect.IsEmpty());
568 EXPECT_TRUE(rect.IsFinite());
569 }
570
571 {
572 Rect rect = Rect::MakeLTRB(5.0f, min + 10.0f, 20.0f, max - 15.0f);
573
574 EXPECT_EQ(rect.GetLeft(), 5.0f);
575 EXPECT_EQ(rect.GetTop(), min + 10.0f);
576 EXPECT_EQ(rect.GetRight(), 20.0f);
577 EXPECT_EQ(rect.GetBottom(), max - 15.0f);
578 EXPECT_EQ(rect.GetX(), 5.0f);
579 EXPECT_EQ(rect.GetY(), min + 10.0f);
580 EXPECT_EQ(rect.GetWidth(), 15.0f);
581 EXPECT_EQ(rect.GetHeight(), inf);
582 EXPECT_FALSE(rect.IsEmpty());
583 EXPECT_TRUE(rect.IsFinite());
584 }
585
586 {
587 Rect rect = Rect::MakeLTRB(5.0f, 10.0f, min, 25.0f);
588
589 EXPECT_EQ(rect.GetLeft(), 5.0f);
590 EXPECT_EQ(rect.GetTop(), 10.0f);
591 EXPECT_EQ(rect.GetRight(), min);
592 EXPECT_EQ(rect.GetBottom(), 25.0f);
593 EXPECT_EQ(rect.GetX(), 5.0f);
594 EXPECT_EQ(rect.GetY(), 10.0f);
595 EXPECT_EQ(rect.GetWidth(), min);
596 EXPECT_EQ(rect.GetHeight(), 15.0f);
597 EXPECT_TRUE(rect.IsEmpty());
598 EXPECT_TRUE(rect.IsFinite());
599 }
600
601 {
602 Rect rect = Rect::MakeLTRB(max - 5.0f, 10.0f, min + 10.0f, 25.0f);
603
604 EXPECT_EQ(rect.GetLeft(), max - 5.0f);
605 EXPECT_EQ(rect.GetTop(), 10.0f);
606 EXPECT_EQ(rect.GetRight(), min + 10.0f);
607 EXPECT_EQ(rect.GetBottom(), 25.0f);
608 EXPECT_EQ(rect.GetX(), max - 5.0f);
609 EXPECT_EQ(rect.GetY(), 10.0f);
610 EXPECT_EQ(rect.GetWidth(), -inf);
611 EXPECT_EQ(rect.GetHeight(), 15.0f);
612 EXPECT_TRUE(rect.IsEmpty());
613 EXPECT_TRUE(rect.IsFinite());
614 }
615
616 {
617 Rect rect = Rect::MakeLTRB(5.0f, 10.0f, 20.0f, min);
618
619 EXPECT_EQ(rect.GetLeft(), 5.0f);
620 EXPECT_EQ(rect.GetTop(), 10.0f);
621 EXPECT_EQ(rect.GetRight(), 20.0f);
622 EXPECT_EQ(rect.GetBottom(), min);
623 EXPECT_EQ(rect.GetX(), 5.0f);
624 EXPECT_EQ(rect.GetY(), 10.0f);
625 EXPECT_EQ(rect.GetWidth(), 15.0f);
626 EXPECT_EQ(rect.GetHeight(), min);
627 EXPECT_TRUE(rect.IsEmpty());
628 EXPECT_TRUE(rect.IsFinite());
629 }
630
631 {
632 Rect rect = Rect::MakeLTRB(5.0f, max - 5.0f, 20.0f, min + 10.0f);
633
634 EXPECT_EQ(rect.GetLeft(), 5.0f);
635 EXPECT_EQ(rect.GetTop(), max - 5.0f);
636 EXPECT_EQ(rect.GetRight(), 20.0f);
637 EXPECT_EQ(rect.GetBottom(), min + 10.0f);
638 EXPECT_EQ(rect.GetX(), 5.0f);
639 EXPECT_EQ(rect.GetY(), max - 5.0f);
640 EXPECT_EQ(rect.GetWidth(), 15.0f);
641 EXPECT_EQ(rect.GetHeight(), -inf);
642 EXPECT_TRUE(rect.IsEmpty());
643 EXPECT_TRUE(rect.IsFinite());
644 }
645}
646
647TEST(RectTest, IRectOverflowLTRB) {
648 auto min = std::numeric_limits<int64_t>::min();
649 auto max = std::numeric_limits<int64_t>::max();
650
651 // 4 cases
652 // negative l, r near max takes width past max
653 // positive l, r near min takes width below min
654 // negative t, b near max takes width past max
655 // positive t, b near min takes width below min
656
657 {
658 IRect rect = IRect::MakeLTRB(-10, 10, max - 5, 26);
659
660 EXPECT_EQ(rect.GetLeft(), -10);
661 EXPECT_EQ(rect.GetTop(), 10);
662 EXPECT_EQ(rect.GetRight(), max - 5);
663 EXPECT_EQ(rect.GetBottom(), 26);
664 EXPECT_EQ(rect.GetX(), -10);
665 EXPECT_EQ(rect.GetY(), 10);
666 EXPECT_EQ(rect.GetWidth(), max);
667 EXPECT_EQ(rect.GetHeight(), 16);
668 EXPECT_FALSE(rect.IsEmpty());
669 }
670
671 {
672 IRect rect = IRect::MakeLTRB(10, 10, min + 5, 26);
673
674 EXPECT_EQ(rect.GetLeft(), 10);
675 EXPECT_EQ(rect.GetTop(), 10);
676 EXPECT_EQ(rect.GetRight(), min + 5);
677 EXPECT_EQ(rect.GetBottom(), 26);
678 EXPECT_EQ(rect.GetX(), 10);
679 EXPECT_EQ(rect.GetY(), 10);
680 EXPECT_EQ(rect.GetWidth(), min);
681 EXPECT_EQ(rect.GetHeight(), 16);
682 EXPECT_TRUE(rect.IsEmpty());
683 }
684
685 {
686 IRect rect = IRect::MakeLTRB(5, -10, 15, max - 5);
687
688 EXPECT_EQ(rect.GetLeft(), 5);
689 EXPECT_EQ(rect.GetTop(), -10);
690 EXPECT_EQ(rect.GetRight(), 15);
691 EXPECT_EQ(rect.GetBottom(), max - 5);
692 EXPECT_EQ(rect.GetX(), 5);
693 EXPECT_EQ(rect.GetY(), -10);
694 EXPECT_EQ(rect.GetWidth(), 10);
695 EXPECT_EQ(rect.GetHeight(), max);
696 EXPECT_FALSE(rect.IsEmpty());
697 }
698
699 {
700 IRect rect = IRect::MakeLTRB(5, 10, 15, min + 5);
701
702 EXPECT_EQ(rect.GetLeft(), 5);
703 EXPECT_EQ(rect.GetTop(), 10);
704 EXPECT_EQ(rect.GetRight(), 15);
705 EXPECT_EQ(rect.GetBottom(), min + 5);
706 EXPECT_EQ(rect.GetX(), 5);
707 EXPECT_EQ(rect.GetY(), 10);
708 EXPECT_EQ(rect.GetWidth(), 10);
709 EXPECT_EQ(rect.GetHeight(), min);
710 EXPECT_TRUE(rect.IsEmpty());
711 }
712}
713
714TEST(RectTest, RectMakeSize) {
715 {
716 Size s(100, 200);
717 Rect r = Rect::MakeSize(s);
718 Rect expected = Rect::MakeLTRB(0, 0, 100, 200);
719 EXPECT_RECT_NEAR(r, expected);
720 }
721
722 {
723 ISize s(100, 200);
724 Rect r = Rect::MakeSize(s);
725 Rect expected = Rect::MakeLTRB(0, 0, 100, 200);
726 EXPECT_RECT_NEAR(r, expected);
727 }
728
729 {
730 Size s(100, 200);
731 IRect r = IRect::MakeSize(s);
732 IRect expected = IRect::MakeLTRB(0, 0, 100, 200);
733 EXPECT_EQ(r, expected);
734 }
735
736 {
737 ISize s(100, 200);
738 IRect r = IRect::MakeSize(s);
739 IRect expected = IRect::MakeLTRB(0, 0, 100, 200);
740 EXPECT_EQ(r, expected);
741 }
742}
743
744TEST(RectTest, RectMakeMaximum) {
745 Rect rect = Rect::MakeMaximum();
746 auto inf = std::numeric_limits<Scalar>::infinity();
747 auto min = std::numeric_limits<Scalar>::lowest();
748 auto max = std::numeric_limits<Scalar>::max();
749
750 EXPECT_EQ(rect.GetLeft(), min);
751 EXPECT_EQ(rect.GetTop(), min);
752 EXPECT_EQ(rect.GetRight(), max);
753 EXPECT_EQ(rect.GetBottom(), max);
754 EXPECT_EQ(rect.GetX(), min);
755 EXPECT_EQ(rect.GetY(), min);
756 EXPECT_EQ(rect.GetWidth(), inf);
757 EXPECT_EQ(rect.GetHeight(), inf);
758 EXPECT_FALSE(rect.IsEmpty());
759 EXPECT_TRUE(rect.IsFinite());
760}
761
762TEST(RectTest, IRectMakeMaximum) {
763 IRect rect = IRect::MakeMaximum();
764 auto min = std::numeric_limits<int64_t>::min();
765 auto max = std::numeric_limits<int64_t>::max();
766
767 EXPECT_EQ(rect.GetLeft(), min);
768 EXPECT_EQ(rect.GetTop(), min);
769 EXPECT_EQ(rect.GetRight(), max);
770 EXPECT_EQ(rect.GetBottom(), max);
771 EXPECT_EQ(rect.GetX(), min);
772 EXPECT_EQ(rect.GetY(), min);
773 EXPECT_EQ(rect.GetWidth(), max);
774 EXPECT_EQ(rect.GetHeight(), max);
775 EXPECT_FALSE(rect.IsEmpty());
776}
777
778TEST(RectTest, RectFromRect) {
779 EXPECT_EQ(Rect(Rect::MakeXYWH(2, 3, 7, 15)),
780 Rect::MakeXYWH(2.0, 3.0, 7.0, 15.0));
781 EXPECT_EQ(Rect(Rect::MakeLTRB(2, 3, 7, 15)),
782 Rect::MakeLTRB(2.0, 3.0, 7.0, 15.0));
783}
784
785TEST(RectTest, IRectFromIRect) {
786 EXPECT_EQ(IRect(IRect::MakeXYWH(2, 3, 7, 15)), //
787 IRect::MakeXYWH(2, 3, 7, 15));
788 EXPECT_EQ(IRect(IRect::MakeLTRB(2, 3, 7, 15)), //
789 IRect::MakeLTRB(2, 3, 7, 15));
790}
791
792TEST(RectTest, RectCopy) {
793 // Using fractional-power-of-2 friendly values for equality tests
794 Rect rect = Rect::MakeLTRB(5.125f, 10.25f, 20.625f, 25.375f);
795 Rect copy = rect;
796
797 EXPECT_EQ(rect, copy);
798 EXPECT_EQ(copy.GetLeft(), 5.125f);
799 EXPECT_EQ(copy.GetTop(), 10.25f);
800 EXPECT_EQ(copy.GetRight(), 20.625f);
801 EXPECT_EQ(copy.GetBottom(), 25.375f);
802 EXPECT_EQ(copy.GetX(), 5.125f);
803 EXPECT_EQ(copy.GetY(), 10.25f);
804 EXPECT_EQ(copy.GetWidth(), 15.5f);
805 EXPECT_EQ(copy.GetHeight(), 15.125f);
806 EXPECT_FALSE(copy.IsEmpty());
807 EXPECT_TRUE(copy.IsFinite());
808}
809
810TEST(RectTest, IRectCopy) {
811 IRect rect = IRect::MakeLTRB(5, 10, 20, 25);
812 IRect copy = rect;
813
814 EXPECT_EQ(rect, copy);
815 EXPECT_EQ(copy.GetLeft(), 5);
816 EXPECT_EQ(copy.GetTop(), 10);
817 EXPECT_EQ(copy.GetRight(), 20);
818 EXPECT_EQ(copy.GetBottom(), 25);
819 EXPECT_EQ(copy.GetX(), 5);
820 EXPECT_EQ(copy.GetY(), 10);
821 EXPECT_EQ(copy.GetWidth(), 15);
822 EXPECT_EQ(copy.GetHeight(), 15);
823 EXPECT_FALSE(copy.IsEmpty());
824}
825
826TEST(RectTest, RectOriginSizeXYWHGetters) {
827 {
828 Rect r = Rect::MakeOriginSize({10, 20}, {50, 40});
829 EXPECT_EQ(r.GetOrigin(), Point(10, 20));
830 EXPECT_EQ(r.GetSize(), Size(50, 40));
831 EXPECT_EQ(r.GetX(), 10);
832 EXPECT_EQ(r.GetY(), 20);
833 EXPECT_EQ(r.GetWidth(), 50);
834 EXPECT_EQ(r.GetHeight(), 40);
835 auto expected_array = std::array<Scalar, 4>{10, 20, 50, 40};
836 EXPECT_EQ(r.GetXYWH(), expected_array);
837 }
838
839 {
840 Rect r = Rect::MakeLTRB(10, 20, 50, 40);
841 EXPECT_EQ(r.GetOrigin(), Point(10, 20));
842 EXPECT_EQ(r.GetSize(), Size(40, 20));
843 EXPECT_EQ(r.GetX(), 10);
844 EXPECT_EQ(r.GetY(), 20);
845 EXPECT_EQ(r.GetWidth(), 40);
846 EXPECT_EQ(r.GetHeight(), 20);
847 auto expected_array = std::array<Scalar, 4>{10, 20, 40, 20};
848 EXPECT_EQ(r.GetXYWH(), expected_array);
849 }
850}
851
852TEST(RectTest, IRectOriginSizeXYWHGetters) {
853 {
854 IRect r = IRect::MakeOriginSize({10, 20}, {50, 40});
855 EXPECT_EQ(r.GetOrigin(), IPoint(10, 20));
856 EXPECT_EQ(r.GetSize(), ISize(50, 40));
857 EXPECT_EQ(r.GetX(), 10);
858 EXPECT_EQ(r.GetY(), 20);
859 EXPECT_EQ(r.GetWidth(), 50);
860 EXPECT_EQ(r.GetHeight(), 40);
861 auto expected_array = std::array<int64_t, 4>{10, 20, 50, 40};
862 EXPECT_EQ(r.GetXYWH(), expected_array);
863 }
864
865 {
866 IRect r = IRect::MakeLTRB(10, 20, 50, 40);
867 EXPECT_EQ(r.GetOrigin(), IPoint(10, 20));
868 EXPECT_EQ(r.GetSize(), ISize(40, 20));
869 EXPECT_EQ(r.GetX(), 10);
870 EXPECT_EQ(r.GetY(), 20);
871 EXPECT_EQ(r.GetWidth(), 40);
872 EXPECT_EQ(r.GetHeight(), 20);
873 auto expected_array = std::array<int64_t, 4>{10, 20, 40, 20};
874 EXPECT_EQ(r.GetXYWH(), expected_array);
875 }
876}
877
878TEST(RectTest, RectRoundOutEmpty) {
879 Rect rect;
880
881 EXPECT_EQ(Rect::RoundOut(rect), Rect());
882
883 EXPECT_EQ(IRect::RoundOut(rect), IRect());
884}
885
886TEST(RectTest, RectRoundOutSimple) {
887 Rect rect = Rect::MakeLTRB(5.125f, 10.75f, 20.625f, 25.375f);
888
889 EXPECT_EQ(Rect::RoundOut(rect), Rect::MakeLTRB(5.0f, 10.0f, 21.0f, 26.0f));
890
891 EXPECT_EQ(IRect::RoundOut(rect), IRect::MakeLTRB(5, 10, 21, 26));
892}
893
894TEST(RectTest, RectRoundOutToIRectHuge) {
895 auto test = [](int corners) {
896 EXPECT_TRUE(corners >= 0 && corners <= 0xf);
897 Scalar l, t, r, b;
898 int64_t il, it, ir, ib;
899 l = il = 50;
900 t = it = 50;
901 r = ir = 80;
902 b = ib = 80;
903 if ((corners & (1 << 0)) != 0) {
904 l = -1E20;
905 il = std::numeric_limits<int64_t>::min();
906 }
907 if ((corners & (1 << 1)) != 0) {
908 t = -1E20;
909 it = std::numeric_limits<int64_t>::min();
910 }
911 if ((corners & (1 << 2)) != 0) {
912 r = +1E20;
913 ir = std::numeric_limits<int64_t>::max();
914 }
915 if ((corners & (1 << 3)) != 0) {
916 b = +1E20;
917 ib = std::numeric_limits<int64_t>::max();
918 }
919
920 Rect rect = Rect::MakeLTRB(l, t, r, b);
921 IRect irect = IRect::RoundOut(rect);
922 EXPECT_EQ(irect.GetLeft(), il) << corners;
923 EXPECT_EQ(irect.GetTop(), it) << corners;
924 EXPECT_EQ(irect.GetRight(), ir) << corners;
925 EXPECT_EQ(irect.GetBottom(), ib) << corners;
926 };
927
928 for (int corners = 0; corners <= 15; corners++) {
929 test(corners);
930 }
931}
932
933TEST(RectTest, RectDoesNotIntersectEmpty) {
934 Rect rect = Rect::MakeLTRB(50, 50, 100, 100);
935
936 auto test = [&rect](Scalar l, Scalar t, Scalar r, Scalar b,
937 const std::string& label) {
938 EXPECT_FALSE(rect.IntersectsWithRect(Rect::MakeLTRB(l, b, r, t)))
939 << label << " with Top/Bottom swapped";
940 EXPECT_FALSE(rect.IntersectsWithRect(Rect::MakeLTRB(r, b, l, t)))
941 << label << " with Left/Right swapped";
942 EXPECT_FALSE(rect.IntersectsWithRect(Rect::MakeLTRB(r, t, l, b)))
943 << label << " with all sides swapped";
944 };
945
946 test(20, 20, 30, 30, "Above and Left");
947 test(70, 20, 80, 30, "Above");
948 test(120, 20, 130, 30, "Above and Right");
949 test(120, 70, 130, 80, "Right");
950 test(120, 120, 130, 130, "Below and Right");
951 test(70, 120, 80, 130, "Below");
952 test(20, 120, 30, 130, "Below and Left");
953 test(20, 70, 30, 80, "Left");
954
955 test(70, 70, 80, 80, "Inside");
956
957 test(40, 70, 60, 80, "Straddling Left");
958 test(70, 40, 80, 60, "Straddling Top");
959 test(90, 70, 110, 80, "Straddling Right");
960 test(70, 90, 80, 110, "Straddling Bottom");
961}
962
963TEST(RectTest, IRectDoesNotIntersectEmpty) {
964 IRect rect = IRect::MakeLTRB(50, 50, 100, 100);
965
966 auto test = [&rect](int64_t l, int64_t t, int64_t r, int64_t b,
967 const std::string& label) {
968 EXPECT_FALSE(rect.IntersectsWithRect(IRect::MakeLTRB(l, b, r, t)))
969 << label << " with Top/Bottom swapped";
970 EXPECT_FALSE(rect.IntersectsWithRect(IRect::MakeLTRB(r, b, l, t)))
971 << label << " with Left/Right swapped";
972 EXPECT_FALSE(rect.IntersectsWithRect(IRect::MakeLTRB(r, t, l, b)))
973 << label << " with all sides swapped";
974 };
975
976 test(20, 20, 30, 30, "Above and Left");
977 test(70, 20, 80, 30, "Above");
978 test(120, 20, 130, 30, "Above and Right");
979 test(120, 70, 130, 80, "Right");
980 test(120, 120, 130, 130, "Below and Right");
981 test(70, 120, 80, 130, "Below");
982 test(20, 120, 30, 130, "Below and Left");
983 test(20, 70, 30, 80, "Left");
984
985 test(70, 70, 80, 80, "Inside");
986
987 test(40, 70, 60, 80, "Straddling Left");
988 test(70, 40, 80, 60, "Straddling Top");
989 test(90, 70, 110, 80, "Straddling Right");
990 test(70, 90, 80, 110, "Straddling Bottom");
991}
992
993TEST(RectTest, EmptyRectDoesNotIntersect) {
994 Rect rect = Rect::MakeLTRB(50, 50, 100, 100);
995
996 auto test = [&rect](Scalar l, Scalar t, Scalar r, Scalar b,
997 const std::string& label) {
998 EXPECT_FALSE(Rect::MakeLTRB(l, b, r, t).IntersectsWithRect(rect))
999 << label << " with Top/Bottom swapped";
1000 EXPECT_FALSE(Rect::MakeLTRB(r, b, l, t).IntersectsWithRect(rect))
1001 << label << " with Left/Right swapped";
1002 EXPECT_FALSE(Rect::MakeLTRB(r, t, l, b).IntersectsWithRect(rect))
1003 << label << " with all sides swapped";
1004 };
1005
1006 test(20, 20, 30, 30, "Above and Left");
1007 test(70, 20, 80, 30, "Above");
1008 test(120, 20, 130, 30, "Above and Right");
1009 test(120, 70, 130, 80, "Right");
1010 test(120, 120, 130, 130, "Below and Right");
1011 test(70, 120, 80, 130, "Below");
1012 test(20, 120, 30, 130, "Below and Left");
1013 test(20, 70, 30, 80, "Left");
1014
1015 test(70, 70, 80, 80, "Inside");
1016
1017 test(40, 70, 60, 80, "Straddling Left");
1018 test(70, 40, 80, 60, "Straddling Top");
1019 test(90, 70, 110, 80, "Straddling Right");
1020 test(70, 90, 80, 110, "Straddling Bottom");
1021}
1022
1023TEST(RectTest, EmptyIRectDoesNotIntersect) {
1024 IRect rect = IRect::MakeLTRB(50, 50, 100, 100);
1025
1026 auto test = [&rect](int64_t l, int64_t t, int64_t r, int64_t b,
1027 const std::string& label) {
1028 EXPECT_FALSE(IRect::MakeLTRB(l, b, r, t).IntersectsWithRect(rect))
1029 << label << " with Top/Bottom swapped";
1030 EXPECT_FALSE(IRect::MakeLTRB(r, b, l, t).IntersectsWithRect(rect))
1031 << label << " with Left/Right swapped";
1032 EXPECT_FALSE(IRect::MakeLTRB(r, t, l, b).IntersectsWithRect(rect))
1033 << label << " with all sides swapped";
1034 };
1035
1036 test(20, 20, 30, 30, "Above and Left");
1037 test(70, 20, 80, 30, "Above");
1038 test(120, 20, 130, 30, "Above and Right");
1039 test(120, 70, 130, 80, "Right");
1040 test(120, 120, 130, 130, "Below and Right");
1041 test(70, 120, 80, 130, "Below");
1042 test(20, 120, 30, 130, "Below and Left");
1043 test(20, 70, 30, 80, "Left");
1044
1045 test(70, 70, 80, 80, "Inside");
1046
1047 test(40, 70, 60, 80, "Straddling Left");
1048 test(70, 40, 80, 60, "Straddling Top");
1049 test(90, 70, 110, 80, "Straddling Right");
1050 test(70, 90, 80, 110, "Straddling Bottom");
1051}
1052
1053TEST(RectTest, RectScale) {
1054 auto test1 = [](Rect rect, Scalar scale) {
1055 Rect expected = Rect::MakeXYWH(rect.GetX() * scale, //
1056 rect.GetY() * scale, //
1057 rect.GetWidth() * scale, //
1058 rect.GetHeight() * scale);
1059
1060 EXPECT_RECT_NEAR(rect.Scale(scale), expected) //
1061 << rect << " * " << scale;
1062 EXPECT_RECT_NEAR(rect.Scale(scale, scale), expected) //
1063 << rect << " * " << scale;
1064 EXPECT_RECT_NEAR(rect.Scale(Point(scale, scale)), expected) //
1065 << rect << " * " << scale;
1066 EXPECT_RECT_NEAR(rect.Scale(Size(scale, scale)), expected) //
1067 << rect << " * " << scale;
1068 };
1069
1070 auto test2 = [&test1](Rect rect, Scalar scale_x, Scalar scale_y) {
1071 Rect expected = Rect::MakeXYWH(rect.GetX() * scale_x, //
1072 rect.GetY() * scale_y, //
1073 rect.GetWidth() * scale_x, //
1074 rect.GetHeight() * scale_y);
1075
1076 EXPECT_RECT_NEAR(rect.Scale(scale_x, scale_y), expected) //
1077 << rect << " * " << scale_x << ", " << scale_y;
1078 EXPECT_RECT_NEAR(rect.Scale(Point(scale_x, scale_y)), expected) //
1079 << rect << " * " << scale_x << ", " << scale_y;
1080 EXPECT_RECT_NEAR(rect.Scale(Size(scale_x, scale_y)), expected) //
1081 << rect << " * " << scale_x << ", " << scale_y;
1082
1083 test1(rect, scale_x);
1084 test1(rect, scale_y);
1085 };
1086
1087 test2(Rect::MakeLTRB(10, 15, 100, 150), 1.0, 0.0);
1088 test2(Rect::MakeLTRB(10, 15, 100, 150), 0.0, 1.0);
1089 test2(Rect::MakeLTRB(10, 15, 100, 150), 0.0, 0.0);
1090 test2(Rect::MakeLTRB(10, 15, 100, 150), 2.5, 3.5);
1091 test2(Rect::MakeLTRB(10, 15, 100, 150), 3.5, 2.5);
1092 test2(Rect::MakeLTRB(10, 15, -100, 150), 2.5, 3.5);
1093 test2(Rect::MakeLTRB(10, 15, 100, -150), 2.5, 3.5);
1094 test2(Rect::MakeLTRB(10, 15, 100, 150), -2.5, 3.5);
1095 test2(Rect::MakeLTRB(10, 15, 100, 150), 2.5, -3.5);
1096}
1097
1098TEST(RectTest, IRectScale) {
1099 auto test1 = [](IRect rect, int64_t scale) {
1100 IRect expected = IRect::MakeXYWH(rect.GetX() * scale, //
1101 rect.GetY() * scale, //
1102 rect.GetWidth() * scale, //
1103 rect.GetHeight() * scale);
1104
1105 EXPECT_EQ(rect.Scale(scale), expected) //
1106 << rect << " * " << scale;
1107 EXPECT_EQ(rect.Scale(scale, scale), expected) //
1108 << rect << " * " << scale;
1109 EXPECT_EQ(rect.Scale(IPoint(scale, scale)), expected) //
1110 << rect << " * " << scale;
1111 EXPECT_EQ(rect.Scale(ISize(scale, scale)), expected) //
1112 << rect << " * " << scale;
1113 };
1114
1115 auto test2 = [&test1](IRect rect, int64_t scale_x, int64_t scale_y) {
1116 IRect expected = IRect::MakeXYWH(rect.GetX() * scale_x, //
1117 rect.GetY() * scale_y, //
1118 rect.GetWidth() * scale_x, //
1119 rect.GetHeight() * scale_y);
1120
1121 EXPECT_EQ(rect.Scale(scale_x, scale_y), expected) //
1122 << rect << " * " << scale_x << ", " << scale_y;
1123 EXPECT_EQ(rect.Scale(IPoint(scale_x, scale_y)), expected) //
1124 << rect << " * " << scale_x << ", " << scale_y;
1125 EXPECT_EQ(rect.Scale(ISize(scale_x, scale_y)), expected) //
1126 << rect << " * " << scale_x << ", " << scale_y;
1127
1128 test1(rect, scale_x);
1129 test1(rect, scale_y);
1130 };
1131
1132 test2(IRect::MakeLTRB(10, 15, 100, 150), 2, 3);
1133 test2(IRect::MakeLTRB(10, 15, 100, 150), 3, 2);
1134 test2(IRect::MakeLTRB(10, 15, -100, 150), 2, 3);
1135 test2(IRect::MakeLTRB(10, 15, 100, -150), 2, 3);
1136 test2(IRect::MakeLTRB(10, 15, 100, 150), -2, 3);
1137 test2(IRect::MakeLTRB(10, 15, 100, 150), 2, -3);
1138}
1139
1140TEST(RectTest, RectArea) {
1141 EXPECT_EQ(Rect::MakeXYWH(0, 0, 100, 200).Area(), 20000);
1142 EXPECT_EQ(Rect::MakeXYWH(10, 20, 100, 200).Area(), 20000);
1143 EXPECT_EQ(Rect::MakeXYWH(0, 0, 200, 100).Area(), 20000);
1144 EXPECT_EQ(Rect::MakeXYWH(10, 20, 200, 100).Area(), 20000);
1145 EXPECT_EQ(Rect::MakeXYWH(0, 0, 100, 100).Area(), 10000);
1146 EXPECT_EQ(Rect::MakeXYWH(10, 20, 100, 100).Area(), 10000);
1147}
1148
1149TEST(RectTest, IRectArea) {
1150 EXPECT_EQ(IRect::MakeXYWH(0, 0, 100, 200).Area(), 20000);
1151 EXPECT_EQ(IRect::MakeXYWH(10, 20, 100, 200).Area(), 20000);
1152 EXPECT_EQ(IRect::MakeXYWH(0, 0, 200, 100).Area(), 20000);
1153 EXPECT_EQ(IRect::MakeXYWH(10, 20, 200, 100).Area(), 20000);
1154 EXPECT_EQ(IRect::MakeXYWH(0, 0, 100, 100).Area(), 10000);
1155 EXPECT_EQ(IRect::MakeXYWH(10, 20, 100, 100).Area(), 10000);
1156}
1157
1158TEST(RectTest, RectGetNormalizingTransform) {
1159 {
1160 // Checks for expected matrix values
1161
1162 auto r = Rect::MakeXYWH(100, 200, 200, 400);
1163
1164 EXPECT_EQ(r.GetNormalizingTransform(),
1165 Matrix::MakeScale({0.005, 0.0025, 1.0}) *
1166 Matrix::MakeTranslation({-100, -200}));
1167 }
1168
1169 {
1170 // Checks for expected transform of points relative to the rect
1171
1172 auto r = Rect::MakeLTRB(300, 500, 400, 700);
1173 auto m = r.GetNormalizingTransform();
1174
1175 // The 4 corners of the rect => (0, 0) to (1, 1)
1176 EXPECT_EQ(m * Point(300, 500), Point(0, 0));
1177 EXPECT_EQ(m * Point(400, 500), Point(1, 0));
1178 EXPECT_EQ(m * Point(400, 700), Point(1, 1));
1179 EXPECT_EQ(m * Point(300, 700), Point(0, 1));
1180
1181 // The center => (0.5, 0.5)
1182 EXPECT_EQ(m * Point(350, 600), Point(0.5, 0.5));
1183
1184 // Outside the 4 corners => (-1, -1) to (2, 2)
1185 EXPECT_EQ(m * Point(200, 300), Point(-1, -1));
1186 EXPECT_EQ(m * Point(500, 300), Point(2, -1));
1187 EXPECT_EQ(m * Point(500, 900), Point(2, 2));
1188 EXPECT_EQ(m * Point(200, 900), Point(-1, 2));
1189 }
1190
1191 {
1192 // Checks for behavior with empty rects
1193
1194 auto zero = Matrix::MakeScale({0.0, 0.0, 1.0});
1195
1196 // Empty for width and/or height == 0
1197 EXPECT_EQ(Rect::MakeXYWH(10, 10, 0, 10).GetNormalizingTransform(), zero);
1198 EXPECT_EQ(Rect::MakeXYWH(10, 10, 10, 0).GetNormalizingTransform(), zero);
1199 EXPECT_EQ(Rect::MakeXYWH(10, 10, 0, 0).GetNormalizingTransform(), zero);
1200
1201 // Empty for width and/or height < 0
1202 EXPECT_EQ(Rect::MakeXYWH(10, 10, -1, 10).GetNormalizingTransform(), zero);
1203 EXPECT_EQ(Rect::MakeXYWH(10, 10, 10, -1).GetNormalizingTransform(), zero);
1204 EXPECT_EQ(Rect::MakeXYWH(10, 10, -1, -1).GetNormalizingTransform(), zero);
1205 }
1206
1207 {
1208 // Checks for behavior with non-finite rects
1209
1210 auto z = Matrix::MakeScale({0.0, 0.0, 1.0});
1211 auto nan = std::numeric_limits<Scalar>::quiet_NaN();
1212 auto inf = std::numeric_limits<Scalar>::infinity();
1213
1214 // Non-finite for width and/or height == nan
1215 EXPECT_EQ(Rect::MakeXYWH(10, 10, nan, 10).GetNormalizingTransform(), z);
1216 EXPECT_EQ(Rect::MakeXYWH(10, 10, 10, nan).GetNormalizingTransform(), z);
1217 EXPECT_EQ(Rect::MakeXYWH(10, 10, nan, nan).GetNormalizingTransform(), z);
1218
1219 // Non-finite for width and/or height == inf
1220 EXPECT_EQ(Rect::MakeXYWH(10, 10, inf, 10).GetNormalizingTransform(), z);
1221 EXPECT_EQ(Rect::MakeXYWH(10, 10, 10, inf).GetNormalizingTransform(), z);
1222 EXPECT_EQ(Rect::MakeXYWH(10, 10, inf, inf).GetNormalizingTransform(), z);
1223
1224 // Non-finite for width and/or height == -inf
1225 EXPECT_EQ(Rect::MakeXYWH(10, 10, -inf, 10).GetNormalizingTransform(), z);
1226 EXPECT_EQ(Rect::MakeXYWH(10, 10, 10, -inf).GetNormalizingTransform(), z);
1227 EXPECT_EQ(Rect::MakeXYWH(10, 10, -inf, -inf).GetNormalizingTransform(), z);
1228
1229 // Non-finite for origin X and/or Y == nan
1230 EXPECT_EQ(Rect::MakeXYWH(nan, 10, 10, 10).GetNormalizingTransform(), z);
1231 EXPECT_EQ(Rect::MakeXYWH(10, nan, 10, 10).GetNormalizingTransform(), z);
1232 EXPECT_EQ(Rect::MakeXYWH(nan, nan, 10, 10).GetNormalizingTransform(), z);
1233
1234 // Non-finite for origin X and/or Y == inf
1235 EXPECT_EQ(Rect::MakeXYWH(inf, 10, 10, 10).GetNormalizingTransform(), z);
1236 EXPECT_EQ(Rect::MakeXYWH(10, inf, 10, 10).GetNormalizingTransform(), z);
1237 EXPECT_EQ(Rect::MakeXYWH(inf, inf, 10, 10).GetNormalizingTransform(), z);
1238
1239 // Non-finite for origin X and/or Y == -inf
1240 EXPECT_EQ(Rect::MakeXYWH(-inf, 10, 10, 10).GetNormalizingTransform(), z);
1241 EXPECT_EQ(Rect::MakeXYWH(10, -inf, 10, 10).GetNormalizingTransform(), z);
1242 EXPECT_EQ(Rect::MakeXYWH(-inf, -inf, 10, 10).GetNormalizingTransform(), z);
1243 }
1244}
1245
1246TEST(RectTest, IRectGetNormalizingTransform) {
1247 {
1248 // Checks for expected matrix values
1249
1250 auto r = IRect::MakeXYWH(100, 200, 200, 400);
1251
1252 EXPECT_EQ(r.GetNormalizingTransform(),
1253 Matrix::MakeScale({0.005, 0.0025, 1.0}) *
1254 Matrix::MakeTranslation({-100, -200}));
1255 }
1256
1257 {
1258 // Checks for expected transform of points relative to the rect
1259
1260 auto r = IRect::MakeLTRB(300, 500, 400, 700);
1261 auto m = r.GetNormalizingTransform();
1262
1263 // The 4 corners of the rect => (0, 0) to (1, 1)
1264 EXPECT_EQ(m * Point(300, 500), Point(0, 0));
1265 EXPECT_EQ(m * Point(400, 500), Point(1, 0));
1266 EXPECT_EQ(m * Point(400, 700), Point(1, 1));
1267 EXPECT_EQ(m * Point(300, 700), Point(0, 1));
1268
1269 // The center => (0.5, 0.5)
1270 EXPECT_EQ(m * Point(350, 600), Point(0.5, 0.5));
1271
1272 // Outside the 4 corners => (-1, -1) to (2, 2)
1273 EXPECT_EQ(m * Point(200, 300), Point(-1, -1));
1274 EXPECT_EQ(m * Point(500, 300), Point(2, -1));
1275 EXPECT_EQ(m * Point(500, 900), Point(2, 2));
1276 EXPECT_EQ(m * Point(200, 900), Point(-1, 2));
1277 }
1278
1279 {
1280 // Checks for behavior with empty rects
1281
1282 auto zero = Matrix::MakeScale({0.0, 0.0, 1.0});
1283
1284 // Empty for width and/or height == 0
1285 EXPECT_EQ(IRect::MakeXYWH(10, 10, 0, 10).GetNormalizingTransform(), zero);
1286 EXPECT_EQ(IRect::MakeXYWH(10, 10, 10, 0).GetNormalizingTransform(), zero);
1287 EXPECT_EQ(IRect::MakeXYWH(10, 10, 0, 0).GetNormalizingTransform(), zero);
1288
1289 // Empty for width and/or height < 0
1290 EXPECT_EQ(IRect::MakeXYWH(10, 10, -1, 10).GetNormalizingTransform(), zero);
1291 EXPECT_EQ(IRect::MakeXYWH(10, 10, 10, -1).GetNormalizingTransform(), zero);
1292 EXPECT_EQ(IRect::MakeXYWH(10, 10, -1, -1).GetNormalizingTransform(), zero);
1293 }
1294}
1295
1296TEST(RectTest, RectXYWHIsEmpty) {
1297 auto nan = std::numeric_limits<Scalar>::quiet_NaN();
1298
1299 // Non-empty
1300 EXPECT_FALSE(Rect::MakeXYWH(1.5, 2.3, 10.5, 7.2).IsEmpty());
1301
1302 // Empty both width and height both 0 or negative, in all combinations
1303 EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, 0.0, 0.0).IsEmpty());
1304 EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, -1.0, -1.0).IsEmpty());
1305 EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, 0.0, -1.0).IsEmpty());
1306 EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, -1.0, 0.0).IsEmpty());
1307
1308 // Empty for 0 or negative width or height (but not both at the same time)
1309 EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, 10.5, 0.0).IsEmpty());
1310 EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, 10.5, -1.0).IsEmpty());
1311 EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, 0.0, 7.2).IsEmpty());
1312 EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, -1.0, 7.2).IsEmpty());
1313
1314 // Empty for NaN in width or height or both
1315 EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, 10.5, nan).IsEmpty());
1316 EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, nan, 7.2).IsEmpty());
1317 EXPECT_TRUE(Rect::MakeXYWH(1.5, 2.3, nan, nan).IsEmpty());
1318}
1319
1320TEST(RectTest, IRectXYWHIsEmpty) {
1321 // Non-empty
1322 EXPECT_FALSE(IRect::MakeXYWH(1, 2, 10, 7).IsEmpty());
1323
1324 // Empty both width and height both 0 or negative, in all combinations
1325 EXPECT_TRUE(IRect::MakeXYWH(1, 2, 0, 0).IsEmpty());
1326 EXPECT_TRUE(IRect::MakeXYWH(1, 2, -1, -1).IsEmpty());
1327 EXPECT_TRUE(IRect::MakeXYWH(1, 2, -1, 0).IsEmpty());
1328 EXPECT_TRUE(IRect::MakeXYWH(1, 2, 0, -1).IsEmpty());
1329
1330 // Empty for 0 or negative width or height (but not both at the same time)
1331 EXPECT_TRUE(IRect::MakeXYWH(1, 2, 10, 0).IsEmpty());
1332 EXPECT_TRUE(IRect::MakeXYWH(1, 2, 10, -1).IsEmpty());
1333 EXPECT_TRUE(IRect::MakeXYWH(1, 2, 0, 7).IsEmpty());
1334 EXPECT_TRUE(IRect::MakeXYWH(1, 2, -1, 7).IsEmpty());
1335}
1336
1337TEST(RectTest, MakePointBoundsQuad) {
1338 Quad quad = {
1339 Point(10, 10),
1340 Point(20, 10),
1341 Point(10, 20),
1342 Point(20, 20),
1343 };
1344 std::optional<Rect> bounds = Rect::MakePointBounds(quad);
1345 EXPECT_TRUE(bounds.has_value());
1346 if (bounds.has_value()) {
1347 EXPECT_TRUE(RectNear(bounds.value(), Rect::MakeLTRB(10, 10, 20, 20)));
1348 }
1349}
1350
1351TEST(RectTest, IsSquare) {
1352 EXPECT_TRUE(Rect::MakeXYWH(10, 30, 20, 20).IsSquare());
1353 EXPECT_FALSE(Rect::MakeXYWH(10, 30, 20, 19).IsSquare());
1354 EXPECT_FALSE(Rect::MakeXYWH(10, 30, 19, 20).IsSquare());
1355 EXPECT_TRUE(Rect::MakeMaximum().IsSquare());
1356
1357 EXPECT_TRUE(IRect::MakeXYWH(10, 30, 20, 20).IsSquare());
1358 EXPECT_FALSE(IRect::MakeXYWH(10, 30, 20, 19).IsSquare());
1359 EXPECT_FALSE(IRect::MakeXYWH(10, 30, 19, 20).IsSquare());
1360 EXPECT_TRUE(IRect::MakeMaximum().IsSquare());
1361}
1362
1363TEST(RectTest, GetCenter) {
1364 EXPECT_EQ(Rect::MakeXYWH(10, 30, 20, 20).GetCenter(), Point(20, 40));
1365 EXPECT_EQ(Rect::MakeXYWH(10, 30, 20, 19).GetCenter(), Point(20, 39.5));
1366 EXPECT_EQ(Rect::MakeMaximum().GetCenter(), Point(0, 0));
1367
1368 // Note that we expect a Point as the answer from an IRect
1369 EXPECT_EQ(IRect::MakeXYWH(10, 30, 20, 20).GetCenter(), Point(20, 40));
1370 EXPECT_EQ(IRect::MakeXYWH(10, 30, 20, 19).GetCenter(), Point(20, 39.5));
1371 EXPECT_EQ(IRect::MakeMaximum().GetCenter(), Point(0, 0));
1372}
1373
1374TEST(RectTest, RectExpand) {
1375 auto rect = Rect::MakeLTRB(100, 100, 200, 200);
1376
1377 // Expand(T amount)
1378 EXPECT_EQ(rect.Expand(10), Rect::MakeLTRB(90, 90, 210, 210));
1379 EXPECT_EQ(rect.Expand(-10), Rect::MakeLTRB(110, 110, 190, 190));
1380
1381 // Expand(amount, amount)
1382 EXPECT_EQ(rect.Expand(10, 10), Rect::MakeLTRB(90, 90, 210, 210));
1383 EXPECT_EQ(rect.Expand(10, -10), Rect::MakeLTRB(90, 110, 210, 190));
1384 EXPECT_EQ(rect.Expand(-10, 10), Rect::MakeLTRB(110, 90, 190, 210));
1385 EXPECT_EQ(rect.Expand(-10, -10), Rect::MakeLTRB(110, 110, 190, 190));
1386
1387 // Expand(amount, amount, amount, amount)
1388 EXPECT_EQ(rect.Expand(10, 20, 30, 40), Rect::MakeLTRB(90, 80, 230, 240));
1389 EXPECT_EQ(rect.Expand(-10, 20, 30, 40), Rect::MakeLTRB(110, 80, 230, 240));
1390 EXPECT_EQ(rect.Expand(10, -20, 30, 40), Rect::MakeLTRB(90, 120, 230, 240));
1391 EXPECT_EQ(rect.Expand(10, 20, -30, 40), Rect::MakeLTRB(90, 80, 170, 240));
1392 EXPECT_EQ(rect.Expand(10, 20, 30, -40), Rect::MakeLTRB(90, 80, 230, 160));
1393
1394 // Expand(Point amount)
1395 EXPECT_EQ(rect.Expand(Point{10, 10}), Rect::MakeLTRB(90, 90, 210, 210));
1396 EXPECT_EQ(rect.Expand(Point{10, -10}), Rect::MakeLTRB(90, 110, 210, 190));
1397 EXPECT_EQ(rect.Expand(Point{-10, 10}), Rect::MakeLTRB(110, 90, 190, 210));
1398 EXPECT_EQ(rect.Expand(Point{-10, -10}), Rect::MakeLTRB(110, 110, 190, 190));
1399
1400 // Expand(Size amount)
1401 EXPECT_EQ(rect.Expand(Size{10, 10}), Rect::MakeLTRB(90, 90, 210, 210));
1402 EXPECT_EQ(rect.Expand(Size{10, -10}), Rect::MakeLTRB(90, 110, 210, 190));
1403 EXPECT_EQ(rect.Expand(Size{-10, 10}), Rect::MakeLTRB(110, 90, 190, 210));
1404 EXPECT_EQ(rect.Expand(Size{-10, -10}), Rect::MakeLTRB(110, 110, 190, 190));
1405}
1406
1407TEST(RectTest, IRectExpand) {
1408 auto rect = IRect::MakeLTRB(100, 100, 200, 200);
1409
1410 // Expand(T amount)
1411 EXPECT_EQ(rect.Expand(10), IRect::MakeLTRB(90, 90, 210, 210));
1412 EXPECT_EQ(rect.Expand(-10), IRect::MakeLTRB(110, 110, 190, 190));
1413
1414 // Expand(amount, amount)
1415 EXPECT_EQ(rect.Expand(10, 10), IRect::MakeLTRB(90, 90, 210, 210));
1416 EXPECT_EQ(rect.Expand(10, -10), IRect::MakeLTRB(90, 110, 210, 190));
1417 EXPECT_EQ(rect.Expand(-10, 10), IRect::MakeLTRB(110, 90, 190, 210));
1418 EXPECT_EQ(rect.Expand(-10, -10), IRect::MakeLTRB(110, 110, 190, 190));
1419
1420 // Expand(amount, amount, amount, amount)
1421 EXPECT_EQ(rect.Expand(10, 20, 30, 40), IRect::MakeLTRB(90, 80, 230, 240));
1422 EXPECT_EQ(rect.Expand(-10, 20, 30, 40), IRect::MakeLTRB(110, 80, 230, 240));
1423 EXPECT_EQ(rect.Expand(10, -20, 30, 40), IRect::MakeLTRB(90, 120, 230, 240));
1424 EXPECT_EQ(rect.Expand(10, 20, -30, 40), IRect::MakeLTRB(90, 80, 170, 240));
1425 EXPECT_EQ(rect.Expand(10, 20, 30, -40), IRect::MakeLTRB(90, 80, 230, 160));
1426
1427 // Expand(IPoint amount)
1428 EXPECT_EQ(rect.Expand(IPoint{10, 10}), IRect::MakeLTRB(90, 90, 210, 210));
1429 EXPECT_EQ(rect.Expand(IPoint{10, -10}), IRect::MakeLTRB(90, 110, 210, 190));
1430 EXPECT_EQ(rect.Expand(IPoint{-10, 10}), IRect::MakeLTRB(110, 90, 190, 210));
1431 EXPECT_EQ(rect.Expand(IPoint{-10, -10}), IRect::MakeLTRB(110, 110, 190, 190));
1432
1433 // Expand(ISize amount)
1434 EXPECT_EQ(rect.Expand(ISize{10, 10}), IRect::MakeLTRB(90, 90, 210, 210));
1435 EXPECT_EQ(rect.Expand(ISize{10, -10}), IRect::MakeLTRB(90, 110, 210, 190));
1436 EXPECT_EQ(rect.Expand(ISize{-10, 10}), IRect::MakeLTRB(110, 90, 190, 210));
1437 EXPECT_EQ(rect.Expand(ISize{-10, -10}), IRect::MakeLTRB(110, 110, 190, 190));
1438}
1439
1440TEST(RectTest, RectExpandToMinTransformedSizeNullForScaledToZero) {
1441 auto rect = Rect();
1442 auto transform = Matrix::MakeScale(Vector3(0.0f, 1.0f));
1443 EXPECT_EQ(rect.ExpandToMinTransformedSize({1.0f, 1.0f}, transform),
1444 std::nullopt);
1445}
1446
1447TEST(RectTest, RectExpandToMinTransformedSizeReturnsUnmodifiedWhenLargeEnough) {
1448 Rect rect = Rect::MakeXYWH(0, 0, 10, 20);
1449 auto transform = Matrix::MakeScale(Vector3(0.5f, 0.5f));
1450 EXPECT_EQ(rect.ExpandToMinTransformedSize({1.0f, 1.0f}, transform), rect);
1451}
1452
1454 RectTest,
1455 RectExpandToMinTransformedSizeReturnsUnmodifiedWhenLargeEnoughWithFractionalSize) {
1456 // Regression test for https://github.com/flutter/flutter/issues/189807.
1457 Rect rect = Rect::MakeXYWH(100.0f, 50.0f, 64.2f, 40.0f);
1458 auto transform = Matrix();
1459 EXPECT_EQ(rect.ExpandToMinTransformedSize({1.0f, 1.0f}, transform), rect);
1460}
1461
1462TEST(RectTest, RectExpandToMinTransformedSizeRectWithIdentityTransform) {
1463 Size size = Size(2.0f, 2.0f);
1464 Rect rect = Rect::MakeEllipseBounds(Point(), size * 0.5f);
1465
1466 auto transform = Matrix();
1467
1468 // Expand to a width and height less than the original size.
1469 {
1470 auto expanded = rect.ExpandToMinTransformedSize({0.5f, 0.75f}, transform);
1471 ASSERT_TRUE(expanded.has_value());
1472 if (expanded.has_value()) {
1473 EXPECT_EQ(expanded.value().GetSize(), size);
1474 }
1475 }
1476
1477 // Expand to a minimum width. Minimum height is less than the original size.
1478 {
1479 auto expanded = rect.ExpandToMinTransformedSize({4.0f, 1.5f}, transform);
1480 ASSERT_TRUE(expanded.has_value());
1481 if (expanded.has_value()) {
1482 EXPECT_EQ(expanded.value().GetSize(), Size(4.0f, 2.0f));
1483 }
1484 }
1485
1486 // Expand to a minimum height. Minimum width is less than the original size.
1487 {
1488 auto expanded = rect.ExpandToMinTransformedSize({1.5f, 4.0f}, transform);
1489 ASSERT_TRUE(expanded.has_value());
1490 if (expanded.has_value()) {
1491 EXPECT_EQ(expanded.value().GetSize(), Size(2.0f, 4.0f));
1492 }
1493 }
1494
1495 // Expand to a minimum width and height.
1496 {
1497 auto expanded = rect.ExpandToMinTransformedSize({3.0f, 4.0f}, transform);
1498 ASSERT_TRUE(expanded.has_value());
1499 if (expanded.has_value()) {
1500 EXPECT_EQ(expanded.value().GetSize(), Size(3.0f, 4.0f));
1501 }
1502 }
1503}
1504
1505TEST(RectTest, RectExpandToMinTransformedSizeRectWithScalingTransform) {
1506 Size size = Size(2.0f, 2.0f);
1507 Rect rect = Rect::MakeEllipseBounds(Point(), size * 0.5f);
1508
1509 // Scale by 2x in the X direction and 3x in the Y direction.
1510 // Transformed rect size is (4.0, 6.0).
1511 auto transform = Matrix::MakeScale(Vector3(2.0f, 3.0f));
1512
1513 // Expand to a transformed width and height less than the transformed size of
1514 // the original rectangle.
1515 {
1516 auto expanded = rect.ExpandToMinTransformedSize({3.0f, 4.0f}, transform);
1517 ASSERT_TRUE(expanded.has_value());
1518 if (expanded.has_value()) {
1519 EXPECT_EQ(expanded.value().GetSize(), size);
1520 }
1521 }
1522
1523 // Expand to 5.0 transformed width.
1524 // This is equal to 5.0 / 2.0 = 2.5 local width.
1525 {
1526 auto expanded = rect.ExpandToMinTransformedSize({5.0f, 4.0f}, transform);
1527 ASSERT_TRUE(expanded.has_value());
1528 if (expanded.has_value()) {
1529 EXPECT_EQ(expanded.value().GetSize(), Size(2.5f, 2.0f));
1530 }
1531 }
1532
1533 // Expand to 9.0 transformed height.
1534 // This is equal to 9.0 / 3.0 = 3.0 local height.
1535 {
1536 auto expanded = rect.ExpandToMinTransformedSize({3.0f, 9.0f}, transform);
1537 ASSERT_TRUE(expanded.has_value());
1538 if (expanded.has_value()) {
1539 EXPECT_EQ(expanded.value().GetSize(), Size(2.0f, 3.0));
1540 }
1541 }
1542
1543 // Expand both width and height.
1544 {
1545 auto expanded = rect.ExpandToMinTransformedSize({5.0f, 9.0f}, transform);
1546 ASSERT_TRUE(expanded.has_value());
1547 if (expanded.has_value()) {
1548 EXPECT_EQ(expanded.value().GetSize(), Size(2.5f, 3.0f));
1549 }
1550 }
1551}
1552
1553TEST(RectTest, ContainsFloatingPoint) {
1554 auto rect1 =
1555 Rect::MakeXYWH(472.599945f, 440.999969f, 1102.80005f, 654.000061f);
1556 auto rect2 = Rect::MakeXYWH(724.f, 618.f, 600.f, 300.f);
1557 EXPECT_TRUE(rect1.Contains(rect2));
1558}
1559
1560TEST(RectTest, FloatContainsInteger) {
1561 auto rect1 =
1562 Rect::MakeLTRB(472.599945f, 440.999969f, 1574.80005f, 1094.000000f);
1563 EXPECT_TRUE(rect1.Contains(IRect::MakeLTRB(473, 441, 1574, 1094)));
1564
1565 // Now test failure to contain same rect expanded by 1 on each side
1566 EXPECT_FALSE(rect1.Contains(IRect::MakeLTRB(472, 441, 1574, 1094)));
1567 EXPECT_FALSE(rect1.Contains(IRect::MakeLTRB(473, 440, 1574, 1094)));
1568 EXPECT_FALSE(rect1.Contains(IRect::MakeLTRB(473, 441, 1575, 1094)));
1569 EXPECT_FALSE(rect1.Contains(IRect::MakeLTRB(473, 441, 1574, 1095)));
1570}
1571
1572template <typename R>
1573static constexpr inline R flip_lr(R rect) {
1574 return R::MakeLTRB(rect.GetRight(), rect.GetTop(), //
1575 rect.GetLeft(), rect.GetBottom());
1576}
1577
1578template <typename R>
1579static constexpr inline R flip_tb(R rect) {
1580 return R::MakeLTRB(rect.GetLeft(), rect.GetBottom(), //
1581 rect.GetRight(), rect.GetTop());
1582}
1583
1584template <typename R>
1585static constexpr inline R flip_lrtb(R rect) {
1586 return flip_lr(flip_tb(rect));
1587}
1588
1589static constexpr inline Rect swap_nan(const Rect& rect, int index) {
1590 Scalar nan = std::numeric_limits<Scalar>::quiet_NaN();
1591 FML_DCHECK(index >= 0 && index <= 15);
1592 Scalar l = ((index & (1 << 0)) != 0) ? nan : rect.GetLeft();
1593 Scalar t = ((index & (1 << 1)) != 0) ? nan : rect.GetTop();
1594 Scalar r = ((index & (1 << 2)) != 0) ? nan : rect.GetRight();
1595 Scalar b = ((index & (1 << 3)) != 0) ? nan : rect.GetBottom();
1596 return Rect::MakeLTRB(l, t, r, b);
1597}
1598
1599static constexpr inline Point swap_nan(const Point& point, int index) {
1600 Scalar nan = std::numeric_limits<Scalar>::quiet_NaN();
1601 FML_DCHECK(index >= 0 && index <= 3);
1602 Scalar x = ((index & (1 << 0)) != 0) ? nan : point.x;
1603 Scalar y = ((index & (1 << 1)) != 0) ? nan : point.y;
1604 return Point(x, y);
1605}
1606
1607TEST(RectTest, RectUnion) {
1608 auto check_nans = [](const Rect& a, const Rect& b, const std::string& label) {
1609 ASSERT_TRUE(a.IsFinite()) << label;
1610 ASSERT_TRUE(b.IsFinite()) << label;
1611 ASSERT_FALSE(a.Union(b).IsEmpty());
1612
1613 for (int i = 1; i < 16; i++) {
1614 // NaN in a produces b
1615 EXPECT_EQ(swap_nan(a, i).Union(b), b) << label << ", index = " << i;
1616 // NaN in b produces a
1617 EXPECT_EQ(a.Union(swap_nan(b, i)), a) << label << ", index = " << i;
1618 // NaN in both is empty
1619 for (int j = 1; j < 16; j++) {
1620 EXPECT_TRUE(swap_nan(a, i).Union(swap_nan(b, j)).IsEmpty())
1621 << label << ", indices = " << i << ", " << j;
1622 }
1623 }
1624 };
1625
1626 auto check_empty_flips = [](const Rect& a, const Rect& b,
1627 const std::string& label) {
1628 ASSERT_FALSE(a.IsEmpty());
1629 // b is allowed to be empty
1630
1631 // unflipped a vs flipped (empty) b yields a
1632 EXPECT_EQ(a.Union(flip_lr(b)), a) << label;
1633 EXPECT_EQ(a.Union(flip_tb(b)), a) << label;
1634 EXPECT_EQ(a.Union(flip_lrtb(b)), a) << label;
1635
1636 // flipped (empty) a vs unflipped b yields b
1637 EXPECT_EQ(flip_lr(a).Union(b), b) << label;
1638 EXPECT_EQ(flip_tb(a).Union(b), b) << label;
1639 EXPECT_EQ(flip_lrtb(a).Union(b), b) << label;
1640
1641 // flipped (empty) a vs flipped (empty) b yields empty
1642 EXPECT_TRUE(flip_lr(a).Union(flip_lr(b)).IsEmpty()) << label;
1643 EXPECT_TRUE(flip_tb(a).Union(flip_tb(b)).IsEmpty()) << label;
1644 EXPECT_TRUE(flip_lrtb(a).Union(flip_lrtb(b)).IsEmpty()) << label;
1645 };
1646
1647 auto test = [&check_nans, &check_empty_flips](const Rect& a, const Rect& b,
1648 const Rect& result) {
1649 ASSERT_FALSE(a.IsEmpty()) << a;
1650 // b is allowed to be empty
1651
1652 std::stringstream stream;
1653 stream << a << " union " << b;
1654 auto label = stream.str();
1655
1656 EXPECT_EQ(a.Union(b), result) << label;
1657 EXPECT_EQ(b.Union(a), result) << label;
1658 check_empty_flips(a, b, label);
1659 check_nans(a, b, label);
1660 };
1661
1662 {
1663 auto a = Rect::MakeXYWH(100, 100, 100, 100);
1664 auto b = Rect::MakeXYWH(0, 0, 0, 0);
1665 auto expected = Rect::MakeXYWH(100, 100, 100, 100);
1666 test(a, b, expected);
1667 }
1668
1669 {
1670 auto a = Rect::MakeXYWH(100, 100, 100, 100);
1671 auto b = Rect::MakeXYWH(0, 0, 1, 1);
1672 auto expected = Rect::MakeXYWH(0, 0, 200, 200);
1673 test(a, b, expected);
1674 }
1675
1676 {
1677 auto a = Rect::MakeXYWH(100, 100, 100, 100);
1678 auto b = Rect::MakeXYWH(10, 10, 1, 1);
1679 auto expected = Rect::MakeXYWH(10, 10, 190, 190);
1680 test(a, b, expected);
1681 }
1682
1683 {
1684 auto a = Rect::MakeXYWH(0, 0, 100, 100);
1685 auto b = Rect::MakeXYWH(10, 10, 100, 100);
1686 auto expected = Rect::MakeXYWH(0, 0, 110, 110);
1687 test(a, b, expected);
1688 }
1689
1690 {
1691 auto a = Rect::MakeXYWH(0, 0, 100, 100);
1692 auto b = Rect::MakeXYWH(100, 100, 100, 100);
1693 auto expected = Rect::MakeXYWH(0, 0, 200, 200);
1694 test(a, b, expected);
1695 }
1696}
1697
1698TEST(RectTest, OptRectUnion) {
1699 auto a = Rect::MakeLTRB(0, 0, 100, 100);
1700 auto b = Rect::MakeLTRB(100, 100, 200, 200);
1701 auto c = Rect::MakeLTRB(100, 0, 200, 100);
1702
1703 // NullOpt, NullOpt
1704 EXPECT_FALSE(Rect::Union(std::nullopt, std::nullopt).has_value());
1705 EXPECT_EQ(Rect::Union(std::nullopt, std::nullopt), std::nullopt);
1706
1707 auto test1 = [](const Rect& r) {
1708 // Rect, NullOpt
1709 EXPECT_EQ(Rect::Union(r, std::nullopt), r);
1710
1711 // OptRect, NullOpt
1712 EXPECT_TRUE(Rect::Union(std::optional(r), std::nullopt).has_value());
1713 EXPECT_EQ(Rect::Union(std::optional(r), std::nullopt).value(), r);
1714
1715 // NullOpt, Rect
1716 EXPECT_EQ(Rect::Union(std::nullopt, r), r);
1717
1718 // NullOpt, OptRect
1719 EXPECT_TRUE(Rect::Union(std::nullopt, std::optional(r)).has_value());
1720 EXPECT_EQ(Rect::Union(std::nullopt, std::optional(r)).value(), r);
1721 };
1722
1723 test1(a);
1724 test1(b);
1725 test1(c);
1726
1727 auto test2 = [](const Rect& a, const Rect& b, const Rect& u) {
1728 ASSERT_EQ(a.Union(b), u);
1729
1730 // Rect, OptRect
1731 EXPECT_EQ(Rect::Union(a, std::optional(b)), u);
1732
1733 // OptRect, Rect
1734 EXPECT_EQ(Rect::Union(std::optional(a), b), u);
1735
1736 // OptRect, OptRect
1737 EXPECT_TRUE(Rect::Union(std::optional(a), std::optional(b)).has_value());
1738 EXPECT_EQ(Rect::Union(std::optional(a), std::optional(b)).value(), u);
1739 };
1740
1741 test2(a, b, Rect::MakeLTRB(0, 0, 200, 200));
1742 test2(a, c, Rect::MakeLTRB(0, 0, 200, 100));
1743 test2(b, c, Rect::MakeLTRB(100, 0, 200, 200));
1744}
1745
1746TEST(RectTest, IRectUnion) {
1747 auto check_empty_flips = [](const IRect& a, const IRect& b,
1748 const std::string& label) {
1749 ASSERT_FALSE(a.IsEmpty());
1750 // b is allowed to be empty
1751
1752 // unflipped a vs flipped (empty) b yields a
1753 EXPECT_EQ(a.Union(flip_lr(b)), a) << label;
1754 EXPECT_EQ(a.Union(flip_tb(b)), a) << label;
1755 EXPECT_EQ(a.Union(flip_lrtb(b)), a) << label;
1756
1757 // flipped (empty) a vs unflipped b yields b
1758 EXPECT_EQ(flip_lr(a).Union(b), b) << label;
1759 EXPECT_EQ(flip_tb(a).Union(b), b) << label;
1760 EXPECT_EQ(flip_lrtb(a).Union(b), b) << label;
1761
1762 // flipped (empty) a vs flipped (empty) b yields empty
1763 EXPECT_TRUE(flip_lr(a).Union(flip_lr(b)).IsEmpty()) << label;
1764 EXPECT_TRUE(flip_tb(a).Union(flip_tb(b)).IsEmpty()) << label;
1765 EXPECT_TRUE(flip_lrtb(a).Union(flip_lrtb(b)).IsEmpty()) << label;
1766 };
1767
1768 auto test = [&check_empty_flips](const IRect& a, const IRect& b,
1769 const IRect& result) {
1770 ASSERT_FALSE(a.IsEmpty()) << a;
1771 // b is allowed to be empty
1772
1773 std::stringstream stream;
1774 stream << a << " union " << b;
1775 auto label = stream.str();
1776
1777 EXPECT_EQ(a.Union(b), result) << label;
1778 EXPECT_EQ(b.Union(a), result) << label;
1779 check_empty_flips(a, b, label);
1780 };
1781
1782 {
1783 auto a = IRect::MakeXYWH(100, 100, 100, 100);
1784 auto b = IRect::MakeXYWH(0, 0, 0, 0);
1785 auto expected = IRect::MakeXYWH(100, 100, 100, 100);
1786 test(a, b, expected);
1787 }
1788
1789 {
1790 auto a = IRect::MakeXYWH(100, 100, 100, 100);
1791 auto b = IRect::MakeXYWH(0, 0, 1, 1);
1792 auto expected = IRect::MakeXYWH(0, 0, 200, 200);
1793 test(a, b, expected);
1794 }
1795
1796 {
1797 auto a = IRect::MakeXYWH(100, 100, 100, 100);
1798 auto b = IRect::MakeXYWH(10, 10, 1, 1);
1799 auto expected = IRect::MakeXYWH(10, 10, 190, 190);
1800 test(a, b, expected);
1801 }
1802
1803 {
1804 auto a = IRect::MakeXYWH(0, 0, 100, 100);
1805 auto b = IRect::MakeXYWH(10, 10, 100, 100);
1806 auto expected = IRect::MakeXYWH(0, 0, 110, 110);
1807 test(a, b, expected);
1808 }
1809
1810 {
1811 auto a = IRect::MakeXYWH(0, 0, 100, 100);
1812 auto b = IRect::MakeXYWH(100, 100, 100, 100);
1813 auto expected = IRect::MakeXYWH(0, 0, 200, 200);
1814 test(a, b, expected);
1815 }
1816}
1817
1818TEST(RectTest, OptIRectUnion) {
1819 auto a = IRect::MakeLTRB(0, 0, 100, 100);
1820 auto b = IRect::MakeLTRB(100, 100, 200, 200);
1821 auto c = IRect::MakeLTRB(100, 0, 200, 100);
1822
1823 // NullOpt, NullOpt
1824 EXPECT_FALSE(IRect::Union(std::nullopt, std::nullopt).has_value());
1825 EXPECT_EQ(IRect::Union(std::nullopt, std::nullopt), std::nullopt);
1826
1827 auto test1 = [](const IRect& r) {
1828 // Rect, NullOpt
1829 EXPECT_EQ(IRect::Union(r, std::nullopt), r);
1830
1831 // OptRect, NullOpt
1832 EXPECT_TRUE(IRect::Union(std::optional(r), std::nullopt).has_value());
1833 EXPECT_EQ(IRect::Union(std::optional(r), std::nullopt).value(), r);
1834
1835 // NullOpt, Rect
1836 EXPECT_EQ(IRect::Union(std::nullopt, r), r);
1837
1838 // NullOpt, OptRect
1839 EXPECT_TRUE(IRect::Union(std::nullopt, std::optional(r)).has_value());
1840 EXPECT_EQ(IRect::Union(std::nullopt, std::optional(r)).value(), r);
1841 };
1842
1843 test1(a);
1844 test1(b);
1845 test1(c);
1846
1847 auto test2 = [](const IRect& a, const IRect& b, const IRect& u) {
1848 ASSERT_EQ(a.Union(b), u);
1849
1850 // Rect, OptRect
1851 EXPECT_EQ(IRect::Union(a, std::optional(b)), u);
1852
1853 // OptRect, Rect
1854 EXPECT_EQ(IRect::Union(std::optional(a), b), u);
1855
1856 // OptRect, OptRect
1857 EXPECT_TRUE(IRect::Union(std::optional(a), std::optional(b)).has_value());
1858 EXPECT_EQ(IRect::Union(std::optional(a), std::optional(b)).value(), u);
1859 };
1860
1861 test2(a, b, IRect::MakeLTRB(0, 0, 200, 200));
1862 test2(a, c, IRect::MakeLTRB(0, 0, 200, 100));
1863 test2(b, c, IRect::MakeLTRB(100, 0, 200, 200));
1864}
1865
1866TEST(RectTest, RectIntersection) {
1867 auto check_nans = [](const Rect& a, const Rect& b, const std::string& label) {
1868 ASSERT_TRUE(a.IsFinite()) << label;
1869 ASSERT_TRUE(b.IsFinite()) << label;
1870
1871 for (int i = 1; i < 16; i++) {
1872 // NaN in a produces empty
1873 EXPECT_FALSE(swap_nan(a, i).Intersection(b).has_value())
1874 << label << ", index = " << i;
1875 // NaN in b produces empty
1876 EXPECT_FALSE(a.Intersection(swap_nan(b, i)).has_value())
1877 << label << ", index = " << i;
1878 // NaN in both is empty
1879 for (int j = 1; j < 16; j++) {
1880 EXPECT_FALSE(swap_nan(a, i).Intersection(swap_nan(b, j)).has_value())
1881 << label << ", indices = " << i << ", " << j;
1882 }
1883 }
1884 };
1885
1886 auto check_empty_flips = [](const Rect& a, const Rect& b,
1887 const std::string& label) {
1888 ASSERT_FALSE(a.IsEmpty());
1889 // b is allowed to be empty
1890
1891 // unflipped a vs flipped (empty) b yields a
1892 EXPECT_FALSE(a.Intersection(flip_lr(b)).has_value()) << label;
1893 EXPECT_TRUE(a.IntersectionOrEmpty(flip_lr(b)).IsEmpty()) << label;
1894 EXPECT_FALSE(a.Intersection(flip_tb(b)).has_value()) << label;
1895 EXPECT_TRUE(a.IntersectionOrEmpty(flip_tb(b)).IsEmpty()) << label;
1896 EXPECT_FALSE(a.Intersection(flip_lrtb(b)).has_value()) << label;
1897 EXPECT_TRUE(a.IntersectionOrEmpty(flip_lrtb(b)).IsEmpty()) << label;
1898
1899 // flipped (empty) a vs unflipped b yields b
1900 EXPECT_FALSE(flip_lr(a).Intersection(b).has_value()) << label;
1901 EXPECT_TRUE(flip_lr(a).IntersectionOrEmpty(b).IsEmpty()) << label;
1902 EXPECT_FALSE(flip_tb(a).Intersection(b).has_value()) << label;
1903 EXPECT_TRUE(flip_tb(a).IntersectionOrEmpty(b).IsEmpty()) << label;
1904 EXPECT_FALSE(flip_lrtb(a).Intersection(b).has_value()) << label;
1905 EXPECT_TRUE(flip_lrtb(a).IntersectionOrEmpty(b).IsEmpty()) << label;
1906
1907 // flipped (empty) a vs flipped (empty) b yields empty
1908 EXPECT_FALSE(flip_lr(a).Intersection(flip_lr(b)).has_value()) << label;
1909 EXPECT_TRUE(flip_lr(a).IntersectionOrEmpty(flip_lr(b)).IsEmpty()) << label;
1910 EXPECT_FALSE(flip_tb(a).Intersection(flip_tb(b)).has_value()) << label;
1911 EXPECT_TRUE(flip_tb(a).IntersectionOrEmpty(flip_tb(b)).IsEmpty()) << label;
1912 EXPECT_FALSE(flip_lrtb(a).Intersection(flip_lrtb(b)).has_value()) << label;
1913 EXPECT_TRUE(flip_lrtb(a).IntersectionOrEmpty(flip_lrtb(b)).IsEmpty())
1914 << label;
1915 };
1916
1917 auto test_non_empty = [&check_nans, &check_empty_flips](
1918 const Rect& a, const Rect& b, const Rect& result) {
1919 ASSERT_FALSE(a.IsEmpty()) << a;
1920 // b is allowed to be empty
1921
1922 std::stringstream stream;
1923 stream << a << " union " << b;
1924 auto label = stream.str();
1925
1926 EXPECT_TRUE(a.Intersection(b).has_value()) << label;
1927 EXPECT_TRUE(b.Intersection(a).has_value()) << label;
1928 EXPECT_EQ(a.Intersection(b), result) << label;
1929 EXPECT_EQ(b.Intersection(a), result) << label;
1930 check_empty_flips(a, b, label);
1931 check_nans(a, b, label);
1932 };
1933
1934 auto test_empty = [&check_nans, &check_empty_flips](const Rect& a,
1935 const Rect& b) {
1936 ASSERT_FALSE(a.IsEmpty()) << a;
1937 // b is allowed to be empty
1938
1939 std::stringstream stream;
1940 stream << a << " union " << b;
1941 auto label = stream.str();
1942
1943 EXPECT_FALSE(a.Intersection(b).has_value()) << label;
1944 EXPECT_TRUE(a.IntersectionOrEmpty(b).IsEmpty()) << label;
1945 EXPECT_FALSE(b.Intersection(a).has_value()) << label;
1946 EXPECT_TRUE(b.IntersectionOrEmpty(a).IsEmpty()) << label;
1947 check_empty_flips(a, b, label);
1948 check_nans(a, b, label);
1949 };
1950
1951 {
1952 auto a = Rect::MakeXYWH(100, 100, 100, 100);
1953 auto b = Rect::MakeXYWH(0, 0, 0, 0);
1954
1955 test_empty(a, b);
1956 }
1957
1958 {
1959 auto a = Rect::MakeXYWH(100, 100, 100, 100);
1960 auto b = Rect::MakeXYWH(10, 10, 0, 0);
1961
1962 test_empty(a, b);
1963 }
1964
1965 {
1966 auto a = Rect::MakeXYWH(0, 0, 100, 100);
1967 auto b = Rect::MakeXYWH(10, 10, 100, 100);
1968 auto expected = Rect::MakeXYWH(10, 10, 90, 90);
1969
1970 test_non_empty(a, b, expected);
1971 }
1972
1973 {
1974 auto a = Rect::MakeXYWH(0, 0, 100, 100);
1975 auto b = Rect::MakeXYWH(100, 100, 100, 100);
1976
1977 test_empty(a, b);
1978 }
1979
1980 {
1981 auto a = Rect::MakeMaximum();
1982 auto b = Rect::MakeXYWH(10, 10, 300, 300);
1983
1984 test_non_empty(a, b, b);
1985 }
1986
1987 {
1988 auto a = Rect::MakeMaximum();
1989 auto b = Rect::MakeMaximum();
1990
1991 test_non_empty(a, b, Rect::MakeMaximum());
1992 }
1993}
1994
1995TEST(RectTest, OptRectIntersection) {
1996 auto a = Rect::MakeLTRB(0, 0, 110, 110);
1997 auto b = Rect::MakeLTRB(100, 100, 200, 200);
1998 auto c = Rect::MakeLTRB(100, 0, 200, 110);
1999
2000 // NullOpt, NullOpt
2001 EXPECT_FALSE(Rect::Intersection(std::nullopt, std::nullopt).has_value());
2002 EXPECT_EQ(Rect::Intersection(std::nullopt, std::nullopt), std::nullopt);
2003
2004 auto test1 = [](const Rect& r) {
2005 // Rect, NullOpt
2006 EXPECT_TRUE(Rect::Intersection(r, std::nullopt).has_value());
2007 EXPECT_EQ(Rect::Intersection(r, std::nullopt).value(), r);
2008
2009 // OptRect, NullOpt
2010 EXPECT_TRUE(Rect::Intersection(std::optional(r), std::nullopt).has_value());
2011 EXPECT_EQ(Rect::Intersection(std::optional(r), std::nullopt).value(), r);
2012
2013 // NullOpt, Rect
2014 EXPECT_TRUE(Rect::Intersection(std::nullopt, r).has_value());
2015 EXPECT_EQ(Rect::Intersection(std::nullopt, r).value(), r);
2016
2017 // NullOpt, OptRect
2018 EXPECT_TRUE(Rect::Intersection(std::nullopt, std::optional(r)).has_value());
2019 EXPECT_EQ(Rect::Intersection(std::nullopt, std::optional(r)).value(), r);
2020 };
2021
2022 test1(a);
2023 test1(b);
2024 test1(c);
2025
2026 auto test2 = [](const Rect& a, const Rect& b, const Rect& i) {
2027 ASSERT_EQ(a.Intersection(b), i);
2028
2029 // Rect, OptRect
2030 EXPECT_TRUE(Rect::Intersection(a, std::optional(b)).has_value());
2031 EXPECT_EQ(Rect::Intersection(a, std::optional(b)).value(), i);
2032
2033 // OptRect, Rect
2034 EXPECT_TRUE(Rect::Intersection(std::optional(a), b).has_value());
2035 EXPECT_EQ(Rect::Intersection(std::optional(a), b).value(), i);
2036
2037 // OptRect, OptRect
2038 EXPECT_TRUE(
2039 Rect::Intersection(std::optional(a), std::optional(b)).has_value());
2040 EXPECT_EQ(Rect::Intersection(std::optional(a), std::optional(b)).value(),
2041 i);
2042 };
2043
2044 test2(a, b, Rect::MakeLTRB(100, 100, 110, 110));
2045 test2(a, c, Rect::MakeLTRB(100, 0, 110, 110));
2046 test2(b, c, Rect::MakeLTRB(100, 100, 200, 110));
2047}
2048
2049TEST(RectTest, IRectIntersection) {
2050 auto check_empty_flips = [](const IRect& a, const IRect& b,
2051 const std::string& label) {
2052 ASSERT_FALSE(a.IsEmpty());
2053 // b is allowed to be empty
2054
2055 // unflipped a vs flipped (empty) b yields a
2056 EXPECT_FALSE(a.Intersection(flip_lr(b)).has_value()) << label;
2057 EXPECT_FALSE(a.Intersection(flip_tb(b)).has_value()) << label;
2058 EXPECT_FALSE(a.Intersection(flip_lrtb(b)).has_value()) << label;
2059
2060 // flipped (empty) a vs unflipped b yields b
2061 EXPECT_FALSE(flip_lr(a).Intersection(b).has_value()) << label;
2062 EXPECT_FALSE(flip_tb(a).Intersection(b).has_value()) << label;
2063 EXPECT_FALSE(flip_lrtb(a).Intersection(b).has_value()) << label;
2064
2065 // flipped (empty) a vs flipped (empty) b yields empty
2066 EXPECT_FALSE(flip_lr(a).Intersection(flip_lr(b)).has_value()) << label;
2067 EXPECT_FALSE(flip_tb(a).Intersection(flip_tb(b)).has_value()) << label;
2068 EXPECT_FALSE(flip_lrtb(a).Intersection(flip_lrtb(b)).has_value()) << label;
2069 };
2070
2071 auto test_non_empty = [&check_empty_flips](const IRect& a, const IRect& b,
2072 const IRect& result) {
2073 ASSERT_FALSE(a.IsEmpty()) << a;
2074 // b is allowed to be empty
2075
2076 std::stringstream stream;
2077 stream << a << " union " << b;
2078 auto label = stream.str();
2079
2080 EXPECT_TRUE(a.Intersection(b).has_value()) << label;
2081 EXPECT_TRUE(b.Intersection(a).has_value()) << label;
2082 EXPECT_EQ(a.Intersection(b), result) << label;
2083 EXPECT_EQ(b.Intersection(a), result) << label;
2084 check_empty_flips(a, b, label);
2085 };
2086
2087 auto test_empty = [&check_empty_flips](const IRect& a, const IRect& b) {
2088 ASSERT_FALSE(a.IsEmpty()) << a;
2089 // b is allowed to be empty
2090
2091 std::stringstream stream;
2092 stream << a << " union " << b;
2093 auto label = stream.str();
2094
2095 EXPECT_FALSE(a.Intersection(b).has_value()) << label;
2096 EXPECT_FALSE(b.Intersection(a).has_value()) << label;
2097 check_empty_flips(a, b, label);
2098 };
2099
2100 {
2101 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2102 auto b = IRect::MakeXYWH(0, 0, 0, 0);
2103
2104 test_empty(a, b);
2105 }
2106
2107 {
2108 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2109 auto b = IRect::MakeXYWH(10, 10, 0, 0);
2110
2111 test_empty(a, b);
2112 }
2113
2114 {
2115 auto a = IRect::MakeXYWH(0, 0, 100, 100);
2116 auto b = IRect::MakeXYWH(10, 10, 100, 100);
2117 auto expected = IRect::MakeXYWH(10, 10, 90, 90);
2118
2119 test_non_empty(a, b, expected);
2120 }
2121
2122 {
2123 auto a = IRect::MakeXYWH(0, 0, 100, 100);
2124 auto b = IRect::MakeXYWH(100, 100, 100, 100);
2125
2126 test_empty(a, b);
2127 }
2128
2129 {
2130 auto a = IRect::MakeMaximum();
2131 auto b = IRect::MakeXYWH(10, 10, 300, 300);
2132
2133 test_non_empty(a, b, b);
2134 }
2135
2136 {
2137 auto a = IRect::MakeMaximum();
2138 auto b = IRect::MakeMaximum();
2139
2140 test_non_empty(a, b, IRect::MakeMaximum());
2141 }
2142}
2143
2144TEST(RectTest, OptIRectIntersection) {
2145 auto a = IRect::MakeLTRB(0, 0, 110, 110);
2146 auto b = IRect::MakeLTRB(100, 100, 200, 200);
2147 auto c = IRect::MakeLTRB(100, 0, 200, 110);
2148
2149 // NullOpt, NullOpt
2150 EXPECT_FALSE(IRect::Intersection(std::nullopt, std::nullopt).has_value());
2151 EXPECT_EQ(IRect::Intersection(std::nullopt, std::nullopt), std::nullopt);
2152
2153 auto test1 = [](const IRect& r) {
2154 // Rect, NullOpt
2155 EXPECT_TRUE(IRect::Intersection(r, std::nullopt).has_value());
2156 EXPECT_EQ(IRect::Intersection(r, std::nullopt).value(), r);
2157
2158 // OptRect, NullOpt
2159 EXPECT_TRUE(
2160 IRect::Intersection(std::optional(r), std::nullopt).has_value());
2161 EXPECT_EQ(IRect::Intersection(std::optional(r), std::nullopt).value(), r);
2162
2163 // NullOpt, Rect
2164 EXPECT_TRUE(IRect::Intersection(std::nullopt, r).has_value());
2165 EXPECT_EQ(IRect::Intersection(std::nullopt, r).value(), r);
2166
2167 // NullOpt, OptRect
2168 EXPECT_TRUE(
2169 IRect::Intersection(std::nullopt, std::optional(r)).has_value());
2170 EXPECT_EQ(IRect::Intersection(std::nullopt, std::optional(r)).value(), r);
2171 };
2172
2173 test1(a);
2174 test1(b);
2175 test1(c);
2176
2177 auto test2 = [](const IRect& a, const IRect& b, const IRect& i) {
2178 ASSERT_EQ(a.Intersection(b), i);
2179
2180 // Rect, OptRect
2181 EXPECT_TRUE(IRect::Intersection(a, std::optional(b)).has_value());
2182 EXPECT_EQ(IRect::Intersection(a, std::optional(b)).value(), i);
2183
2184 // OptRect, Rect
2185 EXPECT_TRUE(IRect::Intersection(std::optional(a), b).has_value());
2186 EXPECT_EQ(IRect::Intersection(std::optional(a), b).value(), i);
2187
2188 // OptRect, OptRect
2189 EXPECT_TRUE(
2190 IRect::Intersection(std::optional(a), std::optional(b)).has_value());
2191 EXPECT_EQ(IRect::Intersection(std::optional(a), std::optional(b)).value(),
2192 i);
2193 };
2194
2195 test2(a, b, IRect::MakeLTRB(100, 100, 110, 110));
2196 test2(a, c, IRect::MakeLTRB(100, 0, 110, 110));
2197 test2(b, c, IRect::MakeLTRB(100, 100, 200, 110));
2198}
2199
2200TEST(RectTest, RectIntersectsWithRect) {
2201 auto check_nans = [](const Rect& a, const Rect& b, const std::string& label) {
2202 ASSERT_TRUE(a.IsFinite()) << label;
2203 ASSERT_TRUE(b.IsFinite()) << label;
2204
2205 for (int i = 1; i < 16; i++) {
2206 // NaN in a produces b
2207 EXPECT_FALSE(swap_nan(a, i).IntersectsWithRect(b))
2208 << label << ", index = " << i;
2209 // NaN in b produces a
2210 EXPECT_FALSE(a.IntersectsWithRect(swap_nan(b, i)))
2211 << label << ", index = " << i;
2212 // NaN in both is empty
2213 for (int j = 1; j < 16; j++) {
2214 EXPECT_FALSE(swap_nan(a, i).IntersectsWithRect(swap_nan(b, j)))
2215 << label << ", indices = " << i << ", " << j;
2216 }
2217 }
2218 };
2219
2220 auto check_empty_flips = [](const Rect& a, const Rect& b,
2221 const std::string& label) {
2222 ASSERT_FALSE(a.IsEmpty());
2223 // b is allowed to be empty
2224
2225 // unflipped a vs flipped (empty) b yields a
2226 EXPECT_FALSE(a.IntersectsWithRect(flip_lr(b))) << label;
2227 EXPECT_FALSE(a.IntersectsWithRect(flip_tb(b))) << label;
2228 EXPECT_FALSE(a.IntersectsWithRect(flip_lrtb(b))) << label;
2229
2230 // flipped (empty) a vs unflipped b yields b
2231 EXPECT_FALSE(flip_lr(a).IntersectsWithRect(b)) << label;
2232 EXPECT_FALSE(flip_tb(a).IntersectsWithRect(b)) << label;
2233 EXPECT_FALSE(flip_lrtb(a).IntersectsWithRect(b)) << label;
2234
2235 // flipped (empty) a vs flipped (empty) b yields empty
2236 EXPECT_FALSE(flip_lr(a).IntersectsWithRect(flip_lr(b))) << label;
2237 EXPECT_FALSE(flip_tb(a).IntersectsWithRect(flip_tb(b))) << label;
2238 EXPECT_FALSE(flip_lrtb(a).IntersectsWithRect(flip_lrtb(b))) << label;
2239 };
2240
2241 auto test_non_empty = [&check_nans, &check_empty_flips](const Rect& a,
2242 const Rect& b) {
2243 ASSERT_FALSE(a.IsEmpty()) << a;
2244 // b is allowed to be empty
2245
2246 std::stringstream stream;
2247 stream << a << " union " << b;
2248 auto label = stream.str();
2249
2250 EXPECT_TRUE(a.IntersectsWithRect(b)) << label;
2251 EXPECT_TRUE(b.IntersectsWithRect(a)) << label;
2252 check_empty_flips(a, b, label);
2253 check_nans(a, b, label);
2254 };
2255
2256 auto test_empty = [&check_nans, &check_empty_flips](const Rect& a,
2257 const Rect& b) {
2258 ASSERT_FALSE(a.IsEmpty()) << a;
2259 // b is allowed to be empty
2260
2261 std::stringstream stream;
2262 stream << a << " union " << b;
2263 auto label = stream.str();
2264
2265 EXPECT_FALSE(a.IntersectsWithRect(b)) << label;
2266 EXPECT_FALSE(b.IntersectsWithRect(a)) << label;
2267 check_empty_flips(a, b, label);
2268 check_nans(a, b, label);
2269 };
2270
2271 {
2272 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2273 auto b = Rect::MakeXYWH(0, 0, 0, 0);
2274
2275 test_empty(a, b);
2276 }
2277
2278 {
2279 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2280 auto b = Rect::MakeXYWH(10, 10, 0, 0);
2281
2282 test_empty(a, b);
2283 }
2284
2285 {
2286 auto a = Rect::MakeXYWH(0, 0, 100, 100);
2287 auto b = Rect::MakeXYWH(10, 10, 100, 100);
2288
2289 test_non_empty(a, b);
2290 }
2291
2292 {
2293 auto a = Rect::MakeXYWH(0, 0, 100, 100);
2294 auto b = Rect::MakeXYWH(100, 100, 100, 100);
2295
2296 test_empty(a, b);
2297 }
2298
2299 {
2300 auto a = Rect::MakeMaximum();
2301 auto b = Rect::MakeXYWH(10, 10, 100, 100);
2302
2303 test_non_empty(a, b);
2304 }
2305
2306 {
2307 auto a = Rect::MakeMaximum();
2308 auto b = Rect::MakeMaximum();
2309
2310 test_non_empty(a, b);
2311 }
2312}
2313
2314TEST(RectTest, IRectIntersectsWithRect) {
2315 auto check_empty_flips = [](const IRect& a, const IRect& b,
2316 const std::string& label) {
2317 ASSERT_FALSE(a.IsEmpty());
2318 // b is allowed to be empty
2319
2320 // unflipped a vs flipped (empty) b yields a
2321 EXPECT_FALSE(a.IntersectsWithRect(flip_lr(b))) << label;
2322 EXPECT_FALSE(a.IntersectsWithRect(flip_tb(b))) << label;
2323 EXPECT_FALSE(a.IntersectsWithRect(flip_lrtb(b))) << label;
2324
2325 // flipped (empty) a vs unflipped b yields b
2326 EXPECT_FALSE(flip_lr(a).IntersectsWithRect(b)) << label;
2327 EXPECT_FALSE(flip_tb(a).IntersectsWithRect(b)) << label;
2328 EXPECT_FALSE(flip_lrtb(a).IntersectsWithRect(b)) << label;
2329
2330 // flipped (empty) a vs flipped (empty) b yields empty
2331 EXPECT_FALSE(flip_lr(a).IntersectsWithRect(flip_lr(b))) << label;
2332 EXPECT_FALSE(flip_tb(a).IntersectsWithRect(flip_tb(b))) << label;
2333 EXPECT_FALSE(flip_lrtb(a).IntersectsWithRect(flip_lrtb(b))) << label;
2334 };
2335
2336 auto test_non_empty = [&check_empty_flips](const IRect& a, const IRect& b) {
2337 ASSERT_FALSE(a.IsEmpty()) << a;
2338 // b is allowed to be empty
2339
2340 std::stringstream stream;
2341 stream << a << " union " << b;
2342 auto label = stream.str();
2343
2344 EXPECT_TRUE(a.IntersectsWithRect(b)) << label;
2345 EXPECT_TRUE(b.IntersectsWithRect(a)) << label;
2346 check_empty_flips(a, b, label);
2347 };
2348
2349 auto test_empty = [&check_empty_flips](const IRect& a, const IRect& b) {
2350 ASSERT_FALSE(a.IsEmpty()) << a;
2351 // b is allowed to be empty
2352
2353 std::stringstream stream;
2354 stream << a << " union " << b;
2355 auto label = stream.str();
2356
2357 EXPECT_FALSE(a.IntersectsWithRect(b)) << label;
2358 EXPECT_FALSE(b.IntersectsWithRect(a)) << label;
2359 check_empty_flips(a, b, label);
2360 };
2361
2362 {
2363 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2364 auto b = IRect::MakeXYWH(0, 0, 0, 0);
2365
2366 test_empty(a, b);
2367 }
2368
2369 {
2370 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2371 auto b = IRect::MakeXYWH(10, 10, 0, 0);
2372
2373 test_empty(a, b);
2374 }
2375
2376 {
2377 auto a = IRect::MakeXYWH(0, 0, 100, 100);
2378 auto b = IRect::MakeXYWH(10, 10, 100, 100);
2379
2380 test_non_empty(a, b);
2381 }
2382
2383 {
2384 auto a = IRect::MakeXYWH(0, 0, 100, 100);
2385 auto b = IRect::MakeXYWH(100, 100, 100, 100);
2386
2387 test_empty(a, b);
2388 }
2389
2390 {
2391 auto a = IRect::MakeMaximum();
2392 auto b = IRect::MakeXYWH(10, 10, 100, 100);
2393
2394 test_non_empty(a, b);
2395 }
2396
2397 {
2398 auto a = IRect::MakeMaximum();
2399 auto b = IRect::MakeMaximum();
2400
2401 test_non_empty(a, b);
2402 }
2403}
2404
2405TEST(RectTest, RectContainsPoint) {
2406 auto check_nans = [](const Rect& rect, const Point& point,
2407 const std::string& label) {
2408 ASSERT_TRUE(rect.IsFinite()) << label;
2409 ASSERT_TRUE(point.IsFinite()) << label;
2410
2411 for (int i = 1; i < 16; i++) {
2412 EXPECT_FALSE(swap_nan(rect, i).Contains(point))
2413 << label << ", index = " << i;
2414 for (int j = 1; j < 4; j++) {
2415 EXPECT_FALSE(swap_nan(rect, i).Contains(swap_nan(point, j)))
2416 << label << ", indices = " << i << ", " << j;
2417 }
2418 }
2419 };
2420
2421 auto check_empty_flips = [](const Rect& rect, const Point& point,
2422 const std::string& label) {
2423 ASSERT_FALSE(rect.IsEmpty());
2424
2425 EXPECT_FALSE(flip_lr(rect).Contains(point)) << label;
2426 EXPECT_FALSE(flip_tb(rect).Contains(point)) << label;
2427 EXPECT_FALSE(flip_lrtb(rect).Contains(point)) << label;
2428 };
2429
2430 auto test_inside = [&check_nans, &check_empty_flips](const Rect& rect,
2431 const Point& point) {
2432 ASSERT_FALSE(rect.IsEmpty()) << rect;
2433
2434 std::stringstream stream;
2435 stream << rect << " contains " << point;
2436 auto label = stream.str();
2437
2438 EXPECT_TRUE(rect.Contains(point)) << label;
2439 check_empty_flips(rect, point, label);
2440 check_nans(rect, point, label);
2441 };
2442
2443 auto test_outside = [&check_nans, &check_empty_flips](const Rect& rect,
2444 const Point& point) {
2445 ASSERT_FALSE(rect.IsEmpty()) << rect;
2446
2447 std::stringstream stream;
2448 stream << rect << " contains " << point;
2449 auto label = stream.str();
2450
2451 EXPECT_FALSE(rect.Contains(point)) << label;
2452 check_empty_flips(rect, point, label);
2453 check_nans(rect, point, label);
2454 };
2455
2456 {
2457 // Origin is inclusive
2458 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2459 auto p = Point(100, 100);
2460
2461 test_inside(r, p);
2462 }
2463 {
2464 // Size is exclusive
2465 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2466 auto p = Point(200, 200);
2467
2468 test_outside(r, p);
2469 }
2470 {
2471 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2472 auto p = Point(99, 99);
2473
2474 test_outside(r, p);
2475 }
2476 {
2477 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2478 auto p = Point(199, 199);
2479
2480 test_inside(r, p);
2481 }
2482
2483 {
2484 auto r = Rect::MakeMaximum();
2485 auto p = Point(199, 199);
2486
2487 test_inside(r, p);
2488 }
2489}
2490
2491TEST(RectTest, IRectContainsIPoint) {
2492 auto check_empty_flips = [](const IRect& rect, const IPoint& point,
2493 const std::string& label) {
2494 ASSERT_FALSE(rect.IsEmpty());
2495
2496 EXPECT_FALSE(flip_lr(rect).Contains(point)) << label;
2497 EXPECT_FALSE(flip_tb(rect).Contains(point)) << label;
2498 EXPECT_FALSE(flip_lrtb(rect).Contains(point)) << label;
2499 };
2500
2501 auto test_inside = [&check_empty_flips](const IRect& rect,
2502 const IPoint& point) {
2503 ASSERT_FALSE(rect.IsEmpty()) << rect;
2504
2505 std::stringstream stream;
2506 stream << rect << " contains " << point;
2507 auto label = stream.str();
2508
2509 EXPECT_TRUE(rect.Contains(point)) << label;
2510 check_empty_flips(rect, point, label);
2511 };
2512
2513 auto test_outside = [&check_empty_flips](const IRect& rect,
2514 const IPoint& point) {
2515 ASSERT_FALSE(rect.IsEmpty()) << rect;
2516
2517 std::stringstream stream;
2518 stream << rect << " contains " << point;
2519 auto label = stream.str();
2520
2521 EXPECT_FALSE(rect.Contains(point)) << label;
2522 check_empty_flips(rect, point, label);
2523 };
2524
2525 {
2526 // Origin is inclusive
2527 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2528 auto p = IPoint(100, 100);
2529
2530 test_inside(r, p);
2531 }
2532 {
2533 // Size is exclusive
2534 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2535 auto p = IPoint(200, 200);
2536
2537 test_outside(r, p);
2538 }
2539 {
2540 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2541 auto p = IPoint(99, 99);
2542
2543 test_outside(r, p);
2544 }
2545 {
2546 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2547 auto p = IPoint(199, 199);
2548
2549 test_inside(r, p);
2550 }
2551
2552 {
2553 auto r = IRect::MakeMaximum();
2554 auto p = IPoint(199, 199);
2555
2556 test_inside(r, p);
2557 }
2558}
2559
2560TEST(RectTest, RectContainsInclusivePoint) {
2561 auto check_nans = [](const Rect& rect, const Point& point,
2562 const std::string& label) {
2563 ASSERT_TRUE(rect.IsFinite()) << label;
2564 ASSERT_TRUE(point.IsFinite()) << label;
2565
2566 for (int i = 1; i < 16; i++) {
2567 EXPECT_FALSE(swap_nan(rect, i).ContainsInclusive(point))
2568 << label << ", index = " << i;
2569 for (int j = 1; j < 4; j++) {
2570 EXPECT_FALSE(swap_nan(rect, i).ContainsInclusive(swap_nan(point, j)))
2571 << label << ", indices = " << i << ", " << j;
2572 }
2573 }
2574 };
2575
2576 auto check_empty_flips = [](const Rect& rect, const Point& point,
2577 const std::string& label) {
2578 ASSERT_FALSE(rect.IsEmpty());
2579
2580 EXPECT_FALSE(flip_lr(rect).ContainsInclusive(point)) << label;
2581 EXPECT_FALSE(flip_tb(rect).ContainsInclusive(point)) << label;
2582 EXPECT_FALSE(flip_lrtb(rect).ContainsInclusive(point)) << label;
2583 };
2584
2585 auto test_inside = [&check_nans, &check_empty_flips](const Rect& rect,
2586 const Point& point) {
2587 ASSERT_FALSE(rect.IsEmpty()) << rect;
2588
2589 std::stringstream stream;
2590 stream << rect << " contains " << point;
2591 auto label = stream.str();
2592
2593 EXPECT_TRUE(rect.ContainsInclusive(point)) << label;
2594 check_empty_flips(rect, point, label);
2595 check_nans(rect, point, label);
2596 };
2597
2598 auto test_outside = [&check_nans, &check_empty_flips](const Rect& rect,
2599 const Point& point) {
2600 ASSERT_FALSE(rect.IsEmpty()) << rect;
2601
2602 std::stringstream stream;
2603 stream << rect << " contains " << point;
2604 auto label = stream.str();
2605
2606 EXPECT_FALSE(rect.ContainsInclusive(point)) << label;
2607 check_empty_flips(rect, point, label);
2608 check_nans(rect, point, label);
2609 };
2610
2611 {
2612 // Origin is inclusive
2613 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2614 auto p = Point(100, 100);
2615
2616 test_inside(r, p);
2617 }
2618 {
2619 // Size is inclusive
2620 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2621 auto p = Point(200, 200);
2622
2623 test_inside(r, p);
2624 }
2625 {
2626 // Size + epsilon is exclusive
2627 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2628 auto p = Point(200 + kEhCloseEnough, 200 + kEhCloseEnough);
2629
2630 test_outside(r, p);
2631 }
2632 {
2633 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2634 auto p = Point(99, 99);
2635
2636 test_outside(r, p);
2637 }
2638 {
2639 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2640 auto p = Point(199, 199);
2641
2642 test_inside(r, p);
2643 }
2644
2645 {
2646 auto r = Rect::MakeMaximum();
2647 auto p = Point(199, 199);
2648
2649 test_inside(r, p);
2650 }
2651}
2652
2653TEST(RectTest, IRectContainsInclusiveIPoint) {
2654 auto check_empty_flips = [](const IRect& rect, const IPoint& point,
2655 const std::string& label) {
2656 ASSERT_FALSE(rect.IsEmpty());
2657
2658 EXPECT_FALSE(flip_lr(rect).ContainsInclusive(point)) << label;
2659 EXPECT_FALSE(flip_tb(rect).ContainsInclusive(point)) << label;
2660 EXPECT_FALSE(flip_lrtb(rect).ContainsInclusive(point)) << label;
2661 };
2662
2663 auto test_inside = [&check_empty_flips](const IRect& rect,
2664 const IPoint& point) {
2665 ASSERT_FALSE(rect.IsEmpty()) << rect;
2666
2667 std::stringstream stream;
2668 stream << rect << " contains " << point;
2669 auto label = stream.str();
2670
2671 EXPECT_TRUE(rect.ContainsInclusive(point)) << label;
2672 check_empty_flips(rect, point, label);
2673 };
2674
2675 auto test_outside = [&check_empty_flips](const IRect& rect,
2676 const IPoint& point) {
2677 ASSERT_FALSE(rect.IsEmpty()) << rect;
2678
2679 std::stringstream stream;
2680 stream << rect << " contains " << point;
2681 auto label = stream.str();
2682
2683 EXPECT_FALSE(rect.ContainsInclusive(point)) << label;
2684 check_empty_flips(rect, point, label);
2685 };
2686
2687 {
2688 // Origin is inclusive
2689 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2690 auto p = IPoint(100, 100);
2691
2692 test_inside(r, p);
2693 }
2694 {
2695 // Size is inclusive
2696 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2697 auto p = IPoint(200, 200);
2698
2699 test_inside(r, p);
2700 }
2701 {
2702 // Size + "epsilon" is exclusive
2703 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2704 auto p = IPoint(201, 201);
2705
2706 test_outside(r, p);
2707 }
2708 {
2709 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2710 auto p = IPoint(99, 99);
2711
2712 test_outside(r, p);
2713 }
2714 {
2715 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2716 auto p = IPoint(199, 199);
2717
2718 test_inside(r, p);
2719 }
2720
2721 {
2722 auto r = IRect::MakeMaximum();
2723 auto p = IPoint(199, 199);
2724
2725 test_inside(r, p);
2726 }
2727}
2728
2729TEST(RectTest, RectContainsRect) {
2730 auto check_nans = [](const Rect& a, const Rect& b, const std::string& label) {
2731 ASSERT_TRUE(a.IsFinite()) << label;
2732 ASSERT_TRUE(b.IsFinite()) << label;
2733 ASSERT_FALSE(a.IsEmpty());
2734
2735 for (int i = 1; i < 16; i++) {
2736 // NaN in a produces false
2737 EXPECT_FALSE(swap_nan(a, i).Contains(b)) << label << ", index = " << i;
2738 // NaN in b produces false
2739 EXPECT_TRUE(a.Contains(swap_nan(b, i))) << label << ", index = " << i;
2740 // NaN in both is false
2741 for (int j = 1; j < 16; j++) {
2742 EXPECT_FALSE(swap_nan(a, i).Contains(swap_nan(b, j)))
2743 << label << ", indices = " << i << ", " << j;
2744 }
2745 }
2746 };
2747
2748 auto check_empty_flips = [](const Rect& a, const Rect& b,
2749 const std::string& label) {
2750 ASSERT_FALSE(a.IsEmpty());
2751 // test b rects are allowed to have 0 w/h, but not be backwards
2752 ASSERT_FALSE(b.GetLeft() > b.GetRight() || b.GetTop() > b.GetBottom());
2753
2754 // unflipped a vs flipped (empty) b yields false
2755 EXPECT_TRUE(a.Contains(flip_lr(b))) << label;
2756 EXPECT_TRUE(a.Contains(flip_tb(b))) << label;
2757 EXPECT_TRUE(a.Contains(flip_lrtb(b))) << label;
2758
2759 // flipped (empty) a vs unflipped b yields false
2760 EXPECT_FALSE(flip_lr(a).Contains(b)) << label;
2761 EXPECT_FALSE(flip_tb(a).Contains(b)) << label;
2762 EXPECT_FALSE(flip_lrtb(a).Contains(b)) << label;
2763
2764 // flipped (empty) a vs flipped (empty) b yields empty
2765 EXPECT_FALSE(flip_lr(a).Contains(flip_lr(b))) << label;
2766 EXPECT_FALSE(flip_tb(a).Contains(flip_tb(b))) << label;
2767 EXPECT_FALSE(flip_lrtb(a).Contains(flip_lrtb(b))) << label;
2768 };
2769
2770 auto test_inside = [&check_nans, &check_empty_flips](const Rect& a,
2771 const Rect& b) {
2772 ASSERT_FALSE(a.IsEmpty()) << a;
2773 // test b rects are allowed to have 0 w/h, but not be backwards
2774 ASSERT_FALSE(b.GetLeft() > b.GetRight() || b.GetTop() > b.GetBottom());
2775
2776 std::stringstream stream;
2777 stream << a << " contains " << b;
2778 auto label = stream.str();
2779
2780 EXPECT_TRUE(a.Contains(b)) << label;
2781 check_empty_flips(a, b, label);
2782 check_nans(a, b, label);
2783 };
2784
2785 auto test_not_inside = [&check_nans, &check_empty_flips](const Rect& a,
2786 const Rect& b) {
2787 ASSERT_FALSE(a.IsEmpty()) << a;
2788 // If b was empty, it would be contained and should not be tested with
2789 // this function - use |test_inside| instead.
2790 ASSERT_FALSE(b.IsEmpty()) << b;
2791
2792 std::stringstream stream;
2793 stream << a << " contains " << b;
2794 auto label = stream.str();
2795
2796 EXPECT_FALSE(a.Contains(b)) << label;
2797 check_empty_flips(a, b, label);
2798 check_nans(a, b, label);
2799 };
2800
2801 {
2802 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2803
2804 test_inside(a, a);
2805 }
2806 {
2807 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2808 auto b = Rect::MakeXYWH(0, 0, 0, 0);
2809
2810 test_inside(a, b);
2811 }
2812 {
2813 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2814 auto b = Rect::MakeXYWH(150, 150, 20, 20);
2815
2816 test_inside(a, b);
2817 }
2818 {
2819 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2820 auto b = Rect::MakeXYWH(150, 150, 100, 100);
2821
2822 test_not_inside(a, b);
2823 }
2824 {
2825 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2826 auto b = Rect::MakeXYWH(50, 50, 100, 100);
2827
2828 test_not_inside(a, b);
2829 }
2830 {
2831 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2832 auto b = Rect::MakeXYWH(0, 0, 300, 300);
2833
2834 test_not_inside(a, b);
2835 }
2836 {
2837 auto a = Rect::MakeMaximum();
2838 auto b = Rect::MakeXYWH(0, 0, 300, 300);
2839
2840 test_inside(a, b);
2841 }
2842}
2843
2844TEST(RectTest, IRectContainsIRect) {
2845 auto check_empty_flips = [](const IRect& a, const IRect& b,
2846 const std::string& label) {
2847 ASSERT_FALSE(a.IsEmpty());
2848 // test b rects are allowed to have 0 w/h, but not be backwards
2849 ASSERT_FALSE(b.GetLeft() > b.GetRight() || b.GetTop() > b.GetBottom());
2850
2851 // unflipped a vs flipped (empty) b yields true
2852 EXPECT_TRUE(a.Contains(flip_lr(b))) << label;
2853 EXPECT_TRUE(a.Contains(flip_tb(b))) << label;
2854 EXPECT_TRUE(a.Contains(flip_lrtb(b))) << label;
2855
2856 // flipped (empty) a vs unflipped b yields false
2857 EXPECT_FALSE(flip_lr(a).Contains(b)) << label;
2858 EXPECT_FALSE(flip_tb(a).Contains(b)) << label;
2859 EXPECT_FALSE(flip_lrtb(a).Contains(b)) << label;
2860
2861 // flipped (empty) a vs flipped (empty) b yields empty
2862 EXPECT_FALSE(flip_lr(a).Contains(flip_lr(b))) << label;
2863 EXPECT_FALSE(flip_tb(a).Contains(flip_tb(b))) << label;
2864 EXPECT_FALSE(flip_lrtb(a).Contains(flip_lrtb(b))) << label;
2865 };
2866
2867 auto test_inside = [&check_empty_flips](const IRect& a, const IRect& b) {
2868 ASSERT_FALSE(a.IsEmpty()) << a;
2869 // test b rects are allowed to have 0 w/h, but not be backwards
2870 ASSERT_FALSE(b.GetLeft() > b.GetRight() || b.GetTop() > b.GetBottom());
2871
2872 std::stringstream stream;
2873 stream << a << " contains " << b;
2874 auto label = stream.str();
2875
2876 EXPECT_TRUE(a.Contains(b)) << label;
2877 check_empty_flips(a, b, label);
2878 };
2879
2880 auto test_not_inside = [&check_empty_flips](const IRect& a, const IRect& b) {
2881 ASSERT_FALSE(a.IsEmpty()) << a;
2882 // If b was empty, it would be contained and should not be tested with
2883 // this function - use |test_inside| instead.
2884 ASSERT_FALSE(b.IsEmpty()) << b;
2885
2886 std::stringstream stream;
2887 stream << a << " contains " << b;
2888 auto label = stream.str();
2889
2890 EXPECT_FALSE(a.Contains(b)) << label;
2891 check_empty_flips(a, b, label);
2892 };
2893
2894 {
2895 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2896
2897 test_inside(a, a);
2898 }
2899 {
2900 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2901 auto b = IRect::MakeXYWH(0, 0, 0, 0);
2902
2903 test_inside(a, b);
2904 }
2905 {
2906 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2907 auto b = IRect::MakeXYWH(150, 150, 20, 20);
2908
2909 test_inside(a, b);
2910 }
2911 {
2912 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2913 auto b = IRect::MakeXYWH(150, 150, 100, 100);
2914
2915 test_not_inside(a, b);
2916 }
2917 {
2918 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2919 auto b = IRect::MakeXYWH(50, 50, 100, 100);
2920
2921 test_not_inside(a, b);
2922 }
2923 {
2924 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2925 auto b = IRect::MakeXYWH(0, 0, 300, 300);
2926
2927 test_not_inside(a, b);
2928 }
2929 {
2930 auto a = IRect::MakeMaximum();
2931 auto b = IRect::MakeXYWH(0, 0, 300, 300);
2932
2933 test_inside(a, b);
2934 }
2935}
2936
2937TEST(RectTest, RectCutOut) {
2938 Rect cull_rect = Rect::MakeLTRB(20, 20, 40, 40);
2939
2940 auto check_nans = [&cull_rect](const Rect& diff_rect,
2941 const std::string& label) {
2942 EXPECT_TRUE(cull_rect.IsFinite()) << label;
2943 EXPECT_TRUE(diff_rect.IsFinite()) << label;
2944
2945 for (int i = 1; i < 16; i++) {
2946 // NaN in cull_rect produces empty
2947 EXPECT_FALSE(swap_nan(cull_rect, i).Cutout(diff_rect).has_value())
2948 << label << ", index " << i;
2949 EXPECT_EQ(swap_nan(cull_rect, i).CutoutOrEmpty(diff_rect), Rect())
2950 << label << ", index " << i;
2951
2952 // NaN in diff_rect is nop
2953 EXPECT_TRUE(cull_rect.Cutout(swap_nan(diff_rect, i)).has_value())
2954 << label << ", index " << i;
2955 EXPECT_EQ(cull_rect.CutoutOrEmpty(swap_nan(diff_rect, i)), cull_rect)
2956 << label << ", index " << i;
2957
2958 for (int j = 1; j < 16; j++) {
2959 // NaN in both is also empty
2960 EXPECT_FALSE(
2961 swap_nan(cull_rect, i).Cutout(swap_nan(diff_rect, j)).has_value())
2962 << label << ", indices " << i << ", " << j;
2963 EXPECT_EQ(swap_nan(cull_rect, i).CutoutOrEmpty(swap_nan(diff_rect, j)),
2964 Rect())
2965 << label << ", indices " << i << ", " << j;
2966 }
2967 }
2968 };
2969
2970 auto check_empty_flips = [&cull_rect](const Rect& diff_rect,
2971 const std::string& label) {
2972 EXPECT_FALSE(cull_rect.IsEmpty()) << label;
2973 EXPECT_FALSE(diff_rect.IsEmpty()) << label;
2974
2975 // unflipped cull_rect vs flipped(empty) diff_rect
2976 // == cull_rect
2977 EXPECT_TRUE(cull_rect.Cutout(flip_lr(diff_rect)).has_value()) << label;
2978 EXPECT_EQ(cull_rect.Cutout(flip_lr(diff_rect)), cull_rect) << label;
2979 EXPECT_TRUE(cull_rect.Cutout(flip_tb(diff_rect)).has_value()) << label;
2980 EXPECT_EQ(cull_rect.Cutout(flip_tb(diff_rect)), cull_rect) << label;
2981 EXPECT_TRUE(cull_rect.Cutout(flip_lrtb(diff_rect)).has_value()) << label;
2982 EXPECT_EQ(cull_rect.Cutout(flip_lrtb(diff_rect)), cull_rect) << label;
2983
2984 // flipped(empty) cull_rect vs unflipped diff_rect
2985 // == empty
2986 EXPECT_FALSE(flip_lr(cull_rect).Cutout(diff_rect).has_value()) << label;
2987 EXPECT_EQ(flip_lr(cull_rect).CutoutOrEmpty(diff_rect), Rect()) << label;
2988 EXPECT_FALSE(flip_tb(cull_rect).Cutout(diff_rect).has_value()) << label;
2989 EXPECT_EQ(flip_tb(cull_rect).CutoutOrEmpty(diff_rect), Rect()) << label;
2990 EXPECT_FALSE(flip_lrtb(cull_rect).Cutout(diff_rect).has_value()) << label;
2991 EXPECT_EQ(flip_lrtb(cull_rect).CutoutOrEmpty(diff_rect), Rect()) << label;
2992
2993 // flipped(empty) cull_rect vs flipped(empty) diff_rect
2994 // == empty
2995 EXPECT_FALSE(flip_lr(cull_rect).Cutout(flip_lr(diff_rect)).has_value())
2996 << label;
2997 EXPECT_EQ(flip_lr(cull_rect).CutoutOrEmpty(flip_lr(diff_rect)), Rect())
2998 << label;
2999 EXPECT_FALSE(flip_tb(cull_rect).Cutout(flip_tb(diff_rect)).has_value())
3000 << label;
3001 EXPECT_EQ(flip_tb(cull_rect).CutoutOrEmpty(flip_tb(diff_rect)), Rect())
3002 << label;
3003 EXPECT_FALSE(flip_lrtb(cull_rect).Cutout(flip_lrtb(diff_rect)).has_value())
3004 << label;
3005 EXPECT_EQ(flip_lrtb(cull_rect).CutoutOrEmpty(flip_lrtb(diff_rect)), Rect())
3006 << label;
3007 };
3008
3009 auto non_reducing = [&cull_rect, &check_empty_flips, &check_nans](
3010 const Rect& diff_rect, const std::string& label) {
3011 EXPECT_EQ(cull_rect.Cutout(diff_rect), cull_rect) << label;
3012 EXPECT_EQ(cull_rect.CutoutOrEmpty(diff_rect), cull_rect) << label;
3013 check_empty_flips(diff_rect, label);
3014 check_nans(diff_rect, label);
3015 };
3016
3017 auto reducing = [&cull_rect, &check_empty_flips, &check_nans](
3018 const Rect& diff_rect, const Rect& result_rect,
3019 const std::string& label) {
3020 EXPECT_TRUE(!result_rect.IsEmpty());
3021 EXPECT_EQ(cull_rect.Cutout(diff_rect), result_rect) << label;
3022 EXPECT_EQ(cull_rect.CutoutOrEmpty(diff_rect), result_rect) << label;
3023 check_empty_flips(diff_rect, label);
3024 check_nans(diff_rect, label);
3025 };
3026
3027 auto emptying = [&cull_rect, &check_empty_flips, &check_nans](
3028 const Rect& diff_rect, const std::string& label) {
3029 EXPECT_FALSE(cull_rect.Cutout(diff_rect).has_value()) << label;
3030 EXPECT_EQ(cull_rect.CutoutOrEmpty(diff_rect), Rect()) << label;
3031 check_empty_flips(diff_rect, label);
3032 check_nans(diff_rect, label);
3033 };
3034
3035 // Skim the corners and edge
3036 non_reducing(Rect::MakeLTRB(10, 10, 20, 20), "outside UL corner");
3037 non_reducing(Rect::MakeLTRB(20, 10, 40, 20), "Above");
3038 non_reducing(Rect::MakeLTRB(40, 10, 50, 20), "outside UR corner");
3039 non_reducing(Rect::MakeLTRB(40, 20, 50, 40), "Right");
3040 non_reducing(Rect::MakeLTRB(40, 40, 50, 50), "outside LR corner");
3041 non_reducing(Rect::MakeLTRB(20, 40, 40, 50), "Below");
3042 non_reducing(Rect::MakeLTRB(10, 40, 20, 50), "outside LR corner");
3043 non_reducing(Rect::MakeLTRB(10, 20, 20, 40), "Left");
3044
3045 // Overlap corners
3046 non_reducing(Rect::MakeLTRB(15, 15, 25, 25), "covering UL corner");
3047 non_reducing(Rect::MakeLTRB(35, 15, 45, 25), "covering UR corner");
3048 non_reducing(Rect::MakeLTRB(35, 35, 45, 45), "covering LR corner");
3049 non_reducing(Rect::MakeLTRB(15, 35, 25, 45), "covering LL corner");
3050
3051 // Overlap edges, but not across an entire side
3052 non_reducing(Rect::MakeLTRB(20, 15, 39, 25), "Top edge left-biased");
3053 non_reducing(Rect::MakeLTRB(21, 15, 40, 25), "Top edge, right biased");
3054 non_reducing(Rect::MakeLTRB(35, 20, 45, 39), "Right edge, top-biased");
3055 non_reducing(Rect::MakeLTRB(35, 21, 45, 40), "Right edge, bottom-biased");
3056 non_reducing(Rect::MakeLTRB(20, 35, 39, 45), "Bottom edge, left-biased");
3057 non_reducing(Rect::MakeLTRB(21, 35, 40, 45), "Bottom edge, right-biased");
3058 non_reducing(Rect::MakeLTRB(15, 20, 25, 39), "Left edge, top-biased");
3059 non_reducing(Rect::MakeLTRB(15, 21, 25, 40), "Left edge, bottom-biased");
3060
3061 // Slice all the way through the middle
3062 non_reducing(Rect::MakeLTRB(25, 15, 35, 45), "Vertical interior slice");
3063 non_reducing(Rect::MakeLTRB(15, 25, 45, 35), "Horizontal interior slice");
3064
3065 // Slice off each edge
3066 reducing(Rect::MakeLTRB(20, 15, 40, 25), //
3067 Rect::MakeLTRB(20, 25, 40, 40), //
3068 "Slice off top");
3069 reducing(Rect::MakeLTRB(35, 20, 45, 40), //
3070 Rect::MakeLTRB(20, 20, 35, 40), //
3071 "Slice off right");
3072 reducing(Rect::MakeLTRB(20, 35, 40, 45), //
3073 Rect::MakeLTRB(20, 20, 40, 35), //
3074 "Slice off bottom");
3075 reducing(Rect::MakeLTRB(15, 20, 25, 40), //
3076 Rect::MakeLTRB(25, 20, 40, 40), //
3077 "Slice off left");
3078
3079 // cull rect contains diff rect
3080 non_reducing(Rect::MakeLTRB(21, 21, 39, 39), "Contained, non-covering");
3081
3082 // cull rect equals diff rect
3083 emptying(cull_rect, "Perfectly covering");
3084
3085 // diff rect contains cull rect
3086 emptying(Rect::MakeLTRB(15, 15, 45, 45), "Smothering");
3087}
3088
3089TEST(RectTest, IRectCutOut) {
3090 IRect cull_rect = IRect::MakeLTRB(20, 20, 40, 40);
3091
3092 auto check_empty_flips = [&cull_rect](const IRect& diff_rect,
3093 const std::string& label) {
3094 EXPECT_FALSE(diff_rect.IsEmpty());
3095 EXPECT_FALSE(cull_rect.IsEmpty());
3096
3097 // unflipped cull_rect vs flipped(empty) diff_rect
3098 // == cull_rect
3099 EXPECT_TRUE(cull_rect.Cutout(flip_lr(diff_rect)).has_value()) << label;
3100 EXPECT_EQ(cull_rect.Cutout(flip_lr(diff_rect)), cull_rect) << label;
3101 EXPECT_TRUE(cull_rect.Cutout(flip_tb(diff_rect)).has_value()) << label;
3102 EXPECT_EQ(cull_rect.Cutout(flip_tb(diff_rect)), cull_rect) << label;
3103 EXPECT_TRUE(cull_rect.Cutout(flip_lrtb(diff_rect)).has_value()) << label;
3104 EXPECT_EQ(cull_rect.Cutout(flip_lrtb(diff_rect)), cull_rect) << label;
3105
3106 // flipped(empty) cull_rect vs flipped(empty) diff_rect
3107 // == empty
3108 EXPECT_FALSE(flip_lr(cull_rect).Cutout(diff_rect).has_value()) << label;
3109 EXPECT_EQ(flip_lr(cull_rect).CutoutOrEmpty(diff_rect), IRect()) << label;
3110 EXPECT_FALSE(flip_tb(cull_rect).Cutout(diff_rect).has_value()) << label;
3111 EXPECT_EQ(flip_tb(cull_rect).CutoutOrEmpty(diff_rect), IRect()) << label;
3112 EXPECT_FALSE(flip_lrtb(cull_rect).Cutout(diff_rect).has_value()) << label;
3113 EXPECT_EQ(flip_lrtb(cull_rect).CutoutOrEmpty(diff_rect), IRect()) << label;
3114
3115 // flipped(empty) cull_rect vs unflipped diff_rect
3116 // == empty
3117 EXPECT_FALSE(flip_lr(cull_rect).Cutout(flip_lr(diff_rect)).has_value())
3118 << label;
3119 EXPECT_EQ(flip_lr(cull_rect).CutoutOrEmpty(flip_lr(diff_rect)), IRect())
3120 << label;
3121 EXPECT_FALSE(flip_tb(cull_rect).Cutout(flip_tb(diff_rect)).has_value())
3122 << label;
3123 EXPECT_EQ(flip_tb(cull_rect).CutoutOrEmpty(flip_tb(diff_rect)), IRect())
3124 << label;
3125 EXPECT_FALSE(flip_lrtb(cull_rect).Cutout(flip_lrtb(diff_rect)).has_value())
3126 << label;
3127 EXPECT_EQ(flip_lrtb(cull_rect).CutoutOrEmpty(flip_lrtb(diff_rect)), IRect())
3128 << label;
3129 };
3130
3131 auto non_reducing = [&cull_rect, &check_empty_flips](
3132 const IRect& diff_rect, const std::string& label) {
3133 EXPECT_EQ(cull_rect.Cutout(diff_rect), cull_rect) << label;
3134 EXPECT_EQ(cull_rect.CutoutOrEmpty(diff_rect), cull_rect) << label;
3135 check_empty_flips(diff_rect, label);
3136 };
3137
3138 auto reducing = [&cull_rect, &check_empty_flips](const IRect& diff_rect,
3139 const IRect& result_rect,
3140 const std::string& label) {
3141 EXPECT_TRUE(!result_rect.IsEmpty());
3142 EXPECT_EQ(cull_rect.Cutout(diff_rect), result_rect) << label;
3143 EXPECT_EQ(cull_rect.CutoutOrEmpty(diff_rect), result_rect) << label;
3144 check_empty_flips(diff_rect, label);
3145 };
3146
3147 auto emptying = [&cull_rect, &check_empty_flips](const IRect& diff_rect,
3148 const std::string& label) {
3149 EXPECT_FALSE(cull_rect.Cutout(diff_rect).has_value()) << label;
3150 EXPECT_EQ(cull_rect.CutoutOrEmpty(diff_rect), IRect()) << label;
3151 check_empty_flips(diff_rect, label);
3152 };
3153
3154 // Skim the corners and edge
3155 non_reducing(IRect::MakeLTRB(10, 10, 20, 20), "outside UL corner");
3156 non_reducing(IRect::MakeLTRB(20, 10, 40, 20), "Above");
3157 non_reducing(IRect::MakeLTRB(40, 10, 50, 20), "outside UR corner");
3158 non_reducing(IRect::MakeLTRB(40, 20, 50, 40), "Right");
3159 non_reducing(IRect::MakeLTRB(40, 40, 50, 50), "outside LR corner");
3160 non_reducing(IRect::MakeLTRB(20, 40, 40, 50), "Below");
3161 non_reducing(IRect::MakeLTRB(10, 40, 20, 50), "outside LR corner");
3162 non_reducing(IRect::MakeLTRB(10, 20, 20, 40), "Left");
3163
3164 // Overlap corners
3165 non_reducing(IRect::MakeLTRB(15, 15, 25, 25), "covering UL corner");
3166 non_reducing(IRect::MakeLTRB(35, 15, 45, 25), "covering UR corner");
3167 non_reducing(IRect::MakeLTRB(35, 35, 45, 45), "covering LR corner");
3168 non_reducing(IRect::MakeLTRB(15, 35, 25, 45), "covering LL corner");
3169
3170 // Overlap edges, but not across an entire side
3171 non_reducing(IRect::MakeLTRB(20, 15, 39, 25), "Top edge left-biased");
3172 non_reducing(IRect::MakeLTRB(21, 15, 40, 25), "Top edge, right biased");
3173 non_reducing(IRect::MakeLTRB(35, 20, 45, 39), "Right edge, top-biased");
3174 non_reducing(IRect::MakeLTRB(35, 21, 45, 40), "Right edge, bottom-biased");
3175 non_reducing(IRect::MakeLTRB(20, 35, 39, 45), "Bottom edge, left-biased");
3176 non_reducing(IRect::MakeLTRB(21, 35, 40, 45), "Bottom edge, right-biased");
3177 non_reducing(IRect::MakeLTRB(15, 20, 25, 39), "Left edge, top-biased");
3178 non_reducing(IRect::MakeLTRB(15, 21, 25, 40), "Left edge, bottom-biased");
3179
3180 // Slice all the way through the middle
3181 non_reducing(IRect::MakeLTRB(25, 15, 35, 45), "Vertical interior slice");
3182 non_reducing(IRect::MakeLTRB(15, 25, 45, 35), "Horizontal interior slice");
3183
3184 // Slice off each edge
3185 reducing(IRect::MakeLTRB(20, 15, 40, 25), //
3186 IRect::MakeLTRB(20, 25, 40, 40), //
3187 "Slice off top");
3188 reducing(IRect::MakeLTRB(35, 20, 45, 40), //
3189 IRect::MakeLTRB(20, 20, 35, 40), //
3190 "Slice off right");
3191 reducing(IRect::MakeLTRB(20, 35, 40, 45), //
3192 IRect::MakeLTRB(20, 20, 40, 35), //
3193 "Slice off bottom");
3194 reducing(IRect::MakeLTRB(15, 20, 25, 40), //
3195 IRect::MakeLTRB(25, 20, 40, 40), //
3196 "Slice off left");
3197
3198 // cull rect contains diff rect
3199 non_reducing(IRect::MakeLTRB(21, 21, 39, 39), "Contained, non-covering");
3200
3201 // cull rect equals diff rect
3202 emptying(cull_rect, "Perfectly covering");
3203
3204 // diff rect contains cull rect
3205 emptying(IRect::MakeLTRB(15, 15, 45, 45), "Smothering");
3206}
3207
3208TEST(RectTest, RectGetPoints) {
3209 {
3210 Rect r = Rect::MakeXYWH(100, 200, 300, 400);
3211 auto points = r.GetPoints();
3212 EXPECT_POINT_NEAR(points[0], Point(100, 200));
3213 EXPECT_POINT_NEAR(points[1], Point(400, 200));
3214 EXPECT_POINT_NEAR(points[2], Point(100, 600));
3215 EXPECT_POINT_NEAR(points[3], Point(400, 600));
3216 }
3217
3218 {
3219 Rect r = Rect::MakeMaximum();
3220 auto points = r.GetPoints();
3221 EXPECT_EQ(points[0], Point(std::numeric_limits<float>::lowest(),
3222 std::numeric_limits<float>::lowest()));
3223 EXPECT_EQ(points[1], Point(std::numeric_limits<float>::max(),
3224 std::numeric_limits<float>::lowest()));
3225 EXPECT_EQ(points[2], Point(std::numeric_limits<float>::lowest(),
3226 std::numeric_limits<float>::max()));
3227 EXPECT_EQ(points[3], Point(std::numeric_limits<float>::max(),
3228 std::numeric_limits<float>::max()));
3229 }
3230}
3231
3232TEST(RectTest, RectShift) {
3233 auto r = Rect::MakeLTRB(0, 0, 100, 100);
3234
3235 EXPECT_EQ(r.Shift(Point(10, 5)), Rect::MakeLTRB(10, 5, 110, 105));
3236 EXPECT_EQ(r.Shift(Point(-10, -5)), Rect::MakeLTRB(-10, -5, 90, 95));
3237}
3238
3239TEST(RectTest, RectGetTransformedPoints) {
3240 Rect r = Rect::MakeXYWH(100, 200, 300, 400);
3241 auto points = r.GetTransformedPoints(Matrix::MakeTranslation({10, 20}));
3242 EXPECT_POINT_NEAR(points[0], Point(110, 220));
3243 EXPECT_POINT_NEAR(points[1], Point(410, 220));
3244 EXPECT_POINT_NEAR(points[2], Point(110, 620));
3245 EXPECT_POINT_NEAR(points[3], Point(410, 620));
3246}
3247
3248TEST(RectTest, RectMakePointBounds) {
3249 {
3250 std::vector<Point> points{{1, 5}, {4, -1}, {0, 6}};
3251 auto r = Rect::MakePointBounds(points.begin(), points.end());
3252 auto expected = Rect::MakeXYWH(0, -1, 4, 7);
3253 EXPECT_TRUE(r.has_value());
3254 if (r.has_value()) {
3255 EXPECT_RECT_NEAR(r.value(), expected);
3256 }
3257 }
3258 {
3259 std::vector<Point> points;
3260 std::optional<Rect> r = Rect::MakePointBounds(points.begin(), points.end());
3261 EXPECT_FALSE(r.has_value());
3262 }
3263}
3264
3265TEST(RectTest, RectGetPositive) {
3266 {
3267 Rect r = Rect::MakeXYWH(100, 200, 300, 400);
3268 auto actual = r.GetPositive();
3269 EXPECT_RECT_NEAR(r, actual);
3270 }
3271 {
3272 Rect r = Rect::MakeXYWH(100, 200, -100, -100);
3273 auto actual = r.GetPositive();
3274 Rect expected = Rect::MakeXYWH(0, 100, 100, 100);
3275 EXPECT_RECT_NEAR(expected, actual);
3276 }
3277}
3278
3279TEST(RectTest, RectDirections) {
3280 auto r = Rect::MakeLTRB(1, 2, 3, 4);
3281
3282 EXPECT_EQ(r.GetLeft(), 1);
3283 EXPECT_EQ(r.GetTop(), 2);
3284 EXPECT_EQ(r.GetRight(), 3);
3285 EXPECT_EQ(r.GetBottom(), 4);
3286
3287 EXPECT_POINT_NEAR(r.GetLeftTop(), Point(1, 2));
3288 EXPECT_POINT_NEAR(r.GetRightTop(), Point(3, 2));
3289 EXPECT_POINT_NEAR(r.GetLeftBottom(), Point(1, 4));
3290 EXPECT_POINT_NEAR(r.GetRightBottom(), Point(3, 4));
3291}
3292
3293TEST(RectTest, RectProject) {
3294 {
3295 auto r = Rect::MakeLTRB(-100, -100, 100, 100);
3296 auto actual = r.Project(r);
3297 auto expected = Rect::MakeLTRB(0, 0, 1, 1);
3298 EXPECT_RECT_NEAR(expected, actual);
3299 }
3300 {
3301 auto r = Rect::MakeLTRB(-100, -100, 100, 100);
3302 auto actual = r.Project(Rect::MakeLTRB(0, 0, 100, 100));
3303 auto expected = Rect::MakeLTRB(0.5, 0.5, 1, 1);
3304 EXPECT_RECT_NEAR(expected, actual);
3305 }
3306}
3307
3308TEST(RectTest, RectRoundOut) {
3309 {
3310 auto r = Rect::MakeLTRB(-100, -200, 300, 400);
3311 EXPECT_EQ(Rect::RoundOut(r), r);
3312 }
3313 {
3314 auto r = Rect::MakeLTRB(-100.1, -200.1, 300.1, 400.1);
3315 EXPECT_EQ(Rect::RoundOut(r), Rect::MakeLTRB(-101, -201, 301, 401));
3316 }
3317}
3318
3319TEST(RectTest, IRectRoundOut) {
3320 {
3321 auto r = Rect::MakeLTRB(-100, -200, 300, 400);
3322 auto ir = IRect::MakeLTRB(-100, -200, 300, 400);
3323 EXPECT_EQ(IRect::RoundOut(r), ir);
3324 }
3325 {
3326 auto r = Rect::MakeLTRB(-100.1, -200.1, 300.1, 400.1);
3327 auto ir = IRect::MakeLTRB(-101, -201, 301, 401);
3328 EXPECT_EQ(IRect::RoundOut(r), ir);
3329 }
3330}
3331
3332TEST(RectTest, RectRound) {
3333 {
3334 auto r = Rect::MakeLTRB(-100, -200, 300, 400);
3335 EXPECT_EQ(Rect::Round(r), r);
3336 }
3337 {
3338 auto r = Rect::MakeLTRB(-100.4, -200.4, 300.4, 400.4);
3339 EXPECT_EQ(Rect::Round(r), Rect::MakeLTRB(-100, -200, 300, 400));
3340 }
3341 {
3342 auto r = Rect::MakeLTRB(-100.5, -200.5, 300.5, 400.5);
3343 EXPECT_EQ(Rect::Round(r), Rect::MakeLTRB(-101, -201, 301, 401));
3344 }
3345}
3346
3347TEST(RectTest, IRectRound) {
3348 {
3349 auto r = Rect::MakeLTRB(-100, -200, 300, 400);
3350 auto ir = IRect::MakeLTRB(-100, -200, 300, 400);
3351 EXPECT_EQ(IRect::Round(r), ir);
3352 }
3353 {
3354 auto r = Rect::MakeLTRB(-100.4, -200.4, 300.4, 400.4);
3355 auto ir = IRect::MakeLTRB(-100, -200, 300, 400);
3356 EXPECT_EQ(IRect::Round(r), ir);
3357 }
3358 {
3359 auto r = Rect::MakeLTRB(-100.5, -200.5, 300.5, 400.5);
3360 auto ir = IRect::MakeLTRB(-101, -201, 301, 401);
3361 EXPECT_EQ(IRect::Round(r), ir);
3362 }
3363}
3364
3365TEST(RectTest, TransformAndClipBoundsNoCornersClipped) {
3366 // This matrix should clip no corners.
3367 auto matrix = impeller::Matrix::MakeColumn(
3368 // clang-format off
3369 2.0f, 0.0f, 0.0f, 0.0f,
3370 0.0f, 4.0f, 0.0f, 0.0f,
3371 0.0f, 0.0f, 1.0f, 0.0f,
3372 0.0f, 0.0f, 0.0f, 8.0f
3373 // clang-format on
3374 );
3375 Rect src = Rect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3376
3377 // None of these should have a W<0
3378
3379 EXPECT_EQ(matrix.TransformHomogenous(src.GetLeftTop()),
3380 Vector3(200.0f, 400.0f, 8.0f));
3381 EXPECT_EQ(matrix.TransformHomogenous(src.GetRightTop()),
3382 Vector3(400.0f, 400.0f, 8.0f));
3383 EXPECT_EQ(matrix.TransformHomogenous(src.GetLeftBottom()),
3384 Vector3(200.0f, 800.0f, 8.0f));
3385 EXPECT_EQ(matrix.TransformHomogenous(src.GetRightBottom()),
3386 Vector3(400.0f, 800.0f, 8.0f));
3387
3388 Rect expect = Rect::MakeLTRB(25.0f, 50.0f, 50.0f, 100.0f);
3389 EXPECT_FALSE(src.TransformAndClipBounds(matrix).IsEmpty());
3390 EXPECT_EQ(src.TransformAndClipBounds(matrix), expect);
3391}
3392
3393TEST(RectTest, TransformAndClipBoundsOneCornerClipped) {
3394 // This matrix should clip one corner.
3395 auto matrix = impeller::Matrix::MakeColumn(
3396 // clang-format off
3397 2.0f, 0.0f, 0.0f, -0.01f,
3398 0.0f, 2.0f, 0.0f, -0.006f,
3399 0.0f, 0.0f, 1.0f, 0.0f,
3400 0.0f, 0.0f, 0.0f, 3.0f
3401 // clang-format on
3402 );
3403 Rect src = Rect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3404
3405 // Exactly one of these should have a W<0
3406 //
3407 // When W<0 we interpolate the point back towards the adjacent points
3408 // that have W>0 to a location just greater than the W=0 half-plane.
3409 // We interpolate them to W=epsilon where epsilon == 2^-14.
3410
3411 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftTop()),
3412 Vector3(200.0f, 200.0f, 1.4f));
3413 // Contributes (200, 200) / 1.4 == (142.85714, 142.85714)
3414
3415 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightTop()),
3416 Vector3(400.0f, 200.0f, 0.4f));
3417 // Contributes (400, 200) / 0.4 == (1000, 500)
3418
3419 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftBottom()),
3420 Vector3(200.0f, 400.0f, 0.8f));
3421 // Contributes (200, 400) / 0.8 == (250, 500)
3422
3423 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightBottom()),
3424 Vector3(400.0f, 400.0f, -0.2f));
3425 // Interpolates at epsilon against RightTop to produce:
3426 // t = (epsilon - -.2) / (.4 - -.2)
3427 // = (epsilon + .2) / .6
3428 // = 0.333435
3429 // Lerp(RightBottom, RightTop, 0.333435) = (400, 333.313, epsilon)
3430 // = (6553600, 5461000)
3431 //
3432 // It also interpolates at epsilon against LeftBottom to produce:
3433 // t = (epsilon - -.2) / (.8 - -.2)
3434 // = (epsilon + .2) / 1
3435 // = 0.200061
3436 // Lerp(RightBottom, LeftBottom, 0.200061) = (359.988, 400, epsilon)
3437 // = (5898040, 6553600)
3438
3439 // Min/Max X and Y of all the points generated above are:
3440 // Min X == 142.85714
3441 // Min Y == 142.85714
3442 // Max X == 6553600
3443 // Max Y == 6553600
3444
3445 Rect expect = Rect::MakeLTRB(142.85714f, 142.85714f, 6553600.f, 6553600.f);
3446 EXPECT_FALSE(src.TransformAndClipBounds(matrix).IsEmpty());
3447 EXPECT_RECT_NEAR(src.TransformAndClipBounds(matrix), expect);
3448}
3449
3450TEST(RectTest, TransformAndClipBoundsTwoCornersClipped) {
3451 // This matrix should clip two corners.
3452 auto matrix = impeller::Matrix::MakeColumn(
3453 // clang-format off
3454 2.0f, 0.0f, 0.0f, -.015f,
3455 0.0f, 2.0f, 0.0f, -.006f,
3456 0.0f, 0.0f, 1.0f, 0.0f,
3457 0.0f, 0.0f, 0.0f, 3.0f
3458 // clang-format on
3459 );
3460 Rect src = Rect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3461
3462 // Exactly two of these homogenous results should have a W<0
3463 //
3464 // When W<0 we interpolate the point back towards the adjacent points
3465 // that have W>0 to a location just greater than the W=0 half-plane.
3466 // We interpolate them to W=epsilon where epsilon == 2^-14.
3467
3468 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftTop()),
3469 Vector3(200.0f, 200.0f, 0.9f));
3470 // Contributes (200, 200) / 0.9 == (222.2222, 222.2222) to bounds
3471
3472 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightTop()),
3473 Vector3(400.0f, 200.0f, -0.6f));
3474 // Interpolates at epsilon against LeftTop to produce:
3475 // t = (epsilon - -.6) / (.9 - -.6)
3476 // = (epsilon + .6) / 1.5
3477 // = 0.4000407
3478 // Lerp(RightTop, LeftTop, 0.4000407) = (319.9919, 200, epsilon)
3479 // = (5242747, 3276800)
3480 // Cannot interpolate against RightBottom because it also has W<0
3481
3482 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftBottom()),
3483 Vector3(200.0f, 400.0f, 0.3f));
3484 // Contributes (200, 400) / 0.3 == (666.6667, 1333.3333) to bounds
3485
3486 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightBottom()),
3487 Vector3(400.0f, 400.0f, -1.2f));
3488 // Interpolates at epsilon against LeftBottom to produce:
3489 // t = (epsilon - -1.2) / (.3 - -1.2)
3490 // = (epsilon + 1.2) / 1.5
3491 // = 0.8000407
3492 // Lerp(RightBottom, LeftBottom, 0.8000407) = (239.9919, 400, epsilon)
3493 // = (3932026.667, 6553600)
3494 // Cannot interpolate against RightTop because it also has W<0
3495
3496 // Min/Max X and Y of all the points generated above are:
3497 // Min X == 222.2222
3498 // Min Y == 222.2222
3499 // Max X == 5242747
3500 // Max Y == 6553600
3501
3502 Rect expect = Rect::MakeLTRB(222.2222f, 222.2222f, 5242747.f, 6553600.f);
3503
3504 EXPECT_FALSE(src.TransformAndClipBounds(matrix).IsEmpty());
3505 EXPECT_RECT_NEAR(src.TransformAndClipBounds(matrix), expect);
3506}
3507
3508TEST(RectTest, TransformAndClipBoundsThreeCornersClipped) {
3509 // This matrix should clip three corners.
3510 auto matrix = impeller::Matrix::MakeColumn(
3511 // clang-format off
3512 2.0f, 0.0f, 0.0f, -.02f,
3513 0.0f, 2.0f, 0.0f, -.006f,
3514 0.0f, 0.0f, 1.0f, 0.0f,
3515 0.0f, 0.0f, 0.0f, 3.0f
3516 // clang-format on
3517 );
3518 Rect src = Rect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3519
3520 // Exactly three of these homogenous results should have a W<0
3521 //
3522 // When W<0 we interpolate the point back towards the adjacent points
3523 // that have W>0 to a location just greater than the W=0 half-plane.
3524 // We interpolate them to W=epsilon where epsilon == 2^-14.
3525
3526 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftTop()),
3527 Vector3(200.0f, 200.0f, 0.4f));
3528 // Contributes (200, 200) / 0.4 == (500, 500) to bounds
3529
3530 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightTop()),
3531 Vector3(400.0f, 200.0f, -1.6f));
3532 // Interpolates at epsilon against LeftTop to produce:
3533 // t = (epsilon - -1.6) / (.4 - -1.6)
3534 // = (epsilon + 1.6) / 2
3535 // = 0.8000305
3536 // Lerp(RightTop, LeftTop, 0.8000305) = (239.9939, 200, epsilon)
3537 // = (3932060, 3276800)
3538 // Cannot interpolate against RightBottom because it also has W<0
3539
3540 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftBottom()),
3541 Vector3(200.0f, 400.0f, -0.2f));
3542 // Interpolates against LeftTop to produce:
3543 // t = (epsilon - -.2) / (.4 - -.2)
3544 // = (epsilon + .2) / .6
3545 // = 0.333435
3546 // Lerp(LeftBottom, LeftTop, .333435) = (200, 333.31299, epsilon)
3547 // = (3276800, 5461000)
3548 // Cannot interpolate against RightBottom because it also has W<0
3549
3550 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightBottom()),
3551 Vector3(400.0f, 400.0f, -2.2f));
3552 // Cannot interpolate against either RightTop or LeftBottom because
3553 // both of those adjacent points transformed to a W<0 homogenous point.
3554
3555 // Min/Max X and Y of all the points generated above are:
3556 // Min X == 500
3557 // Min Y == 500
3558 // Max X == 3932060
3559 // Max Y == 5461000
3560
3561 Rect expect = Rect::MakeLTRB(500.0f, 500.0f, 3932060.f, 5461000.f);
3562
3563 EXPECT_FALSE(src.TransformAndClipBounds(matrix).IsEmpty());
3564 EXPECT_RECT_NEAR(src.TransformAndClipBounds(matrix), expect);
3565}
3566
3567TEST(RectTest, TransformAndClipBoundsAllFourCornersClipped) {
3568 // This matrix should clip all four corners.
3569 auto matrix = impeller::Matrix::MakeColumn(
3570 // clang-format off
3571 2.0f, 0.0f, 0.0f, -.025f,
3572 0.0f, 2.0f, 0.0f, -.006f,
3573 0.0f, 0.0f, 1.0f, 0.0f,
3574 0.0f, 0.0f, 0.0f, 3.0f
3575 // clang-format on
3576 );
3577 Rect src = Rect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3578
3579 // All of these should have a W<0
3580 //
3581 // When W<0 we interpolate the point back towards the adjacent points
3582 // that have W>0 to a location just greater than the W=0 half-plane.
3583 // We interpolate them to W=epsilon where epsilon == 2^-14.
3584
3585 // In this case, none of the homogenous results are in bounds (W > 0)
3586 // so we can perform no interpolation - the operation is not visible.
3587
3588 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftTop()),
3589 Vector3(200.0f, 200.0f, -0.1f));
3590 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightTop()),
3591 Vector3(400.0f, 200.0f, -2.6f));
3592 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftBottom()),
3593 Vector3(200.0f, 400.0f, -0.7f));
3594 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightBottom()),
3595 Vector3(400.0f, 400.0f, -3.2f));
3596
3597 EXPECT_TRUE(src.TransformAndClipBounds(matrix).IsEmpty());
3598}
3599
3600} // namespace testing
3601} // namespace impeller
double x() const
Definition geometry.h:22
double y() const
Definition geometry.h:23
int32_t value
int32_t x
#define FML_DCHECK(condition)
Definition logging.h:122
inline ::testing::AssertionResult RectNear(impeller::Rect a, impeller::Rect b)
#define EXPECT_VECTOR3_NEAR(a, b)
#define EXPECT_RECT_NEAR(a, b)
#define EXPECT_POINT_NEAR(a, b)
double y
TEST(FrameTimingsRecorderTest, RecordVsync)
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
static constexpr DlScalar kEhCloseEnough
static constexpr R flip_tb(R rect)
static constexpr Rect swap_nan(const Rect &rect, int index)
static constexpr R flip_lrtb(R rect)
static constexpr R flip_lr(R rect)
float Scalar
Definition scalar.h:19
IRect64 IRect
Definition rect.h:860
TPoint< int64_t > IPoint
Definition point.h:427
ISize64 ISize
Definition size.h:162
std::array< Point, 4 > Quad
Definition point.h:431
A 4x4 matrix using column-major storage.
Definition matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition matrix.h:95
static constexpr Matrix MakeColumn(Scalar m0, Scalar m1, Scalar m2, Scalar m3, Scalar m4, Scalar m5, Scalar m6, Scalar m7, Scalar m8, Scalar m9, Scalar m10, Scalar m11, Scalar m12, Scalar m13, Scalar m14, Scalar m15)
Definition matrix.h:69
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104
constexpr auto GetBottom() const
Definition rect.h:391
constexpr Type GetY() const
Returns the Y coordinate of the upper left corner, equivalent to |GetOrigin().y|.
Definition rect.h:371
constexpr bool ContainsInclusive(const TPoint< Type > &p) const
Returns true iff the provided point |p| is inside the closed-range interior of this rectangle.
Definition rect.h:274
static constexpr TRect MakeEllipseBounds(const TPoint< Type > &center, const TSize< Type > &radii)
Definition rect.h:164
static constexpr TRect MakeWH(Type width, Type height)
Definition rect.h:140
constexpr auto GetTop() const
Definition rect.h:387
constexpr std::optional< TRect > Intersection(const TRect &o) const
Definition rect.h:562
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
Definition rect.h:361
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition rect.h:381
constexpr std::optional< TRect< T > > Cutout(const TRect &o) const
Returns the new boundary rectangle that would result from this rectangle being cut out by the specifi...
Definition rect.h:591
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
Definition rect.h:331
constexpr bool Contains(const TPoint< Type > &p) const
Returns true iff the provided point |p| is inside the half-open interior of this rectangle.
Definition rect.h:255
constexpr TRect Union(const TRect &o) const
Definition rect.h:547
static constexpr std::enable_if_t< std::is_floating_point_v< FT >, TRect > Make(const TRect< U > &rect)
Definition rect.h:181
constexpr bool IntersectsWithRect(const TRect &o) const
Definition rect.h:580
constexpr auto GetLeft() const
Definition rect.h:385
constexpr TRect CutoutOrEmpty(const TRect &o) const
Definition rect.h:631
Round(const TRect< U > &r)
Definition rect.h:764
RoundOut(const TRect< U > &r)
Definition rect.h:748
constexpr Type GetX() const
Returns the X coordinate of the upper left corner, equivalent to |GetOrigin().x|.
Definition rect.h:367
static constexpr TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition rect.h:144
constexpr auto GetRight() const
Definition rect.h:389
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition rect.h:136
static constexpr TRect MakeCircleBounds(const TPoint< Type > &center, Type radius)
Definition rect.h:156
constexpr TRect Scale(Type scale) const
Definition rect.h:226
static constexpr TRect MakeSize(const TSize< U > &size)
Definition rect.h:150
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition rect.h:375
static constexpr std::optional< TRect > MakePointBounds(const U &value)
Definition rect.h:189
static constexpr TRect MakeMaximum()
Definition rect.h:212
constexpr std::array< T, 4 > GetXYWH() const
Get the x, y coordinates of the origin and the width and height of the rectangle in an array.
Definition rect.h:427
constexpr TPoint< Type > GetOrigin() const
Returns the upper left corner of the rectangle as specified by the left/top or x/y values when it was...
Definition rect.h:354
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition rect.h:129
std::vector< Point > points