| Flutter Impeller |

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