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