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