1/* graphene-vectors.c: Assorted vectors
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#include "graphene-private.h"
27#include "graphene-vectors-private.h"
28#include "graphene-alloc-private.h"
29
30#include <stdio.h>
31
32#ifdef HAVE_PTHREAD
33#include <pthread.h>
34#include <errno.h>
35#include <string.h>
36#include <stdio.h>
37#endif
38
39/**
40 * SECTION:graphene-vectors
41 * @Title: Vectors
42 * @Short_Description: Vectors in 2, 3, and 4 dimensions
43 *
44 * Graphene has three vector types, distinguished by their length:
45 *
46 * 1. #graphene_vec2_t, which holds 2 components x and y
47 * 2. #graphene_vec3_t, which holds 3 components x, y, and z
48 * 3. #graphene_vec4_t, which holds 4 components x, y, z, and w
49 *
50 * Each vector type should be treated as an opaque data type.
51 */
52
53/* vec2 {{{ */
54
55/**
56 * graphene_vec2_alloc: (constructor)
57 *
58 * Allocates a new #graphene_vec2_t structure.
59 *
60 * The contents of the returned structure are undefined.
61 *
62 * Use graphene_vec2_init() to initialize the vector.
63 *
64 * Returns: (transfer full): the newly allocated #graphene_vec2_t
65 * structure. Use graphene_vec2_free() to free the resources allocated
66 * by this function.
67 *
68 * Since: 1.0
69 */
70graphene_vec2_t *
71graphene_vec2_alloc (void)
72{
73 return graphene_aligned_alloc (size: sizeof (graphene_vec2_t), number: 1, alignment: 16);
74}
75
76/**
77 * graphene_vec2_free:
78 * @v: a #graphene_vec2_t
79 *
80 * Frees the resources allocated by @v
81 *
82 * Since: 1.0
83 */
84void
85graphene_vec2_free (graphene_vec2_t *v)
86{
87 graphene_aligned_free (mem: v);
88}
89
90/**
91 * graphene_vec2_init:
92 * @v: a #graphene_vec2_t
93 * @x: the X field of the vector
94 * @y: the Y field of the vector
95 *
96 * Initializes a #graphene_vec2_t using the given values.
97 *
98 * This function can be called multiple times.
99 *
100 * Returns: (transfer none): the initialized vector
101 *
102 * Since: 1.0
103 */
104graphene_vec2_t *
105graphene_vec2_init (graphene_vec2_t *v,
106 float x,
107 float y)
108{
109 v->value = graphene_simd4f_init (x, y, 0.f, 0.f);
110
111 return v;
112}
113
114/**
115 * graphene_vec2_init_from_vec2:
116 * @v: a #graphene_vec2_t
117 * @src: a #graphene_vec2_t
118 *
119 * Copies the contents of @src into @v.
120 *
121 * Returns: (transfer none): the initialized vector
122 *
123 * Since: 1.0
124 */
125graphene_vec2_t *
126graphene_vec2_init_from_vec2 (graphene_vec2_t *v,
127 const graphene_vec2_t *src)
128{
129 v->value = src->value;
130
131 return v;
132}
133
134/**
135 * graphene_vec2_init_from_float:
136 * @v: a #graphene_vec2_t
137 * @src: (array fixed-size=2): an array of floating point values
138 * with at least two elements
139 *
140 * Initializes @v with the contents of the given array.
141 *
142 * Returns: (transfer none): the initialized vector
143 *
144 * Since: 1.0
145 */
146graphene_vec2_t *
147graphene_vec2_init_from_float (graphene_vec2_t *v,
148 const float *src)
149{
150 v->value = graphene_simd4f_init_2f (src);
151
152 return v;
153}
154
155/**
156 * graphene_vec2_get_x:
157 * @v: a #graphene_vec2_t
158 *
159 * Retrieves the X component of the #graphene_vec2_t.
160 *
161 * Returns: the value of the X component
162 *
163 * Since: 1.0
164 */
165float
166graphene_vec2_get_x (const graphene_vec2_t *v)
167{
168 return graphene_simd4f_get_x (v->value);
169}
170
171/**
172 * graphene_vec2_get_y:
173 * @v: a #graphene_vec2_t
174 *
175 * Retrieves the Y component of the #graphene_vec2_t.
176 *
177 * Returns: the value of the Y component
178 *
179 * Since: 1.0
180 */
181float
182graphene_vec2_get_y (const graphene_vec2_t *v)
183{
184 return graphene_simd4f_get_y (v->value);
185}
186
187/**
188 * graphene_vec2_to_float:
189 * @v: a #graphene_vec2_t
190 * @dest: (out caller-allocates) (array fixed-size=2): return location
191 * for an array of floating point values with at least 2 elements
192 *
193 * Stores the components of @v into an array.
194 *
195 * Since: 1.0
196 */
197void
198graphene_vec2_to_float (const graphene_vec2_t *v,
199 float *dest)
200{
201 graphene_simd4f_dup_2f (v->value, dest);
202}
203
204/**
205 * graphene_vec2_add:
206 * @a: a #graphene_vec2_t
207 * @b: a #graphene_vec2_t
208 * @res: (out caller-allocates): return location for the result
209 *
210 * Adds each component of the two passed vectors and places
211 * each result into the components of @res.
212 *
213 * Since: 1.0
214 */
215void
216graphene_vec2_add (const graphene_vec2_t *a,
217 const graphene_vec2_t *b,
218 graphene_vec2_t *res)
219{
220 res->value = graphene_simd4f_add (a->value, b->value);
221}
222
223/**
224 * graphene_vec2_subtract:
225 * @a: a #graphene_vec2_t
226 * @b: a #graphene_vec2_t
227 * @res: (out caller-allocates): return location for the result
228 *
229 * Subtracts from each component of the first operand @a the
230 * corresponding component of the second operand @b and places
231 * each result into the components of @res.
232 *
233 * Since: 1.0
234 */
235void
236graphene_vec2_subtract (const graphene_vec2_t *a,
237 const graphene_vec2_t *b,
238 graphene_vec2_t *res)
239{
240 res->value = graphene_simd4f_sub (a->value, b->value);
241}
242
243/**
244 * graphene_vec2_multiply:
245 * @a: a #graphene_vec2_t
246 * @b: a #graphene_vec2_t
247 * @res: (out caller-allocates): return location for the result
248 *
249 * Multiplies each component of the two passed vectors and places
250 * each result into the components of @res.
251 *
252 * Since: 1.0
253 */
254void
255graphene_vec2_multiply (const graphene_vec2_t *a,
256 const graphene_vec2_t *b,
257 graphene_vec2_t *res)
258{
259 res->value = graphene_simd4f_mul (a->value, b->value);
260}
261
262/**
263 * graphene_vec2_divide:
264 * @a: a #graphene_vec2_t
265 * @b: a #graphene_vec2_t
266 * @res: (out caller-allocates): return location for the result
267 *
268 * Divides each component of the first operand @a by the corresponding
269 * component of the second operand @b, and places the results into the
270 * vector @res.
271 *
272 * Since: 1.0
273 */
274void
275graphene_vec2_divide (const graphene_vec2_t *a,
276 const graphene_vec2_t *b,
277 graphene_vec2_t *res)
278{
279 res->value = graphene_simd4f_div (a->value, b->value);
280}
281
282/**
283 * graphene_vec2_dot:
284 * @a: a #graphene_vec2_t
285 * @b: a #graphene_vec2_t
286 *
287 * Computes the dot product of the two given vectors.
288 *
289 * Returns: the dot product of the vectors
290 *
291 * Since: 1.0
292 */
293float
294graphene_vec2_dot (const graphene_vec2_t *a,
295 const graphene_vec2_t *b)
296{
297 return graphene_simd4f_get_x (graphene_simd4f_dot2 (a->value, b->value));
298}
299
300/**
301 * graphene_vec2_length:
302 * @v: a #graphene_vec2_t
303 *
304 * Computes the length of the given vector.
305 *
306 * Returns: the length of the vector
307 *
308 * Since: 1.0
309 */
310float
311graphene_vec2_length (const graphene_vec2_t *v)
312{
313 return graphene_simd4f_get_x (graphene_simd4f_length2 (v->value));
314}
315
316/**
317 * graphene_vec2_normalize:
318 * @v: a #graphene_vec2_t
319 * @res: (out caller-allocates): return location for the
320 * normalized vector
321 *
322 * Computes the normalized vector for the given vector @v.
323 *
324 * Since: 1.0
325 */
326void
327graphene_vec2_normalize (const graphene_vec2_t *v,
328 graphene_vec2_t *res)
329{
330 if (fabsf (x: graphene_vec2_length (v)) > FLT_EPSILON)
331 res->value = graphene_simd4f_normalize2 (v: v->value);
332 else
333 res->value = graphene_simd4f_init_zero ();
334}
335
336/**
337 * graphene_vec2_min:
338 * @a: a #graphene_vec2_t
339 * @b: a #graphene_vec2_t
340 * @res: (out caller-allocates): the resulting vector
341 *
342 * Compares the two given vectors and places the minimum
343 * values of each component into @res.
344 *
345 * Since: 1.0
346 */
347void
348graphene_vec2_min (const graphene_vec2_t *a,
349 const graphene_vec2_t *b,
350 graphene_vec2_t *res)
351{
352 res->value = graphene_simd4f_min (a->value, b->value);
353}
354
355/**
356 * graphene_vec2_max:
357 * @a: a #graphene_vec2_t
358 * @b: a #graphene_vec2_t
359 * @res: (out caller-allocates): the resulting vector
360 *
361 * Compares the two given vectors and places the maximum
362 * values of each component into @res.
363 *
364 * Since: 1.0
365 */
366void
367graphene_vec2_max (const graphene_vec2_t *a,
368 const graphene_vec2_t *b,
369 graphene_vec2_t *res)
370{
371 res->value = graphene_simd4f_max (a->value, b->value);
372}
373
374/**
375 * graphene_vec2_scale:
376 * @v: a #graphene_vec2_t
377 * @factor: the scalar factor
378 * @res: (out caller-allocates): return location for the result vector
379 *
380 * Multiplies all components of the given vector with the given scalar @factor.
381 *
382 * Since: 1.2
383 */
384void
385graphene_vec2_scale (const graphene_vec2_t *v,
386 float factor,
387 graphene_vec2_t *res)
388{
389 res->value = graphene_simd4f_mul (v->value, graphene_simd4f_splat (factor));
390}
391
392/**
393 * graphene_vec2_negate:
394 * @v: a #graphene_vec2_t
395 * @res: (out caller-allocates): return location for the result vector
396 *
397 * Negates the given #graphene_vec2_t.
398 *
399 * Since: 1.2
400 */
401void
402graphene_vec2_negate (const graphene_vec2_t *v,
403 graphene_vec2_t *res)
404{
405 res->value = graphene_simd4f_neg (v->value);
406}
407
408static bool
409vec2_equal (const void *p1,
410 const void *p2)
411{
412 const graphene_vec2_t *v1 = p1;
413 const graphene_vec2_t *v2 = p2;
414
415 if (graphene_simd4f_cmp_eq (v1->value, v2->value))
416 return true;
417
418 return graphene_vec2_near (v1, v2, GRAPHENE_FLOAT_EPSILON);
419}
420
421/**
422 * graphene_vec2_equal:
423 * @v1: a #graphene_vec2_t
424 * @v2: a #graphene_vec2_t
425 *
426 * Checks whether the two given #graphene_vec2_t are equal.
427 *
428 * Returns: `true` if the two vectors are equal, and false otherwise
429 *
430 * Since: 1.2
431 */
432bool
433graphene_vec2_equal (const graphene_vec2_t *v1,
434 const graphene_vec2_t *v2)
435{
436 return graphene_pointer_equal (p1: v1, p2: v2, func: vec2_equal);
437}
438
439/**
440 * graphene_vec2_near:
441 * @v1: a #graphene_vec2_t
442 * @v2: a #graphene_vec2_t
443 * @epsilon: the threshold between the two vectors
444 *
445 * Compares the two given #graphene_vec2_t vectors and checks
446 * whether their values are within the given @epsilon.
447 *
448 * Returns: `true` if the two vectors are near each other
449 *
450 * Since: 1.2
451 */
452bool
453graphene_vec2_near (const graphene_vec2_t *v1,
454 const graphene_vec2_t *v2,
455 float epsilon)
456{
457 graphene_simd4f_t d = graphene_simd4f_sub (v1->value, v2->value);
458 float epsilon_sq = epsilon * epsilon;
459
460 return graphene_simd4f_get_x (graphene_simd4f_dot2 (d, d)) < epsilon_sq;
461}
462
463/**
464 * graphene_vec2_interpolate:
465 * @v1: a #graphene_vec2_t
466 * @v2: a #graphene_vec2_t
467 * @factor: the interpolation factor
468 * @res: (out caller-allocates): the interpolated vector
469 *
470 * Linearly interpolates @v1 and @v2 using the given @factor.
471 *
472 * Since: 1.10
473 */
474void
475graphene_vec2_interpolate (const graphene_vec2_t *v1,
476 const graphene_vec2_t *v2,
477 double factor,
478 graphene_vec2_t *res)
479{
480 res->value = graphene_simd4f_interpolate (a: v1->value, b: v2->value, f: (float) factor);
481}
482
483enum {
484 VEC2_ZERO,
485 VEC2_ONE,
486 VEC2_X_AXIS,
487 VEC2_Y_AXIS,
488
489 N_STATIC_VEC2
490};
491
492static graphene_vec2_t static_vec2[N_STATIC_VEC2];
493
494static void
495init_static_vec2_once (void)
496{
497 static_vec2[VEC2_ZERO].value = graphene_simd4f_init_zero ();
498 static_vec2[VEC2_ONE].value = graphene_simd4f_init (1.f, 1.f, 0.f, 0.f);
499 static_vec2[VEC2_X_AXIS].value = graphene_simd4f_init (1.f, 0.f, 0.f, 0.f);
500 static_vec2[VEC2_Y_AXIS].value = graphene_simd4f_init (0.f, 1.f, 0.f, 0.f);
501}
502
503#ifdef HAVE_PTHREAD
504static pthread_once_t static_vec2_once = PTHREAD_ONCE_INIT;
505
506static inline void
507init_static_vec2 (void)
508{
509 int status = pthread_once (&static_vec2_once, init_static_vec2_once);
510
511 if (status < 0)
512 {
513 int saved_errno = errno;
514
515 fprintf (stderr, "pthread_once failed: %s (errno:%d)\n",
516 strerror (saved_errno),
517 saved_errno);
518 }
519}
520
521#elif defined(HAVE_INIT_ONCE)
522static INIT_ONCE static_vec2_once = INIT_ONCE_STATIC_INIT;
523
524static BOOL CALLBACK
525InitVec2Func (PINIT_ONCE InitOnce,
526 PVOID Parameter,
527 PVOID *lpContext)
528{
529 init_static_vec2_once ();
530 return TRUE;
531}
532
533static inline void
534init_static_vec2 (void)
535{
536 BOOL bStatus = InitOnceExecuteOnce (&static_vec2_once,
537 InitVec2Func,
538 NULL,
539 NULL);
540
541 if (!bStatus)
542 fprintf (stderr, "InitOnceExecuteOnce failed\n");
543}
544
545#else /* !HAVE_PTHREAD && !HAVE_INIT_ONCE */
546static bool static_vec2_init = false;
547
548static inline void
549init_static_vec2 (void)
550{
551 if (static_vec2_init)
552 return;
553
554 init_static_vec2_once ();
555 static_vec2_init = true;
556}
557#endif /* HAVE_PTHREAD */
558
559/**
560 * graphene_vec2_zero:
561 *
562 * Retrieves a constant vector with (0, 0) components.
563 *
564 * Returns: (transfer none): the zero vector
565 *
566 * Since: 1.0
567 */
568const graphene_vec2_t *
569graphene_vec2_zero (void)
570{
571 init_static_vec2 ();
572
573 return &(static_vec2[VEC2_ZERO]);
574}
575
576/**
577 * graphene_vec2_one:
578 *
579 * Retrieves a constant vector with (1, 1) components.
580 *
581 * Returns: (transfer none): the one vector
582 *
583 * Since: 1.0
584 */
585const graphene_vec2_t *
586graphene_vec2_one (void)
587{
588 init_static_vec2 ();
589
590 return &(static_vec2[VEC2_ONE]);
591}
592
593/**
594 * graphene_vec2_x_axis:
595 *
596 * Retrieves a constant vector with (1, 0) components.
597 *
598 * Returns: (transfer none): the X axis vector
599 *
600 * Since: 1.0
601 */
602const graphene_vec2_t *
603graphene_vec2_x_axis (void)
604{
605 init_static_vec2 ();
606
607 return &(static_vec2[VEC2_X_AXIS]);
608}
609
610/**
611 * graphene_vec2_y_axis:
612 *
613 * Retrieves a constant vector with (0, 1) components.
614 *
615 * Returns: (transfer none): the Y axis vector
616 *
617 * Since: 1.0
618 */
619const graphene_vec2_t *
620graphene_vec2_y_axis (void)
621{
622 init_static_vec2 ();
623
624 return &(static_vec2[VEC2_Y_AXIS]);
625}
626
627/* }}} vec2 */
628
629/* vec3 {{{ */
630
631/**
632 * graphene_vec3_alloc: (constructor)
633 *
634 * Allocates a new #graphene_vec3_t structure.
635 *
636 * The contents of the returned structure are undefined.
637 *
638 * Use graphene_vec3_init() to initialize the vector.
639 *
640 * Returns: (transfer full): the newly allocated #graphene_vec3_t
641 * structure. Use graphene_vec3_free() to free the resources allocated
642 * by this function.
643 *
644 * Since: 1.0
645 */
646graphene_vec3_t *
647graphene_vec3_alloc (void)
648{
649 return graphene_aligned_alloc (size: sizeof (graphene_vec3_t), number: 1, alignment: 16);
650}
651
652/**
653 * graphene_vec3_free:
654 * @v: a #graphene_vec3_t
655 *
656 * Frees the resources allocated by @v
657 *
658 * Since: 1.0
659 */
660void
661graphene_vec3_free (graphene_vec3_t *v)
662{
663 graphene_aligned_free (mem: v);
664}
665
666/**
667 * graphene_vec3_init:
668 * @v: a #graphene_vec3_t
669 * @x: the X field of the vector
670 * @y: the Y field of the vector
671 * @z: the Z field of the vector
672 *
673 * Initializes a #graphene_vec3_t using the given values.
674 *
675 * This function can be called multiple times.
676 *
677 * Returns: (transfer none): a pointer to the initialized
678 * vector
679 *
680 * Since: 1.0
681 */
682graphene_vec3_t *
683graphene_vec3_init (graphene_vec3_t *v,
684 float x,
685 float y,
686 float z)
687{
688 v->value = graphene_simd4f_init (x, y, z, 0.f);
689
690 return v;
691}
692
693/**
694 * graphene_vec3_init_from_vec3:
695 * @v: a #graphene_vec3_t
696 * @src: a #graphene_vec3_t
697 *
698 * Initializes a #graphene_vec3_t with the values of another
699 * #graphene_vec3_t.
700 *
701 * Returns: (transfer none): the initialized vector
702 *
703 * Since: 1.0
704 */
705graphene_vec3_t *
706graphene_vec3_init_from_vec3 (graphene_vec3_t *v,
707 const graphene_vec3_t *src)
708{
709 v->value = src->value;
710
711 return v;
712}
713
714/**
715 * graphene_vec3_init_from_float:
716 * @v: a #graphene_vec3_t
717 * @src: (array fixed-size=3): an array of 3 floating point values
718 *
719 * Initializes a #graphene_vec3_t with the values from an array.
720 *
721 * Returns: (transfer none): the initialized vector
722 *
723 * Since: 1.0
724 */
725graphene_vec3_t *
726graphene_vec3_init_from_float (graphene_vec3_t *v,
727 const float *src)
728{
729 v->value = graphene_simd4f_init_3f (src);
730
731 return v;
732}
733
734/**
735 * graphene_vec3_get_x:
736 * @v: a #graphene_vec3_t
737 *
738 * Retrieves the first component of the given vector @v.
739 *
740 * Returns: the value of the first component of the vector
741 *
742 * Since: 1.0
743 */
744float
745graphene_vec3_get_x (const graphene_vec3_t *v)
746{
747 return graphene_simd4f_get_x (v->value);
748}
749
750/**
751 * graphene_vec3_get_y:
752 * @v: a #graphene_vec3_t
753 *
754 * Retrieves the second component of the given vector @v.
755 *
756 * Returns: the value of the second component of the vector
757 *
758 * Since: 1.0
759 */
760float
761graphene_vec3_get_y (const graphene_vec3_t *v)
762{
763 return graphene_simd4f_get_y (v->value);
764}
765
766/**
767 * graphene_vec3_get_z:
768 * @v: a #graphene_vec3_t
769 *
770 * Retrieves the third component of the given vector @v.
771 *
772 * Returns: the value of the third component of the vector
773 *
774 * Since: 1.0
775 */
776float
777graphene_vec3_get_z (const graphene_vec3_t *v)
778{
779 return graphene_simd4f_get_z (v->value);
780}
781
782/**
783 * graphene_vec3_to_float:
784 * @v: a #graphene_vec3_t
785 * @dest: (out caller-allocates) (array fixed-size=3): return location for
786 * an array of floating point values
787 *
788 * Copies the components of a #graphene_vec3_t into the given array.
789 *
790 * Since: 1.0
791 */
792void
793graphene_vec3_to_float (const graphene_vec3_t *v,
794 float *dest)
795{
796 graphene_simd4f_dup_3f (v->value, dest);
797}
798
799/**
800 * graphene_vec3_add:
801 * @a: a #graphene_vec3_t
802 * @b: a #graphene_vec3_t
803 * @res: (out caller-allocates): return location for the resulting vector
804 *
805 * Adds each component of the two given vectors.
806 *
807 * Since: 1.0
808 */
809void
810graphene_vec3_add (const graphene_vec3_t *a,
811 const graphene_vec3_t *b,
812 graphene_vec3_t *res)
813{
814 res->value = graphene_simd4f_add (a->value, b->value);
815}
816
817/**
818 * graphene_vec3_subtract:
819 * @a: a #graphene_vec3_t
820 * @b: a #graphene_vec3_t
821 * @res: (out caller-allocates): return location for the resulting vector
822 *
823 * Subtracts from each component of the first operand @a the
824 * corresponding component of the second operand @b and places
825 * each result into the components of @res.
826 *
827 * Since: 1.0
828 */
829void
830graphene_vec3_subtract (const graphene_vec3_t *a,
831 const graphene_vec3_t *b,
832 graphene_vec3_t *res)
833{
834 res->value = graphene_simd4f_sub (a->value, b->value);
835}
836
837/**
838 * graphene_vec3_multiply:
839 * @a: a #graphene_vec3_t
840 * @b: a #graphene_vec3_t
841 * @res: (out caller-allocates): return location for the resulting vector
842 *
843 * Multiplies each component of the two given vectors.
844 *
845 * Since: 1.0
846 */
847void
848graphene_vec3_multiply (const graphene_vec3_t *a,
849 const graphene_vec3_t *b,
850 graphene_vec3_t *res)
851{
852 res->value = graphene_simd4f_mul (a->value, b->value);
853}
854
855/**
856 * graphene_vec3_divide:
857 * @a: a #graphene_vec3_t
858 * @b: a #graphene_vec3_t
859 * @res: (out caller-allocates): return location for the resulting vector
860 *
861 * Divides each component of the first operand @a by the corresponding
862 * component of the second operand @b, and places the results into the
863 * vector @res.
864 *
865 * Since: 1.0
866 */
867void
868graphene_vec3_divide (const graphene_vec3_t *a,
869 const graphene_vec3_t *b,
870 graphene_vec3_t *res)
871{
872 res->value = graphene_simd4f_div (a->value, b->value);
873}
874
875/**
876 * graphene_vec3_cross:
877 * @a: a #graphene_vec3_t
878 * @b: a #graphene_vec3_t
879 * @res: (out caller-allocates): return location for the resulting vector
880 *
881 * Computes the cross product of the two given vectors.
882 *
883 * Since: 1.0
884 */
885void
886graphene_vec3_cross (const graphene_vec3_t *a,
887 const graphene_vec3_t *b,
888 graphene_vec3_t *res)
889{
890 res->value = graphene_simd4f_cross3 (a->value, b->value);
891}
892
893/**
894 * graphene_vec3_dot:
895 * @a: a #graphene_vec3_t
896 * @b: a #graphene_vec3_t
897 *
898 * Computes the dot product of the two given vectors.
899 *
900 * Returns: the value of the dot product
901 *
902 * Since: 1.0
903 */
904float
905graphene_vec3_dot (const graphene_vec3_t *a,
906 const graphene_vec3_t *b)
907{
908 return graphene_simd4f_dot3_scalar (a->value, b->value);
909}
910
911/**
912 * graphene_vec3_length:
913 * @v: a #graphene_vec3_t
914 *
915 * Retrieves the length of the given vector @v.
916 *
917 * Returns: the value of the length of the vector
918 *
919 * Since: 1.0
920 */
921float
922graphene_vec3_length (const graphene_vec3_t *v)
923{
924 return graphene_simd4f_get_x (graphene_simd4f_length3 (v->value));
925}
926
927/**
928 * graphene_vec3_normalize:
929 * @v: a #graphene_vec3_t
930 * @res: (out caller-allocates): return location for the normalized vector
931 *
932 * Normalizes the given #graphene_vec3_t.
933 *
934 * Since: 1.0
935 */
936void
937graphene_vec3_normalize (const graphene_vec3_t *v,
938 graphene_vec3_t *res)
939{
940 if (fabsf (x: graphene_vec3_length (v)) > FLT_EPSILON)
941 res->value = graphene_simd4f_normalize3 (v: v->value);
942 else
943 res->value = graphene_simd4f_init_zero ();
944}
945
946/**
947 * graphene_vec3_min:
948 * @a: a #graphene_vec3_t
949 * @b: a #graphene_vec3_t
950 * @res: (out caller-allocates): return location for the result vector
951 *
952 * Compares each component of the two given vectors and creates a
953 * vector that contains the minimum values.
954 *
955 * Since: 1.0
956 */
957void
958graphene_vec3_min (const graphene_vec3_t *a,
959 const graphene_vec3_t *b,
960 graphene_vec3_t *res)
961{
962 res->value = graphene_simd4f_min (a->value, b->value);
963}
964
965/**
966 * graphene_vec3_max:
967 * @a: a #graphene_vec3_t
968 * @b: a #graphene_vec3_t
969 * @res: (out caller-allocates): return location for the result vector
970 *
971 * Compares each component of the two given vectors and creates a
972 * vector that contains the maximum values.
973 *
974 * Since: 1.0
975 */
976void
977graphene_vec3_max (const graphene_vec3_t *a,
978 const graphene_vec3_t *b,
979 graphene_vec3_t *res)
980{
981 res->value = graphene_simd4f_max (a->value, b->value);
982}
983
984/**
985 * graphene_vec3_scale:
986 * @v: a #graphene_vec3_t
987 * @factor: the scalar factor
988 * @res: (out caller-allocates): return location for the result vector
989 *
990 * Multiplies all components of the given vector with the given scalar @factor.
991 *
992 * Since: 1.2
993 */
994void
995graphene_vec3_scale (const graphene_vec3_t *v,
996 float factor,
997 graphene_vec3_t *res)
998{
999 res->value = graphene_simd4f_mul (v->value, graphene_simd4f_splat (factor));
1000}
1001
1002/**
1003 * graphene_vec3_get_xy:
1004 * @v: a #graphene_vec3_t
1005 * @res: (out caller-allocates): return location for a #graphene_vec2_t
1006 *
1007 * Creates a #graphene_vec2_t that contains the first and second
1008 * components of the given #graphene_vec3_t.
1009 *
1010 * Since: 1.0
1011 */
1012void
1013graphene_vec3_get_xy (const graphene_vec3_t *v,
1014 graphene_vec2_t *res)
1015{
1016 res->value = graphene_simd4f_zero_zw (v->value);
1017}
1018
1019/**
1020 * graphene_vec3_get_xy0:
1021 * @v: a #graphene_vec3_t
1022 * @res: (out caller-allocates): return location for a #graphene_vec3_t
1023 *
1024 * Creates a #graphene_vec3_t that contains the first two components of
1025 * the given #graphene_vec3_t, and the third component set to 0.
1026 *
1027 * Since: 1.0
1028 */
1029void
1030graphene_vec3_get_xy0 (const graphene_vec3_t *v,
1031 graphene_vec3_t *res)
1032{
1033 res->value = graphene_simd4f_zero_zw (v->value);
1034}
1035
1036/**
1037 * graphene_vec3_get_xyz0:
1038 * @v: a #graphene_vec3_t
1039 * @res: (out caller-allocates): return location for the vector
1040 *
1041 * Converts a #graphene_vec3_t in a #graphene_vec4_t using 0.0
1042 * as the value for the fourth component of the resulting vector.
1043 *
1044 * Since: 1.0
1045 */
1046void
1047graphene_vec3_get_xyz0 (const graphene_vec3_t *v,
1048 graphene_vec4_t *res)
1049{
1050 res->value = graphene_simd4f_zero_w (v->value);
1051}
1052
1053/**
1054 * graphene_vec3_get_xyz1:
1055 * @v: a #graphene_vec3_t
1056 * @res: (out caller-allocates): return location for the vector
1057 *
1058 * Converts a #graphene_vec3_t in a #graphene_vec4_t using 1.0
1059 * as the value for the fourth component of the resulting vector.
1060 *
1061 * Since: 1.0
1062 */
1063void
1064graphene_vec3_get_xyz1 (const graphene_vec3_t *v,
1065 graphene_vec4_t *res)
1066{
1067 res->value = graphene_simd4f_add (graphene_simd4f_zero_w (v->value),
1068 graphene_simd4f_init (0.f, 0.f, 0.f, 1.f));
1069}
1070
1071/**
1072 * graphene_vec3_get_xyzw:
1073 * @v: a #graphene_vec3_t
1074 * @w: the value of the W component
1075 * @res: (out caller-allocates): return location for the vector
1076 *
1077 * Converts a #graphene_vec3_t in a #graphene_vec4_t using @w as
1078 * the value of the fourth component of the resulting vector.
1079 *
1080 * Since: 1.0
1081 */
1082void
1083graphene_vec3_get_xyzw (const graphene_vec3_t *v,
1084 float w,
1085 graphene_vec4_t *res)
1086{
1087 res->value = graphene_simd4f_add (graphene_simd4f_zero_w (v->value),
1088 graphene_simd4f_init (0.f, 0.f, 0.f, w));
1089}
1090
1091/**
1092 * graphene_vec3_negate:
1093 * @v: a #graphene_vec3_t
1094 * @res: (out caller-allocates): return location for the result vector
1095 *
1096 * Negates the given #graphene_vec3_t.
1097 *
1098 * Since: 1.2
1099 */
1100void
1101graphene_vec3_negate (const graphene_vec3_t *v,
1102 graphene_vec3_t *res)
1103{
1104 res->value = graphene_simd4f_neg (v->value);
1105}
1106
1107static bool
1108vec3_equal (const void *p1,
1109 const void *p2)
1110{
1111 const graphene_vec3_t *v1 = p1;
1112 const graphene_vec3_t *v2 = p2;
1113
1114 if (graphene_simd4f_cmp_eq (v1->value, v2->value))
1115 return true;
1116
1117 return graphene_vec3_near (v1, v2, GRAPHENE_FLOAT_EPSILON);
1118}
1119
1120/**
1121 * graphene_vec3_equal:
1122 * @v1: a #graphene_vec3_t
1123 * @v2: a #graphene_vec3_t
1124 *
1125 * Checks whether the two given #graphene_vec3_t are equal.
1126 *
1127 * Returns: `true` if the two vectors are equal, and false otherwise
1128 *
1129 * Since: 1.2
1130 */
1131bool
1132graphene_vec3_equal (const graphene_vec3_t *v1,
1133 const graphene_vec3_t *v2)
1134{
1135 return graphene_pointer_equal (p1: v1, p2: v2, func: vec3_equal);
1136}
1137
1138/**
1139 * graphene_vec3_near:
1140 * @v1: a #graphene_vec3_t
1141 * @v2: a #graphene_vec3_t
1142 * @epsilon: the threshold between the two vectors
1143 *
1144 * Compares the two given #graphene_vec3_t vectors and checks
1145 * whether their values are within the given @epsilon.
1146 *
1147 * Returns: `true` if the two vectors are near each other
1148 *
1149 * Since: 1.2
1150 */
1151bool
1152graphene_vec3_near (const graphene_vec3_t *v1,
1153 const graphene_vec3_t *v2,
1154 float epsilon)
1155{
1156 graphene_simd4f_t d = graphene_simd4f_sub (v1->value, v2->value);
1157 float epsilon_sq = epsilon * epsilon;
1158
1159 return graphene_simd4f_dot3_scalar (d, d) < epsilon_sq;
1160}
1161
1162/**
1163 * graphene_vec3_interpolate:
1164 * @v1: a #graphene_vec3_t
1165 * @v2: a #graphene_vec3_t
1166 * @factor: the interpolation factor
1167 * @res: (out caller-allocates): the interpolated vector
1168 *
1169 * Linearly interpolates @v1 and @v2 using the given @factor.
1170 *
1171 * Since: 1.10
1172 */
1173void
1174graphene_vec3_interpolate (const graphene_vec3_t *v1,
1175 const graphene_vec3_t *v2,
1176 double factor,
1177 graphene_vec3_t *res)
1178{
1179 res->value = graphene_simd4f_interpolate (a: v1->value, b: v2->value, f: (float) factor);
1180}
1181
1182enum {
1183 VEC3_ZERO,
1184 VEC3_ONE,
1185 VEC3_X_AXIS,
1186 VEC3_Y_AXIS,
1187 VEC3_Z_AXIS,
1188
1189 N_STATIC_VEC3
1190};
1191
1192static graphene_vec3_t static_vec3[N_STATIC_VEC3];
1193
1194static void
1195init_static_vec3_once (void)
1196{
1197 static_vec3[VEC3_ZERO].value = graphene_simd4f_init_zero ();
1198 static_vec3[VEC3_ONE].value = graphene_simd4f_init (1.f, 1.f, 1.f, 0.f);
1199 static_vec3[VEC3_X_AXIS].value = graphene_simd4f_init (1.f, 0.f, 0.f, 0.f);
1200 static_vec3[VEC3_Y_AXIS].value = graphene_simd4f_init (0.f, 1.f, 0.f, 0.f);
1201 static_vec3[VEC3_Z_AXIS].value = graphene_simd4f_init (0.f, 0.f, 1.f, 0.f);
1202}
1203
1204#ifdef HAVE_PTHREAD
1205static pthread_once_t static_vec3_once = PTHREAD_ONCE_INIT;
1206
1207static inline void
1208init_static_vec3 (void)
1209{
1210 int status = pthread_once (&static_vec3_once, init_static_vec3_once);
1211
1212 if (status < 0)
1213 {
1214 int saved_errno = errno;
1215
1216 fprintf (stderr, "pthread_once failed: %s (errno:%d)\n",
1217 strerror (saved_errno),
1218 saved_errno);
1219 }
1220}
1221
1222#elif defined(HAVE_INIT_ONCE)
1223static INIT_ONCE static_vec3_once = INIT_ONCE_STATIC_INIT;
1224
1225static BOOL CALLBACK
1226InitVec3Func (PINIT_ONCE InitOnce,
1227 PVOID Parameter,
1228 PVOID *lpContext)
1229{
1230 init_static_vec3_once ();
1231 return TRUE;
1232}
1233
1234static inline void
1235init_static_vec3 (void)
1236{
1237 BOOL bStatus = InitOnceExecuteOnce (&static_vec3_once,
1238 InitVec3Func,
1239 NULL,
1240 NULL);
1241
1242 if (!bStatus)
1243 fprintf (stderr, "InitOnceExecuteOnce failed\n");
1244}
1245
1246#else /* !HAVE_PTHREAD && !HAVE_INIT_ONCE*/
1247static bool static_vec3_init = false;
1248
1249static inline void
1250init_static_vec3 (void)
1251{
1252 if (static_vec3_init)
1253 return;
1254
1255 init_static_vec3_once ();
1256 static_vec3_init = true;
1257}
1258#endif /* HAVE_PTHREAD */
1259
1260/**
1261 * graphene_vec3_zero:
1262 *
1263 * Provides a constant pointer to a vector with three components,
1264 * all sets to 0.
1265 *
1266 * Returns: (transfer none): a constant vector
1267 *
1268 * Since: 1.0
1269 */
1270const graphene_vec3_t *
1271graphene_vec3_zero (void)
1272{
1273 init_static_vec3 ();
1274
1275 return &(static_vec3[VEC3_ZERO]);
1276}
1277
1278/**
1279 * graphene_vec3_one:
1280 *
1281 * Provides a constant pointer to a vector with three components,
1282 * all sets to 1.
1283 *
1284 * Returns: (transfer none): a constant vector
1285 *
1286 * Since: 1.0
1287 */
1288const graphene_vec3_t *
1289graphene_vec3_one (void)
1290{
1291 init_static_vec3 ();
1292
1293 return &(static_vec3[VEC3_ONE]);
1294}
1295
1296/**
1297 * graphene_vec3_x_axis:
1298 *
1299 * Provides a constant pointer to a vector with three components
1300 * with values set to (1, 0, 0).
1301 *
1302 * Returns: (transfer none): a constant vector
1303 *
1304 * Since: 1.0
1305 */
1306const graphene_vec3_t *
1307graphene_vec3_x_axis (void)
1308{
1309 init_static_vec3 ();
1310
1311 return &(static_vec3[VEC3_X_AXIS]);
1312}
1313
1314/**
1315 * graphene_vec3_y_axis:
1316 *
1317 * Provides a constant pointer to a vector with three components
1318 * with values set to (0, 1, 0).
1319 *
1320 * Returns: (transfer none): a constant vector
1321 *
1322 * Since: 1.0
1323 */
1324const graphene_vec3_t *
1325graphene_vec3_y_axis (void)
1326{
1327 init_static_vec3 ();
1328
1329 return &(static_vec3[VEC3_Y_AXIS]);
1330}
1331
1332/**
1333 * graphene_vec3_z_axis:
1334 *
1335 * Provides a constant pointer to a vector with three components
1336 * with values set to (0, 0, 1).
1337 *
1338 * Returns: (transfer none): a constant vector
1339 *
1340 * Since: 1.0
1341 */
1342const graphene_vec3_t *
1343graphene_vec3_z_axis (void)
1344{
1345 init_static_vec3 ();
1346
1347 return &(static_vec3[VEC3_Z_AXIS]);
1348}
1349
1350/* }}} vec3 */
1351
1352/* vec4 {{{ */
1353/**
1354 * graphene_vec4_alloc: (constructor)
1355 *
1356 * Allocates a new #graphene_vec4_t structure.
1357 *
1358 * The contents of the returned structure are undefined.
1359 *
1360 * Use graphene_vec4_init() to initialize the vector.
1361 *
1362 * Returns: (transfer full): the newly allocated #graphene_vec4_t
1363 * structure. Use graphene_vec4_free() to free the resources allocated
1364 * by this function.
1365 *
1366 * Since: 1.0
1367 */
1368graphene_vec4_t *
1369graphene_vec4_alloc (void)
1370{
1371 return graphene_aligned_alloc (size: sizeof (graphene_vec4_t), number: 1, alignment: 16);
1372}
1373
1374/**
1375 * graphene_vec4_free:
1376 * @v: a #graphene_vec4_t
1377 *
1378 * Frees the resources allocated by @v
1379 *
1380 * Since: 1.0
1381 */
1382void
1383graphene_vec4_free (graphene_vec4_t *v)
1384{
1385 graphene_aligned_free (mem: v);
1386}
1387
1388/**
1389 * graphene_vec4_init:
1390 * @v: a #graphene_vec4_t
1391 * @x: the X field of the vector
1392 * @y: the Y field of the vector
1393 * @z: the Z field of the vector
1394 * @w: the W field of the vector
1395 *
1396 * Initializes a #graphene_vec4_t using the given values.
1397 *
1398 * This function can be called multiple times.
1399 *
1400 * Returns: (transfer none): a pointer to the initialized
1401 * vector
1402 *
1403 * Since: 1.0
1404 */
1405graphene_vec4_t *
1406graphene_vec4_init (graphene_vec4_t *v,
1407 float x,
1408 float y,
1409 float z,
1410 float w)
1411{
1412 v->value = graphene_simd4f_init (x, y, z, w);
1413
1414 return v;
1415}
1416
1417/**
1418 * graphene_vec4_init_from_vec4:
1419 * @v: a #graphene_vec4_t
1420 * @src: a #graphene_vec4_t
1421 *
1422 * Initializes a #graphene_vec4_t using the components of
1423 * another #graphene_vec4_t.
1424 *
1425 * Returns: (transfer none): the initialized vector
1426 *
1427 * Since: 1.0
1428 */
1429graphene_vec4_t *
1430graphene_vec4_init_from_vec4 (graphene_vec4_t *v,
1431 const graphene_vec4_t *src)
1432{
1433 v->value = src->value;
1434
1435 return v;
1436}
1437
1438/**
1439 * graphene_vec4_init_from_vec3:
1440 * @v: a #graphene_vec4_t
1441 * @src: a #graphene_vec3_t
1442 * @w: the value for the fourth component of @v
1443 *
1444 * Initializes a #graphene_vec4_t using the components of a
1445 * #graphene_vec3_t and the value of @w.
1446 *
1447 * Returns: (transfer none): the initialized vector
1448 *
1449 * Since: 1.0
1450 */
1451graphene_vec4_t *
1452graphene_vec4_init_from_vec3 (graphene_vec4_t *v,
1453 const graphene_vec3_t *src,
1454 float w)
1455{
1456 v->value = graphene_simd4f_merge_w (src->value, w);
1457
1458 return v;
1459}
1460
1461/**
1462 * graphene_vec4_init_from_vec2:
1463 * @v: a #graphene_vec4_t
1464 * @src: a #graphene_vec2_t
1465 * @z: the value for the third component of @v
1466 * @w: the value for the fourth component of @v
1467 *
1468 * Initializes a #graphene_vec4_t using the components of a
1469 * #graphene_vec2_t and the values of @z and @w.
1470 *
1471 * Returns: (transfer none): the initialized vector
1472 *
1473 * Since: 1.0
1474 */
1475graphene_vec4_t *
1476graphene_vec4_init_from_vec2 (graphene_vec4_t *v,
1477 const graphene_vec2_t *src,
1478 float z,
1479 float w)
1480{
1481 v->value = graphene_simd4f_merge_low (src->value, graphene_simd4f_init (z, w, 0, 0));
1482
1483 return v;
1484}
1485
1486/**
1487 * graphene_vec4_init_from_float:
1488 * @v: a #graphene_vec4_t
1489 * @src: (array fixed-size=4): an array of four floating point values
1490 *
1491 * Initializes a #graphene_vec4_t with the values inside the given array.
1492 *
1493 * Returns: (transfer none): the initialized vector
1494 *
1495 * Since: 1.0
1496 */
1497graphene_vec4_t *
1498graphene_vec4_init_from_float (graphene_vec4_t *v,
1499 const float *src)
1500{
1501 v->value = graphene_simd4f_init_4f (src);
1502
1503 return v;
1504}
1505
1506/**
1507 * graphene_vec4_get_x:
1508 * @v: a #graphene_vec4_t
1509 *
1510 * Retrieves the value of the first component of the given #graphene_vec4_t.
1511 *
1512 * Returns: the value of the first component
1513 *
1514 * Since: 1.0
1515 */
1516float
1517graphene_vec4_get_x (const graphene_vec4_t *v)
1518{
1519 return graphene_simd4f_get_x (v->value);
1520}
1521
1522/**
1523 * graphene_vec4_get_y:
1524 * @v: a #graphene_vec4_t
1525 *
1526 * Retrieves the value of the second component of the given #graphene_vec4_t.
1527 *
1528 * Returns: the value of the second component
1529 *
1530 * Since: 1.0
1531 */
1532float
1533graphene_vec4_get_y (const graphene_vec4_t *v)
1534{
1535 return graphene_simd4f_get_y (v->value);
1536}
1537
1538/**
1539 * graphene_vec4_get_z:
1540 * @v: a #graphene_vec4_t
1541 *
1542 * Retrieves the value of the third component of the given #graphene_vec4_t.
1543 *
1544 * Returns: the value of the third component
1545 *
1546 * Since: 1.0
1547 */
1548float
1549graphene_vec4_get_z (const graphene_vec4_t *v)
1550{
1551 return graphene_simd4f_get_z (v->value);
1552}
1553
1554/**
1555 * graphene_vec4_get_w:
1556 * @v: a #graphene_vec4_t
1557 *
1558 * Retrieves the value of the fourth component of the given #graphene_vec4_t.
1559 *
1560 * Returns: the value of the fourth component
1561 *
1562 * Since: 1.0
1563 */
1564float
1565graphene_vec4_get_w (const graphene_vec4_t *v)
1566{
1567 return graphene_simd4f_get_w (v->value);
1568}
1569
1570/**
1571 * graphene_vec4_to_float:
1572 * @v: a #graphene_vec4_t
1573 * @dest: (out caller-allocates) (array fixed-size=4): return location for
1574 * an array of floating point values
1575 *
1576 * Stores the components of the given #graphene_vec4_t into an array
1577 * of floating point values.
1578 *
1579 * Since: 1.0
1580 */
1581void
1582graphene_vec4_to_float (const graphene_vec4_t *v,
1583 float *dest)
1584{
1585 graphene_simd4f_dup_4f (v->value, dest);
1586}
1587
1588/**
1589 * graphene_vec4_add:
1590 * @a: a #graphene_vec4_t
1591 * @b: a #graphene_vec4_t
1592 * @res: (out caller-allocates): return location for the resulting vector
1593 *
1594 * Adds each component of the two given vectors.
1595 *
1596 * Since: 1.0
1597 */
1598void
1599graphene_vec4_add (const graphene_vec4_t *a,
1600 const graphene_vec4_t *b,
1601 graphene_vec4_t *res)
1602{
1603 res->value = graphene_simd4f_add (a->value, b->value);
1604}
1605
1606/**
1607 * graphene_vec4_subtract:
1608 * @a: a #graphene_vec4_t
1609 * @b: a #graphene_vec4_t
1610 * @res: (out caller-allocates): return location for the resulting vector
1611 *
1612 * Subtracts from each component of the first operand @a the
1613 * corresponding component of the second operand @b and places
1614 * each result into the components of @res.
1615 *
1616 * Since: 1.0
1617 */
1618void
1619graphene_vec4_subtract (const graphene_vec4_t *a,
1620 const graphene_vec4_t *b,
1621 graphene_vec4_t *res)
1622{
1623 res->value = graphene_simd4f_sub (a->value, b->value);
1624}
1625
1626/**
1627 * graphene_vec4_multiply:
1628 * @a: a #graphene_vec4_t
1629 * @b: a #graphene_vec4_t
1630 * @res: (out caller-allocates): return location for the resulting vector
1631 *
1632 * Multiplies each component of the two given vectors.
1633 *
1634 * Since: 1.0
1635 */
1636void
1637graphene_vec4_multiply (const graphene_vec4_t *a,
1638 const graphene_vec4_t *b,
1639 graphene_vec4_t *res)
1640{
1641 res->value = graphene_simd4f_mul (a->value, b->value);
1642}
1643
1644/**
1645 * graphene_vec4_divide:
1646 * @a: a #graphene_vec4_t
1647 * @b: a #graphene_vec4_t
1648 * @res: (out caller-allocates): return location for the resulting vector
1649 *
1650 * Divides each component of the first operand @a by the corresponding
1651 * component of the second operand @b, and places the results into the
1652 * vector @res.
1653 *
1654 * Since: 1.0
1655 */
1656void
1657graphene_vec4_divide (const graphene_vec4_t *a,
1658 const graphene_vec4_t *b,
1659 graphene_vec4_t *res)
1660{
1661 res->value = graphene_simd4f_div (a->value, b->value);
1662}
1663
1664/**
1665 * graphene_vec4_dot:
1666 * @a: a #graphene_vec4_t
1667 * @b: a #graphene_vec4_t
1668 *
1669 * Computes the dot product of the two given vectors.
1670 *
1671 * Returns: the value of the dot product
1672 *
1673 * Since: 1.0
1674 */
1675float
1676graphene_vec4_dot (const graphene_vec4_t *a,
1677 const graphene_vec4_t *b)
1678{
1679 return graphene_simd4f_get_x (graphene_simd4f_dot4 (a->value, b->value));
1680}
1681
1682/**
1683 * graphene_vec4_length:
1684 * @v: a #graphene_vec4_t
1685 *
1686 * Computes the length of the given #graphene_vec4_t.
1687 *
1688 * Returns: the length of the vector
1689 *
1690 * Since: 1.0
1691 */
1692float
1693graphene_vec4_length (const graphene_vec4_t *v)
1694{
1695 return graphene_simd4f_get_x (graphene_simd4f_length4 (v->value));
1696}
1697
1698/**
1699 * graphene_vec4_normalize:
1700 * @v: a #graphene_vec4_t
1701 * @res: (out caller-allocates): return location for the normalized
1702 * vector
1703 *
1704 * Normalizes the given #graphene_vec4_t.
1705 *
1706 * Since: 1.0
1707 */
1708void
1709graphene_vec4_normalize (const graphene_vec4_t *v,
1710 graphene_vec4_t *res)
1711{
1712 if (fabsf (x: graphene_vec4_length (v)) > FLT_EPSILON)
1713 res->value = graphene_simd4f_normalize4 (v: v->value);
1714 else
1715 res->value = graphene_simd4f_init_zero ();
1716}
1717
1718/**
1719 * graphene_vec4_min:
1720 * @a: a #graphene_vec4_t
1721 * @b: a #graphene_vec4_t
1722 * @res: (out caller-allocates): return location for the result vector
1723 *
1724 * Compares each component of the two given vectors and creates a
1725 * vector that contains the minimum values.
1726 *
1727 * Since: 1.0
1728 */
1729void
1730graphene_vec4_min (const graphene_vec4_t *a,
1731 const graphene_vec4_t *b,
1732 graphene_vec4_t *res)
1733{
1734 res->value = graphene_simd4f_min (a->value, b->value);
1735}
1736
1737/**
1738 * graphene_vec4_max:
1739 * @a: a #graphene_vec4_t
1740 * @b: a #graphene_vec4_t
1741 * @res: (out caller-allocates): return location for the result vector
1742 *
1743 * Compares each component of the two given vectors and creates a
1744 * vector that contains the maximum values.
1745 *
1746 * Since: 1.0
1747 */
1748void
1749graphene_vec4_max (const graphene_vec4_t *a,
1750 const graphene_vec4_t *b,
1751 graphene_vec4_t *res)
1752{
1753 res->value = graphene_simd4f_max (a->value, b->value);
1754}
1755
1756/**
1757 * graphene_vec4_scale:
1758 * @v: a #graphene_vec4_t
1759 * @factor: the scalar factor
1760 * @res: (out caller-allocates): return location for the result vector
1761 *
1762 * Multiplies all components of the given vector with the given scalar @factor.
1763 *
1764 * Since: 1.2
1765 */
1766void
1767graphene_vec4_scale (const graphene_vec4_t *v,
1768 float factor,
1769 graphene_vec4_t *res)
1770{
1771 res->value = graphene_simd4f_mul (v->value, graphene_simd4f_splat (factor));
1772}
1773
1774/**
1775 * graphene_vec4_get_xy:
1776 * @v: a #graphene_vec4_t
1777 * @res: (out caller-allocates): return location for a #graphene_vec2_t
1778 *
1779 * Creates a #graphene_vec2_t that contains the first two components
1780 * of the given #graphene_vec4_t.
1781 *
1782 * Since: 1.0
1783 */
1784void
1785graphene_vec4_get_xy (const graphene_vec4_t *v,
1786 graphene_vec2_t *res)
1787{
1788 res->value = graphene_simd4f_zero_zw (v->value);
1789}
1790
1791/**
1792 * graphene_vec4_get_xyz:
1793 * @v: a #graphene_vec4_t
1794 * @res: (out caller-allocates): return location for a graphene_vec3_t
1795 *
1796 * Creates a #graphene_vec3_t that contains the first three components
1797 * of the given #graphene_vec4_t.
1798 *
1799 * Since: 1.0
1800 */
1801void
1802graphene_vec4_get_xyz (const graphene_vec4_t *v,
1803 graphene_vec3_t *res)
1804{
1805 res->value = graphene_simd4f_zero_w (v->value);
1806}
1807
1808/**
1809 * graphene_vec4_negate:
1810 * @v: a #graphene_vec4_t
1811 * @res: (out caller-allocates): return location for the result vector
1812 *
1813 * Negates the given #graphene_vec4_t.
1814 *
1815 * Since: 1.2
1816 */
1817void
1818graphene_vec4_negate (const graphene_vec4_t *v,
1819 graphene_vec4_t *res)
1820{
1821 res->value = graphene_simd4f_neg (v->value);
1822}
1823
1824static bool
1825vec4_equal (const void *p1,
1826 const void *p2)
1827{
1828 const graphene_vec4_t *v1 = p1;
1829 const graphene_vec4_t *v2 = p2;
1830
1831 if (graphene_simd4f_cmp_eq (v1->value, v2->value))
1832 return true;
1833
1834 return graphene_vec4_near (v1, v2, GRAPHENE_FLOAT_EPSILON);
1835}
1836
1837/**
1838 * graphene_vec4_equal:
1839 * @v1: a #graphene_vec4_t
1840 * @v2: a #graphene_vec4_t
1841 *
1842 * Checks whether the two given #graphene_vec4_t are equal.
1843 *
1844 * Returns: `true` if the two vectors are equal, and false otherwise
1845 *
1846 * Since: 1.2
1847 */
1848bool
1849graphene_vec4_equal (const graphene_vec4_t *v1,
1850 const graphene_vec4_t *v2)
1851{
1852 return graphene_pointer_equal (p1: v1, p2: v2, func: vec4_equal);
1853}
1854
1855/**
1856 * graphene_vec4_near:
1857 * @v1: a #graphene_vec4_t
1858 * @v2: a #graphene_vec4_t
1859 * @epsilon: the threshold between the two vectors
1860 *
1861 * Compares the two given #graphene_vec4_t vectors and checks
1862 * whether their values are within the given @epsilon.
1863 *
1864 * Returns: `true` if the two vectors are near each other
1865 *
1866 * Since: 1.2
1867 */
1868bool
1869graphene_vec4_near (const graphene_vec4_t *v1,
1870 const graphene_vec4_t *v2,
1871 float epsilon)
1872{
1873 graphene_simd4f_t d = graphene_simd4f_sub (v1->value, v2->value);
1874 float epsilon_sq = epsilon * epsilon;
1875
1876 return graphene_simd4f_get_x (graphene_simd4f_dot4 (d, d)) < epsilon_sq;
1877}
1878
1879/**
1880 * graphene_vec4_interpolate:
1881 * @v1: a #graphene_vec4_t
1882 * @v2: a #graphene_vec4_t
1883 * @factor: the interpolation factor
1884 * @res: (out caller-allocates): the interpolated vector
1885 *
1886 * Linearly interpolates @v1 and @v2 using the given @factor.
1887 *
1888 * Since: 1.10
1889 */
1890void
1891graphene_vec4_interpolate (const graphene_vec4_t *v1,
1892 const graphene_vec4_t *v2,
1893 double factor,
1894 graphene_vec4_t *res)
1895{
1896 res->value = graphene_simd4f_interpolate (a: v1->value, b: v2->value, f: (float) factor);
1897}
1898
1899enum {
1900 VEC4_ZERO,
1901 VEC4_ONE,
1902 VEC4_X_AXIS,
1903 VEC4_Y_AXIS,
1904 VEC4_Z_AXIS,
1905 VEC4_W_AXIS,
1906
1907 N_STATIC_VEC4
1908};
1909
1910static graphene_vec4_t static_vec4[N_STATIC_VEC4];
1911
1912static void
1913init_static_vec4_once (void)
1914{
1915 static_vec4[VEC4_ZERO].value = graphene_simd4f_init_zero ();
1916 static_vec4[VEC4_ONE].value = graphene_simd4f_splat (1.f);
1917 static_vec4[VEC4_X_AXIS].value = graphene_simd4f_init (1.f, 0.f, 0.f, 0.f);
1918 static_vec4[VEC4_Y_AXIS].value = graphene_simd4f_init (0.f, 1.f, 0.f, 0.f);
1919 static_vec4[VEC4_Z_AXIS].value = graphene_simd4f_init (0.f, 0.f, 1.f, 0.f);
1920 static_vec4[VEC4_W_AXIS].value = graphene_simd4f_init (0.f, 0.f, 0.f, 1.f);
1921}
1922
1923#ifdef HAVE_PTHREAD
1924static pthread_once_t static_vec4_init_once = PTHREAD_ONCE_INIT;
1925
1926static inline void
1927init_static_vec4 (void)
1928{
1929 int status = pthread_once (&static_vec4_init_once, init_static_vec4_once);
1930
1931 if (status < 0)
1932 {
1933 int saved_errno = errno;
1934
1935 fprintf (stderr, "pthread_once failed: %s (errno:%d)\n",
1936 strerror (saved_errno),
1937 saved_errno);
1938 }
1939}
1940
1941#elif defined(HAVE_INIT_ONCE)
1942static INIT_ONCE static_vec4_once = INIT_ONCE_STATIC_INIT;
1943
1944static BOOL CALLBACK
1945InitVec4Func (PINIT_ONCE InitOnce,
1946 PVOID Parameter,
1947 PVOID *lpContext)
1948{
1949 init_static_vec4_once ();
1950 return TRUE;
1951}
1952
1953static inline void
1954init_static_vec4 (void)
1955{
1956 BOOL bStatus = InitOnceExecuteOnce (&static_vec4_once,
1957 InitVec4Func,
1958 NULL,
1959 NULL);
1960
1961 if (!bStatus)
1962 fprintf (stderr, "InitOnceExecuteOnce failed\n");
1963}
1964
1965#else /* !HAVE_PTHREAD && !HAVE_INIT_ONCE */
1966
1967static bool static_vec4_init = false;
1968
1969static inline void
1970init_static_vec4 (void)
1971{
1972 if (static_vec4_init)
1973 return;
1974
1975 init_static_vec4_once ();
1976 static_vec4_init = true;
1977}
1978#endif /* HAVE_PTHREAD */
1979
1980/**
1981 * graphene_vec4_zero:
1982 *
1983 * Retrieves a pointer to a #graphene_vec4_t with all its
1984 * components set to 0.
1985 *
1986 * Returns: (transfer none): a constant vector
1987 *
1988 * Since: 1.0
1989 */
1990const graphene_vec4_t *
1991graphene_vec4_zero (void)
1992{
1993 init_static_vec4 ();
1994
1995 return &(static_vec4[VEC4_ZERO]);
1996}
1997
1998/**
1999 * graphene_vec4_one:
2000 *
2001 * Retrieves a pointer to a #graphene_vec4_t with all its
2002 * components set to 1.
2003 *
2004 * Returns: (transfer none): a constant vector
2005 *
2006 * Since: 1.0
2007 */
2008const graphene_vec4_t *
2009graphene_vec4_one (void)
2010{
2011 init_static_vec4 ();
2012
2013 return &(static_vec4[VEC4_ONE]);
2014}
2015
2016/**
2017 * graphene_vec4_x_axis:
2018 *
2019 * Retrieves a pointer to a #graphene_vec4_t with its
2020 * components set to (1, 0, 0, 0).
2021 *
2022 * Returns: (transfer none): a constant vector
2023 *
2024 * Since: 1.0
2025 */
2026const graphene_vec4_t *
2027graphene_vec4_x_axis (void)
2028{
2029 init_static_vec4 ();
2030
2031 return &(static_vec4[VEC4_X_AXIS]);
2032}
2033
2034/**
2035 * graphene_vec4_y_axis:
2036 *
2037 * Retrieves a pointer to a #graphene_vec4_t with its
2038 * components set to (0, 1, 0, 0).
2039 *
2040 * Returns: (transfer none): a constant vector
2041 *
2042 * Since: 1.0
2043 */
2044const graphene_vec4_t *
2045graphene_vec4_y_axis (void)
2046{
2047 init_static_vec4 ();
2048
2049 return &(static_vec4[VEC4_Y_AXIS]);
2050}
2051
2052/**
2053 * graphene_vec4_z_axis:
2054 *
2055 * Retrieves a pointer to a #graphene_vec4_t with its
2056 * components set to (0, 0, 1, 0).
2057 *
2058 * Returns: (transfer none): a constant vector
2059 *
2060 * Since: 1.0
2061 */
2062const graphene_vec4_t *
2063graphene_vec4_z_axis (void)
2064{
2065 init_static_vec4 ();
2066
2067 return &(static_vec4[VEC4_Z_AXIS]);
2068}
2069
2070/**
2071 * graphene_vec4_w_axis:
2072 *
2073 * Retrieves a pointer to a #graphene_vec4_t with its
2074 * components set to (0, 0, 0, 1).
2075 *
2076 * Returns: (transfer none): a constant vector
2077 *
2078 * Since: 1.0
2079 */
2080const graphene_vec4_t *
2081graphene_vec4_w_axis (void)
2082{
2083 init_static_vec4 ();
2084
2085 return &(static_vec4[VEC4_W_AXIS]);
2086}
2087
2088/* }}} vec4 */
2089

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