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, ContainsFloatingPoint) {
1441 auto rect1 =
1442 Rect::MakeXYWH(472.599945f, 440.999969f, 1102.80005f, 654.000061f);
1443 auto rect2 = Rect::MakeXYWH(724.f, 618.f, 600.f, 300.f);
1444 EXPECT_TRUE(rect1.Contains(rect2));
1445}
1446
1447TEST(RectTest, FloatContainsInteger) {
1448 auto rect1 =
1449 Rect::MakeLTRB(472.599945f, 440.999969f, 1574.80005f, 1094.000000f);
1450 EXPECT_TRUE(rect1.Contains(IRect::MakeLTRB(473, 441, 1574, 1094)));
1451
1452 // Now test failure to contain same rect expanded by 1 on each side
1453 EXPECT_FALSE(rect1.Contains(IRect::MakeLTRB(472, 441, 1574, 1094)));
1454 EXPECT_FALSE(rect1.Contains(IRect::MakeLTRB(473, 440, 1574, 1094)));
1455 EXPECT_FALSE(rect1.Contains(IRect::MakeLTRB(473, 441, 1575, 1094)));
1456 EXPECT_FALSE(rect1.Contains(IRect::MakeLTRB(473, 441, 1574, 1095)));
1457}
1458
1459template <typename R>
1460static constexpr inline R flip_lr(R rect) {
1461 return R::MakeLTRB(rect.GetRight(), rect.GetTop(), //
1462 rect.GetLeft(), rect.GetBottom());
1463}
1464
1465template <typename R>
1466static constexpr inline R flip_tb(R rect) {
1467 return R::MakeLTRB(rect.GetLeft(), rect.GetBottom(), //
1468 rect.GetRight(), rect.GetTop());
1469}
1470
1471template <typename R>
1472static constexpr inline R flip_lrtb(R rect) {
1473 return flip_lr(flip_tb(rect));
1474}
1475
1476static constexpr inline Rect swap_nan(const Rect& rect, int index) {
1477 Scalar nan = std::numeric_limits<Scalar>::quiet_NaN();
1478 FML_DCHECK(index >= 0 && index <= 15);
1479 Scalar l = ((index & (1 << 0)) != 0) ? nan : rect.GetLeft();
1480 Scalar t = ((index & (1 << 1)) != 0) ? nan : rect.GetTop();
1481 Scalar r = ((index & (1 << 2)) != 0) ? nan : rect.GetRight();
1482 Scalar b = ((index & (1 << 3)) != 0) ? nan : rect.GetBottom();
1483 return Rect::MakeLTRB(l, t, r, b);
1484}
1485
1486static constexpr inline Point swap_nan(const Point& point, int index) {
1487 Scalar nan = std::numeric_limits<Scalar>::quiet_NaN();
1488 FML_DCHECK(index >= 0 && index <= 3);
1489 Scalar x = ((index & (1 << 0)) != 0) ? nan : point.x;
1490 Scalar y = ((index & (1 << 1)) != 0) ? nan : point.y;
1491 return Point(x, y);
1492}
1493
1494TEST(RectTest, RectUnion) {
1495 auto check_nans = [](const Rect& a, const Rect& b, const std::string& label) {
1496 ASSERT_TRUE(a.IsFinite()) << label;
1497 ASSERT_TRUE(b.IsFinite()) << label;
1498 ASSERT_FALSE(a.Union(b).IsEmpty());
1499
1500 for (int i = 1; i < 16; i++) {
1501 // NaN in a produces b
1502 EXPECT_EQ(swap_nan(a, i).Union(b), b) << label << ", index = " << i;
1503 // NaN in b produces a
1504 EXPECT_EQ(a.Union(swap_nan(b, i)), a) << label << ", index = " << i;
1505 // NaN in both is empty
1506 for (int j = 1; j < 16; j++) {
1507 EXPECT_TRUE(swap_nan(a, i).Union(swap_nan(b, j)).IsEmpty())
1508 << label << ", indices = " << i << ", " << j;
1509 }
1510 }
1511 };
1512
1513 auto check_empty_flips = [](const Rect& a, const Rect& b,
1514 const std::string& label) {
1515 ASSERT_FALSE(a.IsEmpty());
1516 // b is allowed to be empty
1517
1518 // unflipped a vs flipped (empty) b yields a
1519 EXPECT_EQ(a.Union(flip_lr(b)), a) << label;
1520 EXPECT_EQ(a.Union(flip_tb(b)), a) << label;
1521 EXPECT_EQ(a.Union(flip_lrtb(b)), a) << label;
1522
1523 // flipped (empty) a vs unflipped b yields b
1524 EXPECT_EQ(flip_lr(a).Union(b), b) << label;
1525 EXPECT_EQ(flip_tb(a).Union(b), b) << label;
1526 EXPECT_EQ(flip_lrtb(a).Union(b), b) << label;
1527
1528 // flipped (empty) a vs flipped (empty) b yields empty
1529 EXPECT_TRUE(flip_lr(a).Union(flip_lr(b)).IsEmpty()) << label;
1530 EXPECT_TRUE(flip_tb(a).Union(flip_tb(b)).IsEmpty()) << label;
1531 EXPECT_TRUE(flip_lrtb(a).Union(flip_lrtb(b)).IsEmpty()) << label;
1532 };
1533
1534 auto test = [&check_nans, &check_empty_flips](const Rect& a, const Rect& b,
1535 const Rect& result) {
1536 ASSERT_FALSE(a.IsEmpty()) << a;
1537 // b is allowed to be empty
1538
1539 std::stringstream stream;
1540 stream << a << " union " << b;
1541 auto label = stream.str();
1542
1543 EXPECT_EQ(a.Union(b), result) << label;
1544 EXPECT_EQ(b.Union(a), result) << label;
1545 check_empty_flips(a, b, label);
1546 check_nans(a, b, label);
1547 };
1548
1549 {
1550 auto a = Rect::MakeXYWH(100, 100, 100, 100);
1551 auto b = Rect::MakeXYWH(0, 0, 0, 0);
1552 auto expected = Rect::MakeXYWH(100, 100, 100, 100);
1553 test(a, b, expected);
1554 }
1555
1556 {
1557 auto a = Rect::MakeXYWH(100, 100, 100, 100);
1558 auto b = Rect::MakeXYWH(0, 0, 1, 1);
1559 auto expected = Rect::MakeXYWH(0, 0, 200, 200);
1560 test(a, b, expected);
1561 }
1562
1563 {
1564 auto a = Rect::MakeXYWH(100, 100, 100, 100);
1565 auto b = Rect::MakeXYWH(10, 10, 1, 1);
1566 auto expected = Rect::MakeXYWH(10, 10, 190, 190);
1567 test(a, b, expected);
1568 }
1569
1570 {
1571 auto a = Rect::MakeXYWH(0, 0, 100, 100);
1572 auto b = Rect::MakeXYWH(10, 10, 100, 100);
1573 auto expected = Rect::MakeXYWH(0, 0, 110, 110);
1574 test(a, b, expected);
1575 }
1576
1577 {
1578 auto a = Rect::MakeXYWH(0, 0, 100, 100);
1579 auto b = Rect::MakeXYWH(100, 100, 100, 100);
1580 auto expected = Rect::MakeXYWH(0, 0, 200, 200);
1581 test(a, b, expected);
1582 }
1583}
1584
1585TEST(RectTest, OptRectUnion) {
1586 auto a = Rect::MakeLTRB(0, 0, 100, 100);
1587 auto b = Rect::MakeLTRB(100, 100, 200, 200);
1588 auto c = Rect::MakeLTRB(100, 0, 200, 100);
1589
1590 // NullOpt, NullOpt
1591 EXPECT_FALSE(Rect::Union(std::nullopt, std::nullopt).has_value());
1592 EXPECT_EQ(Rect::Union(std::nullopt, std::nullopt), std::nullopt);
1593
1594 auto test1 = [](const Rect& r) {
1595 // Rect, NullOpt
1596 EXPECT_EQ(Rect::Union(r, std::nullopt), r);
1597
1598 // OptRect, NullOpt
1599 EXPECT_TRUE(Rect::Union(std::optional(r), std::nullopt).has_value());
1600 EXPECT_EQ(Rect::Union(std::optional(r), std::nullopt).value(), r);
1601
1602 // NullOpt, Rect
1603 EXPECT_EQ(Rect::Union(std::nullopt, r), r);
1604
1605 // NullOpt, OptRect
1606 EXPECT_TRUE(Rect::Union(std::nullopt, std::optional(r)).has_value());
1607 EXPECT_EQ(Rect::Union(std::nullopt, std::optional(r)).value(), r);
1608 };
1609
1610 test1(a);
1611 test1(b);
1612 test1(c);
1613
1614 auto test2 = [](const Rect& a, const Rect& b, const Rect& u) {
1615 ASSERT_EQ(a.Union(b), u);
1616
1617 // Rect, OptRect
1618 EXPECT_EQ(Rect::Union(a, std::optional(b)), u);
1619
1620 // OptRect, Rect
1621 EXPECT_EQ(Rect::Union(std::optional(a), b), u);
1622
1623 // OptRect, OptRect
1624 EXPECT_TRUE(Rect::Union(std::optional(a), std::optional(b)).has_value());
1625 EXPECT_EQ(Rect::Union(std::optional(a), std::optional(b)).value(), u);
1626 };
1627
1628 test2(a, b, Rect::MakeLTRB(0, 0, 200, 200));
1629 test2(a, c, Rect::MakeLTRB(0, 0, 200, 100));
1630 test2(b, c, Rect::MakeLTRB(100, 0, 200, 200));
1631}
1632
1633TEST(RectTest, IRectUnion) {
1634 auto check_empty_flips = [](const IRect& a, const IRect& b,
1635 const std::string& label) {
1636 ASSERT_FALSE(a.IsEmpty());
1637 // b is allowed to be empty
1638
1639 // unflipped a vs flipped (empty) b yields a
1640 EXPECT_EQ(a.Union(flip_lr(b)), a) << label;
1641 EXPECT_EQ(a.Union(flip_tb(b)), a) << label;
1642 EXPECT_EQ(a.Union(flip_lrtb(b)), a) << label;
1643
1644 // flipped (empty) a vs unflipped b yields b
1645 EXPECT_EQ(flip_lr(a).Union(b), b) << label;
1646 EXPECT_EQ(flip_tb(a).Union(b), b) << label;
1647 EXPECT_EQ(flip_lrtb(a).Union(b), b) << label;
1648
1649 // flipped (empty) a vs flipped (empty) b yields empty
1650 EXPECT_TRUE(flip_lr(a).Union(flip_lr(b)).IsEmpty()) << label;
1651 EXPECT_TRUE(flip_tb(a).Union(flip_tb(b)).IsEmpty()) << label;
1652 EXPECT_TRUE(flip_lrtb(a).Union(flip_lrtb(b)).IsEmpty()) << label;
1653 };
1654
1655 auto test = [&check_empty_flips](const IRect& a, const IRect& b,
1656 const IRect& result) {
1657 ASSERT_FALSE(a.IsEmpty()) << a;
1658 // b is allowed to be empty
1659
1660 std::stringstream stream;
1661 stream << a << " union " << b;
1662 auto label = stream.str();
1663
1664 EXPECT_EQ(a.Union(b), result) << label;
1665 EXPECT_EQ(b.Union(a), result) << label;
1666 check_empty_flips(a, b, label);
1667 };
1668
1669 {
1670 auto a = IRect::MakeXYWH(100, 100, 100, 100);
1671 auto b = IRect::MakeXYWH(0, 0, 0, 0);
1672 auto expected = IRect::MakeXYWH(100, 100, 100, 100);
1673 test(a, b, expected);
1674 }
1675
1676 {
1677 auto a = IRect::MakeXYWH(100, 100, 100, 100);
1678 auto b = IRect::MakeXYWH(0, 0, 1, 1);
1679 auto expected = IRect::MakeXYWH(0, 0, 200, 200);
1680 test(a, b, expected);
1681 }
1682
1683 {
1684 auto a = IRect::MakeXYWH(100, 100, 100, 100);
1685 auto b = IRect::MakeXYWH(10, 10, 1, 1);
1686 auto expected = IRect::MakeXYWH(10, 10, 190, 190);
1687 test(a, b, expected);
1688 }
1689
1690 {
1691 auto a = IRect::MakeXYWH(0, 0, 100, 100);
1692 auto b = IRect::MakeXYWH(10, 10, 100, 100);
1693 auto expected = IRect::MakeXYWH(0, 0, 110, 110);
1694 test(a, b, expected);
1695 }
1696
1697 {
1698 auto a = IRect::MakeXYWH(0, 0, 100, 100);
1699 auto b = IRect::MakeXYWH(100, 100, 100, 100);
1700 auto expected = IRect::MakeXYWH(0, 0, 200, 200);
1701 test(a, b, expected);
1702 }
1703}
1704
1705TEST(RectTest, OptIRectUnion) {
1706 auto a = IRect::MakeLTRB(0, 0, 100, 100);
1707 auto b = IRect::MakeLTRB(100, 100, 200, 200);
1708 auto c = IRect::MakeLTRB(100, 0, 200, 100);
1709
1710 // NullOpt, NullOpt
1711 EXPECT_FALSE(IRect::Union(std::nullopt, std::nullopt).has_value());
1712 EXPECT_EQ(IRect::Union(std::nullopt, std::nullopt), std::nullopt);
1713
1714 auto test1 = [](const IRect& r) {
1715 // Rect, NullOpt
1716 EXPECT_EQ(IRect::Union(r, std::nullopt), r);
1717
1718 // OptRect, NullOpt
1719 EXPECT_TRUE(IRect::Union(std::optional(r), std::nullopt).has_value());
1720 EXPECT_EQ(IRect::Union(std::optional(r), std::nullopt).value(), r);
1721
1722 // NullOpt, Rect
1723 EXPECT_EQ(IRect::Union(std::nullopt, r), r);
1724
1725 // NullOpt, OptRect
1726 EXPECT_TRUE(IRect::Union(std::nullopt, std::optional(r)).has_value());
1727 EXPECT_EQ(IRect::Union(std::nullopt, std::optional(r)).value(), r);
1728 };
1729
1730 test1(a);
1731 test1(b);
1732 test1(c);
1733
1734 auto test2 = [](const IRect& a, const IRect& b, const IRect& u) {
1735 ASSERT_EQ(a.Union(b), u);
1736
1737 // Rect, OptRect
1738 EXPECT_EQ(IRect::Union(a, std::optional(b)), u);
1739
1740 // OptRect, Rect
1741 EXPECT_EQ(IRect::Union(std::optional(a), b), u);
1742
1743 // OptRect, OptRect
1744 EXPECT_TRUE(IRect::Union(std::optional(a), std::optional(b)).has_value());
1745 EXPECT_EQ(IRect::Union(std::optional(a), std::optional(b)).value(), u);
1746 };
1747
1748 test2(a, b, IRect::MakeLTRB(0, 0, 200, 200));
1749 test2(a, c, IRect::MakeLTRB(0, 0, 200, 100));
1750 test2(b, c, IRect::MakeLTRB(100, 0, 200, 200));
1751}
1752
1753TEST(RectTest, RectIntersection) {
1754 auto check_nans = [](const Rect& a, const Rect& b, const std::string& label) {
1755 ASSERT_TRUE(a.IsFinite()) << label;
1756 ASSERT_TRUE(b.IsFinite()) << label;
1757
1758 for (int i = 1; i < 16; i++) {
1759 // NaN in a produces empty
1760 EXPECT_FALSE(swap_nan(a, i).Intersection(b).has_value())
1761 << label << ", index = " << i;
1762 // NaN in b produces empty
1763 EXPECT_FALSE(a.Intersection(swap_nan(b, i)).has_value())
1764 << label << ", index = " << i;
1765 // NaN in both is empty
1766 for (int j = 1; j < 16; j++) {
1767 EXPECT_FALSE(swap_nan(a, i).Intersection(swap_nan(b, j)).has_value())
1768 << label << ", indices = " << i << ", " << j;
1769 }
1770 }
1771 };
1772
1773 auto check_empty_flips = [](const Rect& a, const Rect& b,
1774 const std::string& label) {
1775 ASSERT_FALSE(a.IsEmpty());
1776 // b is allowed to be empty
1777
1778 // unflipped a vs flipped (empty) b yields a
1779 EXPECT_FALSE(a.Intersection(flip_lr(b)).has_value()) << label;
1780 EXPECT_TRUE(a.IntersectionOrEmpty(flip_lr(b)).IsEmpty()) << label;
1781 EXPECT_FALSE(a.Intersection(flip_tb(b)).has_value()) << label;
1782 EXPECT_TRUE(a.IntersectionOrEmpty(flip_tb(b)).IsEmpty()) << label;
1783 EXPECT_FALSE(a.Intersection(flip_lrtb(b)).has_value()) << label;
1784 EXPECT_TRUE(a.IntersectionOrEmpty(flip_lrtb(b)).IsEmpty()) << label;
1785
1786 // flipped (empty) a vs unflipped b yields b
1787 EXPECT_FALSE(flip_lr(a).Intersection(b).has_value()) << label;
1788 EXPECT_TRUE(flip_lr(a).IntersectionOrEmpty(b).IsEmpty()) << label;
1789 EXPECT_FALSE(flip_tb(a).Intersection(b).has_value()) << label;
1790 EXPECT_TRUE(flip_tb(a).IntersectionOrEmpty(b).IsEmpty()) << label;
1791 EXPECT_FALSE(flip_lrtb(a).Intersection(b).has_value()) << label;
1792 EXPECT_TRUE(flip_lrtb(a).IntersectionOrEmpty(b).IsEmpty()) << label;
1793
1794 // flipped (empty) a vs flipped (empty) b yields empty
1795 EXPECT_FALSE(flip_lr(a).Intersection(flip_lr(b)).has_value()) << label;
1796 EXPECT_TRUE(flip_lr(a).IntersectionOrEmpty(flip_lr(b)).IsEmpty()) << label;
1797 EXPECT_FALSE(flip_tb(a).Intersection(flip_tb(b)).has_value()) << label;
1798 EXPECT_TRUE(flip_tb(a).IntersectionOrEmpty(flip_tb(b)).IsEmpty()) << label;
1799 EXPECT_FALSE(flip_lrtb(a).Intersection(flip_lrtb(b)).has_value()) << label;
1800 EXPECT_TRUE(flip_lrtb(a).IntersectionOrEmpty(flip_lrtb(b)).IsEmpty())
1801 << label;
1802 };
1803
1804 auto test_non_empty = [&check_nans, &check_empty_flips](
1805 const Rect& a, const Rect& b, const Rect& result) {
1806 ASSERT_FALSE(a.IsEmpty()) << a;
1807 // b is allowed to be empty
1808
1809 std::stringstream stream;
1810 stream << a << " union " << b;
1811 auto label = stream.str();
1812
1813 EXPECT_TRUE(a.Intersection(b).has_value()) << label;
1814 EXPECT_TRUE(b.Intersection(a).has_value()) << label;
1815 EXPECT_EQ(a.Intersection(b), result) << label;
1816 EXPECT_EQ(b.Intersection(a), result) << label;
1817 check_empty_flips(a, b, label);
1818 check_nans(a, b, label);
1819 };
1820
1821 auto test_empty = [&check_nans, &check_empty_flips](const Rect& a,
1822 const Rect& b) {
1823 ASSERT_FALSE(a.IsEmpty()) << a;
1824 // b is allowed to be empty
1825
1826 std::stringstream stream;
1827 stream << a << " union " << b;
1828 auto label = stream.str();
1829
1830 EXPECT_FALSE(a.Intersection(b).has_value()) << label;
1831 EXPECT_TRUE(a.IntersectionOrEmpty(b).IsEmpty()) << label;
1832 EXPECT_FALSE(b.Intersection(a).has_value()) << label;
1833 EXPECT_TRUE(b.IntersectionOrEmpty(a).IsEmpty()) << label;
1834 check_empty_flips(a, b, label);
1835 check_nans(a, b, label);
1836 };
1837
1838 {
1839 auto a = Rect::MakeXYWH(100, 100, 100, 100);
1840 auto b = Rect::MakeXYWH(0, 0, 0, 0);
1841
1842 test_empty(a, b);
1843 }
1844
1845 {
1846 auto a = Rect::MakeXYWH(100, 100, 100, 100);
1847 auto b = Rect::MakeXYWH(10, 10, 0, 0);
1848
1849 test_empty(a, b);
1850 }
1851
1852 {
1853 auto a = Rect::MakeXYWH(0, 0, 100, 100);
1854 auto b = Rect::MakeXYWH(10, 10, 100, 100);
1855 auto expected = Rect::MakeXYWH(10, 10, 90, 90);
1856
1857 test_non_empty(a, b, expected);
1858 }
1859
1860 {
1861 auto a = Rect::MakeXYWH(0, 0, 100, 100);
1862 auto b = Rect::MakeXYWH(100, 100, 100, 100);
1863
1864 test_empty(a, b);
1865 }
1866
1867 {
1868 auto a = Rect::MakeMaximum();
1869 auto b = Rect::MakeXYWH(10, 10, 300, 300);
1870
1871 test_non_empty(a, b, b);
1872 }
1873
1874 {
1875 auto a = Rect::MakeMaximum();
1876 auto b = Rect::MakeMaximum();
1877
1878 test_non_empty(a, b, Rect::MakeMaximum());
1879 }
1880}
1881
1882TEST(RectTest, OptRectIntersection) {
1883 auto a = Rect::MakeLTRB(0, 0, 110, 110);
1884 auto b = Rect::MakeLTRB(100, 100, 200, 200);
1885 auto c = Rect::MakeLTRB(100, 0, 200, 110);
1886
1887 // NullOpt, NullOpt
1888 EXPECT_FALSE(Rect::Intersection(std::nullopt, std::nullopt).has_value());
1889 EXPECT_EQ(Rect::Intersection(std::nullopt, std::nullopt), std::nullopt);
1890
1891 auto test1 = [](const Rect& r) {
1892 // Rect, NullOpt
1893 EXPECT_TRUE(Rect::Intersection(r, std::nullopt).has_value());
1894 EXPECT_EQ(Rect::Intersection(r, std::nullopt).value(), r);
1895
1896 // OptRect, NullOpt
1897 EXPECT_TRUE(Rect::Intersection(std::optional(r), std::nullopt).has_value());
1898 EXPECT_EQ(Rect::Intersection(std::optional(r), std::nullopt).value(), r);
1899
1900 // NullOpt, Rect
1901 EXPECT_TRUE(Rect::Intersection(std::nullopt, r).has_value());
1902 EXPECT_EQ(Rect::Intersection(std::nullopt, r).value(), r);
1903
1904 // NullOpt, OptRect
1905 EXPECT_TRUE(Rect::Intersection(std::nullopt, std::optional(r)).has_value());
1906 EXPECT_EQ(Rect::Intersection(std::nullopt, std::optional(r)).value(), r);
1907 };
1908
1909 test1(a);
1910 test1(b);
1911 test1(c);
1912
1913 auto test2 = [](const Rect& a, const Rect& b, const Rect& i) {
1914 ASSERT_EQ(a.Intersection(b), i);
1915
1916 // Rect, OptRect
1917 EXPECT_TRUE(Rect::Intersection(a, std::optional(b)).has_value());
1918 EXPECT_EQ(Rect::Intersection(a, std::optional(b)).value(), i);
1919
1920 // OptRect, Rect
1921 EXPECT_TRUE(Rect::Intersection(std::optional(a), b).has_value());
1922 EXPECT_EQ(Rect::Intersection(std::optional(a), b).value(), i);
1923
1924 // OptRect, OptRect
1925 EXPECT_TRUE(
1926 Rect::Intersection(std::optional(a), std::optional(b)).has_value());
1927 EXPECT_EQ(Rect::Intersection(std::optional(a), std::optional(b)).value(),
1928 i);
1929 };
1930
1931 test2(a, b, Rect::MakeLTRB(100, 100, 110, 110));
1932 test2(a, c, Rect::MakeLTRB(100, 0, 110, 110));
1933 test2(b, c, Rect::MakeLTRB(100, 100, 200, 110));
1934}
1935
1936TEST(RectTest, IRectIntersection) {
1937 auto check_empty_flips = [](const IRect& a, const IRect& b,
1938 const std::string& label) {
1939 ASSERT_FALSE(a.IsEmpty());
1940 // b is allowed to be empty
1941
1942 // unflipped a vs flipped (empty) b yields a
1943 EXPECT_FALSE(a.Intersection(flip_lr(b)).has_value()) << label;
1944 EXPECT_FALSE(a.Intersection(flip_tb(b)).has_value()) << label;
1945 EXPECT_FALSE(a.Intersection(flip_lrtb(b)).has_value()) << label;
1946
1947 // flipped (empty) a vs unflipped b yields b
1948 EXPECT_FALSE(flip_lr(a).Intersection(b).has_value()) << label;
1949 EXPECT_FALSE(flip_tb(a).Intersection(b).has_value()) << label;
1950 EXPECT_FALSE(flip_lrtb(a).Intersection(b).has_value()) << label;
1951
1952 // flipped (empty) a vs flipped (empty) b yields empty
1953 EXPECT_FALSE(flip_lr(a).Intersection(flip_lr(b)).has_value()) << label;
1954 EXPECT_FALSE(flip_tb(a).Intersection(flip_tb(b)).has_value()) << label;
1955 EXPECT_FALSE(flip_lrtb(a).Intersection(flip_lrtb(b)).has_value()) << label;
1956 };
1957
1958 auto test_non_empty = [&check_empty_flips](const IRect& a, const IRect& b,
1959 const IRect& result) {
1960 ASSERT_FALSE(a.IsEmpty()) << a;
1961 // b is allowed to be empty
1962
1963 std::stringstream stream;
1964 stream << a << " union " << b;
1965 auto label = stream.str();
1966
1967 EXPECT_TRUE(a.Intersection(b).has_value()) << label;
1968 EXPECT_TRUE(b.Intersection(a).has_value()) << label;
1969 EXPECT_EQ(a.Intersection(b), result) << label;
1970 EXPECT_EQ(b.Intersection(a), result) << label;
1971 check_empty_flips(a, b, label);
1972 };
1973
1974 auto test_empty = [&check_empty_flips](const IRect& a, const IRect& b) {
1975 ASSERT_FALSE(a.IsEmpty()) << a;
1976 // b is allowed to be empty
1977
1978 std::stringstream stream;
1979 stream << a << " union " << b;
1980 auto label = stream.str();
1981
1982 EXPECT_FALSE(a.Intersection(b).has_value()) << label;
1983 EXPECT_FALSE(b.Intersection(a).has_value()) << label;
1984 check_empty_flips(a, b, label);
1985 };
1986
1987 {
1988 auto a = IRect::MakeXYWH(100, 100, 100, 100);
1989 auto b = IRect::MakeXYWH(0, 0, 0, 0);
1990
1991 test_empty(a, b);
1992 }
1993
1994 {
1995 auto a = IRect::MakeXYWH(100, 100, 100, 100);
1996 auto b = IRect::MakeXYWH(10, 10, 0, 0);
1997
1998 test_empty(a, b);
1999 }
2000
2001 {
2002 auto a = IRect::MakeXYWH(0, 0, 100, 100);
2003 auto b = IRect::MakeXYWH(10, 10, 100, 100);
2004 auto expected = IRect::MakeXYWH(10, 10, 90, 90);
2005
2006 test_non_empty(a, b, expected);
2007 }
2008
2009 {
2010 auto a = IRect::MakeXYWH(0, 0, 100, 100);
2011 auto b = IRect::MakeXYWH(100, 100, 100, 100);
2012
2013 test_empty(a, b);
2014 }
2015
2016 {
2017 auto a = IRect::MakeMaximum();
2018 auto b = IRect::MakeXYWH(10, 10, 300, 300);
2019
2020 test_non_empty(a, b, b);
2021 }
2022
2023 {
2024 auto a = IRect::MakeMaximum();
2025 auto b = IRect::MakeMaximum();
2026
2027 test_non_empty(a, b, IRect::MakeMaximum());
2028 }
2029}
2030
2031TEST(RectTest, OptIRectIntersection) {
2032 auto a = IRect::MakeLTRB(0, 0, 110, 110);
2033 auto b = IRect::MakeLTRB(100, 100, 200, 200);
2034 auto c = IRect::MakeLTRB(100, 0, 200, 110);
2035
2036 // NullOpt, NullOpt
2037 EXPECT_FALSE(IRect::Intersection(std::nullopt, std::nullopt).has_value());
2038 EXPECT_EQ(IRect::Intersection(std::nullopt, std::nullopt), std::nullopt);
2039
2040 auto test1 = [](const IRect& r) {
2041 // Rect, NullOpt
2042 EXPECT_TRUE(IRect::Intersection(r, std::nullopt).has_value());
2043 EXPECT_EQ(IRect::Intersection(r, std::nullopt).value(), r);
2044
2045 // OptRect, NullOpt
2046 EXPECT_TRUE(
2047 IRect::Intersection(std::optional(r), std::nullopt).has_value());
2048 EXPECT_EQ(IRect::Intersection(std::optional(r), std::nullopt).value(), r);
2049
2050 // NullOpt, Rect
2051 EXPECT_TRUE(IRect::Intersection(std::nullopt, r).has_value());
2052 EXPECT_EQ(IRect::Intersection(std::nullopt, r).value(), r);
2053
2054 // NullOpt, OptRect
2055 EXPECT_TRUE(
2056 IRect::Intersection(std::nullopt, std::optional(r)).has_value());
2057 EXPECT_EQ(IRect::Intersection(std::nullopt, std::optional(r)).value(), r);
2058 };
2059
2060 test1(a);
2061 test1(b);
2062 test1(c);
2063
2064 auto test2 = [](const IRect& a, const IRect& b, const IRect& i) {
2065 ASSERT_EQ(a.Intersection(b), i);
2066
2067 // Rect, OptRect
2068 EXPECT_TRUE(IRect::Intersection(a, std::optional(b)).has_value());
2069 EXPECT_EQ(IRect::Intersection(a, std::optional(b)).value(), i);
2070
2071 // OptRect, Rect
2072 EXPECT_TRUE(IRect::Intersection(std::optional(a), b).has_value());
2073 EXPECT_EQ(IRect::Intersection(std::optional(a), b).value(), i);
2074
2075 // OptRect, OptRect
2076 EXPECT_TRUE(
2077 IRect::Intersection(std::optional(a), std::optional(b)).has_value());
2078 EXPECT_EQ(IRect::Intersection(std::optional(a), std::optional(b)).value(),
2079 i);
2080 };
2081
2082 test2(a, b, IRect::MakeLTRB(100, 100, 110, 110));
2083 test2(a, c, IRect::MakeLTRB(100, 0, 110, 110));
2084 test2(b, c, IRect::MakeLTRB(100, 100, 200, 110));
2085}
2086
2087TEST(RectTest, RectIntersectsWithRect) {
2088 auto check_nans = [](const Rect& a, const Rect& b, const std::string& label) {
2089 ASSERT_TRUE(a.IsFinite()) << label;
2090 ASSERT_TRUE(b.IsFinite()) << label;
2091
2092 for (int i = 1; i < 16; i++) {
2093 // NaN in a produces b
2094 EXPECT_FALSE(swap_nan(a, i).IntersectsWithRect(b))
2095 << label << ", index = " << i;
2096 // NaN in b produces a
2097 EXPECT_FALSE(a.IntersectsWithRect(swap_nan(b, i)))
2098 << label << ", index = " << i;
2099 // NaN in both is empty
2100 for (int j = 1; j < 16; j++) {
2101 EXPECT_FALSE(swap_nan(a, i).IntersectsWithRect(swap_nan(b, j)))
2102 << label << ", indices = " << i << ", " << j;
2103 }
2104 }
2105 };
2106
2107 auto check_empty_flips = [](const Rect& a, const Rect& b,
2108 const std::string& label) {
2109 ASSERT_FALSE(a.IsEmpty());
2110 // b is allowed to be empty
2111
2112 // unflipped a vs flipped (empty) b yields a
2113 EXPECT_FALSE(a.IntersectsWithRect(flip_lr(b))) << label;
2114 EXPECT_FALSE(a.IntersectsWithRect(flip_tb(b))) << label;
2115 EXPECT_FALSE(a.IntersectsWithRect(flip_lrtb(b))) << label;
2116
2117 // flipped (empty) a vs unflipped b yields b
2118 EXPECT_FALSE(flip_lr(a).IntersectsWithRect(b)) << label;
2119 EXPECT_FALSE(flip_tb(a).IntersectsWithRect(b)) << label;
2120 EXPECT_FALSE(flip_lrtb(a).IntersectsWithRect(b)) << label;
2121
2122 // flipped (empty) a vs flipped (empty) b yields empty
2123 EXPECT_FALSE(flip_lr(a).IntersectsWithRect(flip_lr(b))) << label;
2124 EXPECT_FALSE(flip_tb(a).IntersectsWithRect(flip_tb(b))) << label;
2125 EXPECT_FALSE(flip_lrtb(a).IntersectsWithRect(flip_lrtb(b))) << label;
2126 };
2127
2128 auto test_non_empty = [&check_nans, &check_empty_flips](const Rect& a,
2129 const Rect& b) {
2130 ASSERT_FALSE(a.IsEmpty()) << a;
2131 // b is allowed to be empty
2132
2133 std::stringstream stream;
2134 stream << a << " union " << b;
2135 auto label = stream.str();
2136
2137 EXPECT_TRUE(a.IntersectsWithRect(b)) << label;
2138 EXPECT_TRUE(b.IntersectsWithRect(a)) << label;
2139 check_empty_flips(a, b, label);
2140 check_nans(a, b, label);
2141 };
2142
2143 auto test_empty = [&check_nans, &check_empty_flips](const Rect& a,
2144 const Rect& b) {
2145 ASSERT_FALSE(a.IsEmpty()) << a;
2146 // b is allowed to be empty
2147
2148 std::stringstream stream;
2149 stream << a << " union " << b;
2150 auto label = stream.str();
2151
2152 EXPECT_FALSE(a.IntersectsWithRect(b)) << label;
2153 EXPECT_FALSE(b.IntersectsWithRect(a)) << label;
2154 check_empty_flips(a, b, label);
2155 check_nans(a, b, label);
2156 };
2157
2158 {
2159 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2160 auto b = Rect::MakeXYWH(0, 0, 0, 0);
2161
2162 test_empty(a, b);
2163 }
2164
2165 {
2166 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2167 auto b = Rect::MakeXYWH(10, 10, 0, 0);
2168
2169 test_empty(a, b);
2170 }
2171
2172 {
2173 auto a = Rect::MakeXYWH(0, 0, 100, 100);
2174 auto b = Rect::MakeXYWH(10, 10, 100, 100);
2175
2176 test_non_empty(a, b);
2177 }
2178
2179 {
2180 auto a = Rect::MakeXYWH(0, 0, 100, 100);
2181 auto b = Rect::MakeXYWH(100, 100, 100, 100);
2182
2183 test_empty(a, b);
2184 }
2185
2186 {
2187 auto a = Rect::MakeMaximum();
2188 auto b = Rect::MakeXYWH(10, 10, 100, 100);
2189
2190 test_non_empty(a, b);
2191 }
2192
2193 {
2194 auto a = Rect::MakeMaximum();
2195 auto b = Rect::MakeMaximum();
2196
2197 test_non_empty(a, b);
2198 }
2199}
2200
2201TEST(RectTest, IRectIntersectsWithRect) {
2202 auto check_empty_flips = [](const IRect& a, const IRect& b,
2203 const std::string& label) {
2204 ASSERT_FALSE(a.IsEmpty());
2205 // b is allowed to be empty
2206
2207 // unflipped a vs flipped (empty) b yields a
2208 EXPECT_FALSE(a.IntersectsWithRect(flip_lr(b))) << label;
2209 EXPECT_FALSE(a.IntersectsWithRect(flip_tb(b))) << label;
2210 EXPECT_FALSE(a.IntersectsWithRect(flip_lrtb(b))) << label;
2211
2212 // flipped (empty) a vs unflipped b yields b
2213 EXPECT_FALSE(flip_lr(a).IntersectsWithRect(b)) << label;
2214 EXPECT_FALSE(flip_tb(a).IntersectsWithRect(b)) << label;
2215 EXPECT_FALSE(flip_lrtb(a).IntersectsWithRect(b)) << label;
2216
2217 // flipped (empty) a vs flipped (empty) b yields empty
2218 EXPECT_FALSE(flip_lr(a).IntersectsWithRect(flip_lr(b))) << label;
2219 EXPECT_FALSE(flip_tb(a).IntersectsWithRect(flip_tb(b))) << label;
2220 EXPECT_FALSE(flip_lrtb(a).IntersectsWithRect(flip_lrtb(b))) << label;
2221 };
2222
2223 auto test_non_empty = [&check_empty_flips](const IRect& a, const IRect& b) {
2224 ASSERT_FALSE(a.IsEmpty()) << a;
2225 // b is allowed to be empty
2226
2227 std::stringstream stream;
2228 stream << a << " union " << b;
2229 auto label = stream.str();
2230
2231 EXPECT_TRUE(a.IntersectsWithRect(b)) << label;
2232 EXPECT_TRUE(b.IntersectsWithRect(a)) << label;
2233 check_empty_flips(a, b, label);
2234 };
2235
2236 auto test_empty = [&check_empty_flips](const IRect& a, const IRect& b) {
2237 ASSERT_FALSE(a.IsEmpty()) << a;
2238 // b is allowed to be empty
2239
2240 std::stringstream stream;
2241 stream << a << " union " << b;
2242 auto label = stream.str();
2243
2244 EXPECT_FALSE(a.IntersectsWithRect(b)) << label;
2245 EXPECT_FALSE(b.IntersectsWithRect(a)) << label;
2246 check_empty_flips(a, b, label);
2247 };
2248
2249 {
2250 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2251 auto b = IRect::MakeXYWH(0, 0, 0, 0);
2252
2253 test_empty(a, b);
2254 }
2255
2256 {
2257 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2258 auto b = IRect::MakeXYWH(10, 10, 0, 0);
2259
2260 test_empty(a, b);
2261 }
2262
2263 {
2264 auto a = IRect::MakeXYWH(0, 0, 100, 100);
2265 auto b = IRect::MakeXYWH(10, 10, 100, 100);
2266
2267 test_non_empty(a, b);
2268 }
2269
2270 {
2271 auto a = IRect::MakeXYWH(0, 0, 100, 100);
2272 auto b = IRect::MakeXYWH(100, 100, 100, 100);
2273
2274 test_empty(a, b);
2275 }
2276
2277 {
2278 auto a = IRect::MakeMaximum();
2279 auto b = IRect::MakeXYWH(10, 10, 100, 100);
2280
2281 test_non_empty(a, b);
2282 }
2283
2284 {
2285 auto a = IRect::MakeMaximum();
2286 auto b = IRect::MakeMaximum();
2287
2288 test_non_empty(a, b);
2289 }
2290}
2291
2292TEST(RectTest, RectContainsPoint) {
2293 auto check_nans = [](const Rect& rect, const Point& point,
2294 const std::string& label) {
2295 ASSERT_TRUE(rect.IsFinite()) << label;
2296 ASSERT_TRUE(point.IsFinite()) << label;
2297
2298 for (int i = 1; i < 16; i++) {
2299 EXPECT_FALSE(swap_nan(rect, i).Contains(point))
2300 << label << ", index = " << i;
2301 for (int j = 1; j < 4; j++) {
2302 EXPECT_FALSE(swap_nan(rect, i).Contains(swap_nan(point, j)))
2303 << label << ", indices = " << i << ", " << j;
2304 }
2305 }
2306 };
2307
2308 auto check_empty_flips = [](const Rect& rect, const Point& point,
2309 const std::string& label) {
2310 ASSERT_FALSE(rect.IsEmpty());
2311
2312 EXPECT_FALSE(flip_lr(rect).Contains(point)) << label;
2313 EXPECT_FALSE(flip_tb(rect).Contains(point)) << label;
2314 EXPECT_FALSE(flip_lrtb(rect).Contains(point)) << label;
2315 };
2316
2317 auto test_inside = [&check_nans, &check_empty_flips](const Rect& rect,
2318 const Point& point) {
2319 ASSERT_FALSE(rect.IsEmpty()) << rect;
2320
2321 std::stringstream stream;
2322 stream << rect << " contains " << point;
2323 auto label = stream.str();
2324
2325 EXPECT_TRUE(rect.Contains(point)) << label;
2326 check_empty_flips(rect, point, label);
2327 check_nans(rect, point, label);
2328 };
2329
2330 auto test_outside = [&check_nans, &check_empty_flips](const Rect& rect,
2331 const Point& point) {
2332 ASSERT_FALSE(rect.IsEmpty()) << rect;
2333
2334 std::stringstream stream;
2335 stream << rect << " contains " << point;
2336 auto label = stream.str();
2337
2338 EXPECT_FALSE(rect.Contains(point)) << label;
2339 check_empty_flips(rect, point, label);
2340 check_nans(rect, point, label);
2341 };
2342
2343 {
2344 // Origin is inclusive
2345 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2346 auto p = Point(100, 100);
2347
2348 test_inside(r, p);
2349 }
2350 {
2351 // Size is exclusive
2352 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2353 auto p = Point(200, 200);
2354
2355 test_outside(r, p);
2356 }
2357 {
2358 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2359 auto p = Point(99, 99);
2360
2361 test_outside(r, p);
2362 }
2363 {
2364 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2365 auto p = Point(199, 199);
2366
2367 test_inside(r, p);
2368 }
2369
2370 {
2371 auto r = Rect::MakeMaximum();
2372 auto p = Point(199, 199);
2373
2374 test_inside(r, p);
2375 }
2376}
2377
2378TEST(RectTest, IRectContainsIPoint) {
2379 auto check_empty_flips = [](const IRect& rect, const IPoint& point,
2380 const std::string& label) {
2381 ASSERT_FALSE(rect.IsEmpty());
2382
2383 EXPECT_FALSE(flip_lr(rect).Contains(point)) << label;
2384 EXPECT_FALSE(flip_tb(rect).Contains(point)) << label;
2385 EXPECT_FALSE(flip_lrtb(rect).Contains(point)) << label;
2386 };
2387
2388 auto test_inside = [&check_empty_flips](const IRect& rect,
2389 const IPoint& point) {
2390 ASSERT_FALSE(rect.IsEmpty()) << rect;
2391
2392 std::stringstream stream;
2393 stream << rect << " contains " << point;
2394 auto label = stream.str();
2395
2396 EXPECT_TRUE(rect.Contains(point)) << label;
2397 check_empty_flips(rect, point, label);
2398 };
2399
2400 auto test_outside = [&check_empty_flips](const IRect& rect,
2401 const IPoint& point) {
2402 ASSERT_FALSE(rect.IsEmpty()) << rect;
2403
2404 std::stringstream stream;
2405 stream << rect << " contains " << point;
2406 auto label = stream.str();
2407
2408 EXPECT_FALSE(rect.Contains(point)) << label;
2409 check_empty_flips(rect, point, label);
2410 };
2411
2412 {
2413 // Origin is inclusive
2414 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2415 auto p = IPoint(100, 100);
2416
2417 test_inside(r, p);
2418 }
2419 {
2420 // Size is exclusive
2421 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2422 auto p = IPoint(200, 200);
2423
2424 test_outside(r, p);
2425 }
2426 {
2427 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2428 auto p = IPoint(99, 99);
2429
2430 test_outside(r, p);
2431 }
2432 {
2433 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2434 auto p = IPoint(199, 199);
2435
2436 test_inside(r, p);
2437 }
2438
2439 {
2440 auto r = IRect::MakeMaximum();
2441 auto p = IPoint(199, 199);
2442
2443 test_inside(r, p);
2444 }
2445}
2446
2447TEST(RectTest, RectContainsInclusivePoint) {
2448 auto check_nans = [](const Rect& rect, const Point& point,
2449 const std::string& label) {
2450 ASSERT_TRUE(rect.IsFinite()) << label;
2451 ASSERT_TRUE(point.IsFinite()) << label;
2452
2453 for (int i = 1; i < 16; i++) {
2454 EXPECT_FALSE(swap_nan(rect, i).ContainsInclusive(point))
2455 << label << ", index = " << i;
2456 for (int j = 1; j < 4; j++) {
2457 EXPECT_FALSE(swap_nan(rect, i).ContainsInclusive(swap_nan(point, j)))
2458 << label << ", indices = " << i << ", " << j;
2459 }
2460 }
2461 };
2462
2463 auto check_empty_flips = [](const Rect& rect, const Point& point,
2464 const std::string& label) {
2465 ASSERT_FALSE(rect.IsEmpty());
2466
2467 EXPECT_FALSE(flip_lr(rect).ContainsInclusive(point)) << label;
2468 EXPECT_FALSE(flip_tb(rect).ContainsInclusive(point)) << label;
2469 EXPECT_FALSE(flip_lrtb(rect).ContainsInclusive(point)) << label;
2470 };
2471
2472 auto test_inside = [&check_nans, &check_empty_flips](const Rect& rect,
2473 const Point& point) {
2474 ASSERT_FALSE(rect.IsEmpty()) << rect;
2475
2476 std::stringstream stream;
2477 stream << rect << " contains " << point;
2478 auto label = stream.str();
2479
2480 EXPECT_TRUE(rect.ContainsInclusive(point)) << label;
2481 check_empty_flips(rect, point, label);
2482 check_nans(rect, point, label);
2483 };
2484
2485 auto test_outside = [&check_nans, &check_empty_flips](const Rect& rect,
2486 const Point& point) {
2487 ASSERT_FALSE(rect.IsEmpty()) << rect;
2488
2489 std::stringstream stream;
2490 stream << rect << " contains " << point;
2491 auto label = stream.str();
2492
2493 EXPECT_FALSE(rect.ContainsInclusive(point)) << label;
2494 check_empty_flips(rect, point, label);
2495 check_nans(rect, point, label);
2496 };
2497
2498 {
2499 // Origin is inclusive
2500 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2501 auto p = Point(100, 100);
2502
2503 test_inside(r, p);
2504 }
2505 {
2506 // Size is inclusive
2507 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2508 auto p = Point(200, 200);
2509
2510 test_inside(r, p);
2511 }
2512 {
2513 // Size + epsilon is exclusive
2514 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2515 auto p = Point(200 + kEhCloseEnough, 200 + kEhCloseEnough);
2516
2517 test_outside(r, p);
2518 }
2519 {
2520 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2521 auto p = Point(99, 99);
2522
2523 test_outside(r, p);
2524 }
2525 {
2526 auto r = Rect::MakeXYWH(100, 100, 100, 100);
2527 auto p = Point(199, 199);
2528
2529 test_inside(r, p);
2530 }
2531
2532 {
2533 auto r = Rect::MakeMaximum();
2534 auto p = Point(199, 199);
2535
2536 test_inside(r, p);
2537 }
2538}
2539
2540TEST(RectTest, IRectContainsInclusiveIPoint) {
2541 auto check_empty_flips = [](const IRect& rect, const IPoint& point,
2542 const std::string& label) {
2543 ASSERT_FALSE(rect.IsEmpty());
2544
2545 EXPECT_FALSE(flip_lr(rect).ContainsInclusive(point)) << label;
2546 EXPECT_FALSE(flip_tb(rect).ContainsInclusive(point)) << label;
2547 EXPECT_FALSE(flip_lrtb(rect).ContainsInclusive(point)) << label;
2548 };
2549
2550 auto test_inside = [&check_empty_flips](const IRect& rect,
2551 const IPoint& point) {
2552 ASSERT_FALSE(rect.IsEmpty()) << rect;
2553
2554 std::stringstream stream;
2555 stream << rect << " contains " << point;
2556 auto label = stream.str();
2557
2558 EXPECT_TRUE(rect.ContainsInclusive(point)) << label;
2559 check_empty_flips(rect, point, label);
2560 };
2561
2562 auto test_outside = [&check_empty_flips](const IRect& rect,
2563 const IPoint& point) {
2564 ASSERT_FALSE(rect.IsEmpty()) << rect;
2565
2566 std::stringstream stream;
2567 stream << rect << " contains " << point;
2568 auto label = stream.str();
2569
2570 EXPECT_FALSE(rect.ContainsInclusive(point)) << label;
2571 check_empty_flips(rect, point, label);
2572 };
2573
2574 {
2575 // Origin is inclusive
2576 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2577 auto p = IPoint(100, 100);
2578
2579 test_inside(r, p);
2580 }
2581 {
2582 // Size is inclusive
2583 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2584 auto p = IPoint(200, 200);
2585
2586 test_inside(r, p);
2587 }
2588 {
2589 // Size + "epsilon" is exclusive
2590 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2591 auto p = IPoint(201, 201);
2592
2593 test_outside(r, p);
2594 }
2595 {
2596 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2597 auto p = IPoint(99, 99);
2598
2599 test_outside(r, p);
2600 }
2601 {
2602 auto r = IRect::MakeXYWH(100, 100, 100, 100);
2603 auto p = IPoint(199, 199);
2604
2605 test_inside(r, p);
2606 }
2607
2608 {
2609 auto r = IRect::MakeMaximum();
2610 auto p = IPoint(199, 199);
2611
2612 test_inside(r, p);
2613 }
2614}
2615
2616TEST(RectTest, RectContainsRect) {
2617 auto check_nans = [](const Rect& a, const Rect& b, const std::string& label) {
2618 ASSERT_TRUE(a.IsFinite()) << label;
2619 ASSERT_TRUE(b.IsFinite()) << label;
2620 ASSERT_FALSE(a.IsEmpty());
2621
2622 for (int i = 1; i < 16; i++) {
2623 // NaN in a produces false
2624 EXPECT_FALSE(swap_nan(a, i).Contains(b)) << label << ", index = " << i;
2625 // NaN in b produces false
2626 EXPECT_TRUE(a.Contains(swap_nan(b, i))) << label << ", index = " << i;
2627 // NaN in both is false
2628 for (int j = 1; j < 16; j++) {
2629 EXPECT_FALSE(swap_nan(a, i).Contains(swap_nan(b, j)))
2630 << label << ", indices = " << i << ", " << j;
2631 }
2632 }
2633 };
2634
2635 auto check_empty_flips = [](const Rect& a, const Rect& b,
2636 const std::string& label) {
2637 ASSERT_FALSE(a.IsEmpty());
2638 // test b rects are allowed to have 0 w/h, but not be backwards
2639 ASSERT_FALSE(b.GetLeft() > b.GetRight() || b.GetTop() > b.GetBottom());
2640
2641 // unflipped a vs flipped (empty) b yields false
2642 EXPECT_TRUE(a.Contains(flip_lr(b))) << label;
2643 EXPECT_TRUE(a.Contains(flip_tb(b))) << label;
2644 EXPECT_TRUE(a.Contains(flip_lrtb(b))) << label;
2645
2646 // flipped (empty) a vs unflipped b yields false
2647 EXPECT_FALSE(flip_lr(a).Contains(b)) << label;
2648 EXPECT_FALSE(flip_tb(a).Contains(b)) << label;
2649 EXPECT_FALSE(flip_lrtb(a).Contains(b)) << label;
2650
2651 // flipped (empty) a vs flipped (empty) b yields empty
2652 EXPECT_FALSE(flip_lr(a).Contains(flip_lr(b))) << label;
2653 EXPECT_FALSE(flip_tb(a).Contains(flip_tb(b))) << label;
2654 EXPECT_FALSE(flip_lrtb(a).Contains(flip_lrtb(b))) << label;
2655 };
2656
2657 auto test_inside = [&check_nans, &check_empty_flips](const Rect& a,
2658 const Rect& b) {
2659 ASSERT_FALSE(a.IsEmpty()) << a;
2660 // test b rects are allowed to have 0 w/h, but not be backwards
2661 ASSERT_FALSE(b.GetLeft() > b.GetRight() || b.GetTop() > b.GetBottom());
2662
2663 std::stringstream stream;
2664 stream << a << " contains " << b;
2665 auto label = stream.str();
2666
2667 EXPECT_TRUE(a.Contains(b)) << label;
2668 check_empty_flips(a, b, label);
2669 check_nans(a, b, label);
2670 };
2671
2672 auto test_not_inside = [&check_nans, &check_empty_flips](const Rect& a,
2673 const Rect& b) {
2674 ASSERT_FALSE(a.IsEmpty()) << a;
2675 // If b was empty, it would be contained and should not be tested with
2676 // this function - use |test_inside| instead.
2677 ASSERT_FALSE(b.IsEmpty()) << b;
2678
2679 std::stringstream stream;
2680 stream << a << " contains " << b;
2681 auto label = stream.str();
2682
2683 EXPECT_FALSE(a.Contains(b)) << label;
2684 check_empty_flips(a, b, label);
2685 check_nans(a, b, label);
2686 };
2687
2688 {
2689 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2690
2691 test_inside(a, a);
2692 }
2693 {
2694 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2695 auto b = Rect::MakeXYWH(0, 0, 0, 0);
2696
2697 test_inside(a, b);
2698 }
2699 {
2700 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2701 auto b = Rect::MakeXYWH(150, 150, 20, 20);
2702
2703 test_inside(a, b);
2704 }
2705 {
2706 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2707 auto b = Rect::MakeXYWH(150, 150, 100, 100);
2708
2709 test_not_inside(a, b);
2710 }
2711 {
2712 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2713 auto b = Rect::MakeXYWH(50, 50, 100, 100);
2714
2715 test_not_inside(a, b);
2716 }
2717 {
2718 auto a = Rect::MakeXYWH(100, 100, 100, 100);
2719 auto b = Rect::MakeXYWH(0, 0, 300, 300);
2720
2721 test_not_inside(a, b);
2722 }
2723 {
2724 auto a = Rect::MakeMaximum();
2725 auto b = Rect::MakeXYWH(0, 0, 300, 300);
2726
2727 test_inside(a, b);
2728 }
2729}
2730
2731TEST(RectTest, IRectContainsIRect) {
2732 auto check_empty_flips = [](const IRect& a, const IRect& b,
2733 const std::string& label) {
2734 ASSERT_FALSE(a.IsEmpty());
2735 // test b rects are allowed to have 0 w/h, but not be backwards
2736 ASSERT_FALSE(b.GetLeft() > b.GetRight() || b.GetTop() > b.GetBottom());
2737
2738 // unflipped a vs flipped (empty) b yields true
2739 EXPECT_TRUE(a.Contains(flip_lr(b))) << label;
2740 EXPECT_TRUE(a.Contains(flip_tb(b))) << label;
2741 EXPECT_TRUE(a.Contains(flip_lrtb(b))) << label;
2742
2743 // flipped (empty) a vs unflipped b yields false
2744 EXPECT_FALSE(flip_lr(a).Contains(b)) << label;
2745 EXPECT_FALSE(flip_tb(a).Contains(b)) << label;
2746 EXPECT_FALSE(flip_lrtb(a).Contains(b)) << label;
2747
2748 // flipped (empty) a vs flipped (empty) b yields empty
2749 EXPECT_FALSE(flip_lr(a).Contains(flip_lr(b))) << label;
2750 EXPECT_FALSE(flip_tb(a).Contains(flip_tb(b))) << label;
2751 EXPECT_FALSE(flip_lrtb(a).Contains(flip_lrtb(b))) << label;
2752 };
2753
2754 auto test_inside = [&check_empty_flips](const IRect& a, const IRect& b) {
2755 ASSERT_FALSE(a.IsEmpty()) << a;
2756 // test b rects are allowed to have 0 w/h, but not be backwards
2757 ASSERT_FALSE(b.GetLeft() > b.GetRight() || b.GetTop() > b.GetBottom());
2758
2759 std::stringstream stream;
2760 stream << a << " contains " << b;
2761 auto label = stream.str();
2762
2763 EXPECT_TRUE(a.Contains(b)) << label;
2764 check_empty_flips(a, b, label);
2765 };
2766
2767 auto test_not_inside = [&check_empty_flips](const IRect& a, const IRect& b) {
2768 ASSERT_FALSE(a.IsEmpty()) << a;
2769 // If b was empty, it would be contained and should not be tested with
2770 // this function - use |test_inside| instead.
2771 ASSERT_FALSE(b.IsEmpty()) << b;
2772
2773 std::stringstream stream;
2774 stream << a << " contains " << b;
2775 auto label = stream.str();
2776
2777 EXPECT_FALSE(a.Contains(b)) << label;
2778 check_empty_flips(a, b, label);
2779 };
2780
2781 {
2782 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2783
2784 test_inside(a, a);
2785 }
2786 {
2787 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2788 auto b = IRect::MakeXYWH(0, 0, 0, 0);
2789
2790 test_inside(a, b);
2791 }
2792 {
2793 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2794 auto b = IRect::MakeXYWH(150, 150, 20, 20);
2795
2796 test_inside(a, b);
2797 }
2798 {
2799 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2800 auto b = IRect::MakeXYWH(150, 150, 100, 100);
2801
2802 test_not_inside(a, b);
2803 }
2804 {
2805 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2806 auto b = IRect::MakeXYWH(50, 50, 100, 100);
2807
2808 test_not_inside(a, b);
2809 }
2810 {
2811 auto a = IRect::MakeXYWH(100, 100, 100, 100);
2812 auto b = IRect::MakeXYWH(0, 0, 300, 300);
2813
2814 test_not_inside(a, b);
2815 }
2816 {
2817 auto a = IRect::MakeMaximum();
2818 auto b = IRect::MakeXYWH(0, 0, 300, 300);
2819
2820 test_inside(a, b);
2821 }
2822}
2823
2824TEST(RectTest, RectCutOut) {
2825 Rect cull_rect = Rect::MakeLTRB(20, 20, 40, 40);
2826
2827 auto check_nans = [&cull_rect](const Rect& diff_rect,
2828 const std::string& label) {
2829 EXPECT_TRUE(cull_rect.IsFinite()) << label;
2830 EXPECT_TRUE(diff_rect.IsFinite()) << label;
2831
2832 for (int i = 1; i < 16; i++) {
2833 // NaN in cull_rect produces empty
2834 EXPECT_FALSE(swap_nan(cull_rect, i).Cutout(diff_rect).has_value())
2835 << label << ", index " << i;
2836 EXPECT_EQ(swap_nan(cull_rect, i).CutoutOrEmpty(diff_rect), Rect())
2837 << label << ", index " << i;
2838
2839 // NaN in diff_rect is nop
2840 EXPECT_TRUE(cull_rect.Cutout(swap_nan(diff_rect, i)).has_value())
2841 << label << ", index " << i;
2842 EXPECT_EQ(cull_rect.CutoutOrEmpty(swap_nan(diff_rect, i)), cull_rect)
2843 << label << ", index " << i;
2844
2845 for (int j = 1; j < 16; j++) {
2846 // NaN in both is also empty
2847 EXPECT_FALSE(
2848 swap_nan(cull_rect, i).Cutout(swap_nan(diff_rect, j)).has_value())
2849 << label << ", indices " << i << ", " << j;
2850 EXPECT_EQ(swap_nan(cull_rect, i).CutoutOrEmpty(swap_nan(diff_rect, j)),
2851 Rect())
2852 << label << ", indices " << i << ", " << j;
2853 }
2854 }
2855 };
2856
2857 auto check_empty_flips = [&cull_rect](const Rect& diff_rect,
2858 const std::string& label) {
2859 EXPECT_FALSE(cull_rect.IsEmpty()) << label;
2860 EXPECT_FALSE(diff_rect.IsEmpty()) << label;
2861
2862 // unflipped cull_rect vs flipped(empty) diff_rect
2863 // == cull_rect
2864 EXPECT_TRUE(cull_rect.Cutout(flip_lr(diff_rect)).has_value()) << label;
2865 EXPECT_EQ(cull_rect.Cutout(flip_lr(diff_rect)), cull_rect) << label;
2866 EXPECT_TRUE(cull_rect.Cutout(flip_tb(diff_rect)).has_value()) << label;
2867 EXPECT_EQ(cull_rect.Cutout(flip_tb(diff_rect)), cull_rect) << label;
2868 EXPECT_TRUE(cull_rect.Cutout(flip_lrtb(diff_rect)).has_value()) << label;
2869 EXPECT_EQ(cull_rect.Cutout(flip_lrtb(diff_rect)), cull_rect) << label;
2870
2871 // flipped(empty) cull_rect vs unflipped diff_rect
2872 // == empty
2873 EXPECT_FALSE(flip_lr(cull_rect).Cutout(diff_rect).has_value()) << label;
2874 EXPECT_EQ(flip_lr(cull_rect).CutoutOrEmpty(diff_rect), Rect()) << label;
2875 EXPECT_FALSE(flip_tb(cull_rect).Cutout(diff_rect).has_value()) << label;
2876 EXPECT_EQ(flip_tb(cull_rect).CutoutOrEmpty(diff_rect), Rect()) << label;
2877 EXPECT_FALSE(flip_lrtb(cull_rect).Cutout(diff_rect).has_value()) << label;
2878 EXPECT_EQ(flip_lrtb(cull_rect).CutoutOrEmpty(diff_rect), Rect()) << label;
2879
2880 // flipped(empty) cull_rect vs flipped(empty) diff_rect
2881 // == empty
2882 EXPECT_FALSE(flip_lr(cull_rect).Cutout(flip_lr(diff_rect)).has_value())
2883 << label;
2884 EXPECT_EQ(flip_lr(cull_rect).CutoutOrEmpty(flip_lr(diff_rect)), Rect())
2885 << label;
2886 EXPECT_FALSE(flip_tb(cull_rect).Cutout(flip_tb(diff_rect)).has_value())
2887 << label;
2888 EXPECT_EQ(flip_tb(cull_rect).CutoutOrEmpty(flip_tb(diff_rect)), Rect())
2889 << label;
2890 EXPECT_FALSE(flip_lrtb(cull_rect).Cutout(flip_lrtb(diff_rect)).has_value())
2891 << label;
2892 EXPECT_EQ(flip_lrtb(cull_rect).CutoutOrEmpty(flip_lrtb(diff_rect)), Rect())
2893 << label;
2894 };
2895
2896 auto non_reducing = [&cull_rect, &check_empty_flips, &check_nans](
2897 const Rect& diff_rect, const std::string& label) {
2898 EXPECT_EQ(cull_rect.Cutout(diff_rect), cull_rect) << label;
2899 EXPECT_EQ(cull_rect.CutoutOrEmpty(diff_rect), cull_rect) << label;
2900 check_empty_flips(diff_rect, label);
2901 check_nans(diff_rect, label);
2902 };
2903
2904 auto reducing = [&cull_rect, &check_empty_flips, &check_nans](
2905 const Rect& diff_rect, const Rect& result_rect,
2906 const std::string& label) {
2907 EXPECT_TRUE(!result_rect.IsEmpty());
2908 EXPECT_EQ(cull_rect.Cutout(diff_rect), result_rect) << label;
2909 EXPECT_EQ(cull_rect.CutoutOrEmpty(diff_rect), result_rect) << label;
2910 check_empty_flips(diff_rect, label);
2911 check_nans(diff_rect, label);
2912 };
2913
2914 auto emptying = [&cull_rect, &check_empty_flips, &check_nans](
2915 const Rect& diff_rect, const std::string& label) {
2916 EXPECT_FALSE(cull_rect.Cutout(diff_rect).has_value()) << label;
2917 EXPECT_EQ(cull_rect.CutoutOrEmpty(diff_rect), Rect()) << label;
2918 check_empty_flips(diff_rect, label);
2919 check_nans(diff_rect, label);
2920 };
2921
2922 // Skim the corners and edge
2923 non_reducing(Rect::MakeLTRB(10, 10, 20, 20), "outside UL corner");
2924 non_reducing(Rect::MakeLTRB(20, 10, 40, 20), "Above");
2925 non_reducing(Rect::MakeLTRB(40, 10, 50, 20), "outside UR corner");
2926 non_reducing(Rect::MakeLTRB(40, 20, 50, 40), "Right");
2927 non_reducing(Rect::MakeLTRB(40, 40, 50, 50), "outside LR corner");
2928 non_reducing(Rect::MakeLTRB(20, 40, 40, 50), "Below");
2929 non_reducing(Rect::MakeLTRB(10, 40, 20, 50), "outside LR corner");
2930 non_reducing(Rect::MakeLTRB(10, 20, 20, 40), "Left");
2931
2932 // Overlap corners
2933 non_reducing(Rect::MakeLTRB(15, 15, 25, 25), "covering UL corner");
2934 non_reducing(Rect::MakeLTRB(35, 15, 45, 25), "covering UR corner");
2935 non_reducing(Rect::MakeLTRB(35, 35, 45, 45), "covering LR corner");
2936 non_reducing(Rect::MakeLTRB(15, 35, 25, 45), "covering LL corner");
2937
2938 // Overlap edges, but not across an entire side
2939 non_reducing(Rect::MakeLTRB(20, 15, 39, 25), "Top edge left-biased");
2940 non_reducing(Rect::MakeLTRB(21, 15, 40, 25), "Top edge, right biased");
2941 non_reducing(Rect::MakeLTRB(35, 20, 45, 39), "Right edge, top-biased");
2942 non_reducing(Rect::MakeLTRB(35, 21, 45, 40), "Right edge, bottom-biased");
2943 non_reducing(Rect::MakeLTRB(20, 35, 39, 45), "Bottom edge, left-biased");
2944 non_reducing(Rect::MakeLTRB(21, 35, 40, 45), "Bottom edge, right-biased");
2945 non_reducing(Rect::MakeLTRB(15, 20, 25, 39), "Left edge, top-biased");
2946 non_reducing(Rect::MakeLTRB(15, 21, 25, 40), "Left edge, bottom-biased");
2947
2948 // Slice all the way through the middle
2949 non_reducing(Rect::MakeLTRB(25, 15, 35, 45), "Vertical interior slice");
2950 non_reducing(Rect::MakeLTRB(15, 25, 45, 35), "Horizontal interior slice");
2951
2952 // Slice off each edge
2953 reducing(Rect::MakeLTRB(20, 15, 40, 25), //
2954 Rect::MakeLTRB(20, 25, 40, 40), //
2955 "Slice off top");
2956 reducing(Rect::MakeLTRB(35, 20, 45, 40), //
2957 Rect::MakeLTRB(20, 20, 35, 40), //
2958 "Slice off right");
2959 reducing(Rect::MakeLTRB(20, 35, 40, 45), //
2960 Rect::MakeLTRB(20, 20, 40, 35), //
2961 "Slice off bottom");
2962 reducing(Rect::MakeLTRB(15, 20, 25, 40), //
2963 Rect::MakeLTRB(25, 20, 40, 40), //
2964 "Slice off left");
2965
2966 // cull rect contains diff rect
2967 non_reducing(Rect::MakeLTRB(21, 21, 39, 39), "Contained, non-covering");
2968
2969 // cull rect equals diff rect
2970 emptying(cull_rect, "Perfectly covering");
2971
2972 // diff rect contains cull rect
2973 emptying(Rect::MakeLTRB(15, 15, 45, 45), "Smothering");
2974}
2975
2976TEST(RectTest, IRectCutOut) {
2977 IRect cull_rect = IRect::MakeLTRB(20, 20, 40, 40);
2978
2979 auto check_empty_flips = [&cull_rect](const IRect& diff_rect,
2980 const std::string& label) {
2981 EXPECT_FALSE(diff_rect.IsEmpty());
2982 EXPECT_FALSE(cull_rect.IsEmpty());
2983
2984 // unflipped cull_rect vs flipped(empty) diff_rect
2985 // == cull_rect
2986 EXPECT_TRUE(cull_rect.Cutout(flip_lr(diff_rect)).has_value()) << label;
2987 EXPECT_EQ(cull_rect.Cutout(flip_lr(diff_rect)), cull_rect) << label;
2988 EXPECT_TRUE(cull_rect.Cutout(flip_tb(diff_rect)).has_value()) << label;
2989 EXPECT_EQ(cull_rect.Cutout(flip_tb(diff_rect)), cull_rect) << label;
2990 EXPECT_TRUE(cull_rect.Cutout(flip_lrtb(diff_rect)).has_value()) << label;
2991 EXPECT_EQ(cull_rect.Cutout(flip_lrtb(diff_rect)), cull_rect) << label;
2992
2993 // flipped(empty) cull_rect vs flipped(empty) diff_rect
2994 // == empty
2995 EXPECT_FALSE(flip_lr(cull_rect).Cutout(diff_rect).has_value()) << label;
2996 EXPECT_EQ(flip_lr(cull_rect).CutoutOrEmpty(diff_rect), IRect()) << label;
2997 EXPECT_FALSE(flip_tb(cull_rect).Cutout(diff_rect).has_value()) << label;
2998 EXPECT_EQ(flip_tb(cull_rect).CutoutOrEmpty(diff_rect), IRect()) << label;
2999 EXPECT_FALSE(flip_lrtb(cull_rect).Cutout(diff_rect).has_value()) << label;
3000 EXPECT_EQ(flip_lrtb(cull_rect).CutoutOrEmpty(diff_rect), IRect()) << label;
3001
3002 // flipped(empty) cull_rect vs unflipped diff_rect
3003 // == empty
3004 EXPECT_FALSE(flip_lr(cull_rect).Cutout(flip_lr(diff_rect)).has_value())
3005 << label;
3006 EXPECT_EQ(flip_lr(cull_rect).CutoutOrEmpty(flip_lr(diff_rect)), IRect())
3007 << label;
3008 EXPECT_FALSE(flip_tb(cull_rect).Cutout(flip_tb(diff_rect)).has_value())
3009 << label;
3010 EXPECT_EQ(flip_tb(cull_rect).CutoutOrEmpty(flip_tb(diff_rect)), IRect())
3011 << label;
3012 EXPECT_FALSE(flip_lrtb(cull_rect).Cutout(flip_lrtb(diff_rect)).has_value())
3013 << label;
3014 EXPECT_EQ(flip_lrtb(cull_rect).CutoutOrEmpty(flip_lrtb(diff_rect)), IRect())
3015 << label;
3016 };
3017
3018 auto non_reducing = [&cull_rect, &check_empty_flips](
3019 const IRect& diff_rect, const std::string& label) {
3020 EXPECT_EQ(cull_rect.Cutout(diff_rect), cull_rect) << label;
3021 EXPECT_EQ(cull_rect.CutoutOrEmpty(diff_rect), cull_rect) << label;
3022 check_empty_flips(diff_rect, label);
3023 };
3024
3025 auto reducing = [&cull_rect, &check_empty_flips](const IRect& diff_rect,
3026 const IRect& result_rect,
3027 const std::string& label) {
3028 EXPECT_TRUE(!result_rect.IsEmpty());
3029 EXPECT_EQ(cull_rect.Cutout(diff_rect), result_rect) << label;
3030 EXPECT_EQ(cull_rect.CutoutOrEmpty(diff_rect), result_rect) << label;
3031 check_empty_flips(diff_rect, label);
3032 };
3033
3034 auto emptying = [&cull_rect, &check_empty_flips](const IRect& diff_rect,
3035 const std::string& label) {
3036 EXPECT_FALSE(cull_rect.Cutout(diff_rect).has_value()) << label;
3037 EXPECT_EQ(cull_rect.CutoutOrEmpty(diff_rect), IRect()) << label;
3038 check_empty_flips(diff_rect, label);
3039 };
3040
3041 // Skim the corners and edge
3042 non_reducing(IRect::MakeLTRB(10, 10, 20, 20), "outside UL corner");
3043 non_reducing(IRect::MakeLTRB(20, 10, 40, 20), "Above");
3044 non_reducing(IRect::MakeLTRB(40, 10, 50, 20), "outside UR corner");
3045 non_reducing(IRect::MakeLTRB(40, 20, 50, 40), "Right");
3046 non_reducing(IRect::MakeLTRB(40, 40, 50, 50), "outside LR corner");
3047 non_reducing(IRect::MakeLTRB(20, 40, 40, 50), "Below");
3048 non_reducing(IRect::MakeLTRB(10, 40, 20, 50), "outside LR corner");
3049 non_reducing(IRect::MakeLTRB(10, 20, 20, 40), "Left");
3050
3051 // Overlap corners
3052 non_reducing(IRect::MakeLTRB(15, 15, 25, 25), "covering UL corner");
3053 non_reducing(IRect::MakeLTRB(35, 15, 45, 25), "covering UR corner");
3054 non_reducing(IRect::MakeLTRB(35, 35, 45, 45), "covering LR corner");
3055 non_reducing(IRect::MakeLTRB(15, 35, 25, 45), "covering LL corner");
3056
3057 // Overlap edges, but not across an entire side
3058 non_reducing(IRect::MakeLTRB(20, 15, 39, 25), "Top edge left-biased");
3059 non_reducing(IRect::MakeLTRB(21, 15, 40, 25), "Top edge, right biased");
3060 non_reducing(IRect::MakeLTRB(35, 20, 45, 39), "Right edge, top-biased");
3061 non_reducing(IRect::MakeLTRB(35, 21, 45, 40), "Right edge, bottom-biased");
3062 non_reducing(IRect::MakeLTRB(20, 35, 39, 45), "Bottom edge, left-biased");
3063 non_reducing(IRect::MakeLTRB(21, 35, 40, 45), "Bottom edge, right-biased");
3064 non_reducing(IRect::MakeLTRB(15, 20, 25, 39), "Left edge, top-biased");
3065 non_reducing(IRect::MakeLTRB(15, 21, 25, 40), "Left edge, bottom-biased");
3066
3067 // Slice all the way through the middle
3068 non_reducing(IRect::MakeLTRB(25, 15, 35, 45), "Vertical interior slice");
3069 non_reducing(IRect::MakeLTRB(15, 25, 45, 35), "Horizontal interior slice");
3070
3071 // Slice off each edge
3072 reducing(IRect::MakeLTRB(20, 15, 40, 25), //
3073 IRect::MakeLTRB(20, 25, 40, 40), //
3074 "Slice off top");
3075 reducing(IRect::MakeLTRB(35, 20, 45, 40), //
3076 IRect::MakeLTRB(20, 20, 35, 40), //
3077 "Slice off right");
3078 reducing(IRect::MakeLTRB(20, 35, 40, 45), //
3079 IRect::MakeLTRB(20, 20, 40, 35), //
3080 "Slice off bottom");
3081 reducing(IRect::MakeLTRB(15, 20, 25, 40), //
3082 IRect::MakeLTRB(25, 20, 40, 40), //
3083 "Slice off left");
3084
3085 // cull rect contains diff rect
3086 non_reducing(IRect::MakeLTRB(21, 21, 39, 39), "Contained, non-covering");
3087
3088 // cull rect equals diff rect
3089 emptying(cull_rect, "Perfectly covering");
3090
3091 // diff rect contains cull rect
3092 emptying(IRect::MakeLTRB(15, 15, 45, 45), "Smothering");
3093}
3094
3095TEST(RectTest, RectGetPoints) {
3096 {
3097 Rect r = Rect::MakeXYWH(100, 200, 300, 400);
3098 auto points = r.GetPoints();
3099 EXPECT_POINT_NEAR(points[0], Point(100, 200));
3100 EXPECT_POINT_NEAR(points[1], Point(400, 200));
3101 EXPECT_POINT_NEAR(points[2], Point(100, 600));
3102 EXPECT_POINT_NEAR(points[3], Point(400, 600));
3103 }
3104
3105 {
3106 Rect r = Rect::MakeMaximum();
3107 auto points = r.GetPoints();
3108 EXPECT_EQ(points[0], Point(std::numeric_limits<float>::lowest(),
3109 std::numeric_limits<float>::lowest()));
3110 EXPECT_EQ(points[1], Point(std::numeric_limits<float>::max(),
3111 std::numeric_limits<float>::lowest()));
3112 EXPECT_EQ(points[2], Point(std::numeric_limits<float>::lowest(),
3113 std::numeric_limits<float>::max()));
3114 EXPECT_EQ(points[3], Point(std::numeric_limits<float>::max(),
3115 std::numeric_limits<float>::max()));
3116 }
3117}
3118
3119TEST(RectTest, RectShift) {
3120 auto r = Rect::MakeLTRB(0, 0, 100, 100);
3121
3122 EXPECT_EQ(r.Shift(Point(10, 5)), Rect::MakeLTRB(10, 5, 110, 105));
3123 EXPECT_EQ(r.Shift(Point(-10, -5)), Rect::MakeLTRB(-10, -5, 90, 95));
3124}
3125
3126TEST(RectTest, RectGetTransformedPoints) {
3127 Rect r = Rect::MakeXYWH(100, 200, 300, 400);
3128 auto points = r.GetTransformedPoints(Matrix::MakeTranslation({10, 20}));
3129 EXPECT_POINT_NEAR(points[0], Point(110, 220));
3130 EXPECT_POINT_NEAR(points[1], Point(410, 220));
3131 EXPECT_POINT_NEAR(points[2], Point(110, 620));
3132 EXPECT_POINT_NEAR(points[3], Point(410, 620));
3133}
3134
3135TEST(RectTest, RectMakePointBounds) {
3136 {
3137 std::vector<Point> points{{1, 5}, {4, -1}, {0, 6}};
3138 auto r = Rect::MakePointBounds(points.begin(), points.end());
3139 auto expected = Rect::MakeXYWH(0, -1, 4, 7);
3140 EXPECT_TRUE(r.has_value());
3141 if (r.has_value()) {
3142 EXPECT_RECT_NEAR(r.value(), expected);
3143 }
3144 }
3145 {
3146 std::vector<Point> points;
3147 std::optional<Rect> r = Rect::MakePointBounds(points.begin(), points.end());
3148 EXPECT_FALSE(r.has_value());
3149 }
3150}
3151
3152TEST(RectTest, RectGetPositive) {
3153 {
3154 Rect r = Rect::MakeXYWH(100, 200, 300, 400);
3155 auto actual = r.GetPositive();
3156 EXPECT_RECT_NEAR(r, actual);
3157 }
3158 {
3159 Rect r = Rect::MakeXYWH(100, 200, -100, -100);
3160 auto actual = r.GetPositive();
3161 Rect expected = Rect::MakeXYWH(0, 100, 100, 100);
3162 EXPECT_RECT_NEAR(expected, actual);
3163 }
3164}
3165
3166TEST(RectTest, RectDirections) {
3167 auto r = Rect::MakeLTRB(1, 2, 3, 4);
3168
3169 EXPECT_EQ(r.GetLeft(), 1);
3170 EXPECT_EQ(r.GetTop(), 2);
3171 EXPECT_EQ(r.GetRight(), 3);
3172 EXPECT_EQ(r.GetBottom(), 4);
3173
3174 EXPECT_POINT_NEAR(r.GetLeftTop(), Point(1, 2));
3175 EXPECT_POINT_NEAR(r.GetRightTop(), Point(3, 2));
3176 EXPECT_POINT_NEAR(r.GetLeftBottom(), Point(1, 4));
3177 EXPECT_POINT_NEAR(r.GetRightBottom(), Point(3, 4));
3178}
3179
3180TEST(RectTest, RectProject) {
3181 {
3182 auto r = Rect::MakeLTRB(-100, -100, 100, 100);
3183 auto actual = r.Project(r);
3184 auto expected = Rect::MakeLTRB(0, 0, 1, 1);
3185 EXPECT_RECT_NEAR(expected, actual);
3186 }
3187 {
3188 auto r = Rect::MakeLTRB(-100, -100, 100, 100);
3189 auto actual = r.Project(Rect::MakeLTRB(0, 0, 100, 100));
3190 auto expected = Rect::MakeLTRB(0.5, 0.5, 1, 1);
3191 EXPECT_RECT_NEAR(expected, actual);
3192 }
3193}
3194
3195TEST(RectTest, RectRoundOut) {
3196 {
3197 auto r = Rect::MakeLTRB(-100, -200, 300, 400);
3198 EXPECT_EQ(Rect::RoundOut(r), r);
3199 }
3200 {
3201 auto r = Rect::MakeLTRB(-100.1, -200.1, 300.1, 400.1);
3202 EXPECT_EQ(Rect::RoundOut(r), Rect::MakeLTRB(-101, -201, 301, 401));
3203 }
3204}
3205
3206TEST(RectTest, IRectRoundOut) {
3207 {
3208 auto r = Rect::MakeLTRB(-100, -200, 300, 400);
3209 auto ir = IRect::MakeLTRB(-100, -200, 300, 400);
3210 EXPECT_EQ(IRect::RoundOut(r), ir);
3211 }
3212 {
3213 auto r = Rect::MakeLTRB(-100.1, -200.1, 300.1, 400.1);
3214 auto ir = IRect::MakeLTRB(-101, -201, 301, 401);
3215 EXPECT_EQ(IRect::RoundOut(r), ir);
3216 }
3217}
3218
3219TEST(RectTest, RectRound) {
3220 {
3221 auto r = Rect::MakeLTRB(-100, -200, 300, 400);
3222 EXPECT_EQ(Rect::Round(r), r);
3223 }
3224 {
3225 auto r = Rect::MakeLTRB(-100.4, -200.4, 300.4, 400.4);
3226 EXPECT_EQ(Rect::Round(r), Rect::MakeLTRB(-100, -200, 300, 400));
3227 }
3228 {
3229 auto r = Rect::MakeLTRB(-100.5, -200.5, 300.5, 400.5);
3230 EXPECT_EQ(Rect::Round(r), Rect::MakeLTRB(-101, -201, 301, 401));
3231 }
3232}
3233
3234TEST(RectTest, IRectRound) {
3235 {
3236 auto r = Rect::MakeLTRB(-100, -200, 300, 400);
3237 auto ir = IRect::MakeLTRB(-100, -200, 300, 400);
3238 EXPECT_EQ(IRect::Round(r), ir);
3239 }
3240 {
3241 auto r = Rect::MakeLTRB(-100.4, -200.4, 300.4, 400.4);
3242 auto ir = IRect::MakeLTRB(-100, -200, 300, 400);
3243 EXPECT_EQ(IRect::Round(r), ir);
3244 }
3245 {
3246 auto r = Rect::MakeLTRB(-100.5, -200.5, 300.5, 400.5);
3247 auto ir = IRect::MakeLTRB(-101, -201, 301, 401);
3248 EXPECT_EQ(IRect::Round(r), ir);
3249 }
3250}
3251
3252TEST(RectTest, TransformAndClipBoundsNoCornersClipped) {
3253 // This matrix should clip no corners.
3254 auto matrix = impeller::Matrix::MakeColumn(
3255 // clang-format off
3256 2.0f, 0.0f, 0.0f, 0.0f,
3257 0.0f, 4.0f, 0.0f, 0.0f,
3258 0.0f, 0.0f, 1.0f, 0.0f,
3259 0.0f, 0.0f, 0.0f, 8.0f
3260 // clang-format on
3261 );
3262 Rect src = Rect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3263
3264 // None of these should have a W<0
3265
3266 EXPECT_EQ(matrix.TransformHomogenous(src.GetLeftTop()),
3267 Vector3(200.0f, 400.0f, 8.0f));
3268 EXPECT_EQ(matrix.TransformHomogenous(src.GetRightTop()),
3269 Vector3(400.0f, 400.0f, 8.0f));
3270 EXPECT_EQ(matrix.TransformHomogenous(src.GetLeftBottom()),
3271 Vector3(200.0f, 800.0f, 8.0f));
3272 EXPECT_EQ(matrix.TransformHomogenous(src.GetRightBottom()),
3273 Vector3(400.0f, 800.0f, 8.0f));
3274
3275 Rect expect = Rect::MakeLTRB(25.0f, 50.0f, 50.0f, 100.0f);
3276 EXPECT_FALSE(src.TransformAndClipBounds(matrix).IsEmpty());
3277 EXPECT_EQ(src.TransformAndClipBounds(matrix), expect);
3278}
3279
3280TEST(RectTest, TransformAndClipBoundsOneCornerClipped) {
3281 // This matrix should clip one corner.
3282 auto matrix = impeller::Matrix::MakeColumn(
3283 // clang-format off
3284 2.0f, 0.0f, 0.0f, -0.01f,
3285 0.0f, 2.0f, 0.0f, -0.006f,
3286 0.0f, 0.0f, 1.0f, 0.0f,
3287 0.0f, 0.0f, 0.0f, 3.0f
3288 // clang-format on
3289 );
3290 Rect src = Rect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3291
3292 // Exactly one of these should have a W<0
3293 //
3294 // When W<0 we interpolate the point back towards the adjacent points
3295 // that have W>0 to a location just greater than the W=0 half-plane.
3296 // We interpolate them to W=epsilon where epsilon == 2^-14.
3297
3298 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftTop()),
3299 Vector3(200.0f, 200.0f, 1.4f));
3300 // Contributes (200, 200) / 1.4 == (142.85714, 142.85714)
3301
3302 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightTop()),
3303 Vector3(400.0f, 200.0f, 0.4f));
3304 // Contributes (400, 200) / 0.4 == (1000, 500)
3305
3306 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftBottom()),
3307 Vector3(200.0f, 400.0f, 0.8f));
3308 // Contributes (200, 400) / 0.8 == (250, 500)
3309
3310 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightBottom()),
3311 Vector3(400.0f, 400.0f, -0.2f));
3312 // Interpolates at epsilon against RightTop to produce:
3313 // t = (epsilon - -.2) / (.4 - -.2)
3314 // = (epsilon + .2) / .6
3315 // = 0.333435
3316 // Lerp(RightBottom, RightTop, 0.333435) = (400, 333.313, epsilon)
3317 // = (6553600, 5461000)
3318 //
3319 // It also interpolates at epsilon against LeftBottom to produce:
3320 // t = (epsilon - -.2) / (.8 - -.2)
3321 // = (epsilon + .2) / 1
3322 // = 0.200061
3323 // Lerp(RightBottom, LeftBottom, 0.200061) = (359.988, 400, epsilon)
3324 // = (5898040, 6553600)
3325
3326 // Min/Max X and Y of all the points generated above are:
3327 // Min X == 142.85714
3328 // Min Y == 142.85714
3329 // Max X == 6553600
3330 // Max Y == 6553600
3331
3332 Rect expect = Rect::MakeLTRB(142.85714f, 142.85714f, 6553600.f, 6553600.f);
3333 EXPECT_FALSE(src.TransformAndClipBounds(matrix).IsEmpty());
3334 EXPECT_RECT_NEAR(src.TransformAndClipBounds(matrix), expect);
3335}
3336
3337TEST(RectTest, TransformAndClipBoundsTwoCornersClipped) {
3338 // This matrix should clip two corners.
3339 auto matrix = impeller::Matrix::MakeColumn(
3340 // clang-format off
3341 2.0f, 0.0f, 0.0f, -.015f,
3342 0.0f, 2.0f, 0.0f, -.006f,
3343 0.0f, 0.0f, 1.0f, 0.0f,
3344 0.0f, 0.0f, 0.0f, 3.0f
3345 // clang-format on
3346 );
3347 Rect src = Rect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3348
3349 // Exactly two of these homogenous results should have a W<0
3350 //
3351 // When W<0 we interpolate the point back towards the adjacent points
3352 // that have W>0 to a location just greater than the W=0 half-plane.
3353 // We interpolate them to W=epsilon where epsilon == 2^-14.
3354
3355 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftTop()),
3356 Vector3(200.0f, 200.0f, 0.9f));
3357 // Contributes (200, 200) / 0.9 == (222.2222, 222.2222) to bounds
3358
3359 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightTop()),
3360 Vector3(400.0f, 200.0f, -0.6f));
3361 // Interpolates at epsilon against LeftTop to produce:
3362 // t = (epsilon - -.6) / (.9 - -.6)
3363 // = (epsilon + .6) / 1.5
3364 // = 0.4000407
3365 // Lerp(RightTop, LeftTop, 0.4000407) = (319.9919, 200, epsilon)
3366 // = (5242747, 3276800)
3367 // Cannot interpolate against RightBottom because it also has W<0
3368
3369 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftBottom()),
3370 Vector3(200.0f, 400.0f, 0.3f));
3371 // Contributes (200, 400) / 0.3 == (666.6667, 1333.3333) to bounds
3372
3373 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightBottom()),
3374 Vector3(400.0f, 400.0f, -1.2f));
3375 // Interpolates at epsilon against LeftBottom to produce:
3376 // t = (epsilon - -1.2) / (.3 - -1.2)
3377 // = (epsilon + 1.2) / 1.5
3378 // = 0.8000407
3379 // Lerp(RightBottom, LeftBottom, 0.8000407) = (239.9919, 400, epsilon)
3380 // = (3932026.667, 6553600)
3381 // Cannot interpolate against RightTop because it also has W<0
3382
3383 // Min/Max X and Y of all the points generated above are:
3384 // Min X == 222.2222
3385 // Min Y == 222.2222
3386 // Max X == 5242747
3387 // Max Y == 6553600
3388
3389 Rect expect = Rect::MakeLTRB(222.2222f, 222.2222f, 5242747.f, 6553600.f);
3390
3391 EXPECT_FALSE(src.TransformAndClipBounds(matrix).IsEmpty());
3392 EXPECT_RECT_NEAR(src.TransformAndClipBounds(matrix), expect);
3393}
3394
3395TEST(RectTest, TransformAndClipBoundsThreeCornersClipped) {
3396 // This matrix should clip three corners.
3397 auto matrix = impeller::Matrix::MakeColumn(
3398 // clang-format off
3399 2.0f, 0.0f, 0.0f, -.02f,
3400 0.0f, 2.0f, 0.0f, -.006f,
3401 0.0f, 0.0f, 1.0f, 0.0f,
3402 0.0f, 0.0f, 0.0f, 3.0f
3403 // clang-format on
3404 );
3405 Rect src = Rect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3406
3407 // Exactly three of these homogenous results should have a W<0
3408 //
3409 // When W<0 we interpolate the point back towards the adjacent points
3410 // that have W>0 to a location just greater than the W=0 half-plane.
3411 // We interpolate them to W=epsilon where epsilon == 2^-14.
3412
3413 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftTop()),
3414 Vector3(200.0f, 200.0f, 0.4f));
3415 // Contributes (200, 200) / 0.4 == (500, 500) to bounds
3416
3417 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightTop()),
3418 Vector3(400.0f, 200.0f, -1.6f));
3419 // Interpolates at epsilon against LeftTop to produce:
3420 // t = (epsilon - -1.6) / (.4 - -1.6)
3421 // = (epsilon + 1.6) / 2
3422 // = 0.8000305
3423 // Lerp(RightTop, LeftTop, 0.8000305) = (239.9939, 200, epsilon)
3424 // = (3932060, 3276800)
3425 // Cannot interpolate against RightBottom because it also has W<0
3426
3427 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftBottom()),
3428 Vector3(200.0f, 400.0f, -0.2f));
3429 // Interpolates against LeftTop to produce:
3430 // t = (epsilon - -.2) / (.4 - -.2)
3431 // = (epsilon + .2) / .6
3432 // = 0.333435
3433 // Lerp(LeftBottom, LeftTop, .333435) = (200, 333.31299, epsilon)
3434 // = (3276800, 5461000)
3435 // Cannot interpolate against RightBottom because it also has W<0
3436
3437 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightBottom()),
3438 Vector3(400.0f, 400.0f, -2.2f));
3439 // Cannot interpolate against either RightTop or LeftBottom because
3440 // both of those adjacent points transformed to a W<0 homogenous point.
3441
3442 // Min/Max X and Y of all the points generated above are:
3443 // Min X == 500
3444 // Min Y == 500
3445 // Max X == 3932060
3446 // Max Y == 5461000
3447
3448 Rect expect = Rect::MakeLTRB(500.0f, 500.0f, 3932060.f, 5461000.f);
3449
3450 EXPECT_FALSE(src.TransformAndClipBounds(matrix).IsEmpty());
3451 EXPECT_RECT_NEAR(src.TransformAndClipBounds(matrix), expect);
3452}
3453
3454TEST(RectTest, TransformAndClipBoundsAllFourCornersClipped) {
3455 // This matrix should clip all four corners.
3456 auto matrix = impeller::Matrix::MakeColumn(
3457 // clang-format off
3458 2.0f, 0.0f, 0.0f, -.025f,
3459 0.0f, 2.0f, 0.0f, -.006f,
3460 0.0f, 0.0f, 1.0f, 0.0f,
3461 0.0f, 0.0f, 0.0f, 3.0f
3462 // clang-format on
3463 );
3464 Rect src = Rect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
3465
3466 // All of these should have a W<0
3467 //
3468 // When W<0 we interpolate the point back towards the adjacent points
3469 // that have W>0 to a location just greater than the W=0 half-plane.
3470 // We interpolate them to W=epsilon where epsilon == 2^-14.
3471
3472 // In this case, none of the homogenous results are in bounds (W > 0)
3473 // so we can perform no interpolation - the operation is not visible.
3474
3475 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftTop()),
3476 Vector3(200.0f, 200.0f, -0.1f));
3477 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightTop()),
3478 Vector3(400.0f, 200.0f, -2.6f));
3479 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetLeftBottom()),
3480 Vector3(200.0f, 400.0f, -0.7f));
3481 EXPECT_VECTOR3_NEAR(matrix.TransformHomogenous(src.GetRightBottom()),
3482 Vector3(400.0f, 400.0f, -3.2f));
3483
3484 EXPECT_TRUE(src.TransformAndClipBounds(matrix).IsEmpty());
3485}
3486
3487} // namespace testing
3488} // 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)
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:825
TPoint< int64_t > IPoint
Definition point.h:427
ISize64 ISize
Definition size.h:162
std::array< Point, 4 > Quad
Definition point.h:431
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:729
RoundOut(const TRect< U > &r)
Definition rect.h:713
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