1 /* SPDX-License-Identifier: MIT */
2 
3 #include <graphene.h>
4 #include <mutest.h>
5 
6 static void
rect_init(mutest_spec_t * spec)7 rect_init (mutest_spec_t *spec)
8 {
9   graphene_rect_t zero;
10   graphene_rect_t *r;
11   graphene_rect_t r2;
12 
13   r = graphene_rect_init (graphene_rect_alloc (), 0.f, 0.f, 10.f, 10.f);
14   mutest_expect ("init to initialize the origin",
15                  mutest_bool_value (graphene_point_equal (&r->origin, &GRAPHENE_POINT_INIT (0.f, 0.f))),
16                  mutest_to_be_true,
17                  NULL);
18   mutest_expect ("init to initialize the size",
19                  mutest_bool_value (graphene_size_equal (&r->size, &GRAPHENE_SIZE_INIT (10.f, 10.f))),
20                  mutest_to_be_true,
21                  NULL);
22 
23   graphene_rect_init_from_rect (&r2, r);
24   mutest_expect ("initializing from a rectangle creates an equivalent rectangle",
25                  mutest_bool_value (graphene_rect_equal (&r2, r)),
26                  mutest_to_be_true,
27                  NULL);
28 
29   zero = GRAPHENE_RECT_INIT_ZERO;
30   mutest_expect ("GRAPHENE_RECT_INIT_ZERO initializes a degenerate rectangle",
31                  mutest_bool_value (graphene_rect_equal (&zero, graphene_rect_zero ())),
32                  mutest_to_be_true,
33                  NULL);
34 
35   graphene_rect_free (r);
36 }
37 
38 static void
rect_normalize(mutest_spec_t * spec)39 rect_normalize (mutest_spec_t *spec)
40 {
41   graphene_rect_t r = GRAPHENE_RECT_INIT (10.f, 10.f, -10.f, -10.f);
42   graphene_rect_t s = GRAPHENE_RECT_INIT ( 0.f,  0.f,  10.f,  10.f);
43   graphene_rect_t t;
44 
45   graphene_rect_normalize_r (&r, &t);
46 
47   mutest_expect ("normalizing(10, 10, -10, -10) will put the origin in (0, 0)",
48                  mutest_bool_value (graphene_point_equal (&t.origin, &GRAPHENE_POINT_INIT (0, 0))),
49                  mutest_to_be_true,
50                  NULL);
51   mutest_expect ("normalizing(10, 10, -10, -10) will make the width positive",
52                  mutest_float_value (t.size.width),
53                  mutest_to_be_greater_than, 0.0,
54                  mutest_to_be_close_to, 10.0, 0.0001,
55                  NULL);
56   mutest_expect ("normalizing(10, 10, -10, -10) will make the height positive",
57                  mutest_float_value (t.size.height),
58                  mutest_to_be_greater_than, 0.0,
59                  mutest_to_be_close_to, 10.0, 0.0001,
60                  NULL);
61   mutest_expect ("equality to operate on normalized rectangles",
62                  mutest_bool_value (graphene_rect_equal (&r, &s)),
63                  mutest_to_be_true,
64                  NULL);
65 }
66 
67 static void
rect_equal(mutest_spec_t * spec)68 rect_equal (mutest_spec_t *spec)
69 {
70   graphene_rect_t *r, *s;
71 
72   r = graphene_rect_init (graphene_rect_alloc (), 0.f, 0.f, 10.f, 10.f);
73   s = graphene_rect_init (graphene_rect_alloc (), 1.f, 1.f,  9.f,  9.f);
74 
75   mutest_expect ("a rectangle to be equal it itself",
76                  mutest_bool_value (graphene_rect_equal (r, r)),
77                  mutest_to_be_true,
78                  NULL);
79   mutest_expect ("a rectangle to not be equal to null",
80                  mutest_bool_value (graphene_rect_equal (r, NULL)),
81                  mutest_to_be_false,
82                  NULL);
83   mutest_expect ("null to not be equal to a rectangle",
84                  mutest_bool_value (graphene_rect_equal (NULL, r)),
85                  mutest_to_be_false,
86                  NULL);
87   mutest_expect ("two rectangles with different values to not be equal",
88                  mutest_bool_value (graphene_rect_equal (r, s)),
89                  mutest_to_be_false,
90                  NULL);
91 
92   graphene_rect_free (r);
93   graphene_rect_free (s);
94 }
95 
96 static void
rect_contains_point(mutest_spec_t * spec)97 rect_contains_point (mutest_spec_t *spec)
98 {
99   graphene_rect_t r = GRAPHENE_RECT_INIT (0.f, 0.f, 10.f, 10.f);
100   graphene_point_t p;
101 
102   graphene_rect_get_top_left (&r, &p);
103   mutest_expect ("a rectangle to contain its top left corner",
104                  mutest_bool_value (graphene_rect_contains_point (&r, &p)),
105                  mutest_to_be_true,
106                  NULL);
107 
108   graphene_rect_get_top_right (&r, &p);
109   mutest_expect ("a rectangle to contain its top right corner",
110                  mutest_bool_value (graphene_rect_contains_point (&r, &p)),
111                  mutest_to_be_true,
112                  NULL);
113 
114   graphene_rect_get_bottom_right (&r, &p);
115   mutest_expect ("a rectangle to contain its bottom right corner",
116                  mutest_bool_value (graphene_rect_contains_point (&r, &p)),
117                  mutest_to_be_true,
118                  NULL);
119 
120   graphene_rect_get_bottom_left (&r, &p);
121   mutest_expect ("a rectangle to contain its bottom left corner",
122                  mutest_bool_value (graphene_rect_contains_point (&r, &p)),
123                  mutest_to_be_true,
124                  NULL);
125 
126   graphene_rect_get_center (&r, &p);
127   mutest_expect ("a rectangle to contain its center",
128                  mutest_bool_value (graphene_rect_contains_point (&r, &p)),
129                  mutest_to_be_true,
130                  NULL);
131 
132   graphene_point_init (&p, -1.f, 11.f);
133   mutest_expect ("a rectangle to not contain a point outside its boundaries",
134                  mutest_bool_value (graphene_rect_contains_point (&r, &p)),
135                  mutest_to_be_false,
136                  NULL);
137 }
138 
139 static void
rect_contains_rect(mutest_spec_t * spec)140 rect_contains_rect (mutest_spec_t *spec)
141 {
142   graphene_rect_t *r, *s;
143 
144   r = graphene_rect_init (graphene_rect_alloc (), 0.f, 0.f, 10.f, 10.f);
145   s = graphene_rect_init (graphene_rect_alloc (), 1.f, 1.f,  9.f,  9.f);
146 
147   mutest_expect ("a rectangle to contain itself",
148                  mutest_bool_value (graphene_rect_contains_rect (r, r)),
149                  mutest_to_be_true,
150                  NULL);
151   mutest_expect ("the smaller rectangle to not contain the larger one",
152                  mutest_bool_value (graphene_rect_contains_rect (s, r)),
153                  mutest_to_be_false,
154                  NULL);
155   mutest_expect ("the larger rectangle to contain the smaller one",
156                  mutest_bool_value (graphene_rect_contains_rect (r, s)),
157                  mutest_to_be_true,
158                  NULL);
159 
160   graphene_rect_free (r);
161   graphene_rect_free (s);
162 }
163 
164 static void
rect_intersect(mutest_spec_t * spec)165 rect_intersect (mutest_spec_t *spec)
166 {
167   graphene_rect_t r = GRAPHENE_RECT_INIT (0.f, 0.f, 10.f, 10.f);
168   graphene_rect_t s = GRAPHENE_RECT_INIT (5.f, 5.f, 15.f, 15.f);
169   graphene_rect_t q = GRAPHENE_RECT_INIT (11.f, 11.f, 2.f, 2.f);
170   graphene_rect_t i, j;
171 
172   mutest_expect ("intersection between (0, 0, 10, 10) and (5, 5, 15, 15) not be empty",
173                  mutest_bool_value (graphene_rect_intersection (&r, &s, &i)),
174                  mutest_not, mutest_to_be_false,
175                  NULL);
176 
177   mutest_expect ("origin of intersection to be at (5, 5)",
178                  mutest_bool_value (graphene_point_equal (&i.origin, &GRAPHENE_POINT_INIT (5, 5))),
179                  mutest_to_be_true,
180                  NULL);
181   mutest_expect ("size of intersection to be (5, 5)",
182                  mutest_bool_value (graphene_size_equal (&i.size, &GRAPHENE_SIZE_INIT (5, 5))),
183                  mutest_to_be_true,
184                  NULL);
185 
186   mutest_expect ("intersection between (5, 5, 5, 5) and (11, 11, 2, 2) to be empty",
187                  mutest_bool_value (graphene_rect_intersection (&i, &q, &j)),
188                  mutest_not, mutest_to_be_true,
189                  NULL);
190   mutest_expect ("empty intersection to be a degenerate rectangle",
191                  mutest_bool_value (graphene_rect_equal (&j, graphene_rect_zero ())),
192                  mutest_to_be_true,
193                  NULL);
194 }
195 
196 static void
rect_union(mutest_spec_t * spec)197 rect_union (mutest_spec_t *spec)
198 {
199   graphene_rect_t r = GRAPHENE_RECT_INIT (0.f, 0.f, 10.f, 10.f);
200   graphene_rect_t s = GRAPHENE_RECT_INIT (5.f, 5.f, 15.f, 15.f);
201   graphene_rect_t u;
202 
203   graphene_rect_union (&r, &s, &u);
204   mutest_expect ("union to take the minimum normalize origin",
205                  mutest_bool_value (graphene_point_equal (&u.origin, &r.origin)),
206                  mutest_to_be_true,
207                  NULL);
208   mutest_expect ("union to take the maximum normalized size",
209                  mutest_bool_value (graphene_size_equal (&u.size, &GRAPHENE_SIZE_INIT (20.f, 20.f))),
210                  mutest_to_be_true,
211                  NULL);
212 
213   mutest_expect ("union rectangle to contain original rectangle A",
214                  mutest_bool_value (graphene_rect_contains_rect (&u, &r)),
215                  mutest_to_be_true,
216                  NULL);
217   mutest_expect ("union rectangle to contain original rectangle B",
218                  mutest_bool_value (graphene_rect_contains_rect (&u, &s)),
219                  mutest_to_be_true,
220                  NULL);
221 }
222 
223 static void
rect_offset(mutest_spec_t * spec)224 rect_offset (mutest_spec_t *spec)
225 {
226   graphene_rect_t r = GRAPHENE_RECT_INIT (0.f, 0.f, 10.f, 10.f);
227 
228   graphene_rect_offset (&r, 5.f, 5.f);
229   mutest_expect ("offset to move the origin",
230                  mutest_bool_value (graphene_point_equal (&r.origin, &GRAPHENE_POINT_INIT (5.f, 5.f))),
231                  mutest_to_be_true,
232                  NULL);
233   mutest_expect ("offset to keep the size",
234                  mutest_bool_value (graphene_size_equal (&r.size, &GRAPHENE_SIZE_INIT (10.f, 10.f))),
235                  mutest_to_be_true,
236                  NULL);
237 }
238 
239 static void
rect_inset(mutest_spec_t * spec)240 rect_inset (mutest_spec_t *spec)
241 {
242   graphene_rect_t r = GRAPHENE_RECT_INIT (0.f, 0.f, 10.f, 10.f);
243 
244   graphene_rect_inset (&r, 2.f, 2.f);
245   mutest_expect ("inset(2, 2) to move the origin",
246                  mutest_bool_value (graphene_point_equal (&r.origin, &GRAPHENE_POINT_INIT (2.f, 2.f))),
247                  mutest_to_be_true,
248                  NULL);
249   mutest_expect ("inset(2, 2) to shrink the size",
250                  mutest_bool_value (graphene_size_equal (&r.size, &GRAPHENE_SIZE_INIT (6.f, 6.f))),
251                  mutest_to_be_true,
252                  NULL);
253 
254   graphene_rect_inset (&r, 2.f, -2.f);
255   mutest_expect ("inset(2, -2) to move back the origin",
256                  mutest_float_value (r.origin.y),
257                  mutest_to_be_close_to, 0.0, 0.0001,
258                  NULL);
259   mutest_expect ("inset(2, -2) to shrink the width",
260                  mutest_float_value (r.size.width),
261                  mutest_to_be_close_to, 2.0, 0.0001,
262                  NULL);
263   mutest_expect ("inset(2, -2) to expand the height",
264                  mutest_float_value (r.size.height),
265                  mutest_to_be_close_to, 10.0, 0.0001,
266                  NULL);
267 }
268 
269 static void
rect_area(void)270 rect_area (void)
271 {
272   graphene_rect_t r = GRAPHENE_RECT_INIT (0.5f, 1.9f,  9.3f, 8.7f);
273   graphene_rect_t n;
274 
275   graphene_rect_normalize_r (&r, &n);
276 
277   mutest_expect ("area to be width × height",
278                  mutest_float_value (graphene_rect_get_area (&r)),
279                  mutest_to_be_close_to, (n.size.width * n.size.height), 0.1,
280                  NULL);
281 }
282 
283 static void
rect_round(mutest_spec_t * spec)284 rect_round (mutest_spec_t *spec)
285 {
286   graphene_rect_t r = GRAPHENE_RECT_INIT (0.5f, 1.9f,  9.3f, 8.7f);
287   graphene_rect_t s = GRAPHENE_RECT_INIT (0.0f, 1.0f, 10.0f, 9.0f);
288   graphene_rect_t tmp;
289 
290   graphene_rect_round (&r, &tmp);
291   mutest_expect ("round() area is larger than or equal to the original rectangle's area",
292                  mutest_float_value (graphene_rect_get_area (&tmp)),
293                  mutest_to_be_greater_than_or_equal, graphene_rect_get_area (&r),
294                  NULL);
295   mutest_expect ("round() may not contain the original rectangle",
296                  mutest_bool_value (graphene_rect_contains_rect (&tmp, &r)),
297                  mutest_not, mutest_to_be, true,
298                  NULL);
299 
300   /* round_to_pixel() operates in place */
301   graphene_rect_init_from_rect (&tmp, &r);
302   graphene_rect_round_to_pixel (&tmp);
303   mutest_expect ("round_to_pixel() may not contain the original rectangle",
304                  mutest_bool_value (graphene_rect_contains_rect (&tmp, &r)),
305                  mutest_not, mutest_to_be, true,
306                  NULL);
307   mutest_expect ("rounding is stable",
308                  mutest_bool_value (graphene_rect_equal (&s, &tmp)),
309                  mutest_to_be, true,
310                  NULL);
311 }
312 
313 static void
rect_round_extents(void)314 rect_round_extents (void)
315 {
316   graphene_rect_t r = GRAPHENE_RECT_INIT (2.5f, 1.9f,  9.3f, 8.7f);
317   graphene_rect_t s = GRAPHENE_RECT_INIT (2.0f, 1.0f, 10.0f, 10.0f);
318   graphene_rect_t rounded;
319   graphene_point_t p;
320 
321   graphene_rect_round_extents (&r, &rounded);
322 
323   graphene_rect_get_top_left (&r, &p);
324   mutest_expect ("round_extents()'s result to contain top-left corner of the original rectangle",
325                  mutest_bool_value (graphene_rect_contains_point (&rounded, &p)),
326                  mutest_to_be, true,
327                  NULL);
328 
329   graphene_rect_get_top_right (&r, &p);
330   mutest_expect ("round_extents()'s result to contain top-right corner of the original rectangle",
331                  mutest_bool_value (graphene_rect_contains_point (&rounded, &p)),
332                  mutest_to_be, true,
333                  NULL);
334 
335   graphene_rect_get_bottom_left (&r, &p);
336   mutest_expect ("round_extents()'s result to contain bottom-left corner of the original rectangle",
337                  mutest_bool_value (graphene_rect_contains_point (&rounded, &p)),
338                  mutest_to_be, true,
339                  NULL);
340 
341   graphene_rect_get_bottom_right (&r, &p);
342   mutest_expect ("round_extents()'s result to contain bottom-right corner of the original rectangle",
343                  mutest_bool_value (graphene_rect_contains_point (&rounded, &p)),
344                  mutest_to_be, true,
345                  NULL);
346 
347   mutest_expect ("round_extents()'s result to contain the original rectangle",
348                  mutest_bool_value (graphene_rect_contains_rect (&rounded, &r)),
349                  mutest_to_be, true,
350                  NULL);
351 
352   mutest_expect ("round_extents()'s rounding to be stable",
353                  mutest_bool_value (graphene_rect_equal (&rounded, &s)),
354                  mutest_to_be, true,
355                  NULL);
356 }
357 
358 static void
rect_expand(mutest_spec_t * spec)359 rect_expand (mutest_spec_t *spec)
360 {
361   graphene_rect_t r = GRAPHENE_RECT_INIT (0.f, 0.f, 100.f, 100.f);
362   graphene_point_t p;
363   graphene_size_t s;
364   graphene_rect_t check;
365 
366   graphene_rect_expand (&r, graphene_point_init (&p, -10.f, -10.f), &check);
367   graphene_size_init (&s, 110.f, 110.f);
368   mutest_expect ("expanding behind the origin changes the origin",
369                  mutest_bool_value (graphene_point_equal (&p, &(check.origin))),
370                  mutest_to_be_true,
371                  NULL);
372   mutest_expect ("expanding behind the origin does not change the size",
373                  mutest_bool_value (graphene_size_equal (&s, &(check.size))),
374                  mutest_to_be_true,
375                  NULL);
376   mutest_expect ("expanded rectangle contains original rectangle",
377                  mutest_bool_value (graphene_rect_contains_rect (&check, &r)),
378                  mutest_to_be_true,
379                  NULL);
380 
381   graphene_rect_expand (&r, graphene_point_init (&p, 150.f, 150.f), &check);
382   graphene_size_init (&s, 150.f, 150.f);
383   mutest_expect ("expanding after the anti-origin does not change the origin",
384                  mutest_bool_value (graphene_point_equal (&r.origin, &check.origin)),
385                  mutest_to_be_true,
386                  NULL);
387   mutest_expect ("expanding after the anti-origin changes the size",
388                  mutest_bool_value (graphene_size_equal (&s, &(check.size))),
389                  mutest_to_be_true,
390                  NULL);
391   mutest_expect ("expanded rectangle contains original rectangle",
392                  mutest_bool_value (graphene_rect_contains_rect (&check, &r)),
393                  mutest_to_be_true,
394                  NULL);
395 
396   graphene_rect_expand (&r, graphene_point_init (&p, 50.f, 150.f), &check);
397   mutest_expect ("expanding half in/half out does not change the origin",
398                  mutest_bool_value (graphene_point_equal (&r.origin, &(check.origin))),
399                  mutest_to_be_true,
400                  NULL);
401   mutest_expect ("expanding horizontally inside the rectangle to not change width",
402                  mutest_float_value (check.size.width),
403                  mutest_to_be_close_to, r.size.width, 0.0001,
404                  NULL);
405   mutest_expect ("expanding vertically outside the rectangle to change the height",
406                  mutest_float_value (check.size.height),
407                  mutest_to_be_close_to, p.y, 0.0001,
408                  NULL);
409   mutest_expect ("expanded rectangle contains original rectangle",
410                  mutest_bool_value (graphene_rect_contains_rect (&check, &r)),
411                  mutest_to_be_true,
412                  NULL);
413 }
414 
415 static void
rect_interpolate(mutest_spec_t * spec)416 rect_interpolate (mutest_spec_t *spec)
417 {
418   graphene_rect_t a = GRAPHENE_RECT_INIT ( 0.f, 0.f, 10.f, 10.f);
419   graphene_rect_t b = GRAPHENE_RECT_INIT (10.f, 5.f, 30.f, 20.f);
420   graphene_rect_t c;
421   graphene_rect_t res;
422 
423   graphene_rect_interpolate (&a, &b, 0.0, &res);
424   mutest_expect ("lerp(0) to produce the initial rectangle",
425                  mutest_bool_value (graphene_rect_equal (&a, &res)),
426                  mutest_to_be_true,
427                  NULL);
428 
429   graphene_rect_interpolate (&a, &b, 1.0, &res);
430   mutest_expect ("lerp(1) to produce the final rectangle",
431                  mutest_bool_value (graphene_rect_equal (&b, &res)),
432                  mutest_to_be_true,
433                  NULL);
434 
435   c.origin.x = a.origin.x + (b.origin.x - a.origin.x) * 0.25f;
436   c.origin.y = a.origin.y + (b.origin.y - a.origin.y) * 0.25f;
437   c.size.width = a.size.width + (b.size.width - a.size.width) * 0.25f;
438   c.size.height = a.size.height + (b.size.height - a.size.height) * 0.25f;
439   graphene_rect_interpolate (&a, &b, 0.25, &res);
440   mutest_expect ("lerp(0.25) to produce the expect rectangle",
441                  mutest_bool_value (graphene_rect_equal (&c, &res)),
442                  mutest_to_be_true,
443                  NULL);
444 }
445 
446 static void
rect_scale(void)447 rect_scale (void)
448 {
449   struct {
450     graphene_rect_t unscaled;
451     graphene_rect_t scaled;
452     float scales[2];
453     const char *desc;
454   } rects[] = {
455     {
456       GRAPHENE_RECT_INIT (0.f, 0.f, 10.f, 10.f),
457       GRAPHENE_RECT_INIT (0.f, 0.f, 10.f, 10.f),
458       { 1.f, 1.f },
459       "Scaling by 1 does not change the rectangle",
460     },
461     {
462       GRAPHENE_RECT_INIT (-2.f, -2.f, -2.f, -2.f),
463       GRAPHENE_RECT_INIT (-8.f, -20.f, 4.f, 10.f),
464       { 2.f, 5.f },
465       "Scaling by (2, 5) preserves the origin sign but changes the size sign",
466     },
467     {
468       GRAPHENE_RECT_INIT (-4.f, 2.f, 10.f,  10.f),
469       GRAPHENE_RECT_INIT (-1.f, 1.f,  2.5f,  5.f),
470       { 0.25f, 0.5f },
471       "Scaling by a fraction moves the origin and reduces the size",
472     },
473     {
474       GRAPHENE_RECT_INIT (1.f, 0.f, 10.f, -1.f),
475       GRAPHENE_RECT_INIT (0.f, 0.f,  0.f, 10.f),
476       { 0.f, -10.f, },
477       "Scaling by (0, -10) moves the origin in (0, 0) and flips the negative sign of the size",
478     },
479   };
480   unsigned int n_rects = sizeof (rects) / sizeof (rects[0]);
481 
482   for (unsigned int i = 0; i < n_rects; i++)
483     {
484       graphene_rect_t res;
485 
486       graphene_rect_scale (&rects[i].unscaled,
487                            rects[i].scales[0],
488                            rects[i].scales[1],
489                            &res);
490 
491       mutest_expect (rects[i].desc,
492                      mutest_bool_value (graphene_rect_equal (&res, &rects[i].scaled)),
493                      mutest_to_be_true,
494                      NULL);
495     }
496 }
497 
498 static void
rect_suite(mutest_suite_t * suite)499 rect_suite (mutest_suite_t *suite)
500 {
501   mutest_it ("initializes origin and size", rect_init);
502   mutest_it ("normalizes origin and size", rect_normalize);
503   mutest_it ("can check for equality", rect_equal);
504   mutest_it ("can check for contained points", rect_contains_point);
505   mutest_it ("can check for contained rectangles", rect_contains_rect);
506   mutest_it ("can compute intersections", rect_intersect);
507   mutest_it ("can compute unions", rect_union);
508   mutest_it ("can compute the area", rect_area);
509   mutest_it ("can offset origin and size", rect_offset);
510   mutest_it ("can inset origin and size", rect_inset);
511   mutest_it ("can round to the nearest integer", rect_round);
512   mutest_it ("can round the extents of a rectangle to the nearest integer", rect_round_extents);
513   mutest_it ("can expand", rect_expand);
514   mutest_it ("can interpolate", rect_interpolate);
515   mutest_it ("can scale", rect_scale);
516 }
517 
518 MUTEST_MAIN (
519   mutest_describe ("graphene_rect_t", rect_suite);
520 )
521