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