diff --git a/src/graphene-box.c b/src/graphene-box.c index 20daea3c..2099c60f 100644 --- a/src/graphene-box.c +++ b/src/graphene-box.c @@ -43,11 +43,10 @@ #include #include +#include #ifdef HAVE_PTHREAD #include -#include -#include #include #endif @@ -425,6 +424,60 @@ graphene_box_get_depth (const graphene_box_t *box) return fabsf (res); } +static inline bool +graphene_box_is_empty (const graphene_box_t *box) +{ +#ifdef HAVE_ISINFF + float vmin[3], vmax[3]; + + graphene_simd4f_dup_3f (box->min.value, vmin); + graphene_simd4f_dup_3f (box->max.value, vmax); + + return (isinff (vmin[0]) == 1 && isinff (vmin[1]) == 1 && isinff (vmin[2]) == 1) && + (isinff (vmax[0]) == -1 && isinff (vmax[1]) == -1 && isinff (vmax[2]) == -1); +#else + graphene_simd4f_t neg_inf = graphene_simd4f_init (-INFINITY, -INFINITY, -INFINITY, 0.f); + graphene_simd4f_t pos_inf = graphene_simd4f_init (INFINITY, INFINITY, INFINITY, 0.f); + + /* This is only every going to be valid for boxes that we have + * initialized ourselves, because we use the same values; the + * bitwise comparison will not hold for infinities generated by + * other operations + */ + int min_cmp = memcmp (&box->min.value, &pos_inf, sizeof (graphene_simd4f_t)); + int max_cmp = memcmp (&box->max.value, &neg_inf, sizeof (graphene_simd4f_t)); + + return min_cmp == 0 && max_cmp == 0; +#endif +} + +static inline bool +graphene_box_is_infinity (const graphene_box_t *box) +{ +#ifdef HAVE_ISINFF + float vmin[3], vmax[3]; + + graphene_simd4f_dup_3f (box->min.value, vmin); + graphene_simd4f_dup_3f (box->max.value, vmax); + + return (isinff (vmin[0]) == -1 && isinff (vmin[1]) == -1 && isinff (vmin[2]) == -1) && + (isinff (vmax[0]) == 1 && isinff (vmax[1]) == 1 && isinff (vmax[2]) == 1); +#else + graphene_simd4f_t neg_inf = graphene_simd4f_init (-INFINITY, -INFINITY, -INFINITY, 0.f); + graphene_simd4f_t pos_inf = graphene_simd4f_init (INFINITY, INFINITY, INFINITY, 0.f); + + /* This is only every going to be valid for boxes that we have + * initialized ourselves, because we use the same values; the + * bitwise comparison will not hold for infinities generated by + * other operations + */ + int min_cmp = memcmp (&box->min.value, &neg_inf, sizeof (graphene_simd4f_t)); + int max_cmp = memcmp (&box->max.value, &pos_inf, sizeof (graphene_simd4f_t)); + + return min_cmp == 0 && max_cmp == 0; +#endif +} + /** * graphene_box_get_size: * @box: a #graphene_box_t @@ -439,7 +492,12 @@ void graphene_box_get_size (const graphene_box_t *box, graphene_vec3_t *size) { - size->value = graphene_simd4f_sub (box->max.value, box->min.value); + if (graphene_box_is_empty (box)) + size->value = graphene_simd4f_init_zero (); + else if (graphene_box_is_infinity (box)) + size->value = graphene_simd4f_init (INFINITY, INFINITY, INFINITY, 0.f); + else + size->value = graphene_simd4f_sub (box->max.value, box->min.value); } /** @@ -458,7 +516,13 @@ graphene_box_get_center (const graphene_box_t *box, { graphene_vec3_t res; - graphene_vec3_subtract (&box->max, &box->min, &res); + if (graphene_box_is_empty (box) || graphene_box_is_infinity (box)) + { + graphene_point3d_init (center, 0.f, 0.f, 0.f); + return; + } + + graphene_vec3_add (&box->min, &box->max, &res); graphene_vec3_scale (&res, 0.5f, &res); graphene_point3d_init_from_vec3 (center, &res); @@ -542,6 +606,12 @@ bool graphene_box_contains_point (const graphene_box_t *box, const graphene_point3d_t *point) { + if (graphene_box_is_empty (box)) + return false; + + if (graphene_box_is_infinity (box)) + return true; + graphene_simd4f_t p = graphene_simd4f_init (point->x, point->y, point->z, 0.f); if (graphene_simd4f_cmp_ge (p, box->min.value) && @@ -567,6 +637,12 @@ bool graphene_box_contains_box (const graphene_box_t *a, const graphene_box_t *b) { + if (graphene_box_is_empty (a) || graphene_box_is_infinity (b)) + return false; + + if (graphene_box_is_infinity (a) || graphene_box_is_empty (b)) + return true; + /* we cheat a bit and access the SIMD directly */ if (graphene_simd4f_cmp_ge (b->min.value, a->min.value) && graphene_simd4f_cmp_le (b->max.value, a->max.value)) @@ -582,6 +658,16 @@ box_equal (const void *p1, const graphene_box_t *a = p1; const graphene_box_t *b = p2; + if (graphene_box_is_empty (a) && graphene_box_is_empty (b)) + return true; + else if (graphene_box_is_empty (a) || graphene_box_is_empty (b)) + return false; + + if (graphene_box_is_infinity (a) && graphene_box_is_infinity (b)) + return true; + else if (graphene_box_is_infinity (a) || graphene_box_is_infinity (b)) + return false; + return graphene_vec3_equal (&a->min, &b->min) && graphene_vec3_equal (&a->max, &b->max); } @@ -618,12 +704,15 @@ void graphene_box_get_bounding_sphere (const graphene_box_t *box, graphene_sphere_t *sphere) { + graphene_point3d_t center; graphene_vec3_t size; - graphene_vec3_subtract (&box->max, &box->min, &size); + graphene_box_get_center (box, ¢er); + + graphene_box_get_size (box, &size); + float radius = graphene_vec3_length (&size) * 0.5f; - graphene_vec3_scale (&size, 0.5f, &sphere->center); - sphere->radius = graphene_vec3_length (&size) * 0.5f; + graphene_sphere_init (sphere, ¢er, radius); } enum { diff --git a/src/tests/box.c b/src/tests/box.c index aa10d051..fe7a9af3 100644 --- a/src/tests/box.c +++ b/src/tests/box.c @@ -192,20 +192,42 @@ box_center (mutest_spec_t *spec) { graphene_point3d_t zero = GRAPHENE_POINT3D_INIT (0.0f, 0.0f, 0.0f); graphene_point3d_t half = GRAPHENE_POINT3D_INIT (0.5f, 0.5f, 0.5f); + graphene_point3d_t minus_half = GRAPHENE_POINT3D_INIT (-0.5f, -0.5f, -0.5f); graphene_point3d_t center; graphene_box_t b; graphene_box_init_from_box (&b, graphene_box_zero ()); graphene_box_get_center (&b, ¢er); - mutest_expect ("box(zero).center() is (0, 0, 0)", + mutest_expect ("box(zero).center() to be in (0, 0, 0)", mutest_bool_value (graphene_point3d_equal (¢er, &zero)), mutest_to_be_true, NULL); graphene_box_init_from_box (&b, graphene_box_one ()); graphene_box_get_center (&b, ¢er); - mutest_expect ("box(1).center() is (0.5, 0.5, 0.5)", - mutest_bool_value (graphene_point3d_equal (¢er, &half)), + mutest_expect ("box(1).center() to be in (0.5, 0.5, 0.5)", + mutest_bool_value (graphene_point3d_near (¢er, &half, 0.0001f)), + mutest_to_be_true, + NULL); + + graphene_box_init_from_box (&b, graphene_box_one_minus_one ()); + graphene_box_get_center (&b, ¢er); + mutest_expect ("box(1, -1).center() to be in (0, 0, 0)", + mutest_bool_value (graphene_point3d_near (¢er, &zero, 0.0001f)), + mutest_to_be_true, + NULL); + + graphene_box_init_from_box (&b, graphene_box_minus_one ()); + graphene_box_get_center (&b, ¢er); + mutest_expect ("box(-1).center() to be in (-0.5, -0.5, -0.5)", + mutest_bool_value (graphene_point3d_near (¢er, &minus_half, 0.0001f)), + mutest_to_be_true, + NULL); + + graphene_box_init_from_box (&b, graphene_box_empty ()); + graphene_box_get_center (&b, ¢er); + mutest_expect ("box(empty).center() to be in (0, 0, 0)", + mutest_bool_value (graphene_point3d_equal (¢er, &zero)), mutest_to_be_true, NULL); }