1/* graphene-sphere.c: A sphere
2 *
3 * SPDX-License-Identifier: MIT
4 *
5 * Copyright 2014 Emmanuele Bassi
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25
26/**
27 * SECTION:graphene-sphere
28 * @Title: Sphere
29 * @Short_Description: A sphere
30 *
31 * #graphene_sphere_t provides a representation of a sphere using its center
32 * and radius.
33 */
34
35#include "graphene-private.h"
36
37#include "graphene-sphere.h"
38
39#include "graphene-alloc-private.h"
40#include "graphene-box.h"
41#include "graphene-point3d.h"
42#include "graphene-simd4f.h"
43
44#include <math.h>
45
46/**
47 * graphene_sphere_alloc: (constructor)
48 *
49 * Allocates a new #graphene_sphere_t.
50 *
51 * The contents of the newly allocated structure are undefined.
52 *
53 * Returns: (transfer full): the newly allocated #graphene_sphere_t. Use
54 * graphene_sphere_free() to free the resources allocated by this function
55 *
56 * Since: 1.2
57 */
58graphene_sphere_t *
59graphene_sphere_alloc (void)
60{
61 return graphene_aligned_alloc0 (size: sizeof (graphene_sphere_t), number: 1, alignment: 16);
62}
63
64/**
65 * graphene_sphere_free:
66 * @s: a #graphene_sphere_t
67 *
68 * Frees the resources allocated by graphene_sphere_alloc().
69 *
70 * Since: 1.2
71 */
72void
73graphene_sphere_free (graphene_sphere_t *s)
74{
75 graphene_aligned_free (mem: s);
76}
77
78/**
79 * graphene_sphere_init:
80 * @s: the #graphene_sphere_t to initialize
81 * @center: (nullable): the coordinates of the center of the sphere, or %NULL
82 * for a center in (0, 0, 0)
83 * @radius: the radius of the sphere
84 *
85 * Initializes the given #graphene_sphere_t with the given @center and @radius.
86 *
87 * Returns: (transfer none): the initialized #graphene_sphere_t
88 *
89 * Since: 1.2
90 */
91graphene_sphere_t *
92graphene_sphere_init (graphene_sphere_t *s,
93 const graphene_point3d_t *center,
94 float radius)
95{
96 if (center != NULL)
97 graphene_point3d_to_vec3 (p: center, v: &s->center);
98 else
99 graphene_vec3_init_from_vec3 (v: &s->center, src: graphene_vec3_zero ());
100
101 s->radius = radius;
102
103 return s;
104}
105
106static float
107distance_sq (const graphene_vec3_t *p1,
108 const graphene_vec3_t *p2)
109{
110 graphene_vec3_t delta;
111
112 graphene_vec3_subtract (a: p1, b: p2, res: &delta);
113 return graphene_vec3_dot (a: &delta, b: &delta);
114}
115
116/**
117 * graphene_sphere_init_from_points:
118 * @s: the #graphene_sphere_t to initialize
119 * @n_points: the number of #graphene_point3d_t in the @points array
120 * @points: (array length=n_points): an array of #graphene_point3d_t
121 * @center: (nullable): the center of the sphere
122 *
123 * Initializes the given #graphene_sphere_t using the given array
124 * of 3D coordinates so that the sphere includes them.
125 *
126 * The center of the sphere can either be specified, or will be center
127 * of the 3D volume that encompasses all @points.
128 *
129 * Returns: (transfer none): the initialized #graphene_sphere_t
130 *
131 * Since: 1.2
132 */
133graphene_sphere_t *
134graphene_sphere_init_from_points (graphene_sphere_t *s,
135 unsigned int n_points,
136 const graphene_point3d_t *points,
137 const graphene_point3d_t *center)
138{
139 float max_radius_sq = 0.f;
140
141 if (center != NULL)
142 graphene_point3d_to_vec3 (p: center, v: &s->center);
143 else
144 {
145 graphene_box_t box;
146 graphene_point3d_t c;
147
148 graphene_box_init_from_points (box: &box, n_points, points);
149 graphene_box_get_center (box: &box, center: &c);
150
151 graphene_point3d_to_vec3 (p: &c, v: &s->center);
152 }
153
154 for (unsigned int i = 0; i < n_points; i++)
155 {
156 graphene_vec3_t p;
157
158 graphene_point3d_to_vec3 (p: &points[i], v: &p);
159
160 max_radius_sq = fmaxf (x: max_radius_sq, y: distance_sq (p1: &s->center, p2: &p));
161 }
162
163 s->radius = sqrtf (x: max_radius_sq);
164
165 return s;
166}
167
168/**
169 * graphene_sphere_init_from_vectors:
170 * @s: the #graphene_sphere_t to initialize
171 * @n_vectors: the number of #graphene_vec3_t in the @vectors array
172 * @vectors: (array length=n_vectors): an array of #graphene_vec3_t
173 * @center: (nullable): the center of the sphere
174 *
175 * Initializes the given #graphene_sphere_t using the given array
176 * of 3D coordinates so that the sphere includes them.
177 *
178 * The center of the sphere can either be specified, or will be center
179 * of the 3D volume that encompasses all @vectors.
180 *
181 * Returns: (transfer none): the initialized #graphene_sphere_t
182 *
183 * Since: 1.2
184 */
185graphene_sphere_t *
186graphene_sphere_init_from_vectors (graphene_sphere_t *s,
187 unsigned int n_vectors,
188 const graphene_vec3_t *vectors,
189 const graphene_point3d_t *center)
190{
191 float max_radius_sq = 0.f;
192
193 if (center != NULL)
194 graphene_point3d_to_vec3 (p: center, v: &s->center);
195 else
196 {
197 graphene_box_t box;
198 graphene_point3d_t c;
199
200 graphene_box_init_from_vectors (box: &box, n_vectors, vectors);
201 graphene_box_get_center (box: &box, center: &c);
202
203 graphene_point3d_to_vec3 (p: &c, v: &s->center);
204 }
205
206 for (unsigned int i = 0; i < n_vectors; i++)
207 max_radius_sq = fmaxf (x: max_radius_sq, y: distance_sq (p1: &s->center, p2: &vectors[i]));
208
209 s->radius = sqrtf (x: max_radius_sq);
210
211 return s;
212}
213
214/**
215 * graphene_sphere_get_center:
216 * @s: a #graphene_sphere_t
217 * @center: (out caller-allocates): return location for the coordinates of
218 * the center
219 *
220 * Retrieves the coordinates of the center of a #graphene_sphere_t.
221 *
222 * Since: 1.2
223 */
224void
225graphene_sphere_get_center (const graphene_sphere_t *s,
226 graphene_point3d_t *center)
227{
228 graphene_point3d_init_from_vec3 (p: center, v: &s->center);
229}
230
231/**
232 * graphene_sphere_get_radius:
233 * @s: a #graphene_sphere_t
234 *
235 * Retrieves the radius of a #graphene_sphere_t.
236 *
237 * Since: 1.2
238 */
239float
240graphene_sphere_get_radius (const graphene_sphere_t *s)
241{
242 return s->radius;
243}
244
245/**
246 * graphene_sphere_is_empty:
247 * @s: a #graphene_sphere_t
248 *
249 * Checks whether the sphere has a zero radius.
250 *
251 * Returns: `true` if the sphere is empty
252 *
253 * Since: 1.2
254 */
255bool
256graphene_sphere_is_empty (const graphene_sphere_t *s)
257{
258 return s != NULL && s->radius <= 0;
259}
260
261/**
262 * graphene_sphere_contains_point:
263 * @s: a #graphene_sphere_t
264 * @point: a #graphene_point3d_t
265 *
266 * Checks whether the given @point is contained in the volume
267 * of a #graphene_sphere_t.
268 *
269 * Returns: `true` if the sphere contains the point
270 *
271 * Since: 1.2
272 */
273bool
274graphene_sphere_contains_point (const graphene_sphere_t *s,
275 const graphene_point3d_t *point)
276{
277 graphene_vec3_t tmp;
278 float radius_sq;
279
280 graphene_point3d_to_vec3 (p: point, v: &tmp);
281 radius_sq = s->radius * s->radius;
282
283 if (distance_sq (p1: &s->center, p2: &tmp) <= radius_sq)
284 return true;
285
286 return false;
287}
288
289/**
290 * graphene_sphere_distance:
291 * @s: a #graphene_sphere_t
292 * @point: a #graphene_point3d_t
293 *
294 * Computes the distance of the given @point from the surface of
295 * a #graphene_sphere_t.
296 *
297 * Returns: the distance of the point
298 *
299 * Since: 1.2
300 */
301float
302graphene_sphere_distance (const graphene_sphere_t *s,
303 const graphene_point3d_t *point)
304{
305 graphene_vec3_t tmp;
306
307 graphene_point3d_to_vec3 (p: point, v: &tmp);
308
309 return sqrtf (x: distance_sq (p1: &s->center, p2: &tmp)) - s->radius;
310}
311
312/**
313 * graphene_sphere_get_bounding_box:
314 * @s: a #graphene_sphere_t
315 * @box: (out caller-allocates): return location for the bounding box
316 *
317 * Computes the bounding box capable of containing the
318 * given #graphene_sphere_t.
319 *
320 * Since: 1.2
321 */
322void
323graphene_sphere_get_bounding_box (const graphene_sphere_t *s,
324 graphene_box_t *box)
325{
326 graphene_box_init_from_vec3 (box, min: &s->center, max: &s->center);
327 graphene_box_expand_scalar (box, scalar: s->radius, res: box);
328}
329
330/**
331 * graphene_sphere_translate:
332 * @s: a #graphene_sphere_t
333 * @point: the coordinates of the translation
334 * @res: (out caller-allocates): return location for the translated sphere
335 *
336 * Translates the center of the given #graphene_sphere_t using the @point
337 * coordinates as the delta of the translation.
338 *
339 * Since: 1.2
340 */
341void
342graphene_sphere_translate (const graphene_sphere_t *s,
343 const graphene_point3d_t *point,
344 graphene_sphere_t *res)
345{
346 graphene_vec3_t tmp;
347
348 graphene_point3d_to_vec3 (p: point, v: &tmp);
349 graphene_vec3_add (a: &s->center, b: &tmp, res: &res->center);
350}
351
352static bool
353sphere_equal (const void *p1,
354 const void *p2)
355{
356 const graphene_sphere_t *a = p1;
357 const graphene_sphere_t *b = p2;
358
359 return graphene_vec3_equal (v1: &a->center, v2: &b->center) &&
360 graphene_approx_val (a: a->radius, b: b->radius);
361}
362
363/**
364 * graphene_sphere_equal:
365 * @a: a #graphene_sphere_t
366 * @b: a #graphene_sphere_t
367 *
368 * Checks whether two #graphene_sphere_t are equal.
369 *
370 * Returns: `true` if the spheres are equal
371 *
372 * Since: 1.2
373 */
374bool
375graphene_sphere_equal (const graphene_sphere_t *a,
376 const graphene_sphere_t *b)
377{
378 return graphene_pointer_equal (p1: a, p2: b, func: sphere_equal);
379}
380

source code of gtk/subprojects/graphene/src/graphene-sphere.c