1 /* graphene-box.c: An axis aligned bounding box
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-box
28  * @Title: Box
29  * @Short_Description: Axis-aligned bounding box
30  *
31  * #graphene_box_t provides a representation of an axis aligned minimum
32  * bounding box using the coordinates of its minimum and maximum vertices.
33  */
34 
35 #include "graphene-private.h"
36 
37 #include "graphene-box.h"
38 
39 #include "graphene-alloc-private.h"
40 #include "graphene-point3d.h"
41 #include "graphene-simd4f.h"
42 #include "graphene-sphere.h"
43 
44 #include <math.h>
45 #include <stdio.h>
46 #include <string.h>
47 
48 #ifdef HAVE_PTHREAD
49 #include <pthread.h>
50 #include <errno.h>
51 #endif
52 
53 /**
54  * graphene_box_alloc: (constructor)
55  *
56  * Allocates a new #graphene_box_t.
57  *
58  * The contents of the returned structure are undefined.
59  *
60  * Returns: (transfer full): the newly allocated #graphene_box_t structure.
61  *   Use graphene_box_free() to free the resources allocated by this function
62  *
63  * Since: 1.2
64  */
65 graphene_box_t *
graphene_box_alloc(void)66 graphene_box_alloc (void)
67 {
68   return graphene_aligned_alloc0 (sizeof (graphene_box_t), 1, 16);
69 }
70 
71 /**
72  * graphene_box_free:
73  * @box: a #graphene_box_t
74  *
75  * Frees the resources allocated by graphene_box_alloc().
76  *
77  * Since: 1.2
78  */
79 void
graphene_box_free(graphene_box_t * box)80 graphene_box_free (graphene_box_t *box)
81 {
82   graphene_aligned_free (box);
83 }
84 
85 /**
86  * graphene_box_init:
87  * @box: the #graphene_box_t to initialize
88  * @min: (nullable): the coordinates of the minimum vertex
89  * @max: (nullable): the coordinates of the maximum vertex
90  *
91  * Initializes the given #graphene_box_t with two vertices.
92  *
93  * Returns: (transfer none): the initialized #graphene_box_t
94  *
95  * Since: 1.2
96  */
97 graphene_box_t *
graphene_box_init(graphene_box_t * box,const graphene_point3d_t * min,const graphene_point3d_t * max)98 graphene_box_init (graphene_box_t           *box,
99                    const graphene_point3d_t *min,
100                    const graphene_point3d_t *max)
101 {
102   if (min != NULL)
103     graphene_point3d_to_vec3 (min, &box->min);
104   else
105     graphene_vec3_init_from_vec3 (&box->min, graphene_vec3_zero ());
106 
107   if (max != NULL)
108     graphene_point3d_to_vec3 (max, &box->max);
109   else
110     graphene_vec3_init_from_vec3 (&box->max, graphene_vec3_zero ());
111 
112   return box;
113 }
114 
115 /**
116  * graphene_box_init_from_points:
117  * @box: the #graphene_box_t to initialize
118  * @n_points: the number #graphene_point3d_t in the @points array
119  * @points: (array length=n_points): an array of #graphene_point3d_t
120  *
121  * Initializes the given #graphene_box_t with the given array
122  * of vertices.
123  *
124  * If @n_points is 0, the returned box is initialized with
125  * graphene_box_empty().
126  *
127  * Returns: (transfer none): the initialized #graphene_box_t
128  *
129  * Since: 1.2
130  */
131 graphene_box_t *
graphene_box_init_from_points(graphene_box_t * box,unsigned int n_points,const graphene_point3d_t * points)132 graphene_box_init_from_points (graphene_box_t           *box,
133                                unsigned int              n_points,
134                                const graphene_point3d_t *points)
135 {
136   graphene_box_init_from_box (box, graphene_box_empty ());
137 
138   for (unsigned int i = 0; i < n_points; i++)
139     {
140       graphene_vec3_t v;
141 
142       graphene_point3d_to_vec3 (&points[i], &v);
143       graphene_box_expand_vec3 (box, &v, box);
144     }
145 
146   return box;
147 }
148 
149 /**
150  * graphene_box_init_from_vectors:
151  * @box: the #graphene_box_t to initialize
152  * @n_vectors: the number #graphene_point3d_t in the @vectors array
153  * @vectors: (array length=n_vectors): an array of #graphene_vec3_t
154  *
155  * Initializes the given #graphene_box_t with the given array
156  * of vertices.
157  *
158  * If @n_vectors is 0, the returned box is initialized with
159  * graphene_box_empty().
160  *
161  * Returns: (transfer none): the initialized #graphene_box_t
162  *
163  * Since: 1.2
164  */
165 graphene_box_t *
graphene_box_init_from_vectors(graphene_box_t * box,unsigned int n_vectors,const graphene_vec3_t * vectors)166 graphene_box_init_from_vectors (graphene_box_t        *box,
167                                 unsigned int           n_vectors,
168                                 const graphene_vec3_t *vectors)
169 {
170   graphene_box_init_from_box (box, graphene_box_empty ());
171 
172   for (unsigned int i = 0; i < n_vectors; i++)
173     graphene_box_expand_vec3 (box, &vectors[i], box);
174 
175   return box;
176 }
177 
178 /**
179  * graphene_box_init_from_box:
180  * @box: the #graphene_box_t to initialize
181  * @src: a #graphene_box_t
182  *
183  * Initializes the given #graphene_box_t with the vertices of
184  * another #graphene_box_t.
185  *
186  * Returns: (transfer none): the initialized #graphene_box_t
187  *
188  * Since: 1.2
189  */
190 graphene_box_t *
graphene_box_init_from_box(graphene_box_t * box,const graphene_box_t * src)191 graphene_box_init_from_box (graphene_box_t       *box,
192                             const graphene_box_t *src)
193 {
194   box->min = src->min;
195   box->max = src->max;
196 
197   return box;
198 }
199 
200 /**
201  * graphene_box_init_from_vec3:
202  * @box: the #graphene_box_t to initialize
203  * @min: (nullable): the coordinates of the minimum vertex
204  * @max: (nullable): the coordinates of the maximum vertex
205  *
206  * Initializes the given #graphene_box_t with two vertices
207  * stored inside #graphene_vec3_t.
208  *
209  * Returns: (transfer none): the initialized #graphene_box_t
210  *
211  * Since: 1.2
212  */
213 graphene_box_t *
graphene_box_init_from_vec3(graphene_box_t * box,const graphene_vec3_t * min,const graphene_vec3_t * max)214 graphene_box_init_from_vec3 (graphene_box_t        *box,
215                              const graphene_vec3_t *min,
216                              const graphene_vec3_t *max)
217 {
218   if (min != NULL)
219     box->min = *min;
220   else
221     graphene_vec3_init_from_vec3 (&box->min, graphene_vec3_zero ());
222 
223   if (max != NULL)
224     box->max = *max;
225   else
226     graphene_vec3_init_from_vec3 (&box->max, graphene_vec3_zero ());
227 
228   return box;
229 }
230 
231 static inline graphene_box_t *
graphene_box_init_from_simd4f(graphene_box_t * box,const graphene_simd4f_t min,const graphene_simd4f_t max)232 graphene_box_init_from_simd4f (graphene_box_t          *box,
233                                const graphene_simd4f_t  min,
234                                const graphene_simd4f_t  max)
235 {
236   box->min.value = min;
237   box->max.value = max;
238 
239   return box;
240 }
241 
242 static inline void
graphene_box_expand_simd4f(const graphene_box_t * box,const graphene_simd4f_t v,graphene_box_t * res)243 graphene_box_expand_simd4f (const graphene_box_t    *box,
244                             const graphene_simd4f_t  v,
245                             graphene_box_t          *res)
246 {
247   res->min.value = graphene_simd4f_min (box->min.value, v);
248   res->max.value = graphene_simd4f_max (box->max.value, v);
249 }
250 
251 /**
252  * graphene_box_expand_vec3:
253  * @box: a #graphene_box_t
254  * @vec: the coordinates of the point to include, as a #graphene_vec3_t
255  * @res: (out caller-allocates): return location for the expanded box
256  *
257  * Expands the dimensions of @box to include the coordinates of the
258  * given vector.
259  *
260  * Since: 1.2
261  */
262 void
graphene_box_expand_vec3(const graphene_box_t * box,const graphene_vec3_t * vec,graphene_box_t * res)263 graphene_box_expand_vec3 (const graphene_box_t  *box,
264                           const graphene_vec3_t *vec,
265                           graphene_box_t        *res)
266 {
267   graphene_box_expand_simd4f (box, vec->value, res);
268 }
269 
270 /**
271  * graphene_box_expand:
272  * @box: a #graphene_box_t
273  * @point: the coordinates of the point to include
274  * @res: (out caller-allocates): return location for the expanded box
275  *
276  * Expands the dimensions of @box to include the coordinates at @point.
277  *
278  * Since: 1.2
279  */
280 void
graphene_box_expand(const graphene_box_t * box,const graphene_point3d_t * point,graphene_box_t * res)281 graphene_box_expand (const graphene_box_t     *box,
282                      const graphene_point3d_t *point,
283                      graphene_box_t           *res)
284 {
285   graphene_simd4f_t v = graphene_simd4f_init (point->x, point->y, point->z, 0.f);
286 
287   graphene_box_expand_simd4f (box, v, res);
288 }
289 
290 /**
291  * graphene_box_expand_scalar:
292  * @box: a #graphene_box_t
293  * @scalar: a scalar value
294  * @res: (out caller-allocates): return location for the expanded box
295  *
296  * Expands the dimensions of @box by the given @scalar value.
297  *
298  * If @scalar is positive, the #graphene_box_t will grow; if @scalar is
299  * negative, the #graphene_box_t will shrink.
300  *
301  * Since: 1.2
302  */
303 void
graphene_box_expand_scalar(const graphene_box_t * box,float scalar,graphene_box_t * res)304 graphene_box_expand_scalar (const graphene_box_t *box,
305                             float                 scalar,
306                             graphene_box_t       *res)
307 {
308   float min = scalar * -1.f;
309   float max = scalar;
310 
311   res->min.value = graphene_simd4f_add (box->min.value, graphene_simd4f_splat (min));
312   res->max.value = graphene_simd4f_add (box->max.value, graphene_simd4f_splat (max));
313 }
314 
315 /**
316  * graphene_box_union:
317  * @a: a #graphene_box_t
318  * @b: the box to union to @a
319  * @res: (out caller-allocates): return location for the result
320  *
321  * Unions the two given #graphene_box_t.
322  *
323  * Since: 1.2
324  */
325 void
graphene_box_union(const graphene_box_t * a,const graphene_box_t * b,graphene_box_t * res)326 graphene_box_union (const graphene_box_t *a,
327                     const graphene_box_t *b,
328                     graphene_box_t       *res)
329 {
330   res->min.value = graphene_simd4f_min (a->min.value, b->min.value);
331   res->max.value = graphene_simd4f_max (a->max.value, b->max.value);
332 }
333 
334 /**
335  * graphene_box_intersection:
336  * @a: a #graphene_box_t
337  * @b: a #graphene_box_t
338  * @res: (out caller-allocates) (optional): return location for the result
339  *
340  * Intersects the two given #graphene_box_t.
341  *
342  * If the two boxes do not intersect, @res will contain a degenerate box
343  * initialized with graphene_box_empty().
344  *
345  * Returns: true if the two boxes intersect
346  *
347  * Since: 1.2
348  */
349 bool
graphene_box_intersection(const graphene_box_t * a,const graphene_box_t * b,graphene_box_t * res)350 graphene_box_intersection (const graphene_box_t *a,
351                            const graphene_box_t *b,
352                            graphene_box_t       *res)
353 {
354   graphene_simd4f_t min, max;
355 
356   min = graphene_simd4f_max (a->min.value, b->min.value);
357   max = graphene_simd4f_min (a->max.value, b->max.value);
358 
359   if (!graphene_simd4f_cmp_le (min, max))
360     {
361       if (res != NULL)
362         graphene_box_init_from_box (res, graphene_box_empty ());
363 
364       return false;
365     }
366 
367   if (res != NULL)
368     graphene_box_init_from_simd4f (res, min, max);
369 
370   return true;
371 }
372 
373 /**
374  * graphene_box_get_width:
375  * @box: a #graphene_box_t
376  *
377  * Retrieves the size of the @box on the X axis.
378  *
379  * Returns: the width of the box
380  *
381  * Since: 1.2
382  */
383 float
graphene_box_get_width(const graphene_box_t * box)384 graphene_box_get_width (const graphene_box_t *box)
385 {
386   float res = graphene_simd4f_get_x (graphene_simd4f_sub (box->max.value, box->min.value));
387 
388   return fabsf (res);
389 }
390 
391 /**
392  * graphene_box_get_height:
393  * @box: a #graphene_box_t
394  *
395  * Retrieves the size of the @box on the Y axis.
396  *
397  * Returns: the height of the box
398  *
399  * Since: 1.2
400  */
401 float
graphene_box_get_height(const graphene_box_t * box)402 graphene_box_get_height (const graphene_box_t *box)
403 {
404   float res = graphene_simd4f_get_y (graphene_simd4f_sub (box->max.value, box->min.value));
405 
406   return fabsf (res);
407 }
408 
409 /**
410  * graphene_box_get_depth:
411  * @box: a #graphene_box_t
412  *
413  * Retrieves the size of the @box on the Z axis.
414  *
415  * Returns: the depth of the box
416  *
417  * Since: 1.2
418  */
419 float
graphene_box_get_depth(const graphene_box_t * box)420 graphene_box_get_depth (const graphene_box_t *box)
421 {
422   float res = graphene_simd4f_get_z (graphene_simd4f_sub (box->max.value, box->min.value));
423 
424   return fabsf (res);
425 }
426 
427 static inline bool
graphene_box_is_empty(const graphene_box_t * box)428 graphene_box_is_empty (const graphene_box_t *box)
429 {
430 #ifdef HAVE_ISINFF
431   float vmin[3], vmax[3];
432 
433   graphene_simd4f_dup_3f (box->min.value, vmin);
434   graphene_simd4f_dup_3f (box->max.value, vmax);
435 
436   return (isinff (vmin[0]) == 1 && isinff (vmin[1]) == 1 && isinff (vmin[2]) == 1) &&
437          (isinff (vmax[0]) == -1 && isinff (vmax[1]) == -1 && isinff (vmax[2]) == -1);
438 #else
439   graphene_simd4f_t neg_inf = graphene_simd4f_init (-INFINITY, -INFINITY, -INFINITY, 0.f);
440   graphene_simd4f_t pos_inf = graphene_simd4f_init (INFINITY, INFINITY, INFINITY, 0.f);
441 
442   /* This is only every going to be valid for boxes that we have
443    * initialized ourselves, because we use the same values; the
444    * bitwise comparison will not hold for infinities generated by
445    * other operations
446    */
447   int min_cmp = memcmp (&box->min.value, &pos_inf, sizeof (graphene_simd4f_t));
448   int max_cmp = memcmp (&box->max.value, &neg_inf, sizeof (graphene_simd4f_t));
449 
450   return min_cmp == 0 && max_cmp == 0;
451 #endif
452 }
453 
454 static inline bool
graphene_box_is_infinity(const graphene_box_t * box)455 graphene_box_is_infinity (const graphene_box_t *box)
456 {
457 #ifdef HAVE_ISINFF
458   float vmin[3], vmax[3];
459 
460   graphene_simd4f_dup_3f (box->min.value, vmin);
461   graphene_simd4f_dup_3f (box->max.value, vmax);
462 
463   return (isinff (vmin[0]) == -1 && isinff (vmin[1]) == -1 && isinff (vmin[2]) == -1) &&
464          (isinff (vmax[0]) == 1 && isinff (vmax[1]) == 1 && isinff (vmax[2]) == 1);
465 #else
466   graphene_simd4f_t neg_inf = graphene_simd4f_init (-INFINITY, -INFINITY, -INFINITY, 0.f);
467   graphene_simd4f_t pos_inf = graphene_simd4f_init (INFINITY, INFINITY, INFINITY, 0.f);
468 
469   /* This is only every going to be valid for boxes that we have
470    * initialized ourselves, because we use the same values; the
471    * bitwise comparison will not hold for infinities generated by
472    * other operations
473    */
474   int min_cmp = memcmp (&box->min.value, &neg_inf, sizeof (graphene_simd4f_t));
475   int max_cmp = memcmp (&box->max.value, &pos_inf, sizeof (graphene_simd4f_t));
476 
477   return min_cmp == 0 && max_cmp == 0;
478 #endif
479 }
480 
481 /**
482  * graphene_box_get_size:
483  * @box: a #graphene_box_t
484  * @size: (out caller-allocates): return location for the size
485  *
486  * Retrieves the size of the box on all three axes, and stores
487  * it into the given @size vector.
488  *
489  * Since: 1.2
490  */
491 void
graphene_box_get_size(const graphene_box_t * box,graphene_vec3_t * size)492 graphene_box_get_size (const graphene_box_t *box,
493                        graphene_vec3_t      *size)
494 {
495   if (graphene_box_is_empty (box))
496     size->value = graphene_simd4f_init_zero ();
497   else if (graphene_box_is_infinity (box))
498     size->value = graphene_simd4f_init (INFINITY, INFINITY, INFINITY, 0.f);
499   else
500     size->value = graphene_simd4f_sub (box->max.value, box->min.value);
501 }
502 
503 /**
504  * graphene_box_get_center:
505  * @box: a #graphene_box_t
506  * @center: (out caller-allocates): return location for the coordinates of
507  *   the center
508  *
509  * Retrieves the coordinates of the center of a #graphene_box_t.
510  *
511  * Since: 1.2
512  */
513 void
graphene_box_get_center(const graphene_box_t * box,graphene_point3d_t * center)514 graphene_box_get_center (const graphene_box_t *box,
515                          graphene_point3d_t   *center)
516 {
517   graphene_vec3_t res;
518 
519   if (graphene_box_is_empty (box) || graphene_box_is_infinity (box))
520     {
521       graphene_point3d_init (center, 0.f, 0.f, 0.f);
522       return;
523     }
524 
525   graphene_vec3_add (&box->min, &box->max, &res);
526   graphene_vec3_scale (&res, 0.5f, &res);
527 
528   graphene_point3d_init_from_vec3 (center, &res);
529 }
530 
531 /**
532  * graphene_box_get_min:
533  * @box: a #graphene_box_t
534  * @min: (out caller-allocates): return location for the minimum point
535  *
536  * Retrieves the coordinates of the minimum point of the given
537  * #graphene_box_t.
538  *
539  * Since: 1.2
540  */
541 void
graphene_box_get_min(const graphene_box_t * box,graphene_point3d_t * min)542 graphene_box_get_min (const graphene_box_t *box,
543                       graphene_point3d_t   *min)
544 {
545   graphene_point3d_init_from_vec3 (min, &box->min);
546 }
547 
548 /**
549  * graphene_box_get_max:
550  * @box: a #graphene_box_t
551  * @max: (out caller-allocates): return location for the maximum point
552  *
553  * Retrieves the coordinates of the maximum point of the given
554  * #graphene_box_t.
555  *
556  * Since: 1.2
557  */
558 void
graphene_box_get_max(const graphene_box_t * box,graphene_point3d_t * max)559 graphene_box_get_max (const graphene_box_t *box,
560                       graphene_point3d_t   *max)
561 {
562   graphene_point3d_init_from_vec3 (max, &box->max);
563 }
564 
565 /**
566  * graphene_box_get_vertices:
567  * @box: a #graphene_box_t
568  * @vertices: (out) (array fixed-size=8): return location for an array
569  *   of 8 #graphene_vec3_t
570  *
571  * Computes the vertices of the given #graphene_box_t.
572  *
573  * Since: 1.2
574  */
575 void
graphene_box_get_vertices(const graphene_box_t * box,graphene_vec3_t vertices[])576 graphene_box_get_vertices (const graphene_box_t *box,
577                            graphene_vec3_t       vertices[])
578 {
579   graphene_point3d_t min, max;
580 
581   graphene_box_get_min (box, &min);
582   graphene_box_get_max (box, &max);
583 
584   graphene_vec3_init (&vertices[0], min.x, min.y, min.z);
585   graphene_vec3_init (&vertices[1], min.x, min.y, max.z);
586   graphene_vec3_init (&vertices[2], min.x, max.y, min.z);
587   graphene_vec3_init (&vertices[3], min.x, max.y, max.z);
588   graphene_vec3_init (&vertices[4], max.x, min.y, min.z);
589   graphene_vec3_init (&vertices[5], max.x, min.y, max.z);
590   graphene_vec3_init (&vertices[6], max.x, max.y, min.z);
591   graphene_vec3_init (&vertices[7], max.x, max.y, max.z);
592 }
593 
594 /**
595  * graphene_box_contains_point:
596  * @box: a #graphene_box_t
597  * @point: the coordinates to check
598  *
599  * Checks whether @box contains the given @point.
600  *
601  * Returns: `true` if the point is contained in the given box
602  *
603  * Since: 1.2
604  */
605 bool
graphene_box_contains_point(const graphene_box_t * box,const graphene_point3d_t * point)606 graphene_box_contains_point (const graphene_box_t     *box,
607                              const graphene_point3d_t *point)
608 {
609   if (graphene_box_is_empty (box))
610     return false;
611 
612   if (graphene_box_is_infinity (box))
613     return true;
614 
615   graphene_simd4f_t p = graphene_simd4f_init (point->x, point->y, point->z, 0.f);
616 
617   if (graphene_simd4f_cmp_ge (p, box->min.value) &&
618       graphene_simd4f_cmp_le (p, box->max.value))
619     return true;
620 
621   return false;
622 }
623 
624 /**
625  * graphene_box_contains_box:
626  * @a: a #graphene_box_t
627  * @b: a #graphene_box_t
628  *
629  * Checks whether the #graphene_box_t @a contains the given
630  * #graphene_box_t @b.
631  *
632  * Returns: `true` if the box is contained in the given box
633  *
634  * Since: 1.2
635  */
636 bool
graphene_box_contains_box(const graphene_box_t * a,const graphene_box_t * b)637 graphene_box_contains_box (const graphene_box_t *a,
638                            const graphene_box_t *b)
639 {
640   if (graphene_box_is_empty (a) || graphene_box_is_infinity (b))
641     return false;
642 
643   if (graphene_box_is_infinity (a) || graphene_box_is_empty (b))
644     return true;
645 
646   /* we cheat a bit and access the SIMD directly */
647   if (graphene_simd4f_cmp_ge (b->min.value, a->min.value) &&
648       graphene_simd4f_cmp_le (b->max.value, a->max.value))
649     return true;
650 
651   return false;
652 }
653 
654 static bool
box_equal(const void * p1,const void * p2)655 box_equal (const void *p1,
656            const void *p2)
657 {
658   const graphene_box_t *a = p1;
659   const graphene_box_t *b = p2;
660 
661   if (graphene_box_is_empty (a) && graphene_box_is_empty (b))
662     return true;
663   else if (graphene_box_is_empty (a) || graphene_box_is_empty (b))
664     return false;
665 
666   if (graphene_box_is_infinity (a) && graphene_box_is_infinity (b))
667     return true;
668   else if (graphene_box_is_infinity (a) || graphene_box_is_infinity (b))
669     return false;
670 
671   return graphene_vec3_equal (&a->min, &b->min) &&
672          graphene_vec3_equal (&a->max, &b->max);
673 }
674 
675 /**
676  * graphene_box_equal:
677  * @a: a #graphene_box_t
678  * @b: a #graphene_box_t
679  *
680  * Checks whether the two given boxes are equal.
681  *
682  * Returns: `true` if the boxes are equal
683  *
684  * Since: 1.2
685  */
686 bool
graphene_box_equal(const graphene_box_t * a,const graphene_box_t * b)687 graphene_box_equal (const graphene_box_t *a,
688                     const graphene_box_t *b)
689 {
690   return graphene_pointer_equal (a, b, box_equal);
691 }
692 
693 /**
694  * graphene_box_get_bounding_sphere:
695  * @box: a #graphene_box_t
696  * @sphere: (out caller-allocates): return location for the bounding sphere
697  *
698  * Computes the bounding #graphene_sphere_t capable of containing the given
699  * #graphene_box_t.
700  *
701  * Since: 1.2
702  */
703 void
graphene_box_get_bounding_sphere(const graphene_box_t * box,graphene_sphere_t * sphere)704 graphene_box_get_bounding_sphere (const graphene_box_t *box,
705                                   graphene_sphere_t    *sphere)
706 {
707   graphene_point3d_t center;
708   graphene_vec3_t size;
709 
710   graphene_box_get_center (box, &center);
711 
712   graphene_box_get_size (box, &size);
713   float radius = graphene_vec3_length (&size) * 0.5f;
714 
715   graphene_sphere_init (sphere, &center, radius);
716 }
717 
718 enum {
719   BOX_ZERO = 0,
720   BOX_ONE,
721   BOX_MINUS_ONE,
722   BOX_ONE_MINUS_ONE,
723   BOX_INFINITY,
724   BOX_EMPTY,
725 
726   N_STATIC_BOX
727 };
728 
729 static graphene_box_t static_box[N_STATIC_BOX];
730 
731 static void
init_static_box_once(void)732 init_static_box_once (void)
733 {
734   static_box[BOX_ZERO].min.value = graphene_simd4f_init_zero ();
735   static_box[BOX_ZERO].max.value = graphene_simd4f_init_zero ();
736 
737   static_box[BOX_ONE].min.value = graphene_simd4f_init_zero ();
738   static_box[BOX_ONE].max.value = graphene_simd4f_init (1.f, 1.f, 1.f, 0.f);
739 
740   static_box[BOX_MINUS_ONE].min.value = graphene_simd4f_init (-1.f, -1.f, -1.f, 0.f);
741   static_box[BOX_MINUS_ONE].max.value = graphene_simd4f_init_zero ();
742 
743   static_box[BOX_ONE_MINUS_ONE].min.value = graphene_simd4f_init (-1.f, -1.f, -1.f, 0.f);
744   static_box[BOX_ONE_MINUS_ONE].max.value = graphene_simd4f_init (1.f, 1.f, 1.f, 0.f);
745 
746   static_box[BOX_INFINITY].min.value = graphene_simd4f_init (-INFINITY, -INFINITY, -INFINITY, 0.f);
747   static_box[BOX_INFINITY].max.value = graphene_simd4f_init (INFINITY, INFINITY, INFINITY, 0.f);
748 
749   static_box[BOX_EMPTY].min.value = graphene_simd4f_init (INFINITY, INFINITY, INFINITY, 0.f);
750   static_box[BOX_EMPTY].max.value = graphene_simd4f_init (-INFINITY, -INFINITY, -INFINITY, 0.f);
751 }
752 
753 #ifdef HAVE_PTHREAD
754 static pthread_once_t static_box_once = PTHREAD_ONCE_INIT;
755 
756 static inline void
init_static_box(void)757 init_static_box (void)
758 {
759   int status = pthread_once (&static_box_once, init_static_box_once);
760 
761   if (status < 0)
762     {
763       int saved_errno = errno;
764 
765       fprintf (stderr, "pthread_once failed: %s (errno:%d)\n",
766                strerror (saved_errno),
767                saved_errno);
768     }
769 }
770 
771 #elif defined(HAVE_INIT_ONCE)
772 static INIT_ONCE static_box_once = INIT_ONCE_STATIC_INIT;
773 
774 static BOOL CALLBACK
InitBoxFunc(PINIT_ONCE InitOnce,PVOID param,PVOID * ctx)775 InitBoxFunc (PINIT_ONCE InitOnce,
776              PVOID      param,
777              PVOID     *ctx)
778 {
779   init_static_box_once ();
780   return TRUE;
781 }
782 
783 static inline void
init_static_box(void)784 init_static_box (void)
785 {
786   BOOL bStatus = InitOnceExecuteOnce (&static_box_once,
787                                       InitBoxFunc,
788                                       NULL,
789                                       NULL);
790 
791   if (!bStatus)
792     fprintf (stderr, "InitOnceExecuteOnce failed\n");
793 }
794 
795 #else /* !HAVE_PTHREAD */
796 static bool static_box_init = false;
797 
798 static inline void
init_static_box(void)799 init_static_box (void)
800 {
801   if (static_box_init)
802     return;
803 
804   init_static_box_once ();
805   static_box_init = true;
806 }
807 
808 #endif /* HAVE_PTHREAD */
809 
810 /**
811  * graphene_box_zero:
812  *
813  * A #graphene_box_t with both the minimum and maximum vertices set at (0, 0, 0).
814  *
815  * The returned value is owned by Graphene and should not be modified or freed.
816  *
817  * Returns: (transfer none): a #graphene_box_t
818  *
819  * Since: 1.2
820  */
821 const graphene_box_t *
graphene_box_zero(void)822 graphene_box_zero (void)
823 {
824   init_static_box ();
825 
826   return &(static_box[BOX_ZERO]);
827 }
828 
829 /**
830  * graphene_box_one:
831  *
832  * A #graphene_box_t with the minimum vertex set at (0, 0, 0) and the
833  * maximum vertex set at (1, 1, 1).
834  *
835  * The returned value is owned by Graphene and should not be modified or freed.
836  *
837  * Returns: (transfer none): a #graphene_box_t
838  *
839  * Since: 1.2
840  */
841 const graphene_box_t *
graphene_box_one(void)842 graphene_box_one (void)
843 {
844   init_static_box ();
845 
846   return &(static_box[BOX_ONE]);
847 }
848 
849 /**
850  * graphene_box_minus_one:
851  *
852  * A #graphene_box_t with the minimum vertex set at (-1, -1, -1) and the
853  * maximum vertex set at (0, 0, 0).
854  *
855  * The returned value is owned by Graphene and should not be modified or freed.
856  *
857  * Returns: (transfer none): a #graphene_box_t
858  *
859  * Since: 1.2
860  */
861 const graphene_box_t *
graphene_box_minus_one(void)862 graphene_box_minus_one (void)
863 {
864   init_static_box ();
865 
866   return &(static_box[BOX_MINUS_ONE]);
867 }
868 
869 /**
870  * graphene_box_one_minus_one:
871  *
872  * A #graphene_box_t with the minimum vertex set at (-1, -1, -1) and the
873  * maximum vertex set at (1, 1, 1).
874  *
875  * The returned value is owned by Graphene and should not be modified or freed.
876  *
877  * Returns: (transfer none): a #graphene_box_t
878  *
879  * Since: 1.2
880  */
881 const graphene_box_t *
graphene_box_one_minus_one(void)882 graphene_box_one_minus_one (void)
883 {
884   init_static_box ();
885 
886   return &(static_box[BOX_ONE_MINUS_ONE]);
887 }
888 
889 /**
890  * graphene_box_infinite:
891  *
892  * A degenerate #graphene_box_t that cannot be expanded.
893  *
894  * The returned value is owned by Graphene and should not be modified or freed.
895  *
896  * Returns: (transfer none): a #graphene_box_t
897  *
898  * Since: 1.2
899  */
900 const graphene_box_t *
graphene_box_infinite(void)901 graphene_box_infinite (void)
902 {
903   init_static_box ();
904 
905   return &(static_box[BOX_INFINITY]);
906 }
907 
908 /**
909  * graphene_box_empty:
910  *
911  * A degenerate #graphene_box_t that can only be expanded.
912  *
913  * The returned value is owned by Graphene and should not be modified or freed.
914  *
915  * Returns: (transfer none): a #graphene_box_t
916  *
917  * Since: 1.2
918  */
919 const graphene_box_t *
graphene_box_empty(void)920 graphene_box_empty (void)
921 {
922   init_static_box ();
923 
924   return &(static_box[BOX_EMPTY]);
925 }
926