From 0853272c3b5f39e3b42ea1f0e0c03cfe0b769cd5 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 10 Aug 2023 12:38:46 +0100 Subject: [PATCH 01/13] Add 2D AABB type Like graphene_box_t, but for 2D areas. It's more efficient than rectangles, at the cost of legibility. --- include/graphene-box2d.h | 122 ++++++ include/graphene-gobject.h | 7 + include/graphene-types.h | 1 + include/graphene.h | 1 + include/meson.build | 1 + src/graphene-box2d.c | 859 +++++++++++++++++++++++++++++++++++++ src/graphene-gobject.c | 2 + src/meson.build | 1 + 8 files changed, 994 insertions(+) create mode 100644 include/graphene-box2d.h create mode 100644 src/graphene-box2d.c diff --git a/include/graphene-box2d.h b/include/graphene-box2d.h new file mode 100644 index 00000000..933468eb --- /dev/null +++ b/include/graphene-box2d.h @@ -0,0 +1,122 @@ +/* graphene-box2d.h: An 2D, axis aligned bounding box + * + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2023 Emmanuele Bassi + */ + +#pragma once + +#if !defined(GRAPHENE_H_INSIDE) && !defined(GRAPHENE_COMPILATION) +#error "Only graphene.h can be included directly." +#endif + +#include "graphene-types.h" +#include "graphene-vec2.h" + +GRAPHENE_BEGIN_DECLS + +/** + * graphene_box2d_t: + * + * A 2D box, described as the axis-aligned area between a minimum and + * a maximum vertices lying on the same plane. + * + * Since: 1.12 + */ +struct _graphene_box2d_t +{ + /*< private >*/ + GRAPHENE_PRIVATE_FIELD (graphene_vec2_t, min); + GRAPHENE_PRIVATE_FIELD (graphene_vec2_t, max); +}; + +GRAPHENE_AVAILABLE_IN_1_12 +graphene_box2d_t * graphene_box2d_alloc (void); +GRAPHENE_AVAILABLE_IN_1_12 +void graphene_box2d_free (graphene_box2d_t *box); + +GRAPHENE_AVAILABLE_IN_1_12 +graphene_box2d_t * graphene_box2d_init (graphene_box2d_t *box, + const graphene_point_t *min, + const graphene_point_t *max); +GRAPHENE_AVAILABLE_IN_1_12 +graphene_box2d_t * graphene_box2d_init_from_points (graphene_box2d_t *box, + unsigned int n_points, + const graphene_point_t *points); +GRAPHENE_AVAILABLE_IN_1_12 +graphene_box2d_t * graphene_box2d_init_from_vectors (graphene_box2d_t *box, + unsigned int n_vectors, + const graphene_vec2_t *vectors); +GRAPHENE_AVAILABLE_IN_1_12 +graphene_box2d_t * graphene_box2d_init_from_box (graphene_box2d_t *box, + const graphene_box2d_t *src); +GRAPHENE_AVAILABLE_IN_1_12 +graphene_box2d_t * graphene_box2d_init_from_vec2 (graphene_box2d_t *box, + const graphene_vec2_t *min, + const graphene_vec2_t *max); + +GRAPHENE_AVAILABLE_IN_1_12 +void graphene_box2d_expand (const graphene_box2d_t *box, + const graphene_point_t *point, + graphene_box2d_t *res); +GRAPHENE_AVAILABLE_IN_1_12 +void graphene_box2d_expand_vec2 (const graphene_box2d_t *box, + const graphene_vec2_t *vec, + graphene_box2d_t *res); +GRAPHENE_AVAILABLE_IN_1_12 +void graphene_box2d_expand_scalar (const graphene_box2d_t *box, + float scalar, + graphene_box2d_t *res); +GRAPHENE_AVAILABLE_IN_1_12 +void graphene_box2d_union (const graphene_box2d_t *a, + const graphene_box2d_t *b, + graphene_box2d_t *res); +GRAPHENE_AVAILABLE_IN_1_12 +bool graphene_box2d_intersection (const graphene_box2d_t *a, + const graphene_box2d_t *b, + graphene_box2d_t *res); + +GRAPHENE_AVAILABLE_IN_1_12 +float graphene_box2d_get_width (const graphene_box2d_t *box); +GRAPHENE_AVAILABLE_IN_1_12 +float graphene_box2d_get_height (const graphene_box2d_t *box); +GRAPHENE_AVAILABLE_IN_1_12 +void graphene_box2d_get_size (const graphene_box2d_t *box, + graphene_vec2_t *size); +GRAPHENE_AVAILABLE_IN_1_12 +void graphene_box2d_get_center (const graphene_box2d_t *box, + graphene_point_t *center); +GRAPHENE_AVAILABLE_IN_1_12 +void graphene_box2d_get_min (const graphene_box2d_t *box, + graphene_point_t *min); +GRAPHENE_AVAILABLE_IN_1_12 +void graphene_box2d_get_max (const graphene_box2d_t *box, + graphene_point_t *max); +GRAPHENE_AVAILABLE_IN_1_12 +void graphene_box2d_get_vertices (const graphene_box2d_t *box, + graphene_vec2_t vertices[]); +GRAPHENE_AVAILABLE_IN_1_12 +bool graphene_box2d_contains_point (const graphene_box2d_t *box, + const graphene_point_t *point); +GRAPHENE_AVAILABLE_IN_1_12 +bool graphene_box2d_contains_box (const graphene_box2d_t *a, + const graphene_box2d_t *b); + +GRAPHENE_AVAILABLE_IN_1_12 +bool graphene_box2d_equal (const graphene_box2d_t *a, + const graphene_box2d_t *b); + +GRAPHENE_AVAILABLE_IN_1_12 +const graphene_box2d_t * graphene_box2d_zero (void); +GRAPHENE_AVAILABLE_IN_1_12 +const graphene_box2d_t * graphene_box2d_one (void); +GRAPHENE_AVAILABLE_IN_1_12 +const graphene_box2d_t * graphene_box2d_minus_one (void); +GRAPHENE_AVAILABLE_IN_1_12 +const graphene_box2d_t * graphene_box2d_one_minus_one (void); +GRAPHENE_AVAILABLE_IN_1_12 +const graphene_box2d_t * graphene_box2d_infinite (void); +GRAPHENE_AVAILABLE_IN_1_12 +const graphene_box2d_t * graphene_box2d_empty (void); + +GRAPHENE_END_DECLS diff --git a/include/graphene-gobject.h b/include/graphene-gobject.h index 85a82c89..794eaa35 100644 --- a/include/graphene-gobject.h +++ b/include/graphene-gobject.h @@ -149,4 +149,11 @@ GType graphene_ray_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC(graphene_ray_t, graphene_ray_free) +#define GRAPHENE_TYPE_BOX (graphene_box_get_type ()) + +GRAPHENE_AVAILABLE_IN_1_12 +GType graphene_box2d_get_type (void); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(graphene_box2d_t, graphene_box2d_free) + G_END_DECLS diff --git a/include/graphene-types.h b/include/graphene-types.h index 0d3d9b0f..d6dc7c56 100644 --- a/include/graphene-types.h +++ b/include/graphene-types.h @@ -113,6 +113,7 @@ typedef struct _graphene_matrix_t graphene_matrix_t; typedef struct _graphene_point_t graphene_point_t; typedef struct _graphene_size_t graphene_size_t; typedef struct _graphene_rect_t graphene_rect_t; +typedef struct _graphene_box2d_t graphene_box2d_t; typedef struct _graphene_point3d_t graphene_point3d_t; typedef struct _graphene_quad_t graphene_quad_t; diff --git a/include/graphene.h b/include/graphene.h index efbfc3f7..ced073d9 100644 --- a/include/graphene.h +++ b/include/graphene.h @@ -46,6 +46,7 @@ #include "graphene-point.h" #include "graphene-size.h" #include "graphene-rect.h" +#include "graphene-box2d.h" #include "graphene-point3d.h" #include "graphene-quad.h" diff --git a/include/meson.build b/include/meson.build index 5c5eee5d..8f6cd10c 100644 --- a/include/meson.build +++ b/include/meson.build @@ -1,5 +1,6 @@ graphene_public_headers = files([ 'graphene-box.h', + 'graphene-box2d.h', 'graphene-euler.h', 'graphene-frustum.h', 'graphene-macros.h', diff --git a/src/graphene-box2d.c b/src/graphene-box2d.c new file mode 100644 index 00000000..fc7fdad6 --- /dev/null +++ b/src/graphene-box2d.c @@ -0,0 +1,859 @@ +/* graphene-box2d.c: An axis aligned 2D bounding box + * + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2023 Emmanuele Bassi + */ + +/** + * SECTION:graphene-box2d + * @Title: Box 2D + * @Short_Description: Axis-aligned bounding box + * + * #graphene_box2d_t provides a representation of an axis aligned minimum + * bounding box in two dimensions using the coordinates of its minimum and + * maximum vertices. + */ + +#include "graphene-private.h" + +#include "graphene-box2d.h" + +#include "graphene-alloc-private.h" +#include "graphene-point.h" +#include "graphene-simd4f.h" + +#include +#include +#include + +#ifdef HAVE_PTHREAD +#include +#include +#endif + +/** + * graphene_box2d_alloc: (constructor) + * + * Allocates a new #graphene_box2d_t. + * + * The contents of the returned structure are undefined. + * + * Returns: (transfer full): the newly allocated #graphene_box2d_t structure. + * Use graphene_box2d_free() to free the resources allocated by this function + * + * Since: 1.12 + */ +graphene_box2d_t * +graphene_box2d_alloc (void) +{ + return graphene_aligned_alloc0 (sizeof (graphene_box2d_t), 1, 16); +} + +/** + * graphene_box2d_free: + * @box: a #graphene_box2d_t + * + * Frees the resources allocated by graphene_box2d_alloc(). + * + * Since: 1.12 + */ +void +graphene_box2d_free (graphene_box2d_t *box) +{ + graphene_aligned_free (box); +} + +/** + * graphene_box2d_init: + * @box: the #graphene_box2d_t to initialize + * @min: (nullable): the coordinates of the minimum vertex + * @max: (nullable): the coordinates of the maximum vertex + * + * Initializes the given #graphene_box2d_t with two vertices. + * + * Returns: (transfer none): the initialized #graphene_box2d_t + * + * Since: 1.12 + */ +graphene_box2d_t * +graphene_box2d_init (graphene_box2d_t *box, + const graphene_point_t *min, + const graphene_point_t *max) +{ + if (min != NULL) + graphene_point_to_vec2 (min, &box->min); + else + graphene_vec2_init_from_vec2 (&box->min, graphene_vec2_zero ()); + + if (max != NULL) + graphene_point_to_vec2 (max, &box->max); + else + graphene_vec2_init_from_vec2 (&box->max, graphene_vec2_zero ()); + + return box; +} + +/** + * graphene_box2d_init_from_points: + * @box: the #graphene_box2d_t to initialize + * @n_points: the number #graphene_point_t in the @points array + * @points: (array length=n_points): an array of #graphene_point_t + * + * Initializes the given #graphene_box2d_t with the given array + * of vertices. + * + * If @n_points is 0, the returned box is initialized with + * graphene_box2d_empty(). + * + * Returns: (transfer none): the initialized #graphene_box2d_t + * + * Since: 1.12 + */ +graphene_box2d_t * +graphene_box2d_init_from_points (graphene_box2d_t *box, + unsigned int n_points, + const graphene_point_t *points) +{ + graphene_box2d_init_from_box (box, graphene_box2d_empty ()); + + for (unsigned int i = 0; i < n_points; i++) + { + graphene_vec2_t v; + + graphene_point_to_vec2 (&points[i], &v); + graphene_box2d_expand_vec2 (box, &v, box); + } + + return box; +} + +/** + * graphene_box2d_init_from_vectors: + * @box: the #graphene_box2d_t to initialize + * @n_vectors: the number #graphene_vec2_t in the @vectors array + * @vectors: (array length=n_vectors): an array of #graphene_vec2_t + * + * Initializes the given #graphene_box2d_t with the given array + * of vertices. + * + * If @n_vectors is 0, the returned box is initialized with + * graphene_box2d_empty(). + * + * Returns: (transfer none): the initialized #graphene_box2d_t + * + * Since: 1.12 + */ +graphene_box2d_t * +graphene_box2d_init_from_vectors (graphene_box2d_t *box, + unsigned int n_vectors, + const graphene_vec2_t *vectors) +{ + graphene_box2d_init_from_box (box, graphene_box2d_empty ()); + + for (unsigned int i = 0; i < n_vectors; i++) + graphene_box2d_expand_vec2 (box, &vectors[i], box); + + return box; +} + +/** + * graphene_box2d_init_from_box: + * @box: the #graphene_box2d_t to initialize + * @src: a #graphene_box2d_t + * + * Initializes the given #graphene_box2d_t with the vertices of + * another #graphene_box2d_t. + * + * Returns: (transfer none): the initialized #graphene_box2d_t + * + * Since: 1.12 + */ +graphene_box2d_t * +graphene_box2d_init_from_box (graphene_box2d_t *box, + const graphene_box2d_t *src) +{ + box->min = src->min; + box->max = src->max; + + return box; +} + +/** + * graphene_box2d_init_from_vec3: + * @box: the #graphene_box2d_t to initialize + * @min: (nullable): the coordinates of the minimum vertex + * @max: (nullable): the coordinates of the maximum vertex + * + * Initializes the given #graphene_box2d_t with two vertices + * stored inside #graphene_vec2_t. + * + * Returns: (transfer none): the initialized #graphene_box2d_t + * + * Since: 1.12 + */ +graphene_box2d_t * +graphene_box2d_init_from_vec2 (graphene_box2d_t *box, + const graphene_vec2_t *min, + const graphene_vec2_t *max) +{ + if (min != NULL) + box->min = *min; + else + graphene_vec2_init_from_vec2 (&box->min, graphene_vec2_zero ()); + + if (max != NULL) + box->max = *max; + else + graphene_vec2_init_from_vec2 (&box->max, graphene_vec2_zero ()); + + return box; +} + +static inline graphene_box2d_t * +graphene_box2d_init_from_simd4f (graphene_box2d_t *box, + const graphene_simd4f_t min, + const graphene_simd4f_t max) +{ + box->min.value = min; + box->max.value = max; + + return box; +} + +static inline void +graphene_box2d_expand_simd4f (const graphene_box2d_t *box, + const graphene_simd4f_t v, + graphene_box2d_t *res) +{ + res->min.value = graphene_simd4f_min (box->min.value, v); + res->max.value = graphene_simd4f_max (box->max.value, v); +} + +/** + * graphene_box2d_expand_vec3: + * @box: a #graphene_box2d_t + * @vec: the coordinates of the point to include, as a #graphene_vec2_t + * @res: (out caller-allocates): return location for the expanded box + * + * Expands the dimensions of @box to include the coordinates of the + * given vector. + * + * Since: 1.12 + */ +void +graphene_box2d_expand_vec2 (const graphene_box2d_t *box, + const graphene_vec2_t *vec, + graphene_box2d_t *res) +{ + graphene_box2d_expand_simd4f (box, vec->value, res); +} + +/** + * graphene_box2d_expand: + * @box: a #graphene_box2d_t + * @point: the coordinates of the point to include + * @res: (out caller-allocates): return location for the expanded box + * + * Expands the dimensions of @box to include the coordinates at @point. + * + * Since: 1.12 + */ +void +graphene_box2d_expand (const graphene_box2d_t *box, + const graphene_point_t *point, + graphene_box2d_t *res) +{ + graphene_simd4f_t v = graphene_simd4f_init (point->x, point->y, 0.f, 0.f); + + graphene_box2d_expand_simd4f (box, v, res); +} + +/** + * graphene_box2d_expand_scalar: + * @box: a #graphene_box2d_t + * @scalar: a scalar value + * @res: (out caller-allocates): return location for the expanded box + * + * Expands the dimensions of @box by the given @scalar value. + * + * If @scalar is positive, the #graphene_box2d_t will grow; if @scalar is + * negative, the #graphene_box2d_t will shrink. + * + * Since: 1.12 + */ +void +graphene_box2d_expand_scalar (const graphene_box2d_t *box, + float scalar, + graphene_box2d_t *res) +{ + float min = scalar * -1.f; + float max = scalar; + + res->min.value = graphene_simd4f_add (box->min.value, graphene_simd4f_splat (min)); + res->max.value = graphene_simd4f_add (box->max.value, graphene_simd4f_splat (max)); +} + +/** + * graphene_box2d_union: + * @a: a #graphene_box2d_t + * @b: the box to union to @a + * @res: (out caller-allocates): return location for the result + * + * Unions the two given #graphene_box2d_t. + * + * Since: 1.12 + */ +void +graphene_box2d_union (const graphene_box2d_t *a, + const graphene_box2d_t *b, + graphene_box2d_t *res) +{ + res->min.value = graphene_simd4f_min (a->min.value, b->min.value); + res->max.value = graphene_simd4f_max (a->max.value, b->max.value); +} + +/** + * graphene_box2d_intersection: + * @a: a #graphene_box2d_t + * @b: a #graphene_box2d_t + * @res: (out caller-allocates) (optional): return location for the result + * + * Intersects the two given #graphene_box2d_t. + * + * If the two boxes do not intersect, @res will contain a degenerate box + * initialized with graphene_box2d_empty(). + * + * Returns: true if the two boxes intersect + * + * Since: 1.12 + */ +bool +graphene_box2d_intersection (const graphene_box2d_t *a, + const graphene_box2d_t *b, + graphene_box2d_t *res) +{ + graphene_simd4f_t min, max; + + min = graphene_simd4f_max (a->min.value, b->min.value); + max = graphene_simd4f_min (a->max.value, b->max.value); + + if (!graphene_simd4f_cmp_le (min, max)) + { + if (res != NULL) + graphene_box2d_init_from_box (res, graphene_box2d_empty ()); + + return false; + } + + if (res != NULL) + graphene_box2d_init_from_simd4f (res, min, max); + + return true; +} + +/** + * graphene_box2d_get_width: + * @box: a #graphene_box2d_t + * + * Retrieves the size of the @box on the X axis. + * + * Returns: the width of the box + * + * Since: 1.12 + */ +float +graphene_box2d_get_width (const graphene_box2d_t *box) +{ + float res = graphene_simd4f_get_x (graphene_simd4f_sub (box->max.value, box->min.value)); + + return fabsf (res); +} + +/** + * graphene_box2d_get_height: + * @box: a #graphene_box2d_t + * + * Retrieves the size of the @box on the Y axis. + * + * Returns: the height of the box + * + * Since: 1.12 + */ +float +graphene_box2d_get_height (const graphene_box2d_t *box) +{ + float res = graphene_simd4f_get_y (graphene_simd4f_sub (box->max.value, box->min.value)); + + return fabsf (res); +} + +static inline bool +graphene_box2d_is_empty (const graphene_box2d_t *box) +{ +#ifdef HAVE_ISINFF + float vmin[2], vmax[2]; + + graphene_simd4f_dup_2f (box->min.value, vmin); + graphene_simd4f_dup_2f (box->max.value, vmax); + + return (isinff (vmin[0]) == 1 && isinff (vmin[1]) == 1) && + (isinff (vmax[0]) == -1 && isinff (vmax[1]) == -1); +#else + graphene_simd4f_t neg_inf = graphene_simd4f_init (-INFINITY, -INFINITY, 0.f, 0.f); + graphene_simd4f_t pos_inf = graphene_simd4f_init (INFINITY, INFINITY, 0.f, 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_box2d_is_infinity (const graphene_box2d_t *box) +{ +#ifdef HAVE_ISINFF + float vmin[2], vmax[2]; + + graphene_simd4f_dup_2f (box->min.value, vmin); + graphene_simd4f_dup_2f (box->max.value, vmax); + + return (isinff (vmin[0]) == -1 && isinff (vmin[1]) == -1) && + (isinff (vmax[0]) == 1 && isinff (vmax[1]) == 1); +#else + graphene_simd4f_t neg_inf = graphene_simd4f_init (-INFINITY, -INFINITY, 0.f, 0.f); + graphene_simd4f_t pos_inf = graphene_simd4f_init (INFINITY, INFINITY, 0.f, 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_box2d_get_size: + * @box: a #graphene_box2d_t + * @size: (out caller-allocates): return location for the size + * + * Retrieves the size of the box on all three axes, and stores + * it into the given @size vector. + * + * Since: 1.12 + */ +void +graphene_box2d_get_size (const graphene_box2d_t *box, + graphene_vec2_t *size) +{ + if (graphene_box2d_is_empty (box)) + size->value = graphene_simd4f_init_zero (); + else if (graphene_box2d_is_infinity (box)) + size->value = graphene_simd4f_init (INFINITY, INFINITY, 0.f, 0.f); + else + size->value = graphene_simd4f_sub (box->max.value, box->min.value); +} + +/** + * graphene_box2d_get_center: + * @box: a #graphene_box2d_t + * @center: (out caller-allocates): return location for the coordinates of + * the center + * + * Retrieves the coordinates of the center of a #graphene_box2d_t. + * + * Since: 1.12 + */ +void +graphene_box2d_get_center (const graphene_box2d_t *box, + graphene_point_t *center) +{ + graphene_vec2_t res; + + if (graphene_box2d_is_empty (box) || graphene_box2d_is_infinity (box)) + { + graphene_point_init (center, 0.f, 0.f); + return; + } + + graphene_vec2_add (&box->min, &box->max, &res); + graphene_vec2_scale (&res, 0.5f, &res); + + graphene_point_init_from_vec2 (center, &res); +} + +/** + * graphene_box2d_get_min: + * @box: a #graphene_box2d_t + * @min: (out caller-allocates): return location for the minimum point + * + * Retrieves the coordinates of the minimum point of the given + * #graphene_box2d_t. + * + * Since: 1.12 + */ +void +graphene_box2d_get_min (const graphene_box2d_t *box, + graphene_point_t *min) +{ + graphene_point_init_from_vec2 (min, &box->min); +} + +/** + * graphene_box2d_get_max: + * @box: a #graphene_box2d_t + * @max: (out caller-allocates): return location for the maximum point + * + * Retrieves the coordinates of the maximum point of the given + * #graphene_box2d_t. + * + * Since: 1.12 + */ +void +graphene_box2d_get_max (const graphene_box2d_t *box, + graphene_point_t *max) +{ + graphene_point_init_from_vec2 (max, &box->max); +} + +/** + * graphene_box2d_get_vertices: + * @box: a #graphene_box2d_t + * @vertices: (out) (array fixed-size=4): return location for an array + * of 4 #graphene_vec2_t + * + * Computes the vertices of the given #graphene_box2d_t. + * + * Since: 1.12 + */ +void +graphene_box2d_get_vertices (const graphene_box2d_t *box, + graphene_vec2_t vertices[]) +{ + graphene_point_t min, max; + + graphene_box2d_get_min (box, &min); + graphene_box2d_get_max (box, &max); + + graphene_vec2_init (&vertices[0], min.x, min.y); + graphene_vec2_init (&vertices[1], min.x, max.y); + graphene_vec2_init (&vertices[2], max.x, min.y); + graphene_vec2_init (&vertices[3], max.x, max.y); +} + +/** + * graphene_box2d_contains_point: + * @box: a #graphene_box2d_t + * @point: the coordinates to check + * + * Checks whether @box contains the given @point. + * + * Returns: `true` if the point is contained in the given box + * + * Since: 1.12 + */ +bool +graphene_box2d_contains_point (const graphene_box2d_t *box, + const graphene_point_t *point) +{ + if (graphene_box2d_is_empty (box)) + return false; + + if (graphene_box2d_is_infinity (box)) + return true; + + graphene_simd4f_t p = graphene_simd4f_init (point->x, point->y, 0.f, 0.f); + + if (graphene_simd4f_cmp_ge (p, box->min.value) && + graphene_simd4f_cmp_le (p, box->max.value)) + return true; + + return false; +} + +/** + * graphene_box2d_contains_box: + * @a: a #graphene_box2d_t + * @b: a #graphene_box2d_t + * + * Checks whether the #graphene_box2d_t @a contains the given + * #graphene_box2d_t @b. + * + * Returns: `true` if the box is contained in the given box + * + * Since: 1.12 + */ +bool +graphene_box2d_contains_box (const graphene_box2d_t *a, + const graphene_box2d_t *b) +{ + if (graphene_box2d_is_empty (a) || graphene_box2d_is_infinity (b)) + return false; + + if (graphene_box2d_is_infinity (a) || graphene_box2d_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)) + return true; + + return false; +} + +static bool +box_equal (const void *p1, + const void *p2) +{ + const graphene_box2d_t *a = p1; + const graphene_box2d_t *b = p2; + + if (graphene_box2d_is_empty (a) && graphene_box2d_is_empty (b)) + return true; + else if (graphene_box2d_is_empty (a) || graphene_box2d_is_empty (b)) + return false; + + if (graphene_box2d_is_infinity (a) && graphene_box2d_is_infinity (b)) + return true; + else if (graphene_box2d_is_infinity (a) || graphene_box2d_is_infinity (b)) + return false; + + return graphene_vec2_equal (&a->min, &b->min) && + graphene_vec2_equal (&a->max, &b->max); +} + +/** + * graphene_box2d_equal: + * @a: a #graphene_box2d_t + * @b: a #graphene_box2d_t + * + * Checks whether the two given boxes are equal. + * + * Returns: `true` if the boxes are equal + * + * Since: 1.12 + */ +bool +graphene_box2d_equal (const graphene_box2d_t *a, + const graphene_box2d_t *b) +{ + return graphene_pointer_equal (a, b, box_equal); +} + +enum { + BOX_ZERO = 0, + BOX_ONE, + BOX_MINUS_ONE, + BOX_ONE_MINUS_ONE, + BOX_INFINITY, + BOX_EMPTY, + + N_STATIC_BOX +}; + +static graphene_box2d_t static_box[N_STATIC_BOX]; + +static void +init_static_box_once (void) +{ + static_box[BOX_ZERO].min.value = graphene_simd4f_init_zero (); + static_box[BOX_ZERO].max.value = graphene_simd4f_init_zero (); + + static_box[BOX_ONE].min.value = graphene_simd4f_init_zero (); + static_box[BOX_ONE].max.value = graphene_simd4f_init (1.f, 1.f, 0.f, 0.f); + + static_box[BOX_MINUS_ONE].min.value = graphene_simd4f_init (-1.f, -1.f, 0.f, 0.f); + static_box[BOX_MINUS_ONE].max.value = graphene_simd4f_init_zero (); + + static_box[BOX_ONE_MINUS_ONE].min.value = graphene_simd4f_init (-1.f, -1.f, 0.f, 0.f); + static_box[BOX_ONE_MINUS_ONE].max.value = graphene_simd4f_init (1.f, 1.f, 0.f, 0.f); + + static_box[BOX_INFINITY].min.value = graphene_simd4f_init (-INFINITY, -INFINITY, 0.f, 0.f); + static_box[BOX_INFINITY].max.value = graphene_simd4f_init (INFINITY, INFINITY, 0.f, 0.f); + + static_box[BOX_EMPTY].min.value = graphene_simd4f_init (INFINITY, INFINITY, 0.f, 0.f); + static_box[BOX_EMPTY].max.value = graphene_simd4f_init (-INFINITY, -INFINITY, 0.f, 0.f); +} + +#ifdef HAVE_PTHREAD +static pthread_once_t static_box_once = PTHREAD_ONCE_INIT; + +static inline void +init_static_box (void) +{ + int status = pthread_once (&static_box_once, init_static_box_once); + + if (status < 0) + { + int saved_errno = errno; + + fprintf (stderr, "pthread_once failed: %s (errno:%d)\n", + strerror (saved_errno), + saved_errno); + } +} + +#elif defined(HAVE_INIT_ONCE) +static INIT_ONCE static_box_once = INIT_ONCE_STATIC_INIT; + +static BOOL CALLBACK +InitBoxFunc (PINIT_ONCE InitOnce, + PVOID param, + PVOID *ctx) +{ + init_static_box_once (); + return TRUE; +} + +static inline void +init_static_box (void) +{ + BOOL bStatus = InitOnceExecuteOnce (&static_box_once, + InitBoxFunc, + NULL, + NULL); + + if (!bStatus) + fprintf (stderr, "InitOnceExecuteOnce failed\n"); +} + +#else /* !HAVE_PTHREAD */ +static bool static_box_init = false; + +static inline void +init_static_box (void) +{ + if (static_box_init) + return; + + init_static_box_once (); + static_box_init = true; +} + +#endif /* HAVE_PTHREAD */ + +/** + * graphene_box2d_zero: + * + * A #graphene_box2d_t with both the minimum and maximum vertices set at (0, 0). + * + * The returned value is owned by Graphene and should not be modified or freed. + * + * Returns: (transfer none): a #graphene_box2d_t + * + * Since: 1.12 + */ +const graphene_box2d_t * +graphene_box2d_zero (void) +{ + init_static_box (); + + return &(static_box[BOX_ZERO]); +} + +/** + * graphene_box2d_one: + * + * A #graphene_box2d_t with the minimum vertex set at (0, 0) and the + * maximum vertex set at (1, 1). + * + * The returned value is owned by Graphene and should not be modified or freed. + * + * Returns: (transfer none): a #graphene_box2d_t + * + * Since: 1.12 + */ +const graphene_box2d_t * +graphene_box2d_one (void) +{ + init_static_box (); + + return &(static_box[BOX_ONE]); +} + +/** + * graphene_box2d_minus_one: + * + * A #graphene_box2d_t with the minimum vertex set at (-1, -1) and the + * maximum vertex set at (0, 0). + * + * The returned value is owned by Graphene and should not be modified or freed. + * + * Returns: (transfer none): a #graphene_box2d_t + * + * Since: 1.12 + */ +const graphene_box2d_t * +graphene_box2d_minus_one (void) +{ + init_static_box (); + + return &(static_box[BOX_MINUS_ONE]); +} + +/** + * graphene_box2d_one_minus_one: + * + * A #graphene_box2d_t with the minimum vertex set at (-1, -1) and the + * maximum vertex set at (1, 1). + * + * The returned value is owned by Graphene and should not be modified or freed. + * + * Returns: (transfer none): a #graphene_box2d_t + * + * Since: 1.12 + */ +const graphene_box2d_t * +graphene_box2d_one_minus_one (void) +{ + init_static_box (); + + return &(static_box[BOX_ONE_MINUS_ONE]); +} + +/** + * graphene_box2d_infinite: + * + * A degenerate #graphene_box2d_t that cannot be expanded. + * + * The returned value is owned by Graphene and should not be modified or freed. + * + * Returns: (transfer none): a #graphene_box2d_t + * + * Since: 1.12 + */ +const graphene_box2d_t * +graphene_box2d_infinite (void) +{ + init_static_box (); + + return &(static_box[BOX_INFINITY]); +} + +/** + * graphene_box2d_empty: + * + * A degenerate #graphene_box2d_t that can only be expanded. + * + * The returned value is owned by Graphene and should not be modified or freed. + * + * Returns: (transfer none): a #graphene_box2d_t + * + * Since: 1.12 + */ +const graphene_box2d_t * +graphene_box2d_empty (void) +{ + init_static_box (); + + return &(static_box[BOX_EMPTY]); +} diff --git a/src/graphene-gobject.c b/src/graphene-gobject.c index c3ca41f3..38162373 100644 --- a/src/graphene-gobject.c +++ b/src/graphene-gobject.c @@ -185,3 +185,5 @@ GRAPHENE_DEFINE_BOXED_TYPE (GrapheneTriangle, graphene_triangle) GRAPHENE_DEFINE_BOXED_TYPE (GrapheneEuler, graphene_euler) GRAPHENE_DEFINE_BOXED_TYPE (GrapheneRay, graphene_ray) + +GRAPHENE_DEFINE_BOXED_TYPE (GrapheneBox2D, graphene_box2d) diff --git a/src/meson.build b/src/meson.build index 8303fc80..60613433 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,6 +1,7 @@ sources = [ 'graphene-alloc.c', 'graphene-box.c', + 'graphene-box2d.c', 'graphene-euler.c', 'graphene-frustum.c', 'graphene-matrix.c', From 190b5a1bcea9a1f0824eda9517cbc89d84de27e3 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 10 Aug 2023 12:39:49 +0100 Subject: [PATCH 02/13] Add docs for graphene_box2d_t --- doc/graphene-docs.xml | 1 + doc/graphene-sections.txt | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/doc/graphene-docs.xml b/doc/graphene-docs.xml index c74b5093..d3d2a780 100644 --- a/doc/graphene-docs.xml +++ b/doc/graphene-docs.xml @@ -35,6 +35,7 @@ + diff --git a/doc/graphene-sections.txt b/doc/graphene-sections.txt index 253f6c05..3e51708d 100644 --- a/doc/graphene-sections.txt +++ b/doc/graphene-sections.txt @@ -35,6 +35,40 @@ graphene_box_empty graphene_box_infinite +
+graphene-box2d +graphene_box2d_t +graphene_box2d_alloc +graphene_box2d_free +graphene_box2d_init +graphene_box2d_init_from_box +graphene_box2d_init_from_points +graphene_box2d_init_from_vec2 +graphene_box2d_init_from_vectors +graphene_box2d_equal +graphene_box2d_expand +graphene_box2d_expand_scalar +graphene_box2d_expand_vec2 +graphene_box2d_get_min +graphene_box2d_get_max +graphene_box2d_get_center +graphene_box2d_get_height +graphene_box2d_get_width +graphene_box2d_get_size +graphene_box2d_get_vertices +graphene_box2d_union +graphene_box2d_intersection +graphene_box2d_contains_box +graphene_box2d_contains_point + +graphene_box2d_zero +graphene_box2d_one +graphene_box2d_minus_one +graphene_box2d_one_minus_one +graphene_box2d_empty +graphene_box2d_infinite +
+
graphene-euler graphene_euler_t From 7cd4cdca40a5f8fe1a42a564008834ed5df67b48 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Thu, 10 Aug 2023 13:51:41 +0100 Subject: [PATCH 03/13] tests: Add spec for graphene_box2d_t --- tests/box2d.c | 521 ++++++++++++++++++++++++++++++++++++++++++++++ tests/meson.build | 1 + 2 files changed, 522 insertions(+) create mode 100644 tests/box2d.c diff --git a/tests/box2d.c b/tests/box2d.c new file mode 100644 index 00000000..a3c967b5 --- /dev/null +++ b/tests/box2d.c @@ -0,0 +1,521 @@ +// SPDX-FileCopyrightText: 2014 Emmanuele Bassi +// SPDX-FileCopyrightText: 2020 Daniel van Vugt +// +// SPDX-License-Identifier: MIT + +#include +#include +#include + +static void +box2d_init_min_max (mutest_spec_t *spec) +{ + graphene_point_t points[] = { + GRAPHENE_POINT_INIT (0.f, 0.f), + GRAPHENE_POINT_INIT (1.f, 1.f) + }; + graphene_point_t zero, one; + graphene_point_t min, max; + graphene_box2d_t *b; + + graphene_point_init (&zero, 0.f, 0.f); + graphene_point_init (&one, 1.f, 1.f); + + b = graphene_box2d_init (graphene_box2d_alloc (), &points[0], &points[1]); + mutest_expect ("init() does not return null", + mutest_pointer (b), + mutest_not, mutest_to_be_null, + NULL); + + graphene_box2d_get_min (b, &min); + mutest_expect ("get_min() to be zero", + mutest_bool_value (graphene_point_equal (&min, &points[0])), + mutest_to_be_true, + NULL); + graphene_box2d_get_max (b, &max); + mutest_expect ("get_max() to be one", + mutest_bool_value (graphene_point_equal (&max, &points[1])), + mutest_to_be_true, + NULL); + + graphene_box2d_init (b, NULL, NULL); + mutest_expect ("init(null, null) to be zero-sized", + mutest_bool_value (graphene_box2d_equal (b, graphene_box2d_zero ())), + mutest_to_be_true, + NULL); + + graphene_box2d_init_from_vec2 (b, graphene_vec2_zero (), graphene_vec2_one ()); + graphene_box2d_get_min (b, &min); + graphene_box2d_get_max (b, &max); + mutest_expect ("init_from_vec2(zero, one).min() maps to point(zero)", + mutest_bool_value (graphene_point_equal (&min, &zero)), + mutest_to_be_true, + NULL); + mutest_expect ("init_from_vec2(zero, one).max() maps to point(one)", + mutest_bool_value (graphene_point_equal (&max, &one)), + mutest_to_be_true, + NULL); + + graphene_box2d_init_from_vec2 (b, NULL, NULL); + mutest_expect ("init_from_vec2(null, null) is equal to box2d(zero)", + mutest_bool_value (graphene_box2d_equal (b, graphene_box2d_zero ())), + mutest_to_be_true, + NULL); + + graphene_box2d_free (b); +} + +static void +box2d_init_from_points (mutest_spec_t *spec) +{ + graphene_point_t points[] = { + GRAPHENE_POINT_INIT (0.f, 0.f), + GRAPHENE_POINT_INIT (1.f, 1.f), + GRAPHENE_POINT_INIT (2.f, 2.f), + }; + graphene_point_t min, max; + graphene_box2d_t *b; + + b = graphene_box2d_init_from_points (graphene_box2d_alloc (), 3, points); + mutest_expect ("init_from_points() does not return null", + mutest_pointer (b), + mutest_not, mutest_to_be_null, + NULL); + + graphene_box2d_get_min (b, &min); + graphene_box2d_get_max (b, &max); + mutest_expect ("init_from_points().min() is (0, 0)", + mutest_bool_value (graphene_point_equal (&min, &points[0])), + mutest_to_be_true, + NULL); + mutest_expect ("init_from_points().max() is (2, 2)", + mutest_bool_value (graphene_point_equal (&max, &points[2])), + mutest_to_be_true, + NULL); + + graphene_box2d_init_from_points (b, 1, points + 1); + graphene_box2d_get_min (b, &min); + graphene_box2d_get_max (b, &max); + mutest_expect ("init_from_points() with one point sets min() to the same point", + mutest_bool_value (graphene_point_equal (&min, &points[1])), + mutest_to_be_true, + NULL); + mutest_expect ("init_from_points() with one point sets max() to the same point", + mutest_bool_value (graphene_point_equal (&max, &points[1])), + mutest_to_be_true, + NULL); + + graphene_box2d_init_from_points (b, 0, NULL); + mutest_expect ("init_from_points() with no points is an empty box", + mutest_bool_value (graphene_box2d_equal (b, graphene_box2d_empty ())), + mutest_to_be_true, + NULL); + + graphene_box2d_free (b); +} + +static void +box2d_init_from_vectors (mutest_spec_t *spec) +{ + graphene_point_t points[] = { + GRAPHENE_POINT_INIT (0.f, 0.f), + GRAPHENE_POINT_INIT (1.f, 1.f), + GRAPHENE_POINT_INIT (2.f, 2.f), + }; + graphene_vec2_t *vectors; + graphene_point_t min, max; + graphene_box2d_t *b; + + vectors = malloc (sizeof (graphene_vec2_t) * 3); + graphene_point_to_vec2 (&points[0], &vectors[0]); + graphene_point_to_vec2 (&points[1], &vectors[1]); + graphene_point_to_vec2 (&points[2], &vectors[2]); + + b = graphene_box2d_init_from_vectors (graphene_box2d_alloc (), 3, vectors); + graphene_box2d_get_min (b, &min); + graphene_box2d_get_max (b, &max); + mutest_expect ("init_from_vectors().min() is (0, 0, 0)", + mutest_bool_value (graphene_point_equal (&min, &points[0])), + mutest_to_be_true, + NULL); + mutest_expect ("init_from_vectors().max() is (2, 2, 2)", + mutest_bool_value (graphene_point_equal (&max, &points[2])), + mutest_to_be_true, + NULL); + + graphene_box2d_init_from_vectors (b, 1, vectors + 1); + graphene_box2d_get_min (b, &min); + graphene_box2d_get_max (b, &max); + mutest_expect ("init_from_vectors() with one point sets min() to the same point", + mutest_bool_value (graphene_point_equal (&min, &points[1])), + mutest_to_be_true, + NULL); + mutest_expect ("init_from_vectors() with one point sets max() to the same point", + mutest_bool_value (graphene_point_equal (&max, &points[1])), + mutest_to_be_true, + NULL); + + graphene_box2d_init_from_vectors (b, 0, NULL); + mutest_expect ("init_from_vectors() with no points is an empty box", + mutest_bool_value (graphene_box2d_equal (b, graphene_box2d_empty ())), + mutest_to_be_true, + NULL); + + graphene_box2d_free (b); + free (vectors); +} + +static void +box2d_size (mutest_spec_t *spec) +{ + graphene_vec2_t size; + + graphene_box2d_get_size (graphene_box2d_zero (), &size); + mutest_expect ("box2d(zero) to have a size of zero", + mutest_bool_value (graphene_vec2_equal (&size, graphene_vec2_zero ())), + mutest_to_be_true, + NULL); + + mutest_expect ("box2d(zero) to have a width of zero", + mutest_float_value (graphene_box2d_get_width (graphene_box2d_zero ())), + mutest_to_be_close_to, 0.0, 0.00001, + NULL); + mutest_expect ("box2d(1) to have a width of 1", + mutest_float_value (graphene_box2d_get_height (graphene_box2d_one ())), + mutest_to_be_close_to, 1.0, 0.00001, + NULL); +} + +static void +box2d_center (mutest_spec_t *spec) +{ + graphene_point_t zero = GRAPHENE_POINT_INIT (0.0f, 0.0f); + graphene_point_t half = GRAPHENE_POINT_INIT (0.5f, 0.5f); + graphene_point_t minus_half = GRAPHENE_POINT_INIT (-0.5f, -0.5f); + graphene_point_t center; + graphene_box2d_t b; + + graphene_box2d_init_from_box (&b, graphene_box2d_zero ()); + graphene_box2d_get_center (&b, ¢er); + mutest_expect ("box2d(zero).center() to be in (0, 0)", + mutest_bool_value (graphene_point_equal (¢er, &zero)), + mutest_to_be_true, + NULL); + + graphene_box2d_init_from_box (&b, graphene_box2d_one ()); + graphene_box2d_get_center (&b, ¢er); + mutest_expect ("box2d(1).center() to be in (0.5, 0.5)", + mutest_bool_value (graphene_point_near (¢er, &half, 0.0001f)), + mutest_to_be_true, + NULL); + + graphene_box2d_init_from_box (&b, graphene_box2d_one_minus_one ()); + graphene_box2d_get_center (&b, ¢er); + mutest_expect ("box2d(1, -1).center() to be in (0, 0)", + mutest_bool_value (graphene_point_near (¢er, &zero, 0.0001f)), + mutest_to_be_true, + NULL); + + graphene_box2d_init_from_box (&b, graphene_box2d_minus_one ()); + graphene_box2d_get_center (&b, ¢er); + mutest_expect ("box2d(-1).center() to be in (-0.5, -0.5)", + mutest_bool_value (graphene_point_near (¢er, &minus_half, 0.0001f)), + mutest_to_be_true, + NULL); + + graphene_box2d_init_from_box (&b, graphene_box2d_empty ()); + graphene_box2d_get_center (&b, ¢er); + mutest_expect ("box2d(empty).center() to be in (0, 0)", + mutest_bool_value (graphene_point_equal (¢er, &zero)), + mutest_to_be_true, + NULL); +} + +static void +box2d_equal (mutest_spec_t *spec) +{ + mutest_expect ("equal(null, null) to be true", + mutest_bool_value (graphene_box2d_equal (NULL, NULL)), + mutest_to_be_true, + NULL); + + mutest_expect ("equal(null, box) to be false", + mutest_bool_value (graphene_box2d_equal (NULL, graphene_box2d_zero ())), + mutest_to_be_false, + NULL); + + mutest_expect ("equal(box, null) to be false", + mutest_bool_value (graphene_box2d_equal (graphene_box2d_zero (), NULL)), + mutest_to_be_false, + NULL); + + mutest_expect ("equal(box, box) to be true", + mutest_bool_value (graphene_box2d_equal (graphene_box2d_zero (), graphene_box2d_zero ())), + mutest_to_be_true, + NULL); +} + +static void +box2d_union (mutest_spec_t *spec) +{ + graphene_box2d_t res; + + graphene_box2d_union (graphene_box2d_zero (), graphene_box2d_zero (), &res); + mutest_expect ("union(zero, zero) to be equal to zero", + mutest_bool_value (graphene_box2d_equal (&res, graphene_box2d_zero ())), + mutest_to_be_true, + NULL); + + graphene_box2d_union (graphene_box2d_zero (), graphene_box2d_one (), &res); + mutest_expect ("union(zero, one) to be equal to one", + mutest_bool_value (graphene_box2d_equal (&res, graphene_box2d_one ())), + mutest_to_be_true, + NULL); + + graphene_box2d_union (graphene_box2d_minus_one (), graphene_box2d_one (), &res); + mutest_expect ("union(-1, 1) to be equal to (-1, 1)", + mutest_bool_value (graphene_box2d_equal (&res, graphene_box2d_one_minus_one ())), + mutest_to_be_true, + NULL); +} + +static void +box2d_intersection (mutest_spec_t *spec) +{ + graphene_box2d_t res; + graphene_box2d_t left, right; + graphene_box2d_t top, bottom; + + graphene_box2d_intersection (graphene_box2d_empty (), graphene_box2d_zero (), &res); + mutest_expect ("intersection(empty, zero) to be empty", + mutest_bool_value (graphene_box2d_equal (&res, graphene_box2d_empty ())), + mutest_to_be_true, + NULL); + + graphene_box2d_intersection (graphene_box2d_one_minus_one (), graphene_box2d_one (), &res); + mutest_expect ("intersection((-1, 1), (1, 1)) to be (1, 1)", + mutest_bool_value (graphene_box2d_equal (&res, graphene_box2d_one ())), + mutest_to_be_true, + NULL); + + graphene_box2d_intersection (graphene_box2d_infinite (), graphene_box2d_one (), &res); + mutest_expect ("intersection(inf, (1, 1)) to be (1, 1)", + mutest_bool_value (graphene_box2d_equal (&res, graphene_box2d_one ())), + mutest_to_be_true, + NULL); + + graphene_box2d_init (&left, + &GRAPHENE_POINT_INIT (5.f, 0.f), + &GRAPHENE_POINT_INIT (6.f, 1.f)); + graphene_box2d_init (&right, + &GRAPHENE_POINT_INIT (7.f, 0.f), + &GRAPHENE_POINT_INIT (8.f, 1.f)); + mutest_expect ("horizontally separated boxes don't intersect", + mutest_bool_value (graphene_box2d_intersection (&left, &right, NULL)), + mutest_to_be_false, + NULL); + + graphene_box2d_init (&top, + &GRAPHENE_POINT_INIT (0.f, 5.f), + &GRAPHENE_POINT_INIT (1.f, 6.f)); + graphene_box2d_init (&bottom, + &GRAPHENE_POINT_INIT (0.f, 7.f), + &GRAPHENE_POINT_INIT (1.f, 8.f)); + mutest_expect ("vertically separated boxes don't intersect", + mutest_bool_value (graphene_box2d_intersection (&top, &bottom, NULL)), + mutest_to_be_false, + NULL); +} + +static void +box2d_expand_by_point (mutest_spec_t *spec) +{ + graphene_box2d_t b; + graphene_vec2_t size, tmp; + graphene_point_t zero = GRAPHENE_POINT_INIT_ZERO; + graphene_point_t one = GRAPHENE_POINT_INIT (1.f, 1.f); + graphene_point_t minus_one = GRAPHENE_POINT_INIT (-1.f, -1.f); + + graphene_box2d_init_from_box (&b, graphene_box2d_zero ()); + graphene_box2d_get_size (&b, &size); + mutest_expect ("initial size to be zero", + mutest_bool_value (graphene_vec2_equal (&size, graphene_vec2_zero ())), + mutest_to_be_true, + NULL); + + graphene_box2d_expand (&b, &zero, &b); + graphene_box2d_get_size (&b, &size); + mutest_expect ("expand(zero) does not expand", + mutest_bool_value (graphene_vec2_equal (&size, graphene_vec2_zero ())), + mutest_to_be_true, + NULL); + + graphene_box2d_expand (&b, &one, &b); + graphene_box2d_get_size (&b, &size); + mutest_expect ("expand(1) expands to 1", + mutest_bool_value (graphene_vec2_equal (&size, graphene_vec2_one ())), + mutest_to_be_true, + NULL); + + graphene_box2d_expand (&b, &minus_one, &b); + graphene_box2d_get_size (&b, &size); + mutest_expect ("expand(-1) expands to 2", + mutest_bool_value (graphene_vec2_equal (&size, graphene_vec2_init (&tmp, 2.f, 2.f))), + mutest_to_be_true, + NULL); +} + +static void +box2d_expand_by_vector (mutest_spec_t *spec) +{ + graphene_box2d_t b; + graphene_vec2_t size, tmp; + graphene_vec2_t minus_one; + + graphene_box2d_init_from_box (&b, graphene_box2d_zero ()); + graphene_box2d_get_size (&b, &size); + mutest_expect ("initial size to be zero", + mutest_bool_value (graphene_vec2_equal (&size, graphene_vec2_zero ())), + mutest_to_be_true, + NULL); + + graphene_box2d_expand_vec2 (&b, graphene_vec2_zero (), &b); + graphene_box2d_get_size (&b, &size); + mutest_expect ("expand(zero) does not expand", + mutest_bool_value (graphene_vec2_equal (&size, graphene_vec2_zero ())), + mutest_to_be_true, + NULL); + + graphene_box2d_expand_vec2 (&b, graphene_vec2_one (), &b); + graphene_box2d_get_size (&b, &size); + mutest_expect ("expand(1) expands to 1", + mutest_bool_value (graphene_vec2_equal (&size, graphene_vec2_one ())), + mutest_to_be_true, + NULL); + + graphene_vec2_init (&minus_one, -1.f, -1.f); + graphene_box2d_expand_vec2 (&b, &minus_one, &b); + graphene_box2d_get_size (&b, &size); + mutest_expect ("expand(-1) expands to 2", + mutest_bool_value (graphene_vec2_equal (&size, graphene_vec2_init (&tmp, 2.f, 2.f))), + mutest_to_be_true, + NULL); +} + +static void +box2d_expand_by_scalar (mutest_spec_t *spec) +{ + graphene_box2d_t b; + graphene_vec2_t size, tmp; + + graphene_box2d_init_from_box (&b, graphene_box2d_zero ()); + graphene_box2d_get_size (&b, &size); + mutest_expect ("initial size to be zero", + mutest_bool_value (graphene_vec2_equal (&size, graphene_vec2_zero ())), + mutest_to_be_true, + NULL); + + graphene_box2d_expand_scalar (&b, 0, &b); + graphene_box2d_get_size (&b, &size); + mutest_expect ("expand(zero) does not expand", + mutest_bool_value (graphene_vec2_equal (&size, graphene_vec2_zero ())), + mutest_to_be_true, + NULL); + + graphene_box2d_expand_scalar (&b, 1, &b); + graphene_box2d_get_size (&b, &size); + mutest_expect ("expand(1) expands to 2", + mutest_bool_value (graphene_vec2_equal (&size, graphene_vec2_init (&tmp, 2.f, 2.f))), + mutest_to_be_true, + NULL); +} + +static void +box2d_contains_point (mutest_spec_t *spec) +{ + graphene_box2d_t b; + graphene_vec2_t points[8]; + graphene_point_t center; + graphene_point_t check; + unsigned int i; + + graphene_box2d_init_from_box (&b, graphene_box2d_one_minus_one ()); + graphene_box2d_get_center (&b, ¢er); + mutest_expect ("box to contain its center", + mutest_bool_value (graphene_box2d_contains_point (&b, ¢er)), + mutest_to_be_true, + NULL); + + graphene_box2d_get_vertices (&b, points); + for (i = 0; i < 4; i++) + { + char desc[128]; + + graphene_point_init_from_vec2 (&check, &points[i]); + snprintf (desc, 128, "box2d to contain its vertices [%d]", i); + mutest_expect (desc, + mutest_bool_value (graphene_box2d_contains_point (&b, &check)), + mutest_to_be_true, + NULL); + } + + graphene_point_init (&check, -2.f, 2.f); + mutest_expect ("box to not contain a point outside its area", + mutest_bool_value (graphene_box2d_contains_point (&b, &check)), + mutest_to_be_false, + NULL); +} + +static void +box2d_contains_box (mutest_spec_t *spec) +{ + graphene_box2d_t a, b; + + graphene_box2d_init_from_box (&a, graphene_box2d_one ()); + graphene_box2d_init_from_box (&b, graphene_box2d_minus_one ()); + mutest_expect ("box(1) to not contain box(-1)", + mutest_bool_value (graphene_box2d_contains_box (&a, &b)), + mutest_to_be_false, + NULL); + + graphene_box2d_init_from_box (&a, graphene_box2d_one_minus_one ()); + graphene_box2d_init_from_box (&b, graphene_box2d_one ()); + mutest_expect ("box(-1, 1) to contain box(1)", + mutest_bool_value (graphene_box2d_contains_box (&a, &b)), + mutest_to_be_true, + NULL); + + graphene_box2d_init_from_box (&a, graphene_box2d_infinite ()); + mutest_expect ("infinite box to contain any box", + mutest_bool_value (graphene_box2d_contains_box (&a, &b)), + mutest_to_be_true, + NULL); + + graphene_box2d_init_from_box (&a, graphene_box2d_empty ()); + mutest_expect ("empty box to not contain any box", + mutest_bool_value (graphene_box2d_contains_box (&a, &b)), + mutest_to_be_false, + NULL); +} + +static void +box2d_suite (mutest_suite_t *suite) +{ + mutest_it ("initializes min/max points", box2d_init_min_max); + mutest_it ("initializes from points", box2d_init_from_points); + mutest_it ("initializes from vectors", box2d_init_from_vectors); + mutest_it ("has the correct sizes", box2d_size); + mutest_it ("has the correct center point", box2d_center); + mutest_it ("has equality", box2d_equal); + mutest_it ("computes unions", box2d_union); + mutest_it ("computes intersections", box2d_intersection); + mutest_it ("expands by point", box2d_expand_by_point); + mutest_it ("expands by vector", box2d_expand_by_vector); + mutest_it ("expands by scalar", box2d_expand_by_scalar); + mutest_it ("contains point", box2d_contains_point); + mutest_it ("contains box", box2d_contains_box); +} + +MUTEST_MAIN ( + mutest_describe ("graphene_box2d_t", box2d_suite); +) diff --git a/tests/meson.build b/tests/meson.build index 0120c29c..5ef8b757 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,5 +1,6 @@ unit_tests = [ 'box', + 'box2d', 'euler', 'frustum', 'matrix', From 94c277cd3cc266365836ff313a101c0c760e275e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 30 Oct 2023 16:34:56 +0000 Subject: [PATCH 04/13] Use single vec4 for box2d Pack the minimum and maximum vertices into a single vector, and use merge low/high operators to extract the values. This reduces the size of a box2d instance, and makes it more compact. It's also more efficient to extract both vertices when passing them to GL, by using a single read. --- include/graphene-box2d.h | 21 ++- src/graphene-box2d.c | 359 +++++++++++++++++++++++++++++---------- 2 files changed, 289 insertions(+), 91 deletions(-) diff --git a/include/graphene-box2d.h b/include/graphene-box2d.h index 933468eb..dcfbe4ec 100644 --- a/include/graphene-box2d.h +++ b/include/graphene-box2d.h @@ -12,6 +12,7 @@ #include "graphene-types.h" #include "graphene-vec2.h" +#include "graphene-vec4.h" GRAPHENE_BEGIN_DECLS @@ -26,8 +27,7 @@ GRAPHENE_BEGIN_DECLS struct _graphene_box2d_t { /*< private >*/ - GRAPHENE_PRIVATE_FIELD (graphene_vec2_t, min); - GRAPHENE_PRIVATE_FIELD (graphene_vec2_t, max); + GRAPHENE_PRIVATE_FIELD (graphene_vec4_t, minmax); }; GRAPHENE_AVAILABLE_IN_1_12 @@ -54,7 +54,9 @@ GRAPHENE_AVAILABLE_IN_1_12 graphene_box2d_t * graphene_box2d_init_from_vec2 (graphene_box2d_t *box, const graphene_vec2_t *min, const graphene_vec2_t *max); - +GRAPHENE_AVAILABLE_IN_1_12 +graphene_box2d_t * graphene_box2d_init_from_rect (graphene_box2d_t *box, + const graphene_rect_t *rect); GRAPHENE_AVAILABLE_IN_1_12 void graphene_box2d_expand (const graphene_box2d_t *box, const graphene_point_t *point, @@ -87,6 +89,10 @@ GRAPHENE_AVAILABLE_IN_1_12 void graphene_box2d_get_center (const graphene_box2d_t *box, graphene_point_t *center); GRAPHENE_AVAILABLE_IN_1_12 +void graphene_box2d_get_minmax (const graphene_box2d_t *box, + graphene_point_t *min, + graphene_point_t *max); +GRAPHENE_AVAILABLE_IN_1_12 void graphene_box2d_get_min (const graphene_box2d_t *box, graphene_point_t *min); GRAPHENE_AVAILABLE_IN_1_12 @@ -96,11 +102,20 @@ GRAPHENE_AVAILABLE_IN_1_12 void graphene_box2d_get_vertices (const graphene_box2d_t *box, graphene_vec2_t vertices[]); GRAPHENE_AVAILABLE_IN_1_12 +void graphene_box2d_to_float (const graphene_box2d_t *box, + float v[4]); +GRAPHENE_AVAILABLE_IN_1_12 +void graphene_box2d_to_rect (const graphene_box2d_t *box, + graphene_rect_t *rect); +GRAPHENE_AVAILABLE_IN_1_12 bool graphene_box2d_contains_point (const graphene_box2d_t *box, const graphene_point_t *point); GRAPHENE_AVAILABLE_IN_1_12 bool graphene_box2d_contains_box (const graphene_box2d_t *a, const graphene_box2d_t *b); +GRAPHENE_AVAILABLE_IN_1_12 +bool graphene_box2d_contains_rect (const graphene_box2d_t *box, + const graphene_rect_t *rect); GRAPHENE_AVAILABLE_IN_1_12 bool graphene_box2d_equal (const graphene_box2d_t *a, diff --git a/src/graphene-box2d.c b/src/graphene-box2d.c index fc7fdad6..6a297694 100644 --- a/src/graphene-box2d.c +++ b/src/graphene-box2d.c @@ -12,6 +12,8 @@ * #graphene_box2d_t provides a representation of an axis aligned minimum * bounding box in two dimensions using the coordinates of its minimum and * maximum vertices. + * + * #graphene_box2d_t is available since Graphene 1.2. */ #include "graphene-private.h" @@ -20,6 +22,7 @@ #include "graphene-alloc-private.h" #include "graphene-point.h" +#include "graphene-rect.h" #include "graphene-simd4f.h" #include @@ -51,7 +54,7 @@ graphene_box2d_alloc (void) /** * graphene_box2d_free: - * @box: a #graphene_box2d_t + * @box: (transfer full): a #graphene_box2d_t * * Frees the resources allocated by graphene_box2d_alloc(). * @@ -80,16 +83,11 @@ graphene_box2d_init (graphene_box2d_t *box, const graphene_point_t *min, const graphene_point_t *max) { - if (min != NULL) - graphene_point_to_vec2 (min, &box->min); - else - graphene_vec2_init_from_vec2 (&box->min, graphene_vec2_zero ()); - - if (max != NULL) - graphene_point_to_vec2 (max, &box->max); - else - graphene_vec2_init_from_vec2 (&box->max, graphene_vec2_zero ()); - + graphene_vec4_init (&box->minmax, + min != NULL ? min->x : 0.f, + min != NULL ? min->y : 0.f, + max != NULL ? max->x : 0.f, + max != NULL ? max->y : 0.f); return box; } @@ -110,8 +108,8 @@ graphene_box2d_init (graphene_box2d_t *box, * Since: 1.12 */ graphene_box2d_t * -graphene_box2d_init_from_points (graphene_box2d_t *box, - unsigned int n_points, +graphene_box2d_init_from_points (graphene_box2d_t *box, + unsigned int n_points, const graphene_point_t *points) { graphene_box2d_init_from_box (box, graphene_box2d_empty ()); @@ -172,14 +170,23 @@ graphene_box2d_t * graphene_box2d_init_from_box (graphene_box2d_t *box, const graphene_box2d_t *src) { - box->min = src->min; - box->max = src->max; + box->minmax = src->minmax; + + return box; +} + +static inline graphene_box2d_t * +graphene_box2d_init_from_simd4f (graphene_box2d_t *box, + const graphene_simd4f_t min, + const graphene_simd4f_t max) +{ + box->minmax.value = graphene_simd4f_merge_low (min, max); return box; } /** - * graphene_box2d_init_from_vec3: + * graphene_box2d_init_from_vec2: * @box: the #graphene_box2d_t to initialize * @min: (nullable): the coordinates of the minimum vertex * @max: (nullable): the coordinates of the maximum vertex @@ -196,26 +203,32 @@ graphene_box2d_init_from_vec2 (graphene_box2d_t *box, const graphene_vec2_t *min, const graphene_vec2_t *max) { - if (min != NULL) - box->min = *min; - else - graphene_vec2_init_from_vec2 (&box->min, graphene_vec2_zero ()); - - if (max != NULL) - box->max = *max; - else - graphene_vec2_init_from_vec2 (&box->max, graphene_vec2_zero ()); - - return box; + return graphene_box2d_init_from_simd4f (box, + min != NULL ? min->value : graphene_simd4f_init_zero (), + max != NULL ? max->value : graphene_simd4f_init_zero ()); } -static inline graphene_box2d_t * -graphene_box2d_init_from_simd4f (graphene_box2d_t *box, - const graphene_simd4f_t min, - const graphene_simd4f_t max) +/** + * graphene_box2d_init_from_rect: + * @box: the #graphene_box2d_t to initialize + * @src: a #graphene_rect_t + * + * Initializes the given #graphene_box2d_t with the origin and + * size of a #graphene_rect_t. + * + * Returns: (transfer none): the initialized #graphene_box2d_t + * + * Since: 1.12 + */ +graphene_box2d_t * +graphene_box2d_init_from_rect (graphene_box2d_t *box, + const graphene_rect_t *src) { - box->min.value = min; - box->max.value = max; + box->minmax.value = + graphene_simd4f_init (src->origin.x, + src->origin.y, + src->origin.x + src->size.width, + src->origin.y + src->size.height); return box; } @@ -225,12 +238,19 @@ graphene_box2d_expand_simd4f (const graphene_box2d_t *box, const graphene_simd4f_t v, graphene_box2d_t *res) { - res->min.value = graphene_simd4f_min (box->min.value, v); - res->max.value = graphene_simd4f_max (box->max.value, v); + graphene_simd4f_t min_v = + graphene_simd4f_merge_low (box->minmax.value, graphene_simd4f_init_zero ()); + graphene_simd4f_t max_v = + graphene_simd4f_merge_high (box->minmax.value, graphene_simd4f_init_zero ()); + + min_v = graphene_simd4f_min (min_v, v); + max_v = graphene_simd4f_max (max_v, v); + + graphene_box2d_init_from_simd4f (res, min_v, max_v); } /** - * graphene_box2d_expand_vec3: + * graphene_box2d_expand_vec2: * @box: a #graphene_box2d_t * @vec: the coordinates of the point to include, as a #graphene_vec2_t * @res: (out caller-allocates): return location for the expanded box @@ -241,9 +261,9 @@ graphene_box2d_expand_simd4f (const graphene_box2d_t *box, * Since: 1.12 */ void -graphene_box2d_expand_vec2 (const graphene_box2d_t *box, - const graphene_vec2_t *vec, - graphene_box2d_t *res) +graphene_box2d_expand_vec2 (const graphene_box2d_t *box, + const graphene_vec2_t *vec, + graphene_box2d_t *res) { graphene_box2d_expand_simd4f (box, vec->value, res); } @@ -286,11 +306,66 @@ graphene_box2d_expand_scalar (const graphene_box2d_t *box, float scalar, graphene_box2d_t *res) { + graphene_simd4f_t zero = graphene_simd4f_init_zero (); + graphene_simd4f_t min_v = graphene_simd4f_merge_low (box->minmax.value, zero); + graphene_simd4f_t max_v = graphene_simd4f_merge_high (box->minmax.value, zero); + float min = scalar * -1.f; float max = scalar; - res->min.value = graphene_simd4f_add (box->min.value, graphene_simd4f_splat (min)); - res->max.value = graphene_simd4f_add (box->max.value, graphene_simd4f_splat (max)); + min_v = graphene_simd4f_add (min_v, graphene_simd4f_splat (min)); + max_v = graphene_simd4f_add (max_v, graphene_simd4f_splat (max)); + + graphene_box2d_init_from_simd4f (res, min_v, max_v); +} + +/** + * graphene_box2d_to_float: + * @box: a #graphene_box2d_t + * @v: (out caller-allocates) (array fixed-size=4): return location + * for an array of floating point values with at least 4 elements + * + * Stores the minimum and maximum vertices of the given #graphene_box2d_t + * into an array. + * + * The array layout is: + * + * - min_x + * - min_y + * - max_x + * - max_y + * + * Since: 1.12 + */ +void +graphene_box2d_to_float (const graphene_box2d_t *box, + float v[4]) +{ + graphene_simd4f_dup_4f (box->minmax.value, v); +} + +/** + * graphene_box2d_to_rect: + * @box: a #graphene_box2d_t + * @rect: (out caller-allocates): the rectangle to initialize + * + * Stores the minimum and maximum vertices of the given #graphene_box2d_t + * into a rectangle of equivalent origin and size. + * + * Since: 1.12 + */ +void +graphene_box2d_to_rect (const graphene_box2d_t *box, + graphene_rect_t *rect) +{ + graphene_point_t min, max; + + graphene_box2d_get_minmax (box, &min, &max); + + rect->origin.x = min.x; + rect->origin.y = min.y; + rect->size.width = max.x - min.x; + rect->size.height = max.y - min.y; } /** @@ -308,8 +383,19 @@ graphene_box2d_union (const graphene_box2d_t *a, const graphene_box2d_t *b, graphene_box2d_t *res) { - res->min.value = graphene_simd4f_min (a->min.value, b->min.value); - res->max.value = graphene_simd4f_max (a->max.value, b->max.value); + graphene_simd4f_t zero = graphene_simd4f_init_zero (); + + graphene_simd4f_t v_a, v_b; + + v_a = graphene_simd4f_merge_low (a->minmax.value, zero); + v_b = graphene_simd4f_merge_low (b->minmax.value, zero); + graphene_simd4f_t min_v = graphene_simd4f_min (v_a, v_b); + + v_a = graphene_simd4f_merge_high (a->minmax.value, zero); + v_b = graphene_simd4f_merge_high (b->minmax.value, zero); + graphene_simd4f_t max_v = graphene_simd4f_max (v_a, v_b); + + graphene_box2d_init_from_simd4f (res, min_v, max_v); } /** @@ -332,12 +418,19 @@ graphene_box2d_intersection (const graphene_box2d_t *a, const graphene_box2d_t *b, graphene_box2d_t *res) { - graphene_simd4f_t min, max; + const graphene_simd4f_t zero = graphene_simd4f_init_zero (); + graphene_simd4f_t v_a, v_b; + graphene_simd4f_t min_v, max_v; - min = graphene_simd4f_max (a->min.value, b->min.value); - max = graphene_simd4f_min (a->max.value, b->max.value); + v_a = graphene_simd4f_merge_low (a->minmax.value, zero); + v_b = graphene_simd4f_merge_low (b->minmax.value, zero); + min_v = graphene_simd4f_max (v_a, v_b); - if (!graphene_simd4f_cmp_le (min, max)) + v_a = graphene_simd4f_merge_high (a->minmax.value, zero); + v_b = graphene_simd4f_merge_high (b->minmax.value, zero); + max_v = graphene_simd4f_min (v_a, v_b); + + if (!graphene_simd4f_cmp_le (min_v, max_v)) { if (res != NULL) graphene_box2d_init_from_box (res, graphene_box2d_empty ()); @@ -346,7 +439,7 @@ graphene_box2d_intersection (const graphene_box2d_t *a, } if (res != NULL) - graphene_box2d_init_from_simd4f (res, min, max); + graphene_box2d_init_from_simd4f (res, min_v, max_v); return true; } @@ -364,7 +457,11 @@ graphene_box2d_intersection (const graphene_box2d_t *a, float graphene_box2d_get_width (const graphene_box2d_t *box) { - float res = graphene_simd4f_get_x (graphene_simd4f_sub (box->max.value, box->min.value)); + graphene_simd4f_t zero = graphene_simd4f_init_zero (); + graphene_simd4f_t min_v = graphene_simd4f_merge_low (box->minmax.value, zero); + graphene_simd4f_t max_v = graphene_simd4f_merge_high (box->minmax.value, zero); + graphene_simd4f_t width = graphene_simd4f_sub (max_v, min_v); + float res = graphene_simd4f_get_x (width); return fabsf (res); } @@ -382,7 +479,11 @@ graphene_box2d_get_width (const graphene_box2d_t *box) float graphene_box2d_get_height (const graphene_box2d_t *box) { - float res = graphene_simd4f_get_y (graphene_simd4f_sub (box->max.value, box->min.value)); + graphene_simd4f_t zero = graphene_simd4f_init_zero (); + graphene_simd4f_t min_v = graphene_simd4f_merge_low (box->minmax.value, zero); + graphene_simd4f_t max_v = graphene_simd4f_merge_high (box->minmax.value, zero); + graphene_simd4f_t height = graphene_simd4f_sub (max_v, min_v); + float res = graphene_simd4f_get_y (height); return fabsf (res); } @@ -391,14 +492,20 @@ static inline bool graphene_box2d_is_empty (const graphene_box2d_t *box) { #ifdef HAVE_ISINFF + graphene_simd4f_t zero = graphene_simd4f_init_zero (); + graphene_simd4f_t min_v = graphene_simd4f_merge_low (box->minmax.value, zero); + graphene_simd4f_t max_v = graphene_simd4f_merge_high (box->minmax.value, zero); float vmin[2], vmax[2]; - graphene_simd4f_dup_2f (box->min.value, vmin); - graphene_simd4f_dup_2f (box->max.value, vmax); + graphene_simd4f_dup_2f (min_v, vmin); + graphene_simd4f_dup_2f (max_v, vmax); return (isinff (vmin[0]) == 1 && isinff (vmin[1]) == 1) && (isinff (vmax[0]) == -1 && isinff (vmax[1]) == -1); #else + graphene_simd4f_t zero = graphene_simd4f_init_zero (); + graphene_simd4f_t min_v = graphene_simd4f_merge_low (box->minmax.value, zero); + graphene_simd4f_t max_v = graphene_simd4f_merge_high (box->minmax.value, zero); graphene_simd4f_t neg_inf = graphene_simd4f_init (-INFINITY, -INFINITY, 0.f, 0.f); graphene_simd4f_t pos_inf = graphene_simd4f_init (INFINITY, INFINITY, 0.f, 0.f); @@ -407,8 +514,8 @@ graphene_box2d_is_empty (const graphene_box2d_t *box) * 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)); + int min_cmp = memcmp (&min_v, &pos_inf, sizeof (graphene_simd4f_t)); + int max_cmp = memcmp (&max_v, &neg_inf, sizeof (graphene_simd4f_t)); return min_cmp == 0 && max_cmp == 0; #endif @@ -418,14 +525,20 @@ static inline bool graphene_box2d_is_infinity (const graphene_box2d_t *box) { #ifdef HAVE_ISINFF + graphene_simd4f_t zero = graphene_simd4f_init_zero (); + graphene_simd4f_t min_v = graphene_simd4f_merge_low (box->minmax.value, zero); + graphene_simd4f_t max_v = graphene_simd4f_merge_high (box->minmax.value, zero); float vmin[2], vmax[2]; - graphene_simd4f_dup_2f (box->min.value, vmin); - graphene_simd4f_dup_2f (box->max.value, vmax); + graphene_simd4f_dup_2f (min_v, vmin); + graphene_simd4f_dup_2f (max_v, vmax); return (isinff (vmin[0]) == -1 && isinff (vmin[1]) == -1) && (isinff (vmax[0]) == 1 && isinff (vmax[1]) == 1); #else + graphene_simd4f_t zero = graphene_simd4f_init_zero (); + graphene_simd4f_t min_v = graphene_simd4f_merge_low (box->minmax.value, zero); + graphene_simd4f_t max_v = graphene_simd4f_merge_high (box->minmax.value, zero); graphene_simd4f_t neg_inf = graphene_simd4f_init (-INFINITY, -INFINITY, 0.f, 0.f); graphene_simd4f_t pos_inf = graphene_simd4f_init (INFINITY, INFINITY, 0.f, 0.f); @@ -434,8 +547,8 @@ graphene_box2d_is_infinity (const graphene_box2d_t *box) * 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)); + int min_cmp = memcmp (&min_v, &neg_inf, sizeof (graphene_simd4f_t)); + int max_cmp = memcmp (&max_v, &pos_inf, sizeof (graphene_simd4f_t)); return min_cmp == 0 && max_cmp == 0; #endif @@ -460,7 +573,13 @@ graphene_box2d_get_size (const graphene_box2d_t *box, else if (graphene_box2d_is_infinity (box)) size->value = graphene_simd4f_init (INFINITY, INFINITY, 0.f, 0.f); else - size->value = graphene_simd4f_sub (box->max.value, box->min.value); + { + graphene_simd4f_t zero = graphene_simd4f_init_zero (); + graphene_simd4f_t min_v = graphene_simd4f_merge_low (box->minmax.value, zero); + graphene_simd4f_t max_v = graphene_simd4f_merge_high (box->minmax.value, zero); + + size->value = graphene_simd4f_sub (max_v, min_v); + } } /** @@ -475,20 +594,23 @@ graphene_box2d_get_size (const graphene_box2d_t *box, */ void graphene_box2d_get_center (const graphene_box2d_t *box, - graphene_point_t *center) + graphene_point_t *center) { - graphene_vec2_t res; - if (graphene_box2d_is_empty (box) || graphene_box2d_is_infinity (box)) { graphene_point_init (center, 0.f, 0.f); return; } - graphene_vec2_add (&box->min, &box->max, &res); - graphene_vec2_scale (&res, 0.5f, &res); + graphene_simd4f_t zero = graphene_simd4f_init_zero (); + graphene_simd4f_t min_v = graphene_simd4f_merge_low (box->minmax.value, zero); + graphene_simd4f_t max_v = graphene_simd4f_merge_high (box->minmax.value, zero); - graphene_point_init_from_vec2 (center, &res); + graphene_simd4f_t res = + graphene_simd4f_mul (graphene_simd4f_add (min_v, max_v), + graphene_simd4f_splat (0.5f)); + + graphene_simd4f_dup_2f (res, &(center->x)); } /** @@ -505,7 +627,7 @@ void graphene_box2d_get_min (const graphene_box2d_t *box, graphene_point_t *min) { - graphene_point_init_from_vec2 (min, &box->min); + graphene_box2d_get_minmax (box, min, NULL); } /** @@ -522,7 +644,33 @@ void graphene_box2d_get_max (const graphene_box2d_t *box, graphene_point_t *max) { - graphene_point_init_from_vec2 (max, &box->max); + graphene_box2d_get_minmax (box, NULL, max); +} + +/** + * graphene_box2d_get_minmax: + * @box: a #graphene_box2d_t + * @min: (out caller-allocates): return location for the minimum point + * @max: (out caller-allocates): return location for the maximum point + * + * Retrieves the coordinates of the minimum and maximum points of the + * given #graphene_box2d_t. + * + * Since: 1.12 + */ +void +graphene_box2d_get_minmax (const graphene_box2d_t *box, + graphene_point_t *min, + graphene_point_t *max) +{ + graphene_simd4f_t zero = graphene_simd4f_init_zero (); + graphene_simd4f_t min_v = graphene_simd4f_merge_low (box->minmax.value, zero); + graphene_simd4f_t max_v = graphene_simd4f_merge_high (box->minmax.value, zero); + + if (min != NULL) + graphene_simd4f_dup_2f (min_v, &(min->x)); + if (max != NULL) + graphene_simd4f_dup_2f (max_v, &(max->x)); } /** @@ -541,8 +689,7 @@ graphene_box2d_get_vertices (const graphene_box2d_t *box, { graphene_point_t min, max; - graphene_box2d_get_min (box, &min); - graphene_box2d_get_max (box, &max); + graphene_box2d_get_minmax (box, &min, &max); graphene_vec2_init (&vertices[0], min.x, min.y); graphene_vec2_init (&vertices[1], min.x, max.y); @@ -572,9 +719,12 @@ graphene_box2d_contains_point (const graphene_box2d_t *box, return true; graphene_simd4f_t p = graphene_simd4f_init (point->x, point->y, 0.f, 0.f); + graphene_simd4f_t zero = graphene_simd4f_init_zero (); + graphene_simd4f_t min_v = graphene_simd4f_merge_low (box->minmax.value, zero); + graphene_simd4f_t max_v = graphene_simd4f_merge_high (box->minmax.value, zero); - if (graphene_simd4f_cmp_ge (p, box->min.value) && - graphene_simd4f_cmp_le (p, box->max.value)) + if (graphene_simd4f_cmp_ge (p, min_v) && + graphene_simd4f_cmp_le (p, max_v)) return true; return false; @@ -603,13 +753,53 @@ graphene_box2d_contains_box (const graphene_box2d_t *a, 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)) - return true; + graphene_simd4f_t min_a = graphene_simd4f_merge_low (a->minmax.value, graphene_simd4f_init_zero ()); + graphene_simd4f_t min_b = graphene_simd4f_merge_low (b->minmax.value, graphene_simd4f_init_zero ()); + if (graphene_simd4f_cmp_ge (min_b, min_a)) + { + graphene_simd4f_t max_a = graphene_simd4f_merge_high (a->minmax.value, graphene_simd4f_init_zero ()); + graphene_simd4f_t max_b = graphene_simd4f_merge_high (b->minmax.value, graphene_simd4f_init_zero ()); + if (graphene_simd4f_cmp_le (max_b, max_a)) + return true; + } return false; } +/** + * graphene_box2d_contains_rect: + * @box: a #graphene_box2d_t + * @rect: the rectangle to check + * + * Checks whether @box contains the given @rect. + * + * Returns: `true` if the rectangle is contained in the given box + * + * Since: 1.12 + */ +bool +graphene_box2d_contains_rect (const graphene_box2d_t *box, + const graphene_rect_t *rect) +{ + if (graphene_box2d_is_empty (box)) + return false; + + if (graphene_box2d_is_infinity (box)) + return true; + + if (!graphene_box2d_contains_point (box, &rect->origin)) + return false; + + graphene_point_t tmp; + graphene_point_init (&tmp, + rect->origin.x + rect->size.width, + rect->origin.y + rect->size.height); + if (!graphene_box2d_contains_point (box, &tmp)) + return false; + + return true; +} + static bool box_equal (const void *p1, const void *p2) @@ -627,8 +817,7 @@ box_equal (const void *p1, else if (graphene_box2d_is_infinity (a) || graphene_box2d_is_infinity (b)) return false; - return graphene_vec2_equal (&a->min, &b->min) && - graphene_vec2_equal (&a->max, &b->max); + return graphene_vec4_equal (&a->minmax, &b->minmax); } /** @@ -665,23 +854,17 @@ static graphene_box2d_t static_box[N_STATIC_BOX]; static void init_static_box_once (void) { - static_box[BOX_ZERO].min.value = graphene_simd4f_init_zero (); - static_box[BOX_ZERO].max.value = graphene_simd4f_init_zero (); + static_box[BOX_ZERO].minmax.value = graphene_simd4f_init_zero (); - static_box[BOX_ONE].min.value = graphene_simd4f_init_zero (); - static_box[BOX_ONE].max.value = graphene_simd4f_init (1.f, 1.f, 0.f, 0.f); + static_box[BOX_ONE].minmax.value = graphene_simd4f_init (0.f, 0.f, 1.f, 1.f); - static_box[BOX_MINUS_ONE].min.value = graphene_simd4f_init (-1.f, -1.f, 0.f, 0.f); - static_box[BOX_MINUS_ONE].max.value = graphene_simd4f_init_zero (); + static_box[BOX_MINUS_ONE].minmax.value = graphene_simd4f_init (-1.f, -1.f, 0.f, 0.f); - static_box[BOX_ONE_MINUS_ONE].min.value = graphene_simd4f_init (-1.f, -1.f, 0.f, 0.f); - static_box[BOX_ONE_MINUS_ONE].max.value = graphene_simd4f_init (1.f, 1.f, 0.f, 0.f); + static_box[BOX_ONE_MINUS_ONE].minmax.value = graphene_simd4f_init (-1.f, -1.f, 1.f, 1.f); - static_box[BOX_INFINITY].min.value = graphene_simd4f_init (-INFINITY, -INFINITY, 0.f, 0.f); - static_box[BOX_INFINITY].max.value = graphene_simd4f_init (INFINITY, INFINITY, 0.f, 0.f); + static_box[BOX_INFINITY].minmax.value = graphene_simd4f_init (-INFINITY, -INFINITY, INFINITY, INFINITY); - static_box[BOX_EMPTY].min.value = graphene_simd4f_init (INFINITY, INFINITY, 0.f, 0.f); - static_box[BOX_EMPTY].max.value = graphene_simd4f_init (-INFINITY, -INFINITY, 0.f, 0.f); + static_box[BOX_EMPTY].minmax.value = graphene_simd4f_init (INFINITY, INFINITY, -INFINITY, -INFINITY); } #ifdef HAVE_PTHREAD From 5bb6949adeab856ad78a21b635b6a6448319a433 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sun, 11 Aug 2024 19:48:09 +0100 Subject: [PATCH 05/13] box2d: Add inlined intersection operator If we want to just know if two boxes intersect each other, we can do that with an inlined function. It's not the fastest possible function, because it still requires calling into graphene_box2d_get_minmax(), as the calling code does not have access to the private fields of the graphene_box2d_t type. --- include/graphene-box2d.h | 42 ++++++++++++++++++++++++++++++++++++++++ tests/box2d.c | 19 ++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/include/graphene-box2d.h b/include/graphene-box2d.h index dcfbe4ec..ccb3c020 100644 --- a/include/graphene-box2d.h +++ b/include/graphene-box2d.h @@ -11,6 +11,8 @@ #endif #include "graphene-types.h" +#include "graphene-point.h" +#include "graphene-simd4f.h" #include "graphene-vec2.h" #include "graphene-vec4.h" @@ -121,6 +123,46 @@ GRAPHENE_AVAILABLE_IN_1_12 bool graphene_box2d_equal (const graphene_box2d_t *a, const graphene_box2d_t *b); +static inline bool +graphene_box2d_intersects (const graphene_box2d_t *a, + const graphene_box2d_t *b); + +/** + * graphene_box2d_intersects: + * @a: a #graphene_box2d_t + * @b: a #graphene_box2d_t + * + * Checks whether two boxes intersect. + * + * See also: graphene_box2d_intersection() + * + * Returns: true if the boxes intersect, and false otherwise + * + * Since: 1.12 + */ +static inline bool +graphene_box2d_intersects (const graphene_box2d_t *a, + const graphene_box2d_t *b) +{ + graphene_point_t min_a, max_a; + graphene_box2d_get_minmax (a, &min_a, &max_a); + + graphene_point_t min_b, max_b; + graphene_box2d_get_minmax (b, &min_b, &max_b); + + graphene_simd4f_t min_v = + graphene_simd4f_max (graphene_simd4f_init (min_a.x, min_a.y, 0.f, 0.f), + graphene_simd4f_init (min_b.x, min_b.y, 0.f, 0.f)); + graphene_simd4f_t max_v = + graphene_simd4f_min (graphene_simd4f_init (max_a.x, max_a.y, 0.f, 0.f), + graphene_simd4f_init (max_b.x, max_b.y, 0.f, 0.f)); + + if (!graphene_simd4f_cmp_le (min_v, max_v)) + return false; + + return true; +} + GRAPHENE_AVAILABLE_IN_1_12 const graphene_box2d_t * graphene_box2d_zero (void); GRAPHENE_AVAILABLE_IN_1_12 diff --git a/tests/box2d.c b/tests/box2d.c index a3c967b5..9fec9a3c 100644 --- a/tests/box2d.c +++ b/tests/box2d.c @@ -325,6 +325,25 @@ box2d_intersection (mutest_spec_t *spec) mutest_bool_value (graphene_box2d_intersection (&top, &bottom, NULL)), mutest_to_be_false, NULL); + + graphene_box2d_t a, b, c; + graphene_box2d_init (&a, &GRAPHENE_POINT_INIT (0.f, 0.f), &GRAPHENE_POINT_INIT (2.f, 2.f)); + graphene_box2d_init (&b, &GRAPHENE_POINT_INIT (1.f, 1.f), &GRAPHENE_POINT_INIT (2.f, 2.f)); + graphene_box2d_init (&c, &GRAPHENE_POINT_INIT (3.f, 3.f), &GRAPHENE_POINT_INIT (4.f, 4.f)); + + bool a_b = graphene_box2d_intersects (&a, &b); + mutest_expect ("intersect to match intersection (positive)", + mutest_bool_value (a_b), + mutest_to_be_true, + mutest_to_be, graphene_box2d_intersection (&a, &b, NULL), + NULL); + + bool a_c = graphene_box2d_intersects (&a, &c); + mutest_expect ("intersect to match intersection (negative)", + mutest_bool_value (a_c), + mutest_to_be_false, + mutest_to_be, graphene_box2d_intersection (&a, &c, NULL), + NULL); } static void From 3f3467c497b52d8d0ea13c85aa3afcac5a7305e9 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 12 Aug 2024 11:16:13 +0100 Subject: [PATCH 06/13] box2d: Add scale/offset transformation Equivalent of graphene_rect_scale() and graphene_rect_offset_r(), but in a single operation. Scale and offset parameters are optional, so it's easy to use for separate transformations as well. --- include/graphene-box2d.h | 9 +++++++++ src/graphene-box2d.c | 42 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/include/graphene-box2d.h b/include/graphene-box2d.h index ccb3c020..8b2648a9 100644 --- a/include/graphene-box2d.h +++ b/include/graphene-box2d.h @@ -59,6 +59,7 @@ graphene_box2d_t * graphene_box2d_init_from_vec2 (graphene_bo GRAPHENE_AVAILABLE_IN_1_12 graphene_box2d_t * graphene_box2d_init_from_rect (graphene_box2d_t *box, const graphene_rect_t *rect); + GRAPHENE_AVAILABLE_IN_1_12 void graphene_box2d_expand (const graphene_box2d_t *box, const graphene_point_t *point, @@ -72,6 +73,12 @@ void graphene_box2d_expand_scalar (const graph float scalar, graphene_box2d_t *res); GRAPHENE_AVAILABLE_IN_1_12 +void graphene_box2d_scale_offset (const graphene_box2d_t *box, + const graphene_vec2_t *scale, + const graphene_point_t *offset, + graphene_box2d_t *res); + +GRAPHENE_AVAILABLE_IN_1_12 void graphene_box2d_union (const graphene_box2d_t *a, const graphene_box2d_t *b, graphene_box2d_t *res); @@ -103,12 +110,14 @@ void graphene_box2d_get_max (const graph GRAPHENE_AVAILABLE_IN_1_12 void graphene_box2d_get_vertices (const graphene_box2d_t *box, graphene_vec2_t vertices[]); + GRAPHENE_AVAILABLE_IN_1_12 void graphene_box2d_to_float (const graphene_box2d_t *box, float v[4]); GRAPHENE_AVAILABLE_IN_1_12 void graphene_box2d_to_rect (const graphene_box2d_t *box, graphene_rect_t *rect); + GRAPHENE_AVAILABLE_IN_1_12 bool graphene_box2d_contains_point (const graphene_box2d_t *box, const graphene_point_t *point); diff --git a/src/graphene-box2d.c b/src/graphene-box2d.c index 6a297694..5d33f93b 100644 --- a/src/graphene-box2d.c +++ b/src/graphene-box2d.c @@ -319,6 +319,48 @@ graphene_box2d_expand_scalar (const graphene_box2d_t *box, graphene_box2d_init_from_simd4f (res, min_v, max_v); } +/** + * graphene_box2d_scale_offset: + * @box: a #graphene_box2d_t + * @scale: (nullable): a vector with two scaling factors to be applied to the box + * @offset: (nullable): the offset to apply to the box + * @res: (out caller-allocates): the transformed box + * + * Applies a scale and an offset to the vertices of the given box. + * + * If @scale is %NULL, the box will be scaled by (1.0, 1.0). + * + * If @offset is %NULL, the box will be offset by (0.0, 0.0). + * + * Since: 1.12 + */ +void +graphene_box2d_scale_offset (const graphene_box2d_t *box, + const graphene_vec2_t *scale, + const graphene_point_t *offset, + graphene_box2d_t *res) +{ + graphene_simd4f_t scale_full; + graphene_simd4f_t offset_full; + + if (scale != NULL) + { + /* swizzle scale from (x, y, 0, 0) to (0, 0, x, y) */ + graphene_simd4f_t scale_zwxy = graphene_simd4f_shuffle_zwxy (scale->value); + /* scale is now (x, y, x, y) */ + scale_full = graphene_simd4f_add (scale->value, scale_zwxy); + } + else + scale_full = graphene_simd4f_init (1.f, 1.f, 1.f, 1.f); + + if (offset != NULL) + offset_full = graphene_simd4f_init (offset->x, offset->y, offset->x, offset->y); + else + offset_full = graphene_simd4f_init_zero (); + + res->minmax.value = graphene_simd4f_madd (box->minmax.value, scale_full, offset_full); +} + /** * graphene_box2d_to_float: * @box: a #graphene_box2d_t From 0d3e6537cc390169052e68c5c198ca5f73f3f23b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 12 Aug 2024 11:17:42 +0100 Subject: [PATCH 07/13] tests: Add more coverage for box2d Check that rectangles roundtrip between 2D boxes, and that the transformation works. --- tests/box2d.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/box2d.c b/tests/box2d.c index 9fec9a3c..2914c7a4 100644 --- a/tests/box2d.c +++ b/tests/box2d.c @@ -165,6 +165,30 @@ box2d_init_from_vectors (mutest_spec_t *spec) free (vectors); } +static void +box2d_init_from_rect (mutest_spec_t *spec) +{ + graphene_box2d_t a, b; + graphene_rect_t r, check; + + graphene_box2d_init_from_box (&a, graphene_box2d_one_minus_one ()); + graphene_box2d_to_rect (&a, &r); + + graphene_rect_init (&check, -1.f, -1.f, 2.f, 2.f); + + mutest_expect ("to_rect() to generate a valid rectangle", + mutest_bool_value (graphene_rect_equal (&r, &check)), + mutest_to_be_true, + NULL); + + graphene_box2d_init_from_rect (&b, &r); + + mutest_expect ("init_from_rect() to generate a valid box", + mutest_bool_value (graphene_box2d_equal (&b, &a)), + mutest_to_be_true, + NULL); +} + static void box2d_size (mutest_spec_t *spec) { @@ -517,12 +541,37 @@ box2d_contains_box (mutest_spec_t *spec) NULL); } +static void +box2d_linear_transform (void) +{ + graphene_box2d_t a, res; + graphene_vec2_t tmp; + + graphene_box2d_init_from_box (&a, graphene_box2d_one ()); + graphene_box2d_scale_offset (&a, + graphene_vec2_init (&tmp, 2.f, 2.f), + &GRAPHENE_POINT_INIT (1.f, 2.f), + &res); + + graphene_rect_t r, check; + graphene_rect_init (&check, 0.f, 0.f, 1.f, 1.f); + graphene_rect_scale (&check, 2.f, 2.f, &check); + graphene_rect_offset_r (&check, 1.f, 2.f, &check); + + graphene_box2d_to_rect (&res, &r); + mutest_expect ("scaled and offset box to match equivalent rectangle", + mutest_bool_value (graphene_rect_equal (&r, &check)), + mutest_to_be_true, + NULL); +} + static void box2d_suite (mutest_suite_t *suite) { mutest_it ("initializes min/max points", box2d_init_min_max); mutest_it ("initializes from points", box2d_init_from_points); mutest_it ("initializes from vectors", box2d_init_from_vectors); + mutest_it ("roundtrips between rect", box2d_init_from_rect); mutest_it ("has the correct sizes", box2d_size); mutest_it ("has the correct center point", box2d_center); mutest_it ("has equality", box2d_equal); @@ -533,6 +582,7 @@ box2d_suite (mutest_suite_t *suite) mutest_it ("expands by scalar", box2d_expand_by_scalar); mutest_it ("contains point", box2d_contains_point); mutest_it ("contains box", box2d_contains_box); + mutest_it ("transforms linearly", box2d_linear_transform); } MUTEST_MAIN ( From 366779cb2acd855c2e1353bf0741231a29c0146a Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 12 Aug 2024 11:21:09 +0100 Subject: [PATCH 08/13] doc: Update the API reference --- doc/graphene-sections.txt | 6 ++++++ include/graphene-box2d.h | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/graphene-sections.txt b/doc/graphene-sections.txt index 3e51708d..ae95819f 100644 --- a/doc/graphene-sections.txt +++ b/doc/graphene-sections.txt @@ -43,12 +43,16 @@ graphene_box2d_free graphene_box2d_init graphene_box2d_init_from_box graphene_box2d_init_from_points +graphene_box2d_init_from_rect graphene_box2d_init_from_vec2 graphene_box2d_init_from_vectors graphene_box2d_equal +graphene_box2d_to_rect +graphene_box2d_to_float graphene_box2d_expand graphene_box2d_expand_scalar graphene_box2d_expand_vec2 +graphene_box2d_get_minmax graphene_box2d_get_min graphene_box2d_get_max graphene_box2d_get_center @@ -58,8 +62,10 @@ graphene_box2d_get_size graphene_box2d_get_vertices graphene_box2d_union graphene_box2d_intersection +graphene_box2d_intersects graphene_box2d_contains_box graphene_box2d_contains_point +graphene_box2d_contains_rect graphene_box2d_zero graphene_box2d_one diff --git a/include/graphene-box2d.h b/include/graphene-box2d.h index 8b2648a9..a0b7b8fe 100644 --- a/include/graphene-box2d.h +++ b/include/graphene-box2d.h @@ -58,7 +58,7 @@ graphene_box2d_t * graphene_box2d_init_from_vec2 (graphene_bo const graphene_vec2_t *max); GRAPHENE_AVAILABLE_IN_1_12 graphene_box2d_t * graphene_box2d_init_from_rect (graphene_box2d_t *box, - const graphene_rect_t *rect); + const graphene_rect_t *src); GRAPHENE_AVAILABLE_IN_1_12 void graphene_box2d_expand (const graphene_box2d_t *box, From 4d169fec48368a70a0d07b248d42acffe45cfae0 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 12 Aug 2024 11:33:10 +0100 Subject: [PATCH 09/13] docs: Add 1.12 index --- doc/graphene-docs.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/graphene-docs.xml b/doc/graphene-docs.xml index d3d2a780..b8b62212 100644 --- a/doc/graphene-docs.xml +++ b/doc/graphene-docs.xml @@ -79,6 +79,10 @@ Index of new symbols in 1.10 + + Index of new symbols in 1.12 + + From dc9ae9196222ccea1fb42e70e14e4443cfd2203b Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 12 Aug 2024 11:33:34 +0100 Subject: [PATCH 10/13] Rework box2d.intersects() GTK-doc gets very confused by static inlines, so we are going to need to add a non-inline stub and use a macro to rename the inline function in the header. --- include/graphene-box2d.h | 55 ++++++++++++++++++---------------------- src/graphene-box2d.c | 20 +++++++++++++++ 2 files changed, 44 insertions(+), 31 deletions(-) diff --git a/include/graphene-box2d.h b/include/graphene-box2d.h index a0b7b8fe..29a01723 100644 --- a/include/graphene-box2d.h +++ b/include/graphene-box2d.h @@ -132,26 +132,31 @@ GRAPHENE_AVAILABLE_IN_1_12 bool graphene_box2d_equal (const graphene_box2d_t *a, const graphene_box2d_t *b); -static inline bool -graphene_box2d_intersects (const graphene_box2d_t *a, - const graphene_box2d_t *b); +GRAPHENE_AVAILABLE_IN_1_12 +const graphene_box2d_t * graphene_box2d_zero (void); +GRAPHENE_AVAILABLE_IN_1_12 +const graphene_box2d_t * graphene_box2d_one (void); +GRAPHENE_AVAILABLE_IN_1_12 +const graphene_box2d_t * graphene_box2d_minus_one (void); +GRAPHENE_AVAILABLE_IN_1_12 +const graphene_box2d_t * graphene_box2d_one_minus_one (void); +GRAPHENE_AVAILABLE_IN_1_12 +const graphene_box2d_t * graphene_box2d_infinite (void); +GRAPHENE_AVAILABLE_IN_1_12 +const graphene_box2d_t * graphene_box2d_empty (void); + +GRAPHENE_AVAILABLE_IN_1_12 +bool graphene_box2d_intersects (const graphene_box2d_t *a, + const graphene_box2d_t *b); + +#ifndef __GTK_DOC_IGNORE__ + +#define graphene_box2d_intersects(a,b) \ + graphene_box2d_intersects_inline ((a), (b)) -/** - * graphene_box2d_intersects: - * @a: a #graphene_box2d_t - * @b: a #graphene_box2d_t - * - * Checks whether two boxes intersect. - * - * See also: graphene_box2d_intersection() - * - * Returns: true if the boxes intersect, and false otherwise - * - * Since: 1.12 - */ static inline bool -graphene_box2d_intersects (const graphene_box2d_t *a, - const graphene_box2d_t *b) +graphene_box2d_intersects_inline (const graphene_box2d_t *a, + const graphene_box2d_t *b) { graphene_point_t min_a, max_a; graphene_box2d_get_minmax (a, &min_a, &max_a); @@ -171,18 +176,6 @@ graphene_box2d_intersects (const graphene_box2d_t *a, return true; } - -GRAPHENE_AVAILABLE_IN_1_12 -const graphene_box2d_t * graphene_box2d_zero (void); -GRAPHENE_AVAILABLE_IN_1_12 -const graphene_box2d_t * graphene_box2d_one (void); -GRAPHENE_AVAILABLE_IN_1_12 -const graphene_box2d_t * graphene_box2d_minus_one (void); -GRAPHENE_AVAILABLE_IN_1_12 -const graphene_box2d_t * graphene_box2d_one_minus_one (void); -GRAPHENE_AVAILABLE_IN_1_12 -const graphene_box2d_t * graphene_box2d_infinite (void); -GRAPHENE_AVAILABLE_IN_1_12 -const graphene_box2d_t * graphene_box2d_empty (void); +#endif /* __GTK_DOC_IGNORE__ */ GRAPHENE_END_DECLS diff --git a/src/graphene-box2d.c b/src/graphene-box2d.c index 5d33f93b..43222ee9 100644 --- a/src/graphene-box2d.c +++ b/src/graphene-box2d.c @@ -486,6 +486,26 @@ graphene_box2d_intersection (const graphene_box2d_t *a, return true; } +/** + * graphene_box2d_intersects: + * @a: a #graphene_box2d_t + * @b: a #graphene_box2d_t + * + * Checks whether two boxes intersect. + * + * See also: graphene_box2d_intersection() + * + * Returns: true if the boxes intersect, and false otherwise + * + * Since: 1.12 + */ +bool +(graphene_box2d_intersects) (const graphene_box2d_t *a, + const graphene_box2d_t *b) +{ + return graphene_box2d_intersects_inline (a, b); +} + /** * graphene_box2d_get_width: * @box: a #graphene_box2d_t From a93c9e0f36d2f337ee2d995804883771c4879444 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 12 Aug 2024 11:35:06 +0100 Subject: [PATCH 11/13] docs: Add missing symbols to the API reference --- doc/graphene-sections.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/graphene-sections.txt b/doc/graphene-sections.txt index ae95819f..575cb55b 100644 --- a/doc/graphene-sections.txt +++ b/doc/graphene-sections.txt @@ -66,6 +66,7 @@ graphene_box2d_intersects graphene_box2d_contains_box graphene_box2d_contains_point graphene_box2d_contains_rect +graphene_box2d_scale_offset graphene_box2d_zero graphene_box2d_one @@ -121,6 +122,7 @@ graphene_frustum_equal graphene-gobject GRAPHENE_TYPE_BOX +GRAPHENE_TYPE_BOX2D GRAPHENE_TYPE_EULER GRAPHENE_TYPE_FRUSTUM GRAPHENE_TYPE_MATRIX @@ -138,6 +140,7 @@ GRAPHENE_TYPE_VEC2 GRAPHENE_TYPE_VEC3 GRAPHENE_TYPE_VEC4 graphene_box_get_type +graphene_box2d_get_type graphene_euler_get_type graphene_frustum_get_type graphene_matrix_get_type @@ -486,6 +489,17 @@ graphene_simd4f_floor graphene_simd4f_union_t graphene_simd4i_union_t graphene_simd2f_t +sw +sx +sy +sz +vandq_s32 +vcombine_f32 +vget_lane_f32 +vgetq_lane_u32 +vld1q_f32 +vmulq_f32 +vreinterpretq_f32_u32
From 053a7469c83981f57a6aaf0bba0b97fe272c7f3e Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 12 Aug 2024 11:35:17 +0100 Subject: [PATCH 12/13] build: Add a check for the API reference coverage Meson's gnome.gtkdoc() can generate a test target that will run gtkdoc-check, to verify that all the symbols are properly documented. --- doc/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/meson.build b/doc/meson.build index 7f1d9469..0007a83a 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -47,4 +47,5 @@ gnome.gtkdoc('graphene', ], html_assets: html_images, install: true, + check: true, ) From c25e7f15dc8e082af3fe79b235d67dc8a64237ff Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Mon, 12 Aug 2024 15:38:27 +0100 Subject: [PATCH 13/13] Fix the GType registration for graphene_box2d_t --- include/graphene-gobject.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/graphene-gobject.h b/include/graphene-gobject.h index 794eaa35..a5545e79 100644 --- a/include/graphene-gobject.h +++ b/include/graphene-gobject.h @@ -149,7 +149,7 @@ GType graphene_ray_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC(graphene_ray_t, graphene_ray_free) -#define GRAPHENE_TYPE_BOX (graphene_box_get_type ()) +#define GRAPHENE_TYPE_BOX2D (graphene_box2d_get_type ()) GRAPHENE_AVAILABLE_IN_1_12 GType graphene_box2d_get_type (void);