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 | */ |
58 | graphene_point3d_t * |
59 | graphene_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 | */ |
72 | void |
73 | graphene_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 | */ |
91 | graphene_point3d_t * |
92 | graphene_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 | |
104 | static inline graphene_point3d_t * |
105 | graphene_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 | */ |
137 | graphene_point3d_t * |
138 | graphene_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 | */ |
158 | graphene_point3d_t * |
159 | graphene_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 | */ |
175 | void |
176 | graphene_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 | |
182 | static bool |
183 | point3d_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 | */ |
203 | bool |
204 | graphene_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 | */ |
223 | bool |
224 | graphene_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 | */ |
251 | void |
252 | graphene_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 | */ |
275 | void |
276 | graphene_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 | */ |
298 | float |
299 | graphene_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 | */ |
319 | float |
320 | graphene_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 | */ |
338 | void |
339 | graphene_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 | */ |
363 | float |
364 | graphene_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 | */ |
398 | void |
399 | graphene_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 | */ |
428 | void |
429 | graphene_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 | |
444 | static 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 | */ |
455 | const graphene_point3d_t * |
456 | graphene_point3d_zero (void) |
457 | { |
458 | return &_graphene_point3d_zero; |
459 | } |
460 | |