1/* graphene-point3d.c: Point in 3D space
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-point3d
28 * @Title: Point3D
29 * @Short_Description: A point with 3 coordinates
30 *
31 * #graphene_point3d_t is a data structure capable of describing a point with
32 * three coordinates:
33 *
34 * * @graphene_point3d_t.x
35 * * @graphene_point3d_t.y
36 * * @graphene_point3d_t.z
37 */
38
39#include "graphene-private.h"
40#include "graphene-point3d.h"
41#include "graphene-rect.h"
42#include "graphene-simd4f.h"
43#include "graphene-vec3.h"
44
45#include <math.h>
46
47/**
48 * graphene_point3d_alloc: (constructor)
49 *
50 * Allocates a #graphene_point3d_t structure.
51 *
52 * Returns: (transfer full): the newly allocated structure.
53 * Use graphene_point3d_free() to free the resources
54 * allocated by this function.
55 *
56 * Since: 1.0
57 */
58graphene_point3d_t *
59graphene_point3d_alloc (void)
60{
61 return calloc (nmemb: 1, size: sizeof (graphene_point3d_t));
62}
63
64/**
65 * graphene_point3d_free:
66 * @p: a #graphene_point3d_t
67 *
68 * Frees the resources allocated via graphene_point3d_alloc().
69 *
70 * Since: 1.0
71 */
72void
73graphene_point3d_free (graphene_point3d_t *p)
74{
75 free (ptr: p);
76}
77
78/**
79 * graphene_point3d_init:
80 * @p: the #graphene_point3d_t to initialize
81 * @x: the X coordinate of the point
82 * @y: the Y coordinate of the point
83 * @z: the Z coordinate of the point
84 *
85 * Initializes a #graphene_point3d_t with the given coordinates.
86 *
87 * Returns: (transfer none): the initialized #graphene_point3d_t
88 *
89 * Since: 1.0
90 */
91graphene_point3d_t *
92graphene_point3d_init (graphene_point3d_t *p,
93 float x,
94 float y,
95 float z)
96{
97 p->x = x;
98 p->y = y;
99 p->z = z;
100
101 return p;
102}
103
104static inline graphene_point3d_t *
105graphene_point3d_init_from_simd4f (graphene_point3d_t *p,
106 const graphene_simd4f_t v)
107{
108 /* XXX: Apparently clang 3.4 does not allow vectorised partial reads
109 * when using a pointer to a structure; this is possibly caused by some
110 * of the optimization flags we use, which means we need this ugly
111 * compiler check here. Clang 3.5 and GCC are perfectly happy with it,
112 * but Travis-CI still uses Clang 3.4.
113 */
114#if defined(__clang_major__) && (__clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 5))
115 p->x = graphene_simd4f_get (v, 0);
116 p->y = graphene_simd4f_get (v, 1);
117 p->z = graphene_simd4f_get (v, 2);
118#else
119 graphene_simd4f_dup_3f (v, &(p->x));
120#endif
121
122 return p;
123}
124
125/**
126 * graphene_point3d_init_from_point:
127 * @p: a #graphene_point3d_t
128 * @src: a #graphene_point3d_t
129 *
130 * Initializes a #graphene_point3d_t using the coordinates of
131 * another #graphene_point3d_t.
132 *
133 * Returns: (transfer none): the initialized point
134 *
135 * Since: 1.0
136 */
137graphene_point3d_t *
138graphene_point3d_init_from_point (graphene_point3d_t *p,
139 const graphene_point3d_t *src)
140{
141 *p = *src;
142
143 return p;
144}
145
146/**
147 * graphene_point3d_init_from_vec3:
148 * @p: a #graphene_point3d_t
149 * @v: a #graphene_vec3_t
150 *
151 * Initializes a #graphene_point3d_t using the components
152 * of a #graphene_vec3_t.
153 *
154 * Returns: (transfer none): the initialized #graphene_point3d_t
155 *
156 * Since: 1.0
157 */
158graphene_point3d_t *
159graphene_point3d_init_from_vec3 (graphene_point3d_t *p,
160 const graphene_vec3_t *v)
161{
162 return graphene_point3d_init_from_simd4f (p, v: v->value);
163}
164
165/**
166 * graphene_point3d_to_vec3:
167 * @p: a #graphene_point3d_t
168 * @v: (out caller-allocates): return location for a #graphene_vec3_t
169 *
170 * Stores the coordinates of a #graphene_point3d_t into a
171 * #graphene_vec3_t.
172 *
173 * Since: 1.0
174 */
175void
176graphene_point3d_to_vec3 (const graphene_point3d_t *p,
177 graphene_vec3_t *v)
178{
179 v->value = graphene_simd4f_init (p->x, p->y, p->z, 0.f);
180}
181
182static bool
183point3d_equal (const void *p1,
184 const void *p2)
185{
186 const graphene_point3d_t *a = p1;
187 const graphene_point3d_t *b = p2;
188
189 return graphene_point3d_near (a, b, GRAPHENE_FLOAT_EPSILON);
190}
191
192/**
193 * graphene_point3d_equal:
194 * @a: a #graphene_point3d_t
195 * @b: a #graphene_point3d_t
196 *
197 * Checks whether two given points are equal.
198 *
199 * Returns: `true` if the points are equal
200 *
201 * Since: 1.0
202 */
203bool
204graphene_point3d_equal (const graphene_point3d_t *a,
205 const graphene_point3d_t *b)
206{
207 return graphene_pointer_equal (p1: a, p2: b, func: point3d_equal);
208}
209
210/**
211 * graphene_point3d_near:
212 * @a: a #graphene_point3d_t
213 * @b: a #graphene_point3d_t
214 * @epsilon: fuzzyness factor
215 *
216 * Checks whether the two points are near each other, within
217 * an @epsilon factor.
218 *
219 * Returns: `true` if the points are near each other
220 *
221 * Since: 1.0
222 */
223bool
224graphene_point3d_near (const graphene_point3d_t *a,
225 const graphene_point3d_t *b,
226 float epsilon)
227{
228 if (a == b)
229 return true;
230
231 graphene_simd4f_t v_a = graphene_simd4f_init (a->x, a->y, a->z, 0.f);
232 graphene_simd4f_t v_b = graphene_simd4f_init (b->x, b->y, b->z, 0.f);
233 graphene_simd4f_t v_res = graphene_simd4f_sub (v_a, v_b);
234
235 return fabsf (graphene_simd4f_get_x (v_res)) < epsilon &&
236 fabsf (graphene_simd4f_get_y (v_res)) < epsilon &&
237 fabsf (graphene_simd4f_get_z (v_res)) < epsilon;
238}
239
240/**
241 * graphene_point3d_scale:
242 * @p: a #graphene_point3d_t
243 * @factor: the scaling factor
244 * @res: (out caller-allocates): return location for the scaled point
245 *
246 * Scales the coordinates of the given #graphene_point3d_t by
247 * the given @factor.
248 *
249 * Since: 1.0
250 */
251void
252graphene_point3d_scale (const graphene_point3d_t *p,
253 float factor,
254 graphene_point3d_t *res)
255{
256 graphene_simd4f_t v;
257
258 v = graphene_simd4f_init (p->x, p->y, p->z, 0.f);
259 v = graphene_simd4f_mul (v, graphene_simd4f_splat (factor));
260
261 graphene_point3d_init_from_simd4f (p: res, v);
262}
263
264/**
265 * graphene_point3d_cross:
266 * @a: a #graphene_point3d_t
267 * @b: a #graphene_point3d_t
268 * @res: (out caller-allocates): return location for the cross
269 * product
270 *
271 * Computes the cross product of the two given #graphene_point3d_t.
272 *
273 * Since: 1.0
274 */
275void
276graphene_point3d_cross (const graphene_point3d_t *a,
277 const graphene_point3d_t *b,
278 graphene_point3d_t *res)
279{
280 graphene_simd4f_t v_a = graphene_simd4f_init (a->x, a->y, a->z, 0.f);
281 graphene_simd4f_t v_b = graphene_simd4f_init (b->x, b->y, b->z, 0.f);
282 graphene_simd4f_t v_res = graphene_simd4f_cross3 (v_a, v_b);
283
284 graphene_point3d_init_from_simd4f (p: res, v: v_res);
285}
286
287/**
288 * graphene_point3d_dot:
289 * @a: a #graphene_point3d_t
290 * @b: a #graphene_point3d_t
291 *
292 * Computes the dot product of the two given #graphene_point3d_t.
293 *
294 * Returns: the value of the dot product
295 *
296 * Since: 1.0
297 */
298float
299graphene_point3d_dot (const graphene_point3d_t *a,
300 const graphene_point3d_t *b)
301{
302 graphene_simd4f_t v_a = graphene_simd4f_init (a->x, a->y, a->z, 0.f);
303 graphene_simd4f_t v_b = graphene_simd4f_init (b->x, b->y, b->z, 0.f);
304
305 return graphene_simd4f_dot3_scalar (v_a, v_b);
306}
307
308/**
309 * graphene_point3d_length:
310 * @p: a #graphene_point3d_t
311 *
312 * Computes the length of the vector represented by the
313 * coordinates of the given #graphene_point3d_t.
314 *
315 * Returns: the length of the vector represented by the point
316 *
317 * Since: 1.0
318 */
319float
320graphene_point3d_length (const graphene_point3d_t *p)
321{
322 graphene_simd4f_t res = graphene_simd4f_init (p->x, p->y, p->z, 0.f);
323
324 return graphene_simd4f_get_x (graphene_simd4f_length3 (res));
325}
326
327/**
328 * graphene_point3d_normalize:
329 * @p: a #graphene_point3d_t
330 * @res: (out caller-allocates): return location for the normalized
331 * #graphene_point3d_t
332 *
333 * Computes the normalization of the vector represented by the
334 * coordinates of the given #graphene_point3d_t.
335 *
336 * Since: 1.0
337 */
338void
339graphene_point3d_normalize (const graphene_point3d_t *p,
340 graphene_point3d_t *res)
341{
342 graphene_simd4f_t v;
343
344 v = graphene_simd4f_init (p->x, p->y, p->z, 0.f);
345 v = graphene_simd4f_normalize3 (v);
346
347 graphene_point3d_init_from_simd4f (p: res, v);
348}
349
350/**
351 * graphene_point3d_distance:
352 * @a: a #graphene_point3d_t
353 * @b: a #graphene_point3d_t
354 * @delta: (out caller-allocates) (optional): return location for the distance
355 * components on the X, Y, and Z axis
356 *
357 * Computes the distance between the two given #graphene_point3d_t.
358 *
359 * Returns: the distance between two points
360 *
361 * Since: 1.4
362 */
363float
364graphene_point3d_distance (const graphene_point3d_t *a,
365 const graphene_point3d_t *b,
366 graphene_vec3_t *delta)
367{
368 graphene_vec3_t v_a, v_b, res;
369
370 graphene_point3d_to_vec3 (p: a, v: &v_a);
371 graphene_point3d_to_vec3 (p: b, v: &v_b);
372
373 graphene_vec3_subtract (a: &v_a, b: &v_b, res: &res);
374 if (delta != NULL)
375 {
376 graphene_vec3_init (v: delta,
377 x: fabsf (x: graphene_vec3_get_x (v: &res)),
378 y: fabsf (x: graphene_vec3_get_y (v: &res)),
379 z: fabsf (x: graphene_vec3_get_z (v: &res)));
380 }
381
382 return graphene_vec3_length (v: &res);
383}
384
385/**
386 * graphene_point3d_interpolate:
387 * @a: a #graphene_point3d_t
388 * @b: a #graphene_point3d_t
389 * @factor: the interpolation factor
390 * @res: (out caller-allocates): the return location for the
391 * interpolated #graphene_point3d_t
392 *
393 * Linearly interpolates each component of @a and @b using the
394 * provided @factor, and places the result in @res.
395 *
396 * Since: 1.0
397 */
398void
399graphene_point3d_interpolate (const graphene_point3d_t *a,
400 const graphene_point3d_t *b,
401 double factor,
402 graphene_point3d_t *res)
403{
404 res->x = graphene_lerp (a: a->x, b: b->x, factor);
405 res->y = graphene_lerp (a: a->y, b: b->y, factor);
406 res->z = graphene_lerp (a: a->z, b: b->z, factor);
407}
408
409/**
410 * graphene_point3d_normalize_viewport:
411 * @p: a #graphene_point3d_t
412 * @viewport: a #graphene_rect_t representing a viewport
413 * @z_near: the coordinate of the near clipping plane, or 0 for
414 * the default near clipping plane
415 * @z_far: the coordinate of the far clipping plane, or 1 for the
416 * default far clipping plane
417 * @res: (out caller-allocates): the return location for the
418 * normalized #graphene_point3d_t
419 *
420 * Normalizes the coordinates of a #graphene_point3d_t using the
421 * given viewport and clipping planes.
422 *
423 * The coordinates of the resulting #graphene_point3d_t will be
424 * in the [ -1, 1 ] range.
425 *
426 * Since: 1.4
427 */
428void
429graphene_point3d_normalize_viewport (const graphene_point3d_t *p,
430 const graphene_rect_t *viewport,
431 float z_near,
432 float z_far,
433 graphene_point3d_t *res)
434{
435 res->x = (p->x - viewport->origin.x) / viewport->size.width;
436 res->y = (p->y - viewport->origin.y) / viewport->size.height;
437 res->z = (p->z - z_near) / (z_far - z_near);
438
439 res->x = CLAMP (res->x * 2.f - 1.f, -1.f, 1.f);
440 res->y = CLAMP (res->y * 2.f - 1.f, -1.f, 1.f);
441 res->z = CLAMP (res->z * 2.f - 1.f, -1.f, 1.f);
442}
443
444static const graphene_point3d_t _graphene_point3d_zero;
445
446/**
447 * graphene_point3d_zero:
448 *
449 * Retrieves a constant point with all three coordinates set to 0.
450 *
451 * Returns: (transfer none): a zero point
452 *
453 * Since: 1.0
454 */
455const graphene_point3d_t *
456graphene_point3d_zero (void)
457{
458 return &_graphene_point3d_zero;
459}
460

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