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