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 * |
66 | graphene_box_alloc (void) |
67 | { |
68 | return graphene_aligned_alloc0 (size: sizeof (graphene_box_t), number: 1, alignment: 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 |
80 | graphene_box_free (graphene_box_t *box) |
81 | { |
82 | graphene_aligned_free (mem: 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 * |
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 (p: min, v: &box->min); |
104 | else |
105 | graphene_vec3_init_from_vec3 (v: &box->min, src: graphene_vec3_zero ()); |
106 | |
107 | if (max != NULL) |
108 | graphene_point3d_to_vec3 (p: max, v: &box->max); |
109 | else |
110 | graphene_vec3_init_from_vec3 (v: &box->max, src: 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 * |
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, src: 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 (p: &points[i], v: &v); |
143 | graphene_box_expand_vec3 (box, vec: &v, res: 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 * |
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, src: graphene_box_empty ()); |
171 | |
172 | for (unsigned int i = 0; i < n_vectors; i++) |
173 | graphene_box_expand_vec3 (box, vec: &vectors[i], res: 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 * |
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 * |
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 (v: &box->min, src: graphene_vec3_zero ()); |
222 | |
223 | if (max != NULL) |
224 | box->max = *max; |
225 | else |
226 | graphene_vec3_init_from_vec3 (v: &box->max, src: graphene_vec3_zero ()); |
227 | |
228 | return box; |
229 | } |
230 | |
231 | static inline graphene_box_t * |
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 |
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 |
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, v: 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 |
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 |
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 |
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 |
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 (box: res, src: graphene_box_empty ()); |
363 | |
364 | return false; |
365 | } |
366 | |
367 | if (res != NULL) |
368 | graphene_box_init_from_simd4f (box: 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 |
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 (x: 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 |
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 (x: 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 |
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 (x: res); |
425 | } |
426 | |
427 | static inline bool |
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 (value: vmin[0]) == 1 && isinff (value: vmin[1]) == 1 && isinff (value: vmin[2]) == 1) && |
437 | (isinff (value: vmax[0]) == -1 && isinff (value: vmax[1]) == -1 && isinff (value: 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 |
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 (value: vmin[0]) == -1 && isinff (value: vmin[1]) == -1 && isinff (value: vmin[2]) == -1) && |
464 | (isinff (value: vmax[0]) == 1 && isinff (value: vmax[1]) == 1 && isinff (value: 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 |
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 |
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 (p: center, x: 0.f, y: 0.f, z: 0.f); |
522 | return; |
523 | } |
524 | |
525 | graphene_vec3_add (a: &box->min, b: &box->max, res: &res); |
526 | graphene_vec3_scale (v: &res, factor: 0.5f, res: &res); |
527 | |
528 | graphene_point3d_init_from_vec3 (p: center, v: &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 |
542 | graphene_box_get_min (const graphene_box_t *box, |
543 | graphene_point3d_t *min) |
544 | { |
545 | graphene_point3d_init_from_vec3 (p: min, v: &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 |
559 | graphene_box_get_max (const graphene_box_t *box, |
560 | graphene_point3d_t *max) |
561 | { |
562 | graphene_point3d_init_from_vec3 (p: max, v: &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 |
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: &min); |
582 | graphene_box_get_max (box, max: &max); |
583 | |
584 | graphene_vec3_init (v: &vertices[0], x: min.x, y: min.y, z: min.z); |
585 | graphene_vec3_init (v: &vertices[1], x: min.x, y: min.y, z: max.z); |
586 | graphene_vec3_init (v: &vertices[2], x: min.x, y: max.y, z: min.z); |
587 | graphene_vec3_init (v: &vertices[3], x: min.x, y: max.y, z: max.z); |
588 | graphene_vec3_init (v: &vertices[4], x: max.x, y: min.y, z: min.z); |
589 | graphene_vec3_init (v: &vertices[5], x: max.x, y: min.y, z: max.z); |
590 | graphene_vec3_init (v: &vertices[6], x: max.x, y: max.y, z: min.z); |
591 | graphene_vec3_init (v: &vertices[7], x: max.x, y: max.y, z: 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 |
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 |
637 | graphene_box_contains_box (const graphene_box_t *a, |
638 | const graphene_box_t *b) |
639 | { |
640 | if (graphene_box_is_empty (box: a) || graphene_box_is_infinity (box: b)) |
641 | return false; |
642 | |
643 | if (graphene_box_is_infinity (box: a) || graphene_box_is_empty (box: 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 |
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 (box: a) && graphene_box_is_empty (box: b)) |
662 | return true; |
663 | else if (graphene_box_is_empty (box: a) || graphene_box_is_empty (box: b)) |
664 | return false; |
665 | |
666 | if (graphene_box_is_infinity (box: a) && graphene_box_is_infinity (box: b)) |
667 | return true; |
668 | else if (graphene_box_is_infinity (box: a) || graphene_box_is_infinity (box: b)) |
669 | return false; |
670 | |
671 | return graphene_vec3_equal (v1: &a->min, v2: &b->min) && |
672 | graphene_vec3_equal (v1: &a->max, v2: &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 |
687 | graphene_box_equal (const graphene_box_t *a, |
688 | const graphene_box_t *b) |
689 | { |
690 | return graphene_pointer_equal (p1: a, p2: b, func: 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 |
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: ¢er); |
711 | |
712 | graphene_box_get_size (box, size: &size); |
713 | float radius = graphene_vec3_length (v: &size) * 0.5f; |
714 | |
715 | graphene_sphere_init (s: sphere, center: ¢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 |
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 |
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 |
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 |
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 |
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 * |
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 * |
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 * |
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 * |
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 * |
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 * |
920 | graphene_box_empty (void) |
921 | { |
922 | init_static_box (); |
923 | |
924 | return &(static_box[BOX_EMPTY]); |
925 | } |
926 | |