1 | /* graphene-rect.c: Rectangular type |
2 | * |
3 | * SPDX-License-Identifier: MIT |
4 | * |
5 | * Copyright 2014 Emmanuele Bassi |
6 | * |
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
8 | * of this software and associated documentation files (the "Software"), to deal |
9 | * in the Software without restriction, including without limitation the rights |
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
11 | * copies of the Software, and to permit persons to whom the Software is |
12 | * furnished to do so, subject to the following conditions: |
13 | * |
14 | * The above copyright notice and this permission notice shall be included in |
15 | * all copies or substantial portions of the Software. |
16 | * |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
23 | * THE SOFTWARE. |
24 | */ |
25 | |
26 | /** |
27 | * SECTION:graphene-rect |
28 | * @title: Rectangle |
29 | * @short_description: Rectangular shape type |
30 | * |
31 | * #graphene_rect_t is a type representing a rectangle through an origin |
32 | * #graphene_point_t point and a #graphene_size_t size. |
33 | * |
34 | * ![](rectangle.png) |
35 | * |
36 | * Operations on a #graphene_rect_t will normalize the rectangle, to |
37 | * ensure that the origin is always the top-left corner of the rectangle |
38 | * and that the size is always positive. |
39 | */ |
40 | |
41 | #include "graphene-private.h" |
42 | |
43 | #include "graphene-rect.h" |
44 | |
45 | #include <math.h> |
46 | |
47 | /*< private > |
48 | * graphene_rect_normalize_in_place: |
49 | * @r: (inout): a #graphene_rect_t |
50 | * |
51 | * Normalizes the passed #graphene_rect_t. |
52 | */ |
53 | static inline void |
54 | graphene_rect_normalize_in_place (graphene_rect_t *r) |
55 | { |
56 | if (r->size.width < 0.f) |
57 | { |
58 | float size = fabsf (x: r->size.width); |
59 | |
60 | r->origin.x -= size; |
61 | r->size.width = size; |
62 | } |
63 | |
64 | if (r->size.height < 0.f) |
65 | { |
66 | float size = fabsf (x: r->size.height); |
67 | |
68 | r->origin.y -= size; |
69 | r->size.height = size; |
70 | } |
71 | } |
72 | |
73 | /** |
74 | * graphene_rect_alloc: |
75 | * |
76 | * Allocates a new #graphene_rect_t. |
77 | * |
78 | * The contents of the returned rectangle are undefined. |
79 | * |
80 | * Returns: (transfer full): the newly allocated rectangle |
81 | * |
82 | * Since: 1.0 |
83 | */ |
84 | graphene_rect_t * |
85 | graphene_rect_alloc (void) |
86 | { |
87 | return calloc (nmemb: 1, size: sizeof (graphene_rect_t)); |
88 | } |
89 | |
90 | /** |
91 | * graphene_rect_free: |
92 | * @r: a #graphene_rect_t |
93 | * |
94 | * Frees the resources allocated by graphene_rect_alloc(). |
95 | * |
96 | * Since: 1.0 |
97 | */ |
98 | void |
99 | graphene_rect_free (graphene_rect_t *r) |
100 | { |
101 | free (ptr: r); |
102 | } |
103 | |
104 | /** |
105 | * graphene_rect_init: |
106 | * @r: a #graphene_rect_t |
107 | * @x: the X coordinate of the @graphene_rect_t.origin |
108 | * @y: the Y coordinate of the @graphene_rect_t.origin |
109 | * @width: the width of the @graphene_rect_t.size |
110 | * @height: the height of the @graphene_rect_t.size |
111 | * |
112 | * Initializes the given #graphene_rect_t with the given values. |
113 | * |
114 | * This function will implicitly normalize the #graphene_rect_t |
115 | * before returning. |
116 | * |
117 | * Returns: (transfer none): the initialized rectangle |
118 | * |
119 | * Since: 1.0 |
120 | */ |
121 | graphene_rect_t * |
122 | graphene_rect_init (graphene_rect_t *r, |
123 | float x, |
124 | float y, |
125 | float width, |
126 | float height) |
127 | { |
128 | graphene_point_init (p: &r->origin, x, y); |
129 | graphene_size_init (s: &r->size, width, height); |
130 | |
131 | graphene_rect_normalize_in_place (r); |
132 | |
133 | return r; |
134 | } |
135 | |
136 | /** |
137 | * graphene_rect_init_from_rect: |
138 | * @r: a #graphene_rect_t |
139 | * @src: a #graphene_rect_t |
140 | * |
141 | * Initializes @r using the given @src rectangle. |
142 | * |
143 | * This function will implicitly normalize the #graphene_rect_t |
144 | * before returning. |
145 | * |
146 | * Returns: (transfer none): the initialized rectangle |
147 | * |
148 | * Since: 1.0 |
149 | */ |
150 | graphene_rect_t * |
151 | graphene_rect_init_from_rect (graphene_rect_t *r, |
152 | const graphene_rect_t *src) |
153 | { |
154 | *r = *src; |
155 | |
156 | graphene_rect_normalize_in_place (r); |
157 | |
158 | return r; |
159 | } |
160 | |
161 | static bool |
162 | rect_equal (const void *p1, |
163 | const void *p2) |
164 | { |
165 | const graphene_rect_t *a = p1; |
166 | const graphene_rect_t *b = p2; |
167 | |
168 | graphene_rect_t r_a, r_b; |
169 | |
170 | graphene_rect_normalize_r (r: a, res: &r_a); |
171 | graphene_rect_normalize_r (r: b, res: &r_b); |
172 | |
173 | return graphene_point_equal (a: &r_a.origin, b: &r_b.origin) && |
174 | graphene_size_equal (a: &r_a.size, b: &r_b.size); |
175 | } |
176 | |
177 | /** |
178 | * graphene_rect_equal: |
179 | * @a: a #graphene_rect_t |
180 | * @b: a #graphene_rect_t |
181 | * |
182 | * Checks whether the two given rectangle are equal. |
183 | * |
184 | * Returns: `true` if the rectangles are equal |
185 | * |
186 | * Since: 1.0 |
187 | */ |
188 | bool |
189 | graphene_rect_equal (const graphene_rect_t *a, |
190 | const graphene_rect_t *b) |
191 | { |
192 | return graphene_pointer_equal (p1: a, p2: b, func: rect_equal); |
193 | } |
194 | |
195 | /** |
196 | * graphene_rect_normalize: |
197 | * @r: a #graphene_rect_t |
198 | * |
199 | * Normalizes the passed rectangle. |
200 | * |
201 | * This function ensures that the size of the rectangle is made of |
202 | * positive values, and that the origin is the top-left corner of |
203 | * the rectangle. |
204 | * |
205 | * Returns: (transfer none): the normalized rectangle |
206 | * |
207 | * Since: 1.0 |
208 | */ |
209 | graphene_rect_t * |
210 | graphene_rect_normalize (graphene_rect_t *r) |
211 | { |
212 | graphene_rect_normalize_in_place (r); |
213 | |
214 | return r; |
215 | } |
216 | |
217 | /** |
218 | * graphene_rect_normalize_r: |
219 | * @r: a #graphene_rect_t |
220 | * @res: (out caller-allocates): the return location for the |
221 | * normalized rectangle |
222 | * |
223 | * Normalizes the passed rectangle. |
224 | * |
225 | * This function ensures that the size of the rectangle is made of |
226 | * positive values, and that the origin is in the top-left corner |
227 | * of the rectangle. |
228 | * |
229 | * Since: 1.4 |
230 | */ |
231 | void |
232 | graphene_rect_normalize_r (const graphene_rect_t *r, |
233 | graphene_rect_t *res) |
234 | { |
235 | if (res != r) |
236 | *res = *r; |
237 | |
238 | graphene_rect_normalize_in_place (r: res); |
239 | } |
240 | |
241 | /** |
242 | * graphene_rect_get_center: |
243 | * @r: a #graphene_rect_t |
244 | * @p: (out caller-allocates): return location for a #graphene_point_t |
245 | * |
246 | * Retrieves the coordinates of the center of the given rectangle. |
247 | * |
248 | * Since: 1.0 |
249 | */ |
250 | void |
251 | graphene_rect_get_center (const graphene_rect_t *r, |
252 | graphene_point_t *p) |
253 | { |
254 | graphene_rect_t rr; |
255 | |
256 | rr = *r; |
257 | graphene_rect_normalize_in_place (r: &rr); |
258 | |
259 | graphene_point_init (p, |
260 | x: rr.origin.x + (rr.size.width / 2.f), |
261 | y: rr.origin.y + (rr.size.height / 2.f)); |
262 | } |
263 | |
264 | /** |
265 | * graphene_rect_get_top_left: |
266 | * @r: a #graphene_rect_t |
267 | * @p: (out caller-allocates): return location for a #graphene_point_t |
268 | * |
269 | * Retrieves the coordinates of the top-left corner of the given rectangle. |
270 | * |
271 | * Since: 1.0 |
272 | */ |
273 | void |
274 | graphene_rect_get_top_left (const graphene_rect_t *r, |
275 | graphene_point_t *p) |
276 | { |
277 | graphene_rect_t rr; |
278 | |
279 | rr = *r; |
280 | graphene_rect_normalize_in_place (r: &rr); |
281 | |
282 | graphene_point_init_from_point (p, src: &rr.origin); |
283 | } |
284 | |
285 | /** |
286 | * graphene_rect_get_top_right: |
287 | * @r: a #graphene_rect_t |
288 | * @p: (out caller-allocates): return location for a #graphene_point_t |
289 | * |
290 | * Retrieves the coordinates of the top-right corner of the given rectangle. |
291 | * |
292 | * Since: 1.0 |
293 | */ |
294 | void |
295 | graphene_rect_get_top_right (const graphene_rect_t *r, |
296 | graphene_point_t *p) |
297 | { |
298 | graphene_rect_t rr; |
299 | |
300 | rr = *r; |
301 | graphene_rect_normalize_in_place (r: &rr); |
302 | |
303 | graphene_point_init (p, x: rr.origin.x + rr.size.width, y: rr.origin.y); |
304 | } |
305 | |
306 | /** |
307 | * graphene_rect_get_bottom_left: |
308 | * @r: a #graphene_rect_t |
309 | * @p: (out caller-allocates): return location for a #graphene_point_t |
310 | * |
311 | * Retrieves the coordinates of the bottom-left corner of the given rectangle. |
312 | * |
313 | * Since: 1.0 |
314 | */ |
315 | void |
316 | graphene_rect_get_bottom_left (const graphene_rect_t *r, |
317 | graphene_point_t *p) |
318 | { |
319 | graphene_rect_t rr; |
320 | |
321 | rr = *r; |
322 | graphene_rect_normalize_in_place (r: &rr); |
323 | |
324 | graphene_point_init (p, x: rr.origin.x, y: rr.origin.y + rr.size.height); |
325 | } |
326 | |
327 | /** |
328 | * graphene_rect_get_bottom_right: |
329 | * @r: a #graphene_rect_t |
330 | * @p: (out caller-allocates): return location for a #graphene_point_t |
331 | * |
332 | * Retrieves the coordinates of the bottom-right corner of the given rectangle. |
333 | * |
334 | * Since: 1.0 |
335 | */ |
336 | void |
337 | graphene_rect_get_bottom_right (const graphene_rect_t *r, |
338 | graphene_point_t *p) |
339 | { |
340 | graphene_rect_t rr; |
341 | |
342 | rr = *r; |
343 | graphene_rect_normalize_in_place (r: &rr); |
344 | |
345 | graphene_point_init (p, |
346 | x: rr.origin.x + rr.size.width, |
347 | y: rr.origin.y + rr.size.height); |
348 | } |
349 | |
350 | /** |
351 | * graphene_rect_get_vertices: |
352 | * @r: a #graphene_rect_t |
353 | * @vertices: (out) (array fixed-size=4): return location for an array |
354 | * of 4 #graphene_vec2_t |
355 | * |
356 | * Computes the four vertices of a #graphene_rect_t. |
357 | * |
358 | * Since: 1.4 |
359 | */ |
360 | void |
361 | graphene_rect_get_vertices (const graphene_rect_t *r, |
362 | graphene_vec2_t vertices[]) |
363 | { |
364 | graphene_rect_t rr; |
365 | |
366 | graphene_rect_normalize_r (r, res: &rr); |
367 | |
368 | graphene_vec2_init (v: &vertices[0], x: rr.origin.x, y: rr.origin.y); |
369 | graphene_vec2_init (v: &vertices[1], x: rr.origin.x + rr.size.width, y: rr.origin.y); |
370 | graphene_vec2_init (v: &vertices[2], x: rr.origin.x + rr.size.width, y: rr.origin.y + rr.size.height); |
371 | graphene_vec2_init (v: &vertices[3], x: rr.origin.x, y: rr.origin.y + rr.size.height); |
372 | } |
373 | |
374 | #define GRAPHENE_RECT_GET(arg, part, field) \ |
375 | float \ |
376 | graphene_rect_get_ ## field (const graphene_rect_t * arg) \ |
377 | { \ |
378 | graphene_rect_t rr; \ |
379 | \ |
380 | rr = *arg; \ |
381 | graphene_rect_normalize_in_place (&rr); \ |
382 | \ |
383 | return rr.part.field; \ |
384 | } |
385 | |
386 | /** |
387 | * graphene_rect_get_x: |
388 | * @r: a #graphene_rect_t |
389 | * |
390 | * Retrieves the normalized X coordinate of the origin of the given |
391 | * rectangle. |
392 | * |
393 | * Returns: the normalized X coordinate of the rectangle |
394 | * |
395 | * Since: 1.0 |
396 | */ |
397 | GRAPHENE_RECT_GET (r, origin, x) |
398 | |
399 | /** |
400 | * graphene_rect_get_y: |
401 | * @r: a #graphene_rect_t |
402 | * |
403 | * Retrieves the normalized Y coordinate of the origin of the given |
404 | * rectangle. |
405 | * |
406 | * Returns: the normalized Y coordinate of the rectangle |
407 | * |
408 | * Since: 1.0 |
409 | */ |
410 | GRAPHENE_RECT_GET (r, origin, y) |
411 | |
412 | /** |
413 | * graphene_rect_get_width: |
414 | * @r: a #graphene_rect_t |
415 | * |
416 | * Retrieves the normalized width of the given rectangle. |
417 | * |
418 | * Returns: the normalized width of the rectangle |
419 | * |
420 | * Since: 1.0 |
421 | */ |
422 | GRAPHENE_RECT_GET (r, size, width) |
423 | |
424 | /** |
425 | * graphene_rect_get_height: |
426 | * @r: a #graphene_rect_t |
427 | * |
428 | * Retrieves the normalized height of the given rectangle. |
429 | * |
430 | * Returns: the normalized height of the rectangle |
431 | * |
432 | * Since: 1.0 |
433 | */ |
434 | GRAPHENE_RECT_GET (r, size, height) |
435 | |
436 | #undef GRAPHENE_RECT_GET |
437 | |
438 | /** |
439 | * graphene_rect_get_area: |
440 | * @r: a #graphene_rect_t |
441 | * |
442 | * Compute the area of given normalized rectangle. |
443 | * |
444 | * Returns: the area of the normalized rectangle |
445 | * |
446 | * Since: 1.10 |
447 | */ |
448 | float |
449 | graphene_rect_get_area (const graphene_rect_t *r) |
450 | { |
451 | graphene_rect_t rr; |
452 | |
453 | graphene_rect_normalize_r (r, res: &rr); |
454 | |
455 | return rr.size.width * rr.size.height; |
456 | } |
457 | |
458 | /** |
459 | * graphene_rect_union: |
460 | * @a: a #graphene_rect_t |
461 | * @b: a #graphene_rect_t |
462 | * @res: (out caller-allocates): return location for a #graphene_rect_t |
463 | * |
464 | * Computes the union of the two given rectangles. |
465 | * |
466 | * ![](rectangle-union.png) |
467 | * |
468 | * The union in the image above is the blue outline. |
469 | * |
470 | * Since: 1.0 |
471 | */ |
472 | void |
473 | graphene_rect_union (const graphene_rect_t *a, |
474 | const graphene_rect_t *b, |
475 | graphene_rect_t *res) |
476 | { |
477 | graphene_rect_t ra, rb; |
478 | |
479 | ra = *a; |
480 | rb = *b; |
481 | |
482 | graphene_rect_normalize_in_place (r: &ra); |
483 | graphene_rect_normalize_in_place (r: &rb); |
484 | |
485 | res->origin.x = MIN (ra.origin.x, rb.origin.x); |
486 | res->origin.y = MIN (ra.origin.y, rb.origin.y); |
487 | |
488 | res->size.width = MAX (ra.origin.x + ra.size.width, rb.origin.x + rb.size.width) - res->origin.x; |
489 | res->size.height = MAX (ra.origin.y + ra.size.height, rb.origin.y + rb.size.height) - res->origin.y; |
490 | } |
491 | |
492 | /** |
493 | * graphene_rect_intersection: |
494 | * @a: a #graphene_rect_t |
495 | * @b: a #graphene_rect_t |
496 | * @res: (out caller-allocates) (optional): return location for |
497 | * a #graphene_rect_t |
498 | * |
499 | * Computes the intersection of the two given rectangles. |
500 | * |
501 | * ![](rectangle-intersection.png) |
502 | * |
503 | * The intersection in the image above is the blue outline. |
504 | * |
505 | * If the two rectangles do not intersect, @res will contain |
506 | * a degenerate rectangle with origin in (0, 0) and a size of 0. |
507 | * |
508 | * Returns: `true` if the two rectangles intersect |
509 | * |
510 | * Since: 1.0 |
511 | */ |
512 | bool |
513 | graphene_rect_intersection (const graphene_rect_t *a, |
514 | const graphene_rect_t *b, |
515 | graphene_rect_t *res) |
516 | { |
517 | graphene_rect_t ra, rb; |
518 | float x_1, y_1, x_2, y_2; |
519 | |
520 | ra = *a; |
521 | rb = *b; |
522 | |
523 | graphene_rect_normalize_in_place (r: &ra); |
524 | graphene_rect_normalize_in_place (r: &rb); |
525 | |
526 | x_1 = MAX (ra.origin.x, rb.origin.x); |
527 | y_1 = MAX (ra.origin.y, rb.origin.y); |
528 | x_2 = MIN (ra.origin.x + ra.size.width, rb.origin.x + rb.size.width); |
529 | y_2 = MIN (ra.origin.y + ra.size.height, rb.origin.y + rb.size.height); |
530 | |
531 | if (x_1 >= x_2 || y_1 >= y_2) |
532 | { |
533 | if (res != NULL) |
534 | graphene_rect_init (r: res, x: 0.0f, y: 0.0f, width: 0.0f, height: 0.0f); |
535 | |
536 | return false; |
537 | } |
538 | |
539 | if (res != NULL) |
540 | graphene_rect_init (r: res, x: x_1, y: y_1, width: x_2 - x_1, height: y_2 - y_1); |
541 | |
542 | return true; |
543 | } |
544 | |
545 | /** |
546 | * graphene_rect_contains_point: |
547 | * @r: a #graphene_rect_t |
548 | * @p: a #graphene_point_t |
549 | * |
550 | * Checks whether a #graphene_rect_t contains the given coordinates. |
551 | * |
552 | * Returns: `true` if the rectangle contains the point |
553 | * |
554 | * Since: 1.0 |
555 | */ |
556 | bool |
557 | graphene_rect_contains_point (const graphene_rect_t *r, |
558 | const graphene_point_t *p) |
559 | { |
560 | graphene_rect_t rr; |
561 | |
562 | rr = *r; |
563 | graphene_rect_normalize_in_place (r: &rr); |
564 | |
565 | return p->x >= rr.origin.x && |
566 | p->y >= rr.origin.y && |
567 | p->x <= (rr.origin.x + rr.size.width) && |
568 | p->y <= (rr.origin.y + rr.size.height); |
569 | } |
570 | |
571 | /** |
572 | * graphene_rect_contains_rect: |
573 | * @a: a #graphene_rect_t |
574 | * @b: a #graphene_rect_t |
575 | * |
576 | * Checks whether a #graphene_rect_t fully contains the given |
577 | * rectangle. |
578 | * |
579 | * Returns: `true` if the rectangle @a fully contains @b |
580 | * |
581 | * Since: 1.0 |
582 | */ |
583 | bool |
584 | graphene_rect_contains_rect (const graphene_rect_t *a, |
585 | const graphene_rect_t *b) |
586 | { |
587 | graphene_rect_t res; |
588 | |
589 | graphene_rect_union (a, b, res: &res); |
590 | |
591 | return graphene_rect_equal (a, b: &res); |
592 | } |
593 | |
594 | /** |
595 | * graphene_rect_offset: |
596 | * @r: a #graphene_rect_t |
597 | * @d_x: the horizontal offset |
598 | * @d_y: the vertical offset |
599 | * |
600 | * Offsets the origin by @d_x and @d_y. |
601 | * |
602 | * The size of the rectangle is unchanged. |
603 | * |
604 | * Returns: (transfer none): the offset rectangle |
605 | * |
606 | * Since: 1.0 |
607 | */ |
608 | graphene_rect_t * |
609 | graphene_rect_offset (graphene_rect_t *r, |
610 | float d_x, |
611 | float d_y) |
612 | { |
613 | graphene_rect_offset_r (r, d_x, d_y, res: r); |
614 | |
615 | return r; |
616 | } |
617 | |
618 | /** |
619 | * graphene_rect_offset_r: |
620 | * @r: a #graphene_rect_t |
621 | * @d_x: the horizontal offset |
622 | * @d_y: the vertical offset |
623 | * @res: (out caller-allocates): return location for the offset |
624 | * rectangle |
625 | * |
626 | * Offsets the origin of the given rectangle by @d_x and @d_y. |
627 | * |
628 | * The size of the rectangle is left unchanged. |
629 | * |
630 | * Since: 1.4 |
631 | */ |
632 | void |
633 | graphene_rect_offset_r (const graphene_rect_t *r, |
634 | float d_x, |
635 | float d_y, |
636 | graphene_rect_t *res) |
637 | { |
638 | graphene_rect_normalize_r (r, res); |
639 | |
640 | res->origin.x += d_x; |
641 | res->origin.y += d_y; |
642 | } |
643 | |
644 | /** |
645 | * graphene_rect_inset: |
646 | * @r: a #graphene_rect_t |
647 | * @d_x: the horizontal inset |
648 | * @d_y: the vertical inset |
649 | * |
650 | * Changes the given rectangle to be smaller, or larger depending on the |
651 | * given inset parameters. |
652 | * |
653 | * To create an inset rectangle, use positive @d_x or @d_y values; to |
654 | * create a larger, encompassing rectangle, use negative @d_x or @d_y |
655 | * values. |
656 | * |
657 | * The origin of the rectangle is offset by @d_x and @d_y, while the size |
658 | * is adjusted by `(2 * @d_x, 2 * @d_y)`. If @d_x and @d_y are positive |
659 | * values, the size of the rectangle is decreased; if @d_x and @d_y are |
660 | * negative values, the size of the rectangle is increased. |
661 | * |
662 | * If the size of the resulting inset rectangle has a negative width or |
663 | * height then the size will be set to zero. |
664 | * |
665 | * Returns: (transfer none): the inset rectangle |
666 | * |
667 | * Since: 1.0 |
668 | */ |
669 | graphene_rect_t * |
670 | graphene_rect_inset (graphene_rect_t *r, |
671 | float d_x, |
672 | float d_y) |
673 | { |
674 | graphene_rect_inset_r (r, d_x, d_y, res: r); |
675 | |
676 | return r; |
677 | } |
678 | |
679 | /** |
680 | * graphene_rect_inset_r: |
681 | * @r: a #graphene_rect_t |
682 | * @d_x: the horizontal inset |
683 | * @d_y: the vertical inset |
684 | * @res: (out caller-allocates): return location for the inset rectangle |
685 | * |
686 | * Changes the given rectangle to be smaller, or larger depending on the |
687 | * given inset parameters. |
688 | * |
689 | * To create an inset rectangle, use positive @d_x or @d_y values; to |
690 | * create a larger, encompassing rectangle, use negative @d_x or @d_y |
691 | * values. |
692 | * |
693 | * The origin of the rectangle is offset by @d_x and @d_y, while the size |
694 | * is adjusted by `(2 * @d_x, 2 * @d_y)`. If @d_x and @d_y are positive |
695 | * values, the size of the rectangle is decreased; if @d_x and @d_y are |
696 | * negative values, the size of the rectangle is increased. |
697 | * |
698 | * If the size of the resulting inset rectangle has a negative width or |
699 | * height then the size will be set to zero. |
700 | * |
701 | * Since: 1.4 |
702 | */ |
703 | void |
704 | graphene_rect_inset_r (const graphene_rect_t *r, |
705 | float d_x, |
706 | float d_y, |
707 | graphene_rect_t *res) |
708 | { |
709 | graphene_rect_normalize_r (r, res); |
710 | |
711 | res->origin.x += d_x; |
712 | res->origin.y += d_y; |
713 | |
714 | if (d_x >= 0.f) |
715 | res->size.width -= (d_x * 2.f); |
716 | else |
717 | res->size.width += (d_x * -2.f); |
718 | |
719 | if (d_y >= 0.f) |
720 | res->size.height -= (d_y * 2.f); |
721 | else |
722 | res->size.height += (d_y * -2.f); |
723 | |
724 | if (res->size.width < 0.f) |
725 | res->size.width = 0.f; |
726 | |
727 | if (res->size.height < 0.f) |
728 | res->size.height = 0.f; |
729 | } |
730 | |
731 | /** |
732 | * graphene_rect_round_to_pixel: |
733 | * @r: a #graphene_rect_t |
734 | * |
735 | * Rounds the origin and the size of the given rectangle to |
736 | * their nearest integer values; the rounding is guaranteed |
737 | * to be large enough to contain the original rectangle. |
738 | * |
739 | * Returns: (transfer none): the pixel-aligned rectangle. |
740 | * |
741 | * Since: 1.0 |
742 | * |
743 | * Deprecated: 1.4: Use graphene_rect_round() instead |
744 | */ |
745 | graphene_rect_t * |
746 | graphene_rect_round_to_pixel (graphene_rect_t *r) |
747 | { |
748 | graphene_rect_round (r, res: r); |
749 | |
750 | return r; |
751 | } |
752 | |
753 | /** |
754 | * graphene_rect_round: |
755 | * @r: a #graphene_rect_t |
756 | * @res: (out caller-allocates): return location for the |
757 | * rounded rectangle |
758 | * |
759 | * Rounds the origin and size of the given rectangle to |
760 | * their nearest integer values; the rounding is guaranteed |
761 | * to be large enough to have an area bigger or equal to the |
762 | * original rectangle, but might not fully contain its extents. |
763 | * Use graphene_rect_round_extents() in case you need to round |
764 | * to a rectangle that covers fully the original one. |
765 | * |
766 | * This function is the equivalent of calling `floor` on |
767 | * the coordinates of the origin, and `ceil` on the size. |
768 | * |
769 | * Since: 1.4 |
770 | * |
771 | * Deprecated: 1.10: Use graphene_rect_round_extents() instead |
772 | */ |
773 | void |
774 | graphene_rect_round (const graphene_rect_t *r, |
775 | graphene_rect_t *res) |
776 | { |
777 | graphene_rect_normalize_r (r, res); |
778 | |
779 | res->origin.x = floorf (x: res->origin.x); |
780 | res->origin.y = floorf (x: res->origin.y); |
781 | |
782 | res->size.width = ceilf (x: res->size.width); |
783 | res->size.height = ceilf (x: res->size.height); |
784 | } |
785 | |
786 | /** |
787 | * graphene_rect_round_extents: |
788 | * @r: a #graphene_rect_t |
789 | * @res: (out caller-allocates): return location for the |
790 | * rectangle with rounded extents |
791 | * |
792 | * Rounds the origin of the given rectangle to its nearest |
793 | * integer value and and recompute the size so that the |
794 | * rectangle is large enough to contain all the conrners |
795 | * of the original rectangle. |
796 | * |
797 | * This function is the equivalent of calling `floor` on |
798 | * the coordinates of the origin, and recomputing the size |
799 | * calling `ceil` on the bottom-right coordinates. |
800 | * |
801 | * If you want to be sure that the rounded rectangle |
802 | * completely covers the area that was covered by the |
803 | * original rectangle — i.e. you want to cover the area |
804 | * including all its corners — this function will make sure |
805 | * that the size is recomputed taking into account the ceiling |
806 | * of the coordinates of the bottom-right corner. |
807 | * If the difference between the original coordinates and the |
808 | * coordinates of the rounded rectangle is greater than the |
809 | * difference between the original size and and the rounded |
810 | * size, then the move of the origin would not be compensated |
811 | * by a move in the anti-origin, leaving the corners of the |
812 | * original rectangle outside the rounded one. |
813 | * |
814 | * Since: 1.10 |
815 | */ |
816 | void |
817 | graphene_rect_round_extents (const graphene_rect_t *r, |
818 | graphene_rect_t *res) |
819 | { |
820 | float x2, y2; |
821 | |
822 | graphene_rect_normalize_r (r, res); |
823 | |
824 | x2 = res->origin.x + res->size.width; |
825 | y2 = res->origin.y + res->size.height; |
826 | |
827 | res->origin.x = floorf (x: res->origin.x); |
828 | res->origin.y = floorf (x: res->origin.y); |
829 | |
830 | res->size.width = ceilf (x: x2) - res->origin.x; |
831 | res->size.height = ceilf (x: y2) - res->origin.y; |
832 | } |
833 | |
834 | /** |
835 | * graphene_rect_expand: |
836 | * @r: a #graphene_rect_t |
837 | * @p: a #graphene_point_t |
838 | * @res: (out caller-allocates): return location for the expanded rectangle |
839 | * |
840 | * Expands a #graphene_rect_t to contain the given #graphene_point_t. |
841 | * |
842 | * Since: 1.4 |
843 | */ |
844 | void |
845 | graphene_rect_expand (const graphene_rect_t *r, |
846 | const graphene_point_t *p, |
847 | graphene_rect_t *res) |
848 | { |
849 | graphene_rect_t tmp; |
850 | |
851 | graphene_rect_init (r: &tmp, x: p->x, y: p->y, width: 0.f, height: 0.f); |
852 | graphene_rect_union (a: r, b: &tmp, res); |
853 | |
854 | graphene_rect_normalize_in_place (r: res); |
855 | } |
856 | |
857 | /** |
858 | * graphene_rect_interpolate: |
859 | * @a: a #graphene_rect_t |
860 | * @b: a #graphene_rect_t |
861 | * @factor: the linear interpolation factor |
862 | * @res: (out caller-allocates): return location for the |
863 | * interpolated rectangle |
864 | * |
865 | * Linearly interpolates the origin and size of the two given |
866 | * rectangles. |
867 | * |
868 | * Since: 1.0 |
869 | */ |
870 | void |
871 | graphene_rect_interpolate (const graphene_rect_t *a, |
872 | const graphene_rect_t *b, |
873 | double factor, |
874 | graphene_rect_t *res) |
875 | { |
876 | graphene_rect_t ra, rb; |
877 | |
878 | ra = *a; |
879 | graphene_rect_normalize_in_place (r: &ra); |
880 | |
881 | rb = *b; |
882 | graphene_rect_normalize_in_place (r: &rb); |
883 | |
884 | res->origin.x = graphene_lerp (a: ra.origin.x, b: rb.origin.x, factor); |
885 | res->origin.y = graphene_lerp (a: ra.origin.y, b: rb.origin.y, factor); |
886 | res->size.width = graphene_lerp (a: ra.size.width, b: rb.size.width, factor); |
887 | res->size.height = graphene_lerp (a: ra.size.height, b: rb.size.height, factor); |
888 | } |
889 | |
890 | static const graphene_rect_t _graphene_rect_zero; |
891 | |
892 | /** |
893 | * graphene_rect_zero: |
894 | * |
895 | * Returns a degenerate rectangle with origin fixed at (0, 0) and |
896 | * a size of 0, 0. |
897 | * |
898 | * Returns: (transfer none): a fixed rectangle |
899 | * |
900 | * Since: 1.4 |
901 | */ |
902 | const graphene_rect_t * |
903 | graphene_rect_zero (void) |
904 | { |
905 | return &_graphene_rect_zero; |
906 | } |
907 | |
908 | /** |
909 | * graphene_rect_scale: |
910 | * @r: a #graphene_rect_t |
911 | * @s_h: horizontal scale factor |
912 | * @s_v: vertical scale factor |
913 | * @res: (out caller-allocates): return location for the |
914 | * scaled rectangle |
915 | * |
916 | * Scales the size and origin of a rectangle horizontaly by @s_h, |
917 | * and vertically by @s_v. The result @res is normalized. |
918 | * |
919 | * Since: 1.10 |
920 | */ |
921 | void |
922 | graphene_rect_scale (const graphene_rect_t *r, |
923 | float s_h, |
924 | float s_v, |
925 | graphene_rect_t *res) |
926 | { |
927 | graphene_rect_normalize_r (r, res); |
928 | |
929 | res->origin.x *= s_h; |
930 | res->origin.y *= s_v; |
931 | res->size.width *= s_h; |
932 | res->size.height *= s_v; |
933 | |
934 | graphene_rect_normalize_r (r: res, res); |
935 | } |
936 | |