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, ¢er);
711
712 graphene_box_get_size (box, &size);
713 float radius = graphene_vec3_length (&size) * 0.5f;
714
715 graphene_sphere_init (sphere, ¢er, 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