1 /* graphene-matrix.h: 4x4 matrix
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-matrix
28  * @title: Matrix
29  * @short_description: 4x4 matrices
30  *
31  * #graphene_matrix_t is a type that provides a 4x4 square matrix, useful for
32  * representing 3D transformations.
33  *
34  * The matrix is treated as row-major, i.e. it has four vectors (x, y, z, and
35  * w) representing rows, and elements of each vector are a column:
36  *
37  * |[<!-- language="plain" -->
38  *   ⎡ m.x ⎤    ⎛ x.x  x.y  x.z  x.w ⎞
39  *   ⎜ m.y ⎟ -\ ⎜ y.x  y.y  y.z  y.w ⎟
40  *   ⎜ m.z ⎟ -/ ⎜ z.x  z.y  z.z  z.w ⎟
41  *   ⎣ m.w ⎦    ⎝ w.x  w.y  w.z  w.w ⎠
42  * ]|
43  *
44  * It is possible to easily convert a #graphene_matrix_t to and from an array
45  * of floating point values that can be used with other libraries.
46  *
47  * The contents of a #graphene_matrix_t are private, and direct access is not
48  * possible. You can modify and read the contents of a #graphene_matrix_t
49  * only through the provided API.
50  *
51  * # Conventions # {#conventions}
52  *
53  * Graphene uses left-multiplication for all its operations on vectors and
54  * matrices; in other words, given a matrix `A` and a vector `b`, the result
55  * of a multiplication is going to be:
56  *
57  * |[
58  *   res = b × A
59  * ]|
60  *
61  * Multiplying two matrices, on the other hand, will use right-multiplication;
62  * given two matrices `A` and `B`, the result of the multiplication is going
63  * to be
64  *
65  * |[
66  *   res = A × B
67  * ]|
68  *
69  * as the implementation will multiply each row vector of matrix `A` with the
70  * matrix `B` to obtain the new row vectors of the result matrix:
71  *
72  * |[
73  *   res = ⎡ A.x × B ⎤
74  *         ⎜ A.y × B ⎟
75  *         ⎜ A.z × B ⎟
76  *         ⎣ A.w × B ⎦
77  * ]|
78  *
79  * For more information, see the documentation for #graphene_simd4x4f_t,
80  * especially the following functions:
81  *
82  *   - graphene_simd4x4f_vec4_mul()
83  *   - graphene_simd4x4f_vec3_mul()
84  *   - graphene_simd4x4f_point3_mul()
85  *   - graphene_simd4x4f_matrix_mul()
86  */
87 
88 #include "graphene-private.h"
89 
90 #include "graphene-matrix.h"
91 
92 #include "graphene-alloc-private.h"
93 #include "graphene-box.h"
94 #include "graphene-euler.h"
95 #include "graphene-point.h"
96 #include "graphene-point3d.h"
97 #include "graphene-quad.h"
98 #include "graphene-quaternion.h"
99 #include "graphene-ray.h"
100 #include "graphene-rect.h"
101 #include "graphene-simd4x4f.h"
102 #include "graphene-sphere.h"
103 #include "graphene-vectors-private.h"
104 
105 #include <stdio.h>
106 
107 /**
108  * graphene_matrix_alloc: (constructor)
109  *
110  * Allocates a new #graphene_matrix_t.
111  *
112  * Returns: (transfer full): the newly allocated matrix
113  *
114  * Since: 1.0
115  */
116 graphene_matrix_t *
graphene_matrix_alloc(void)117 graphene_matrix_alloc (void)
118 {
119   return graphene_aligned_alloc (sizeof (graphene_matrix_t), 1, 16);
120 }
121 
122 /**
123  * graphene_matrix_free:
124  * @m: a #graphene_matrix_t
125  *
126  * Frees the resources allocated by graphene_matrix_alloc().
127  *
128  * Since: 1.0
129  */
130 void
graphene_matrix_free(graphene_matrix_t * m)131 graphene_matrix_free (graphene_matrix_t *m)
132 {
133   graphene_aligned_free (m);
134 }
135 
136 /**
137  * graphene_matrix_to_float:
138  * @m: a #graphene_matrix_t
139  * @v: (array fixed-size=16) (out caller-allocates): return location
140  *   for an array of floating point values. The array must be capable
141  *   of holding at least 16 values.
142  *
143  * Converts a #graphene_matrix_t to an array of floating point
144  * values.
145  *
146  * Since: 1.0
147  */
148 void
graphene_matrix_to_float(const graphene_matrix_t * m,float * v)149 graphene_matrix_to_float (const graphene_matrix_t *m,
150                           float                   *v)
151 {
152   graphene_simd4x4f_to_float (&m->value, v);
153 }
154 
155 static const float graphene_identity_matrix_floats[16] = {
156   1.f, 0.f, 0.f, 0.f,
157   0.f, 1.f, 0.f, 0.f,
158   0.f, 0.f, 1.f, 0.f,
159   0.f, 0.f, 0.f, 1.f,
160 };
161 
162 /**
163  * graphene_matrix_init_identity:
164  * @m: a #graphene_matrix_t
165  *
166  * Initializes a #graphene_matrix_t with the identity matrix.
167  *
168  * Returns: (transfer none): the initialized matrix
169  *
170  * Since: 1.0
171  */
172 graphene_matrix_t *
graphene_matrix_init_identity(graphene_matrix_t * m)173 graphene_matrix_init_identity (graphene_matrix_t *m)
174 {
175   graphene_simd4x4f_init_from_float (&m->value, graphene_identity_matrix_floats);
176 
177   return m;
178 }
179 
180 /**
181  * graphene_matrix_init_from_float:
182  * @m: a #graphene_matrix_t
183  * @v: (array fixed-size=16): an array of at least 16 floating
184  *   point values
185  *
186  * Initializes a #graphene_matrix_t with the given array of floating
187  * point values.
188  *
189  * Returns: (transfer none): the initialized matrix
190  *
191  * Since: 1.0
192  */
193 graphene_matrix_t *
graphene_matrix_init_from_float(graphene_matrix_t * m,const float * v)194 graphene_matrix_init_from_float (graphene_matrix_t *m,
195                                  const float       *v)
196 {
197   graphene_simd4x4f_init_from_float (&m->value, v);
198 
199   return m;
200 }
201 
202 /**
203  * graphene_matrix_init_from_vec4:
204  * @m: a #graphene_matrix_t
205  * @v0: the first row vector
206  * @v1: the second row vector
207  * @v2: the third row vector
208  * @v3: the fourth row vector
209  *
210  * Initializes a #graphene_matrix_t with the given four row
211  * vectors.
212  *
213  * Returns: (transfer none): the initialized matrix
214  *
215  * Since: 1.0
216  */
217 graphene_matrix_t *
graphene_matrix_init_from_vec4(graphene_matrix_t * m,const graphene_vec4_t * v0,const graphene_vec4_t * v1,const graphene_vec4_t * v2,const graphene_vec4_t * v3)218 graphene_matrix_init_from_vec4 (graphene_matrix_t     *m,
219                                 const graphene_vec4_t *v0,
220                                 const graphene_vec4_t *v1,
221                                 const graphene_vec4_t *v2,
222                                 const graphene_vec4_t *v3)
223 {
224   m->value = graphene_simd4x4f_init (v0->value,
225                                      v1->value,
226                                      v2->value,
227                                      v3->value);
228 
229   return m;
230 }
231 
232 /**
233  * graphene_matrix_init_from_matrix:
234  * @m: a #graphene_matrix_t
235  * @src: a #graphene_matrix_t
236  *
237  * Initializes a #graphene_matrix_t using the values of the
238  * given matrix.
239  *
240  * Returns: (transfer none): the initialized matrix
241  *
242  * Since: 1.0
243  */
244 graphene_matrix_t *
graphene_matrix_init_from_matrix(graphene_matrix_t * m,const graphene_matrix_t * src)245 graphene_matrix_init_from_matrix (graphene_matrix_t       *m,
246                                   const graphene_matrix_t *src)
247 {
248   m->value = src->value;
249 
250   return m;
251 }
252 
253 /**
254  * graphene_matrix_init_perspective:
255  * @m: a #graphene_matrix_t
256  * @fovy: the field of view angle, in degrees
257  * @aspect: the aspect value
258  * @z_near: the near Z plane
259  * @z_far: the far Z plane
260  *
261  * Initializes a #graphene_matrix_t with a perspective projection.
262  *
263  * Returns: (transfer none): the initialized matrix
264  *
265  * Since: 1.0
266  */
267 graphene_matrix_t *
graphene_matrix_init_perspective(graphene_matrix_t * m,float fovy,float aspect,float z_near,float z_far)268 graphene_matrix_init_perspective (graphene_matrix_t *m,
269                                   float              fovy,
270                                   float              aspect,
271                                   float              z_near,
272                                   float              z_far)
273 {
274   float fovy_rad = GRAPHENE_DEG_TO_RAD (fovy);
275 
276   graphene_simd4x4f_init_perspective (&m->value, fovy_rad, aspect, z_near, z_far);
277 
278   return m;
279 }
280 
281 /**
282  * graphene_matrix_init_ortho:
283  * @m: a #graphene_matrix_t
284  * @left: the left edge of the clipping plane
285  * @right: the right edge of the clipping plane
286  * @top: the top edge of the clipping plane
287  * @bottom: the bottom edge of the clipping plane
288  * @z_near: the distance of the near clipping plane
289  * @z_far: the distance of the far clipping plane
290  *
291  * Initializes a #graphene_matrix_t with an orthographic projection.
292  *
293  * Returns: (transfer none): the initialized matrix
294  *
295  * Since: 1.0
296  */
297 graphene_matrix_t *
graphene_matrix_init_ortho(graphene_matrix_t * m,float left,float right,float top,float bottom,float z_near,float z_far)298 graphene_matrix_init_ortho (graphene_matrix_t *m,
299                             float              left,
300                             float              right,
301                             float              top,
302                             float              bottom,
303                             float              z_near,
304                             float              z_far)
305 {
306   graphene_simd4x4f_init_ortho (&m->value, left, right, top, bottom, z_near, z_far);
307 
308   return m;
309 }
310 
311 /**
312  * graphene_matrix_init_look_at:
313  * @m: a #graphene_matrix_t
314  * @eye: the vector describing the position to look from
315  * @center: the vector describing the position to look at
316  * @up: the vector describing the world's upward direction; usually,
317  *   this is the graphene_vec3_y_axis() vector
318  *
319  * Initializes a #graphene_matrix_t so that it positions the "camera"
320  * at the given @eye coordinates towards an object at the @center
321  * coordinates. The top of the camera is aligned to the direction
322  * of the @up vector.
323  *
324  * Before the transform, the camera is assumed to be placed at the
325  * origin, looking towards the negative Z axis, with the top side of
326  * the camera facing in the direction of the Y axis and the right
327  * side in the direction of the X axis.
328  *
329  * In theory, one could use @m to transform a model of such a camera
330  * into world-space. However, it is more common to use the inverse of
331  * @m to transform another object from world coordinates to the view
332  * coordinates of the camera. Typically you would then apply the
333  * camera projection transform to get from view to screen
334  * coordinates.
335  *
336  * Returns: (transfer none): the initialized matrix
337  *
338  * Since: 1.0
339  */
340 graphene_matrix_t *
graphene_matrix_init_look_at(graphene_matrix_t * m,const graphene_vec3_t * eye,const graphene_vec3_t * center,const graphene_vec3_t * up)341 graphene_matrix_init_look_at (graphene_matrix_t     *m,
342                               const graphene_vec3_t *eye,
343                               const graphene_vec3_t *center,
344                               const graphene_vec3_t *up)
345 {
346   graphene_simd4x4f_init_look_at (&m->value, eye->value, center->value, up->value);
347 
348   return m;
349 }
350 
351 /**
352  * graphene_matrix_init_frustum:
353  * @m: a #graphene_matrix_t
354  * @left: distance of the left clipping plane
355  * @right: distance of the right clipping plane
356  * @bottom: distance of the bottom clipping plane
357  * @top: distance of the top clipping plane
358  * @z_near: distance of the near clipping plane
359  * @z_far: distance of the far clipping plane
360  *
361  * Initializes a #graphene_matrix_t compatible with #graphene_frustum_t.
362  *
363  * See also: graphene_frustum_init_from_matrix()
364  *
365  * Returns: (transfer none): the initialized matrix
366  *
367  * Since: 1.2
368  */
369 graphene_matrix_t *
graphene_matrix_init_frustum(graphene_matrix_t * m,float left,float right,float bottom,float top,float z_near,float z_far)370 graphene_matrix_init_frustum (graphene_matrix_t *m,
371                               float              left,
372                               float              right,
373                               float              bottom,
374                               float              top,
375                               float              z_near,
376                               float              z_far)
377 {
378   graphene_simd4x4f_init_frustum (&m->value, left, right, bottom, top, z_near, z_far);
379 
380   return m;
381 }
382 
383 /**
384  * graphene_matrix_init_scale:
385  * @m: a #graphene_matrix_t
386  * @x: the scale factor on the X axis
387  * @y: the scale factor on the Y axis
388  * @z: the scale factor on the Z axis
389  *
390  * Initializes a #graphene_matrix_t with the given scaling factors.
391  *
392  * Returns: (transfer none): the initialized matrix
393  *
394  * Since: 1.0
395  */
396 graphene_matrix_t *
graphene_matrix_init_scale(graphene_matrix_t * m,float x,float y,float z)397 graphene_matrix_init_scale (graphene_matrix_t *m,
398                             float              x,
399                             float              y,
400                             float              z)
401 {
402   m->value =
403     graphene_simd4x4f_init (graphene_simd4f_init (   x, 0.0f, 0.0f, 0.0f),
404                             graphene_simd4f_init (0.0f,    y, 0.0f, 0.0f),
405                             graphene_simd4f_init (0.0f, 0.0f,    z, 0.0f),
406                             graphene_simd4f_init (0.0f, 0.0f, 0.0f, 1.0f));
407 
408   return m;
409 }
410 
411 /**
412  * graphene_matrix_init_translate:
413  * @m: a #graphene_matrix_t
414  * @p: the translation coordinates
415  *
416  * Initializes a #graphene_matrix_t with a translation to the
417  * given coordinates.
418  *
419  * Returns: (transfer none): the initialized matrix
420  *
421  * Since: 1.0
422  */
423 graphene_matrix_t *
graphene_matrix_init_translate(graphene_matrix_t * m,const graphene_point3d_t * p)424 graphene_matrix_init_translate (graphene_matrix_t        *m,
425                                 const graphene_point3d_t *p)
426 {
427   m->value =
428     graphene_simd4x4f_init (graphene_simd4f_init (1.0f, 0.0f, 0.0f, 0.0f),
429                             graphene_simd4f_init (0.0f, 1.0f, 0.0f, 0.0f),
430                             graphene_simd4f_init (0.0f, 0.0f, 1.0f, 0.0f),
431                             graphene_simd4f_init (p->x, p->y, p->z, 1.0f));
432 
433   return m;
434 }
435 
436 /**
437  * graphene_matrix_init_skew:
438  * @m: a #graphene_matrix_t
439  * @x_skew: skew factor, in radians, on the X axis
440  * @y_skew: skew factor, in radians, on the Y axis
441  *
442  * Initializes a #graphene_matrix_t with a skew transformation
443  * with the given factors.
444  *
445  * Returns: (transfer none): the initialized matrix
446  *
447  * Since: 1.0
448  */
449 graphene_matrix_t *
graphene_matrix_init_skew(graphene_matrix_t * m,float x_skew,float y_skew)450 graphene_matrix_init_skew (graphene_matrix_t *m,
451                            float              x_skew,
452                            float              y_skew)
453 {
454   float t_x, t_y;
455 
456   t_x = tanf (x_skew);
457   t_y = tanf (y_skew);
458 
459   m->value =
460     graphene_simd4x4f_init (graphene_simd4f_init (1.0f,  t_y, 0.0f, 0.0f),
461                             graphene_simd4f_init ( t_x, 1.0f, 0.0f, 0.0f),
462                             graphene_simd4f_init (0.0f, 0.0f, 1.0f, 0.0f),
463                             graphene_simd4f_init (0.0f, 0.0f, 0.0f, 1.0f));
464 
465   return m;
466 }
467 
468 /**
469  * graphene_matrix_init_rotate:
470  * @m: a #graphene_matrix_t
471  * @angle: the rotation angle, in degrees
472  * @axis: the axis vector as a #graphene_vec3_t
473  *
474  * Initializes @m to represent a rotation of @angle degrees on
475  * the axis represented by the @axis vector.
476  *
477  * Returns: (transfer none): the initialized matrix
478  *
479  * Since: 1.0
480  */
481 graphene_matrix_t *
graphene_matrix_init_rotate(graphene_matrix_t * m,float angle,const graphene_vec3_t * axis)482 graphene_matrix_init_rotate (graphene_matrix_t     *m,
483                              float                  angle,
484                              const graphene_vec3_t *axis)
485 {
486   float rad = GRAPHENE_DEG_TO_RAD (angle);
487 
488   graphene_simd4x4f_rotation (&m->value, rad, axis->value);
489 
490   return m;
491 }
492 
493 /**
494  * graphene_matrix_is_identity:
495  * @m: a #graphene_matrix_t
496  *
497  * Checks whether the given #graphene_matrix_t is the identity matrix.
498  *
499  * Returns: `true` if the matrix is the identity matrix
500  *
501  * Since: 1.0
502  */
503 bool
graphene_matrix_is_identity(const graphene_matrix_t * m)504 graphene_matrix_is_identity (const graphene_matrix_t *m)
505 {
506   return graphene_simd4x4f_is_identity (&m->value);
507 }
508 
509 /**
510  * graphene_matrix_is_2d:
511  * @m: a #graphene_matrix_t
512  *
513  * Checks whether the given #graphene_matrix_t is compatible with an
514  * a 2D affine transformation matrix.
515  *
516  * Returns: `true` if the matrix is compatible with an affine
517  *   transformation matrix
518  *
519  * Since: 1.0
520  */
521 bool
graphene_matrix_is_2d(const graphene_matrix_t * m)522 graphene_matrix_is_2d (const graphene_matrix_t *m)
523 {
524 #if 0
525   float res[4];
526 
527   graphene_simd4f_dup_4f (m->value.x, res);
528   if (!(graphene_fuzzy_equals (res[2], 0.f, 0.000001) &&
529         graphene_fuzzy_equals (res[3], 0.f, 0.000001)))
530     return false;
531 
532   graphene_simd4f_dup_4f (m->value.y, res);
533   if (!(graphene_fuzzy_equals (res[2], 0.f, 0.000001) &&
534         graphene_fuzzy_equals (res[3], 0.f, 0.000001)))
535     return false;
536 
537   graphene_simd4f_dup_4f (m->value.z, res);
538   if (!(graphene_fuzzy_equals (res[0], 0.f, 0.000001) &&
539         graphene_fuzzy_equals (res[1], 0.f, 0.000001) &&
540         graphene_fuzzy_equals (res[2], 1.f, 0.000001) &&
541         graphene_fuzzy_equals (res[3], 0.f, 0.000001)))
542     return false;
543 
544   graphene_simd4f_dup_4f (m->value.w, res);
545   if (!(graphene_fuzzy_equals (res[2], 0.f, 0.000001) &&
546         graphene_fuzzy_equals (res[3], 1.f, 0.000001)))
547     return false;
548 
549   return true;
550 #else
551   return graphene_simd4x4f_is_2d (&m->value);
552 #endif
553 }
554 
555 /**
556  * graphene_matrix_is_backface_visible:
557  * @m: a #graphene_matrix_t
558  *
559  * Checks whether a #graphene_matrix_t has a visible back face.
560  *
561  * Returns: `true` if the back face of the matrix is visible
562  *
563  * Since: 1.0
564  */
565 bool
graphene_matrix_is_backface_visible(const graphene_matrix_t * m)566 graphene_matrix_is_backface_visible (const graphene_matrix_t *m)
567 {
568   graphene_simd4x4f_t tmp;
569 
570   if (!graphene_simd4x4f_inverse (&m->value, &tmp))
571     return false;
572 
573   /* inverse.zz < 0 */
574   return graphene_simd4f_get_z (tmp.z) < 0.f;
575 }
576 
577 /**
578  * graphene_matrix_is_singular:
579  * @m: a #graphene_matrix_t
580  *
581  * Checks whether a matrix is singular.
582  *
583  * Returns: `true` if the matrix is singular
584  *
585  * Since: 1.0
586  */
587 bool
graphene_matrix_is_singular(const graphene_matrix_t * m)588 graphene_matrix_is_singular (const graphene_matrix_t *m)
589 {
590   graphene_simd4f_t det;
591 
592   graphene_simd4x4f_determinant (&m->value, &det, NULL);
593 
594   return fabsf (graphene_simd4f_get_x (det)) <= GRAPHENE_FLOAT_EPSILON;
595 }
596 
597 /**
598  * graphene_matrix_init_from_2d:
599  * @m: a #graphene_matrix_t
600  * @xx: the xx member
601  * @yx: the yx member
602  * @xy: the xy member
603  * @yy: the yy member
604  * @x_0: the x0 member
605  * @y_0: the y0 member
606  *
607  * Initializes a #graphene_matrix_t from the values of an affine
608  * transformation matrix.
609  *
610  * The arguments map to the following matrix layout:
611  *
612  * |[<!-- language="plain" -->
613  *   ⎛ xx  yx ⎞   ⎛  a   b  0 ⎞
614  *   ⎜ xy  yy ⎟ = ⎜  c   d  0 ⎟
615  *   ⎝ x0  y0 ⎠   ⎝ tx  ty  1 ⎠
616  * ]|
617  *
618  * This function can be used to convert between an affine matrix type
619  * from other libraries and a #graphene_matrix_t.
620  *
621  * Returns: (transfer none): the initialized matrix
622  *
623  * Since: 1.0
624  */
625 graphene_matrix_t *
graphene_matrix_init_from_2d(graphene_matrix_t * m,double xx,double yx,double xy,double yy,double x_0,double y_0)626 graphene_matrix_init_from_2d (graphene_matrix_t *m,
627                               double             xx,
628                               double             yx,
629                               double             xy,
630                               double             yy,
631                               double             x_0,
632                               double             y_0)
633 {
634   m->value = graphene_simd4x4f_init (graphene_simd4f_init ((float) xx, (float) yx, 0.f, 0.f),
635                                      graphene_simd4f_init ((float) xy, (float) yy, 0.f, 0.f),
636                                      graphene_simd4f_init (0.f, 0.f, 1.f, 0.f),
637                                      graphene_simd4f_init ((float) x_0, (float) y_0, 0.f, 1.f));
638 
639   return m;
640 }
641 
642 /**
643  * graphene_matrix_to_2d:
644  * @m: a #graphene_matrix_t
645  * @xx: (out): return location for the xx member
646  * @yx: (out): return location for the yx member
647  * @xy: (out): return location for the xy member
648  * @yy: (out): return location for the yy member
649  * @x_0: (out): return location for the x0 member
650  * @y_0: (out): return location for the y0 member
651  *
652  * Converts a #graphene_matrix_t to an affine transformation
653  * matrix, if the given matrix is compatible.
654  *
655  * The returned values have the following layout:
656  *
657  * |[<!-- language="plain" -->
658  *   ⎛ xx  yx ⎞   ⎛  a   b  0 ⎞
659  *   ⎜ xy  yy ⎟ = ⎜  c   d  0 ⎟
660  *   ⎝ x0  y0 ⎠   ⎝ tx  ty  1 ⎠
661  * ]|
662  *
663  * This function can be used to convert between a #graphene_matrix_t
664  * and an affine matrix type from other libraries.
665  *
666  * Returns: `true` if the matrix is compatible with an affine
667  *   transformation matrix
668  *
669  * Since: 1.0
670  */
671 bool
graphene_matrix_to_2d(const graphene_matrix_t * m,double * xx,double * yx,double * xy,double * yy,double * x_0,double * y_0)672 graphene_matrix_to_2d (const graphene_matrix_t *m,
673                        double                  *xx,
674                        double                  *yx,
675                        double                  *xy,
676                        double                  *yy,
677                        double                  *x_0,
678                        double                  *y_0)
679 {
680   float res[4];
681 
682   if (!graphene_simd4x4f_is_2d (&m->value))
683     return false;
684 
685   graphene_simd4f_dup_4f (m->value.x, res);
686   if (xx != NULL)
687     *xx = res[0];
688   if (yx != NULL)
689     *yx = res[1];
690 
691   graphene_simd4f_dup_4f (m->value.y, res);
692   if (xy != NULL)
693     *xy = res[0];
694   if (yy != NULL)
695     *yy = res[1];
696 
697   graphene_simd4f_dup_4f (m->value.w, res);
698   if (x_0 != NULL)
699     *x_0 = res[0];
700   if (y_0 != NULL)
701     *y_0 = res[1];
702 
703   return true;
704 }
705 
706 /**
707  * graphene_matrix_get_row:
708  * @m: a #graphene_matrix_t
709  * @index_: the index of the row vector, between 0 and 3
710  * @res: (out caller-allocates): return location for the #graphene_vec4_t
711  *   that is used to store the row vector
712  *
713  * Retrieves the given row vector at @index_ inside a matrix.
714  *
715  * Since: 1.0
716  */
717 void
graphene_matrix_get_row(const graphene_matrix_t * m,unsigned int index_,graphene_vec4_t * res)718 graphene_matrix_get_row (const graphene_matrix_t *m,
719                          unsigned int             index_,
720                          graphene_vec4_t         *res)
721 {
722   switch (index_)
723     {
724     case 0:
725       res->value = m->value.x;
726       break;
727 
728     case 1:
729       res->value = m->value.y;
730       break;
731 
732     case 2:
733       res->value = m->value.z;
734       break;
735 
736     case 3:
737       res->value = m->value.w;
738       break;
739 
740     default:
741       res->value = graphene_simd4f_init_zero ();
742       break;
743     }
744 }
745 
746 /**
747  * graphene_matrix_get_value:
748  * @m: a #graphene_matrix_t
749  * @row: the row index
750  * @col: the column index
751  *
752  * Retrieves the value at the given @row and @col index.
753  *
754  * Returns: the value at the given indices
755  *
756  * Since: 1.0
757  */
758 float
graphene_matrix_get_value(const graphene_matrix_t * m,unsigned int row,unsigned int col)759 graphene_matrix_get_value (const graphene_matrix_t *m,
760                            unsigned int             row,
761                            unsigned int             col)
762 {
763   graphene_simd4f_t r;
764 
765   if (row > 3 || col > 3)
766     return 0.f;
767 
768   switch (row)
769     {
770     case 0:
771       r = m->value.x;
772       break;
773 
774     case 1:
775       r = m->value.y;
776       break;
777 
778     case 2:
779       r = m->value.z;
780       break;
781 
782     case 3:
783       r = m->value.w;
784       break;
785 
786     default:
787       return 0.f;
788     }
789 
790   switch (col)
791     {
792     case 0:
793       return graphene_simd4f_get (r, 0);
794 
795     case 1:
796       return graphene_simd4f_get (r, 1);
797 
798     case 2:
799       return graphene_simd4f_get (r, 2);
800 
801     case 3:
802       return graphene_simd4f_get (r, 3);
803 
804     default:
805       return 0.f;
806     }
807 
808   return 0.f;
809 }
810 
811 /**
812  * graphene_matrix_multiply:
813  * @a: a #graphene_matrix_t
814  * @b: a #graphene_matrix_t
815  * @res: (out caller-allocates): return location for the matrix
816  *   result
817  *
818  * Multiplies two #graphene_matrix_t.
819  *
820  * Matrix multiplication is not commutative in general; the order of the factors matters.
821  * The product of this multiplication is (@a × @b)
822  *
823  * Since: 1.0
824  */
825 void
graphene_matrix_multiply(const graphene_matrix_t * a,const graphene_matrix_t * b,graphene_matrix_t * res)826 graphene_matrix_multiply (const graphene_matrix_t *a,
827                           const graphene_matrix_t *b,
828                           graphene_matrix_t       *res)
829 {
830   graphene_simd4x4f_matrix_mul (&a->value, &b->value, &res->value);
831 }
832 
833 /**
834  * graphene_matrix_determinant:
835  * @m: a #graphene_matrix_t
836  *
837  * Computes the determinant of the given matrix.
838  *
839  * Returns: the value of the determinant
840  *
841  * Since: 1.0
842  */
843 float
graphene_matrix_determinant(const graphene_matrix_t * m)844 graphene_matrix_determinant (const graphene_matrix_t *m)
845 {
846   graphene_simd4f_t det;
847 
848   graphene_simd4x4f_determinant (&m->value, &det, NULL);
849 
850   return graphene_simd4f_get_x (det);
851 }
852 
853 /**
854  * graphene_matrix_transform_vec3:
855  * @m: a #graphene_matrix_t
856  * @v: a #graphene_vec3_t
857  * @res: (out caller-allocates): return location for a #graphene_vec3_t
858  *
859  * Transforms the given #graphene_vec3_t using the matrix @m.
860  *
861  * This function will multiply the X, Y, and Z row vectors of the matrix @m
862  * with the corresponding components of the vector @v. The W row vector will
863  * be ignored.
864  *
865  * See also: graphene_simd4x4f_vec3_mul()
866  *
867  * Since: 1.0
868  */
869 void
graphene_matrix_transform_vec3(const graphene_matrix_t * m,const graphene_vec3_t * v,graphene_vec3_t * res)870 graphene_matrix_transform_vec3 (const graphene_matrix_t *m,
871                                 const graphene_vec3_t   *v,
872                                 graphene_vec3_t         *res)
873 {
874   graphene_simd4x4f_vec3_mul (&m->value, &v->value, &res->value);
875 }
876 
877 /**
878  * graphene_matrix_transform_vec4:
879  * @m: a #graphene_matrix_t
880  * @v: a #graphene_vec4_t
881  * @res: (out caller-allocates): return location for a #graphene_vec4_t
882  *
883  * Transforms the given #graphene_vec4_t using the matrix @m.
884  *
885  * See also: graphene_simd4x4f_vec4_mul()
886  *
887  * Since: 1.0
888  */
889 void
graphene_matrix_transform_vec4(const graphene_matrix_t * m,const graphene_vec4_t * v,graphene_vec4_t * res)890 graphene_matrix_transform_vec4 (const graphene_matrix_t *m,
891                                 const graphene_vec4_t   *v,
892                                 graphene_vec4_t         *res)
893 {
894   graphene_simd4x4f_vec4_mul (&m->value, &v->value, &res->value);
895 }
896 
897 /**
898  * graphene_matrix_transform_point:
899  * @m: a #graphene_matrix_t
900  * @p: a #graphene_point_t
901  * @res: (out caller-allocates): return location for the
902  *   transformed #graphene_point_t
903  *
904  * Transforms the given #graphene_point_t using the matrix @m.
905  *
906  * Unlike graphene_matrix_transform_vec3(), this function will take into
907  * account the fourth row vector of the #graphene_matrix_t when computing
908  * the dot product of each row vector of the matrix.
909  *
910  * See also: graphene_simd4x4f_point3_mul()
911  *
912  * Since: 1.0
913  */
914 void
graphene_matrix_transform_point(const graphene_matrix_t * m,const graphene_point_t * p,graphene_point_t * res)915 graphene_matrix_transform_point (const graphene_matrix_t *m,
916                                  const graphene_point_t  *p,
917                                  graphene_point_t        *res)
918 {
919   graphene_simd4f_t vec3;
920 
921   vec3 = graphene_simd4f_init (p->x, p->y, 0.0f, 1.0f);
922   graphene_simd4x4f_point3_mul (&m->value, &vec3, &vec3);
923 
924   res->x = graphene_simd4f_get_x (vec3);
925   res->y = graphene_simd4f_get_y (vec3);
926 }
927 
928 /**
929  * graphene_matrix_transform_point3d:
930  * @m: a #graphene_matrix_t
931  * @p: a #graphene_point3d_t
932  * @res: (out caller-allocates): return location for the result
933  *
934  * Transforms the given #graphene_point3d_t using the matrix @m.
935  *
936  * Unlike graphene_matrix_transform_vec3(), this function will take into
937  * account the fourth row vector of the #graphene_matrix_t when computing
938  * the dot product of each row vector of the matrix.
939  *
940  * See also: graphene_simd4x4f_point3_mul()
941  *
942  * Since: 1.2
943  */
944 void
graphene_matrix_transform_point3d(const graphene_matrix_t * m,const graphene_point3d_t * p,graphene_point3d_t * res)945 graphene_matrix_transform_point3d (const graphene_matrix_t  *m,
946                                    const graphene_point3d_t *p,
947                                    graphene_point3d_t       *res)
948 {
949   graphene_simd4f_t vec3;
950 
951   vec3 = graphene_simd4f_init (p->x, p->y, p->z, 1.f);
952   graphene_simd4x4f_point3_mul (&m->value, &vec3, &vec3);
953 
954   res->x = graphene_simd4f_get_x (vec3);
955   res->y = graphene_simd4f_get_y (vec3);
956   res->z = graphene_simd4f_get_z (vec3);
957 }
958 
959 /**
960  * graphene_matrix_transform_rect:
961  * @m: a #graphene_matrix_t
962  * @r: a #graphene_rect_t
963  * @res: (out caller-allocates): return location for the
964  *   transformed quad
965  *
966  * Transforms each corner of a #graphene_rect_t using the given matrix @m.
967  *
968  * The result is a coplanar quadrilateral.
969  *
970  * See also: graphene_matrix_transform_point()
971  *
972  * Since: 1.0
973  */
974 void
graphene_matrix_transform_rect(const graphene_matrix_t * m,const graphene_rect_t * r,graphene_quad_t * res)975 graphene_matrix_transform_rect (const graphene_matrix_t *m,
976                                 const graphene_rect_t   *r,
977                                 graphene_quad_t         *res)
978 {
979   graphene_point_t ret[4];
980   graphene_rect_t rr;
981 
982   graphene_rect_normalize_r (r, &rr);
983 
984 #define TRANSFORM_POINT(matrix, rect, corner, out_p)   do {\
985   graphene_simd4f_t __s; \
986   graphene_point_t __p; \
987   graphene_rect_get_ ## corner (rect, &__p); \
988   __s = graphene_simd4f_init (__p.x, __p.y, 0.f, 1.f); \
989   graphene_simd4x4f_vec4_mul (&matrix->value, &__s, &__s); \
990   out_p.x = graphene_simd4f_get_x (__s); \
991   out_p.y = graphene_simd4f_get_y (__s);           } while (0)
992 
993   TRANSFORM_POINT (m, &rr, top_left, ret[0]);
994   TRANSFORM_POINT (m, &rr, top_right, ret[1]);
995   TRANSFORM_POINT (m, &rr, bottom_right, ret[2]);
996   TRANSFORM_POINT (m, &rr, bottom_left, ret[3]);
997 
998 #undef TRANSFORM_POINT
999 
1000   graphene_quad_init (res, &ret[0], &ret[1], &ret[2], &ret[3]);
1001 }
1002 
1003 /**
1004  * graphene_matrix_transform_bounds:
1005  * @m: a #graphene_matrix_t
1006  * @r: a #graphene_rect_t
1007  * @res: (out caller-allocates): return location for the bounds
1008  *   of the transformed rectangle
1009  *
1010  * Transforms each corner of a #graphene_rect_t using the given matrix @m.
1011  *
1012  * The result is the axis aligned bounding rectangle containing the coplanar
1013  * quadrilateral.
1014  *
1015  * See also: graphene_matrix_transform_point()
1016  *
1017  * Since: 1.0
1018  */
1019 void
graphene_matrix_transform_bounds(const graphene_matrix_t * m,const graphene_rect_t * r,graphene_rect_t * res)1020 graphene_matrix_transform_bounds (const graphene_matrix_t *m,
1021                                   const graphene_rect_t   *r,
1022                                   graphene_rect_t         *res)
1023 {
1024   graphene_point_t ret[4];
1025   float min_x, min_y;
1026   float max_x, max_y;
1027 
1028   graphene_rect_t rr;
1029 
1030   graphene_rect_normalize_r (r, &rr);
1031 
1032 #define TRANSFORM_POINT(matrix, rect, corner, out_p)   do {\
1033   graphene_simd4f_t __s; \
1034   graphene_point_t __p; \
1035   graphene_rect_get_ ## corner (rect, &__p); \
1036   __s = graphene_simd4f_init (__p.x, __p.y, 0.f, 1.f); \
1037   graphene_simd4x4f_vec4_mul (&matrix->value, &__s, &__s); \
1038   out_p.x = graphene_simd4f_get_x (__s); \
1039   out_p.y = graphene_simd4f_get_y (__s);           } while (0)
1040 
1041   TRANSFORM_POINT (m, &rr, top_left, ret[0]);
1042   TRANSFORM_POINT (m, &rr, top_right, ret[1]);
1043   TRANSFORM_POINT (m, &rr, bottom_right, ret[2]);
1044   TRANSFORM_POINT (m, &rr, bottom_left, ret[3]);
1045 
1046 #undef TRANSFORM_POINT
1047 
1048 #if 0
1049   {
1050     int i;
1051 
1052     min_x = max_x = ret[0].x;
1053     min_y = max_y = ret[0].y;
1054 
1055     for (i = 1; i < 4; i += 1)
1056       {
1057         min_x = MIN (ret[i].x, min_x);
1058         min_y = MIN (ret[i].y, min_y);
1059 
1060         max_x = MAX (ret[i].x, max_x);
1061         max_y = MAX (ret[i].y, max_y);
1062       }
1063   }
1064 #else
1065   {
1066     const graphene_simd4f_t vx = graphene_simd4f_init (ret[0].x, ret[1].x, ret[2].x, ret[3].x);
1067     const graphene_simd4f_t vy = graphene_simd4f_init (ret[0].y, ret[1].y, ret[2].y, ret[3].y);
1068 
1069     min_x = graphene_simd4f_get_x (graphene_simd4f_min_val (vx));
1070     min_y = graphene_simd4f_get_x (graphene_simd4f_min_val (vy));
1071 
1072     max_x = graphene_simd4f_get_x (graphene_simd4f_max_val (vx));
1073     max_y = graphene_simd4f_get_x (graphene_simd4f_max_val (vy));
1074   }
1075 #endif
1076 
1077   graphene_rect_init (res, min_x, min_y, max_x - min_x, max_y - min_y);
1078 }
1079 
1080 /**
1081  * graphene_matrix_transform_sphere:
1082  * @m: a #graphene_matrix_t
1083  * @s: a #graphene_sphere_t
1084  * @res: (out caller-allocates): return location for the bounds
1085  *   of the transformed sphere
1086  *
1087  * Transforms a #graphene_sphere_t using the given matrix @m. The
1088  * result is the bounding sphere containing the transformed sphere.
1089  *
1090  * Since: 1.2
1091  */
1092 void
graphene_matrix_transform_sphere(const graphene_matrix_t * m,const graphene_sphere_t * s,graphene_sphere_t * res)1093 graphene_matrix_transform_sphere (const graphene_matrix_t *m,
1094                                   const graphene_sphere_t *s,
1095                                   graphene_sphere_t       *res)
1096 {
1097   float max_scale;
1098 
1099   graphene_simd4x4f_point3_mul (&m->value, &s->center.value, &res->center.value);
1100 
1101   max_scale = graphene_simd4f_dot3_scalar (m->value.x, m->value.x);
1102   max_scale = fmaxf (max_scale, graphene_simd4f_dot3_scalar (m->value.y, m->value.y));
1103   max_scale = fmaxf (max_scale, graphene_simd4f_dot3_scalar (m->value.z, m->value.z));
1104 
1105   res->radius = s->radius * sqrtf (max_scale);
1106 }
1107 
1108 /**
1109  * graphene_matrix_transform_box:
1110  * @m: a #graphene_matrix_t
1111  * @b: a #graphene_box_t
1112  * @res: (out caller-allocates): return location for the bounds
1113  *   of the transformed box
1114  *
1115  * Transforms the vertices of a #graphene_box_t using the given matrix @m.
1116  *
1117  * The result is the axis aligned bounding box containing the transformed
1118  * vertices.
1119  *
1120  * Since: 1.2
1121  */
1122 void
graphene_matrix_transform_box(const graphene_matrix_t * m,const graphene_box_t * b,graphene_box_t * res)1123 graphene_matrix_transform_box (const graphene_matrix_t *m,
1124                                const graphene_box_t    *b,
1125                                graphene_box_t          *res)
1126 {
1127   graphene_vec3_t points[8];
1128 
1129   graphene_box_get_vertices (b, points);
1130 
1131   for (int i = 0; i < 8; i++)
1132     graphene_simd4x4f_point3_mul (&m->value, &(points[i].value), &(points[i].value));
1133 
1134   graphene_box_init_from_vectors (res, 8, points);
1135 }
1136 
1137 /**
1138  * graphene_matrix_transform_ray:
1139  * @m: a #graphene_matrix_t
1140  * @r: a #graphene_ray_t
1141  * @res: (out caller-allocates): return location for the
1142  *   transformed ray
1143  *
1144  * Transform a #graphene_ray_t using the given matrix @m.
1145  *
1146  * Since: 1.4
1147  */
1148 void
graphene_matrix_transform_ray(const graphene_matrix_t * m,const graphene_ray_t * r,graphene_ray_t * res)1149 graphene_matrix_transform_ray (const graphene_matrix_t *m,
1150                                const graphene_ray_t    *r,
1151                                graphene_ray_t          *res)
1152 {
1153   graphene_vec3_t origin, direction;
1154   graphene_vec4_t origin4;
1155 
1156   graphene_vec4_init_from_vec3 (&origin4, &r->origin, 1);
1157   graphene_matrix_transform_vec4 (m, &origin4, &origin4);
1158   graphene_vec4_get_xyz (&origin4, &origin);
1159 
1160   graphene_matrix_transform_vec3 (m, &r->direction, &direction);
1161 
1162   graphene_ray_init_from_vec3 (res, &origin, &direction);
1163 }
1164 
1165 /**
1166  * graphene_matrix_project_point:
1167  * @m: a #graphene_matrix_t
1168  * @p: a #graphene_point_t
1169  * @res: (out caller-allocates): return location for the projected
1170  *   point
1171  *
1172  * Projects a #graphene_point_t using the matrix @m.
1173  *
1174  * Since: 1.0
1175  */
1176 void
graphene_matrix_project_point(const graphene_matrix_t * m,const graphene_point_t * p,graphene_point_t * res)1177 graphene_matrix_project_point (const graphene_matrix_t *m,
1178                                const graphene_point_t  *p,
1179                                graphene_point_t        *res)
1180 {
1181   graphene_simd4f_t pa, pb, pc;
1182   float a[3], b[3];
1183   float t;
1184 
1185   pa = graphene_simd4f_init (p->x, p->y, 0.f, 0.f);
1186   pb = graphene_simd4f_init (p->x, p->y, 1.f, 0.f);
1187 
1188   graphene_simd4x4f_vec3_mul (&m->value, &pa, &pa);
1189   graphene_simd4x4f_vec3_mul (&m->value, &pb, &pb);
1190   pc = graphene_simd4f_sub (pa, pb);
1191 
1192   graphene_simd4f_dup_3f (pa, a);
1193   graphene_simd4f_dup_3f (pc, b);
1194   t = -a[2] / b[2];
1195 
1196   graphene_point_init (res, a[0] + t * b[0], a[1] + t * b[1]);
1197 }
1198 
1199 /**
1200  * graphene_matrix_project_rect_bounds:
1201  * @m: a #graphene_matrix_t
1202  * @r: a #graphene_rect_t
1203  * @res: (out caller-allocates): return location for the projected
1204  *   rectangle
1205  *
1206  * Projects a #graphene_rect_t using the given matrix.
1207  *
1208  * The resulting rectangle is the axis aligned bounding rectangle capable
1209  * of fully containing the projected rectangle.
1210  *
1211  * Since: 1.0
1212  */
1213 void
graphene_matrix_project_rect_bounds(const graphene_matrix_t * m,const graphene_rect_t * r,graphene_rect_t * res)1214 graphene_matrix_project_rect_bounds (const graphene_matrix_t *m,
1215                                      const graphene_rect_t   *r,
1216                                      graphene_rect_t         *res)
1217 {
1218   graphene_point_t points[4];
1219   graphene_point_t ret[4];
1220   graphene_rect_t rr;
1221 
1222   graphene_rect_normalize_r (r, &rr);
1223 
1224   graphene_rect_get_top_left (&rr, &points[0]);
1225   graphene_rect_get_top_right (&rr, &points[1]);
1226   graphene_rect_get_bottom_left (&rr, &points[2]);
1227   graphene_rect_get_bottom_right (&rr, &points[3]);
1228 
1229   graphene_matrix_project_point (m, &points[0], &ret[0]);
1230   graphene_matrix_project_point (m, &points[1], &ret[1]);
1231   graphene_matrix_project_point (m, &points[2], &ret[2]);
1232   graphene_matrix_project_point (m, &points[3], &ret[3]);
1233 
1234   graphene_simd4f_t v_x = graphene_simd4f_init (ret[0].x, ret[1].x, ret[2].x, ret[3].x);
1235   graphene_simd4f_t v_y = graphene_simd4f_init (ret[0].y, ret[1].y, ret[2].y, ret[3].y);
1236 
1237   float min_x = graphene_simd4f_get_x (graphene_simd4f_min_val (v_x));
1238   float max_x = graphene_simd4f_get_x (graphene_simd4f_max_val (v_x));
1239   float min_y = graphene_simd4f_get_x (graphene_simd4f_min_val (v_y));
1240   float max_y = graphene_simd4f_get_x (graphene_simd4f_max_val (v_y));
1241 
1242   graphene_rect_init (res, min_x, min_y, max_x - min_x, max_y - min_y);
1243 }
1244 
1245 /**
1246  * graphene_matrix_project_rect:
1247  * @m: a #graphene_matrix_t
1248  * @r: a #graphene_rect_t
1249  * @res: (out caller-allocates): return location for the projected
1250  *   rectangle
1251  *
1252  * Projects all corners of a #graphene_rect_t using the given matrix.
1253  *
1254  * See also: graphene_matrix_project_point()
1255  *
1256  * Since: 1.2
1257  */
1258 void
graphene_matrix_project_rect(const graphene_matrix_t * m,const graphene_rect_t * r,graphene_quad_t * res)1259 graphene_matrix_project_rect (const graphene_matrix_t *m,
1260                               const graphene_rect_t   *r,
1261                               graphene_quad_t         *res)
1262 {
1263   graphene_point_t p[4];
1264   graphene_rect_t rr;
1265 
1266   graphene_rect_normalize_r (r, &rr);
1267 
1268   graphene_rect_get_top_left (&rr, &p[0]);
1269   graphene_matrix_project_point (m, &p[0], &p[0]);
1270 
1271   graphene_rect_get_top_right (&rr, &p[1]);
1272   graphene_matrix_project_point (m, &p[1], &p[1]);
1273 
1274   graphene_rect_get_bottom_left (&rr, &p[2]);
1275   graphene_matrix_project_point (m, &p[2], &p[2]);
1276 
1277   graphene_rect_get_bottom_right (&rr, &p[3]);
1278   graphene_matrix_project_point (m, &p[3], &p[3]);
1279 
1280   graphene_quad_init_from_points (res, p);
1281 }
1282 
1283 /**
1284  * graphene_matrix_untransform_point:
1285  * @m: a #graphene_matrix_t
1286  * @p: a #graphene_point_t
1287  * @bounds: the bounds of the transformation
1288  * @res: (out caller-allocates): return location for the
1289  *   untransformed point
1290  *
1291  * Undoes the transformation of a #graphene_point_t using the
1292  * given matrix, within the given axis aligned rectangular @bounds.
1293  *
1294  * Returns: `true` if the point was successfully untransformed
1295  *
1296  * Since: 1.0
1297  */
1298 bool
graphene_matrix_untransform_point(const graphene_matrix_t * m,const graphene_point_t * p,const graphene_rect_t * bounds,graphene_point_t * res)1299 graphene_matrix_untransform_point (const graphene_matrix_t *m,
1300                                    const graphene_point_t  *p,
1301                                    const graphene_rect_t   *bounds,
1302                                    graphene_point_t        *res)
1303 {
1304   graphene_matrix_t inverse;
1305   graphene_rect_t bounds_t;
1306 
1307   if (graphene_matrix_is_2d (m))
1308     {
1309       if (!graphene_matrix_inverse (m, &inverse))
1310         return false;
1311 
1312       graphene_matrix_transform_point (&inverse, p, res);
1313       return true;
1314     }
1315 
1316   graphene_matrix_transform_bounds (m, bounds, &bounds_t);
1317   if (!graphene_rect_contains_point (&bounds_t, p))
1318     return false;
1319 
1320   if (!graphene_matrix_inverse (m, &inverse))
1321     return false;
1322 
1323   graphene_matrix_project_point (&inverse, p, res);
1324 
1325   return true;
1326 }
1327 
1328 /**
1329  * graphene_matrix_untransform_bounds:
1330  * @m: a #graphene_matrix_t
1331  * @r: a #graphene_rect_t
1332  * @bounds: the bounds of the transformation
1333  * @res: (out caller-allocates): return location for the
1334  *   untransformed rectangle
1335  *
1336  * Undoes the transformation on the corners of a #graphene_rect_t using the
1337  * given matrix, within the given axis aligned rectangular @bounds.
1338  *
1339  * Since: 1.0
1340  */
1341 void
graphene_matrix_untransform_bounds(const graphene_matrix_t * m,const graphene_rect_t * r,const graphene_rect_t * bounds,graphene_rect_t * res)1342 graphene_matrix_untransform_bounds (const graphene_matrix_t *m,
1343                                     const graphene_rect_t   *r,
1344                                     const graphene_rect_t   *bounds,
1345                                     graphene_rect_t         *res)
1346 {
1347   graphene_matrix_t inverse;
1348   graphene_rect_t bounds_t;
1349   graphene_rect_t rect;
1350 
1351   if (graphene_matrix_is_2d (m))
1352     {
1353       if (!graphene_matrix_inverse (m, &inverse))
1354         return;
1355 
1356       graphene_matrix_transform_bounds (&inverse, r, res);
1357       return;
1358     }
1359 
1360   graphene_matrix_transform_bounds (m, bounds, &bounds_t);
1361   if (!graphene_rect_intersection (r, &bounds_t, &rect))
1362     {
1363       graphene_rect_init (res, 0.f, 0.f, 0.f, 0.f);
1364       return;
1365     }
1366 
1367   if (!graphene_matrix_inverse (m, &inverse))
1368     return;
1369 
1370   graphene_matrix_project_rect_bounds (&inverse, &rect, res);
1371 }
1372 
1373 /**
1374  * graphene_matrix_unproject_point3d:
1375  * @projection: a #graphene_matrix_t for the projection matrix
1376  * @modelview: a #graphene_matrix_t for the modelview matrix; this is
1377  *   the inverse of the modelview used when projecting the point
1378  * @point: a #graphene_point3d_t with the coordinates of the point
1379  * @res: (out caller-allocates): return location for the unprojected
1380  *   point
1381  *
1382  * Unprojects the given @point using the @projection matrix and
1383  * a @modelview matrix.
1384  *
1385  * Since: 1.2
1386  */
1387 void
graphene_matrix_unproject_point3d(const graphene_matrix_t * projection,const graphene_matrix_t * modelview,const graphene_point3d_t * point,graphene_point3d_t * res)1388 graphene_matrix_unproject_point3d (const graphene_matrix_t  *projection,
1389                                    const graphene_matrix_t  *modelview,
1390                                    const graphene_point3d_t *point,
1391                                    graphene_point3d_t       *res)
1392 {
1393   graphene_simd4x4f_t tmp;
1394   graphene_simd4f_t v;
1395   float values[4];
1396   float inv_w;
1397 
1398   if (!graphene_simd4x4f_inverse (&projection->value, &tmp))
1399     return;
1400 
1401   graphene_simd4x4f_matrix_mul (&tmp, &modelview->value, &tmp);
1402 
1403   v = graphene_simd4f_init (point->x, point->y, point->z, 1.f);
1404   graphene_simd4x4f_vec4_mul (&tmp, &v, &v);
1405 
1406   inv_w = 1.f / graphene_simd4f_get_w (v);
1407   v = graphene_simd4f_mul (v, graphene_simd4f_splat (inv_w));
1408 
1409   graphene_simd4f_dup_4f (v, values);
1410   graphene_point3d_init (res, values[0], values[1], values[2]);
1411 }
1412 
1413 /**
1414  * graphene_matrix_translate:
1415  * @m: a #graphene_matrix_t
1416  * @pos: a #graphene_point3d_t
1417  *
1418  * Adds a translation transformation to @m using the coordinates
1419  * of the given #graphene_point3d_t.
1420  *
1421  * This is the equivalent of calling graphene_matrix_init_translate() and
1422  * then multiplying @m with the translation matrix.
1423  *
1424  * Since: 1.0
1425  */
1426 void
graphene_matrix_translate(graphene_matrix_t * m,const graphene_point3d_t * pos)1427 graphene_matrix_translate (graphene_matrix_t        *m,
1428                            const graphene_point3d_t *pos)
1429 {
1430   graphene_simd4x4f_t trans_m;
1431 
1432   graphene_simd4x4f_translation (&trans_m, pos->x, pos->y, pos->z);
1433   graphene_simd4x4f_matrix_mul (&m->value, &trans_m, &m->value);
1434 }
1435 
1436 /**
1437  * graphene_matrix_rotate_quaternion:
1438  * @m: a #graphene_matrix_t
1439  * @q: a rotation described by a #graphene_quaternion_t
1440  *
1441  * Adds a rotation transformation to @m, using the given
1442  * #graphene_quaternion_t.
1443  *
1444  * This is the equivalent of calling graphene_quaternion_to_matrix() and
1445  * then multiplying @m with the rotation matrix.
1446  *
1447  * Since: 1.2
1448  */
1449 void
graphene_matrix_rotate_quaternion(graphene_matrix_t * m,const graphene_quaternion_t * q)1450 graphene_matrix_rotate_quaternion (graphene_matrix_t           *m,
1451                                    const graphene_quaternion_t *q)
1452 {
1453   graphene_matrix_t rot;
1454 
1455   graphene_quaternion_to_matrix (q, &rot);
1456   graphene_matrix_multiply (m, &rot, m);
1457 }
1458 
1459 /**
1460  * graphene_matrix_rotate_euler:
1461  * @m: a #graphene_matrix_t
1462  * @e: a rotation described by a #graphene_euler_t
1463  *
1464  * Adds a rotation transformation to @m, using the given
1465  * #graphene_euler_t.
1466  *
1467  * Since: 1.2
1468  */
1469 void
graphene_matrix_rotate_euler(graphene_matrix_t * m,const graphene_euler_t * e)1470 graphene_matrix_rotate_euler (graphene_matrix_t      *m,
1471                               const graphene_euler_t *e)
1472 {
1473   graphene_quaternion_t q;
1474 
1475   graphene_quaternion_init_from_euler (&q, e);
1476   graphene_matrix_rotate_quaternion (m, &q);
1477 }
1478 
1479 static inline void
graphene_matrix_rotate_internal(graphene_simd4x4f_t * m,float rad,const graphene_simd4f_t axis)1480 graphene_matrix_rotate_internal (graphene_simd4x4f_t     *m,
1481                                  float                    rad,
1482                                  const graphene_simd4f_t  axis)
1483 {
1484   graphene_simd4x4f_t rot_m;
1485 
1486   graphene_simd4x4f_rotation (&rot_m, rad, axis);
1487   graphene_simd4x4f_matrix_mul (m, &rot_m, m);
1488 }
1489 
1490 /**
1491  * graphene_matrix_rotate:
1492  * @m: a #graphene_matrix_t
1493  * @angle: the rotation angle, in degrees
1494  * @axis: the rotation axis, as a #graphene_vec3_t
1495  *
1496  * Adds a rotation transformation to @m, using the given @angle
1497  * and @axis vector.
1498  *
1499  * This is the equivalent of calling graphene_matrix_init_rotate() and
1500  * then multiplying the matrix @m with the rotation matrix.
1501  *
1502  * Since: 1.0
1503  */
1504 void
graphene_matrix_rotate(graphene_matrix_t * m,float angle,const graphene_vec3_t * axis)1505 graphene_matrix_rotate (graphene_matrix_t     *m,
1506                         float                  angle,
1507                         const graphene_vec3_t *axis)
1508 {
1509   graphene_matrix_rotate_internal (&m->value, GRAPHENE_DEG_TO_RAD (angle), axis->value);
1510 }
1511 
1512 /**
1513  * graphene_matrix_rotate_x:
1514  * @m: a #graphene_matrix_t
1515  * @angle: the rotation angle, in degrees
1516  *
1517  * Adds a rotation transformation around the X axis to @m, using
1518  * the given @angle.
1519  *
1520  * See also: graphene_matrix_rotate()
1521  *
1522  * Since: 1.0
1523  */
1524 void
graphene_matrix_rotate_x(graphene_matrix_t * m,float angle)1525 graphene_matrix_rotate_x (graphene_matrix_t *m,
1526                           float              angle)
1527 {
1528   graphene_matrix_rotate_internal (&m->value, GRAPHENE_DEG_TO_RAD (angle),
1529                                    graphene_simd4f_init (1.f, 0.f, 0.f, 0.f));
1530 }
1531 
1532 /**
1533  * graphene_matrix_rotate_y:
1534  * @m: a #graphene_matrix_t
1535  * @angle: the rotation angle, in degrees
1536  *
1537  * Adds a rotation transformation around the Y axis to @m, using
1538  * the given @angle.
1539  *
1540  * See also: graphene_matrix_rotate()
1541  *
1542  * Since: 1.0
1543  */
1544 void
graphene_matrix_rotate_y(graphene_matrix_t * m,float angle)1545 graphene_matrix_rotate_y (graphene_matrix_t *m,
1546                           float              angle)
1547 {
1548   graphene_matrix_rotate_internal (&m->value, GRAPHENE_DEG_TO_RAD (angle),
1549                                    graphene_simd4f_init (0.f, 1.f, 0.f, 0.f));
1550 }
1551 
1552 /**
1553  * graphene_matrix_rotate_z:
1554  * @m: a #graphene_matrix_t
1555  * @angle: the rotation angle, in degrees
1556  *
1557  * Adds a rotation transformation around the Z axis to @m, using
1558  * the given @angle.
1559  *
1560  * See also: graphene_matrix_rotate()
1561  *
1562  * Since: 1.0
1563  */
1564 void
graphene_matrix_rotate_z(graphene_matrix_t * m,float angle)1565 graphene_matrix_rotate_z (graphene_matrix_t *m,
1566                           float              angle)
1567 {
1568   graphene_matrix_rotate_internal (&m->value, GRAPHENE_DEG_TO_RAD (angle),
1569                                    graphene_simd4f_init (0.f, 0.f, 1.f, 0.f));
1570 }
1571 
1572 /**
1573  * graphene_matrix_scale:
1574  * @m: a #graphene_matrix_t
1575  * @factor_x: scaling factor on the X axis
1576  * @factor_y: scaling factor on the Y axis
1577  * @factor_z: scaling factor on the Z axis
1578  *
1579  * Adds a scaling transformation to @m, using the three
1580  * given factors.
1581  *
1582  * This is the equivalent of calling graphene_matrix_init_scale() and then
1583  * multiplying the matrix @m with the scale matrix.
1584  *
1585  * Since: 1.0
1586  */
1587 void
graphene_matrix_scale(graphene_matrix_t * m,float factor_x,float factor_y,float factor_z)1588 graphene_matrix_scale (graphene_matrix_t *m,
1589                        float              factor_x,
1590                        float              factor_y,
1591                        float              factor_z)
1592 {
1593   graphene_simd4x4f_t scale_m;
1594 
1595   graphene_simd4x4f_scale (&scale_m, factor_x, factor_y, factor_z);
1596   graphene_simd4x4f_matrix_mul (&m->value, &scale_m, &m->value);
1597 }
1598 
1599 /**
1600  * graphene_matrix_skew_xy:
1601  * @m: a #graphene_matrix_t
1602  * @factor: skew factor
1603  *
1604  * Adds a skew of @factor on the X and Y axis to the given matrix.
1605  *
1606  * Since: 1.0
1607  */
1608 void
graphene_matrix_skew_xy(graphene_matrix_t * m,float factor)1609 graphene_matrix_skew_xy (graphene_matrix_t *m,
1610                          float              factor)
1611 {
1612   graphene_simd4f_t m_x, m_y;
1613 
1614   m_x = m->value.x;
1615   m_y = m->value.y;
1616 
1617   m->value.y = graphene_simd4f_madd (m_x, graphene_simd4f_splat (factor), m_y);
1618 }
1619 
1620 /**
1621  * graphene_matrix_skew_xz:
1622  * @m: a #graphene_matrix_t
1623  * @factor: skew factor
1624  *
1625  * Adds a skew of @factor on the X and Z axis to the given matrix.
1626  *
1627  * Since: 1.0
1628  */
1629 void
graphene_matrix_skew_xz(graphene_matrix_t * m,float factor)1630 graphene_matrix_skew_xz (graphene_matrix_t *m,
1631                          float              factor)
1632 {
1633   graphene_simd4f_t m_x, m_z;
1634 
1635   m_x = m->value.x;
1636   m_z = m->value.z;
1637 
1638   m->value.z = graphene_simd4f_madd (m_x, graphene_simd4f_splat (factor), m_z);
1639 }
1640 
1641 /**
1642  * graphene_matrix_skew_yz:
1643  * @m: a #graphene_matrix_t
1644  * @factor: skew factor
1645  *
1646  * Adds a skew of @factor on the Y and Z axis to the given matrix.
1647  *
1648  * Since: 1.0
1649  */
1650 void
graphene_matrix_skew_yz(graphene_matrix_t * m,float factor)1651 graphene_matrix_skew_yz (graphene_matrix_t *m,
1652                          float              factor)
1653 {
1654   graphene_simd4f_t m_y, m_z;
1655 
1656   m_y = m->value.y;
1657   m_z = m->value.z;
1658 
1659   m->value.z = graphene_simd4f_madd (m_y, graphene_simd4f_splat (factor), m_z);
1660 }
1661 
1662 /**
1663  * graphene_matrix_transpose:
1664  * @m: a #graphene_matrix_t
1665  * @res: (out caller-allocates): return location for the
1666  *   transposed matrix
1667  *
1668  * Transposes the given matrix.
1669  *
1670  * Since: 1.0
1671  */
1672 void
graphene_matrix_transpose(const graphene_matrix_t * m,graphene_matrix_t * res)1673 graphene_matrix_transpose (const graphene_matrix_t *m,
1674                            graphene_matrix_t       *res)
1675 {
1676   graphene_simd4x4f_transpose (&m->value, &res->value);
1677 }
1678 
1679 /**
1680  * graphene_matrix_inverse:
1681  * @m: a #graphene_matrix_t
1682  * @res: (out caller-allocates): return location for the
1683  *   inverse matrix
1684  *
1685  * Inverts the given matrix.
1686  *
1687  * Returns: `true` if the matrix is invertible
1688  *
1689  * Since: 1.0
1690  */
1691 bool
graphene_matrix_inverse(const graphene_matrix_t * m,graphene_matrix_t * res)1692 graphene_matrix_inverse (const graphene_matrix_t *m,
1693                          graphene_matrix_t       *res)
1694 {
1695   return graphene_simd4x4f_inverse (&m->value, &res->value);
1696 }
1697 
1698 /**
1699  * graphene_matrix_perspective:
1700  * @m: a #graphene_matrix_t
1701  * @depth: the depth of the perspective
1702  * @res: (out caller-allocates): return location for the
1703  *   perspective matrix
1704  *
1705  * Applies a perspective of @depth to the matrix.
1706  *
1707  * Since: 1.0
1708  */
1709 void
graphene_matrix_perspective(const graphene_matrix_t * m,float depth,graphene_matrix_t * res)1710 graphene_matrix_perspective (const graphene_matrix_t *m,
1711                              float                    depth,
1712                              graphene_matrix_t       *res)
1713 {
1714 
1715   res->value = m->value;
1716 
1717   graphene_simd4x4f_perspective (&res->value, depth);
1718 }
1719 
1720 /**
1721  * graphene_matrix_normalize:
1722  * @m: a #graphene_matrix_t
1723  * @res: (out caller-allocates): return location for the normalized matrix
1724  *
1725  * Normalizes the given #graphene_matrix_t.
1726  *
1727  * Since: 1.0
1728  */
1729 void
graphene_matrix_normalize(const graphene_matrix_t * m,graphene_matrix_t * res)1730 graphene_matrix_normalize (const graphene_matrix_t *m,
1731                            graphene_matrix_t       *res)
1732 {
1733 
1734   float ww = graphene_simd4f_get_w (m->value.w);
1735 
1736   if (graphene_approx_val (ww, 0.f))
1737     return;
1738 
1739   graphene_simd4f_t n = graphene_simd4f_splat (1.f / ww);
1740 
1741   res->value.x = graphene_simd4f_mul (m->value.x, n);
1742   res->value.y = graphene_simd4f_mul (m->value.y, n);
1743   res->value.z = graphene_simd4f_mul (m->value.z, n);
1744   res->value.w = graphene_simd4f_mul (m->value.w, n);
1745 }
1746 
1747 /**
1748  * graphene_matrix_get_x_translation:
1749  * @m: a #graphene_matrix_t
1750  *
1751  * Retrieves the translation component on the X axis from @m.
1752  *
1753  * Returns: the translation component
1754  *
1755  * Since: 1.10
1756  */
1757 float
graphene_matrix_get_x_translation(const graphene_matrix_t * m)1758 graphene_matrix_get_x_translation (const graphene_matrix_t *m)
1759 {
1760   return graphene_simd4f_get_x (m->value.w);
1761 }
1762 
1763 /**
1764  * graphene_matrix_get_y_translation:
1765  * @m: a #graphene_matrix_t
1766  *
1767  * Retrieves the translation component on the Y axis from @m.
1768  *
1769  * Returns: the translation component
1770  *
1771  * Since: 1.10
1772  */
1773 float
graphene_matrix_get_y_translation(const graphene_matrix_t * m)1774 graphene_matrix_get_y_translation (const graphene_matrix_t *m)
1775 {
1776   return graphene_simd4f_get_y (m->value.w);
1777 }
1778 
1779 /**
1780  * graphene_matrix_get_z_translation:
1781  * @m: a #graphene_matrix_t
1782  *
1783  * Retrieves the translation component on the Z axis from @m.
1784  *
1785  * Returns: the translation component
1786  *
1787  * Since: 1.10
1788  */
1789 float
graphene_matrix_get_z_translation(const graphene_matrix_t * m)1790 graphene_matrix_get_z_translation (const graphene_matrix_t *m)
1791 {
1792   return graphene_simd4f_get_z (m->value.w);
1793 }
1794 
1795 /**
1796  * graphene_matrix_get_x_scale:
1797  * @m: a #graphene_matrix_t
1798  *
1799  * Retrieves the scaling factor on the X axis in @m.
1800  *
1801  * Returns: the value of the scaling factor
1802  *
1803  * Since: 1.0
1804  */
1805 float
graphene_matrix_get_x_scale(const graphene_matrix_t * m)1806 graphene_matrix_get_x_scale (const graphene_matrix_t *m)
1807 {
1808   return graphene_simd4f_get_x (m->value.x);
1809 }
1810 
1811 /**
1812  * graphene_matrix_get_y_scale:
1813  * @m: a #graphene_matrix_t
1814  *
1815  * Retrieves the scaling factor on the Y axis in @m.
1816  *
1817  * Returns: the value of the scaling factor
1818  *
1819  * Since: 1.0
1820  */
1821 float
graphene_matrix_get_y_scale(const graphene_matrix_t * m)1822 graphene_matrix_get_y_scale (const graphene_matrix_t *m)
1823 {
1824   return graphene_simd4f_get_y (m->value.y);
1825 }
1826 
1827 /**
1828  * graphene_matrix_get_z_scale:
1829  * @m: a #graphene_matrix_t
1830  *
1831  * Retrieves the scaling factor on the Z axis in @m.
1832  *
1833  * Returns: the value of the scaling factor
1834  *
1835  * Since: 1.0
1836  */
1837 float
graphene_matrix_get_z_scale(const graphene_matrix_t * m)1838 graphene_matrix_get_z_scale (const graphene_matrix_t *m)
1839 {
1840   return graphene_simd4f_get_z (m->value.z);
1841 }
1842 
1843 #define XY_SHEAR 0
1844 #define XZ_SHEAR 1
1845 #define YZ_SHEAR 2
1846 
1847 #define M_11 0
1848 #define M_12 1
1849 #define M_21 2
1850 #define M_22 3
1851 
1852 static bool
matrix_decompose_2d(const graphene_matrix_t * m,graphene_vec2_t * translate_r,graphene_vec2_t * scale_r,double * angle_r,float m_r[4])1853 matrix_decompose_2d (const graphene_matrix_t *m,
1854                      graphene_vec2_t         *translate_r,
1855                      graphene_vec2_t         *scale_r,
1856                      double                  *angle_r,
1857                      float                    m_r[4])
1858 {
1859   float row0x = graphene_matrix_get_value (m, 0, 0);
1860   float row0y = graphene_matrix_get_value (m, 1, 0);
1861   float row1x = graphene_matrix_get_value (m, 0, 1);
1862   float row1y = graphene_matrix_get_value (m, 1, 1);
1863   float scale_x, scale_y;
1864   float angle;
1865   float det;
1866 
1867   if (fabsf (row0x * row1y - row0y * row1x) < FLT_EPSILON)
1868     return false;
1869 
1870   graphene_vec2_init (translate_r,
1871                       graphene_matrix_get_value (m, 3, 0),
1872                       graphene_matrix_get_value (m, 3, 1));
1873 
1874   scale_x = sqrtf (row0x * row0x + row0y * row0y);
1875   scale_y = sqrtf (row1x * row1x + row1y * row1y);
1876 
1877   det = row0x * row1y - row0y * row1x;
1878   if (det < 0)
1879     {
1880       if (row0x < row1y)
1881         scale_x = -scale_x;
1882       else
1883         scale_y = -scale_y;
1884     }
1885 
1886   if (!graphene_approx_val (scale_x, 0.f))
1887     {
1888       row0x = row0x * (1.f / scale_x);
1889       row0y = row0y * (1.f / scale_y);
1890     }
1891 
1892   if (!graphene_approx_val (scale_y, 0.f))
1893     {
1894       row1x = row1x * (1.f / scale_x);
1895       row1y = row1y * (1.f / scale_y);
1896     }
1897 
1898   graphene_vec2_init (scale_r, scale_x, scale_y);
1899 
1900   angle = atan2f (row0y, row0x);
1901 
1902   if (fabsf (angle) > FLT_EPSILON)
1903     {
1904       double sn = -row0y, cs = row0x;
1905       double m11 = row0x, m12 = row0y;
1906       double m21 = row1x, m22 = row1y;
1907 
1908       row0x = (float) (cs * m11 + sn * m21);
1909       row0y = (float) (cs * m12 + sn * m22);
1910       row1x = (float) (-sn * m11 + cs * m21);
1911       row1y = (float) (-sn * m12 + cs * m22);
1912     }
1913 
1914   m_r[M_11] = row0x;
1915   m_r[M_12] = row0y;
1916   m_r[M_21] = row1x;
1917   m_r[M_22] = row1y;
1918 
1919   *angle_r = GRAPHENE_RAD_TO_DEG (angle);
1920 
1921   return true;
1922 }
1923 
1924 static bool
matrix_decompose_3d(const graphene_matrix_t * m,graphene_vec3_t * scale_r,graphene_vec3_t * shear_r,graphene_quaternion_t * rotate_r,graphene_vec3_t * translate_r,graphene_vec4_t * perspective_r)1925 matrix_decompose_3d (const graphene_matrix_t *m,
1926                      graphene_vec3_t         *scale_r,
1927                      graphene_vec3_t         *shear_r,
1928                      graphene_quaternion_t   *rotate_r,
1929                      graphene_vec3_t         *translate_r,
1930                      graphene_vec4_t         *perspective_r)
1931 {
1932   graphene_matrix_t local;
1933   float shear_xy, shear_xz, shear_yz;
1934   float scale_x, scale_y, scale_z;
1935   graphene_simd4f_t perspective_v;
1936   graphene_simd4f_t cross;
1937 
1938   if (graphene_approx_val (graphene_simd4f_get_w (m->value.w), 0.f))
1939     return false;
1940 
1941   local = *m;
1942 
1943   /* normalize the matrix */
1944   graphene_matrix_normalize (&local, &local);
1945 
1946   /* perspective is used to solve for the perspective component,
1947    * but it also provides an easy way to test for singularity of
1948    * the upper 3x3 component
1949    */
1950   perspective_v = graphene_simd4f_init (graphene_simd4f_get_w (local.value.x),
1951                                         graphene_simd4f_get_w (local.value.y),
1952                                         graphene_simd4f_get_w (local.value.z),
1953                                         graphene_simd4f_get_w (local.value.w));
1954 
1955   /* Clear the perspective component */
1956   local.value.x = graphene_simd4f_merge_w (local.value.x, 0.f);
1957   local.value.y = graphene_simd4f_merge_w (local.value.y, 0.f);
1958   local.value.z = graphene_simd4f_merge_w (local.value.z, 0.f);
1959   local.value.w = graphene_simd4f_merge_w (local.value.w, 1.f);
1960 
1961   if (graphene_approx_val (graphene_matrix_determinant (&local), 0.f))
1962     return false;
1963 
1964   /* isolate the perspective */
1965   if (!graphene_simd4f_is_zero3 (perspective_v))
1966     {
1967       graphene_matrix_t tmp;
1968 
1969       /* perspective_r is the right hand side of the equation */
1970       perspective_r->value = perspective_v;
1971 
1972       /* solve the equation by inverting perspective and multiplying
1973        * the inverse with the perspective vector; we don't need to
1974        * check if the matrix is invertible here because we just checked
1975        * whether the determinant is not zero.
1976        */
1977       graphene_matrix_inverse (&local, &tmp);
1978       graphene_matrix_transform_vec4 (&tmp, perspective_r, perspective_r);
1979     }
1980   else
1981     graphene_vec4_init (perspective_r, 0.f, 0.f, 0.f, 1.f);
1982 
1983   /* next, take care of the translation partition */
1984   translate_r->value = graphene_simd4f_merge_w (local.value.w, 0.f);
1985   local.value.w = graphene_simd4f_init (0.f, 0.f, 0.f, graphene_simd4f_get_w (local.value.w));
1986 
1987   /* now get scale and shear */
1988 
1989   /* compute the X scale factor and normalize the first row */
1990   scale_x = graphene_simd4f_get_x (graphene_simd4f_length4 (local.value.x));
1991   local.value.x = graphene_simd4f_normalize4 (local.value.x);
1992 
1993   /* compute XY shear factor and the second row orthogonal to the first */
1994   shear_xy = graphene_simd4f_get_x (graphene_simd4f_dot4 (local.value.x, local.value.y));
1995   local.value.y = graphene_simd4f_sub (local.value.y, graphene_simd4f_mul (local.value.x, graphene_simd4f_splat (shear_xy)));
1996 
1997   /* now, compute the Y scale factor and normalize the second row */
1998   scale_y = graphene_simd4f_get_x (graphene_simd4f_length4 (local.value.y));
1999   local.value.y = graphene_simd4f_normalize4 (local.value.y);
2000   shear_xy /= scale_y;
2001 
2002   /* compute XZ and YZ shears, make the third row orthogonal */
2003   shear_xz = graphene_simd4f_get_x (graphene_simd4f_dot4 (local.value.x, local.value.z));
2004   local.value.z = graphene_simd4f_sub (local.value.z, graphene_simd4f_mul (local.value.x, graphene_simd4f_splat (shear_xz)));
2005   shear_yz = graphene_simd4f_get_x (graphene_simd4f_dot4 (local.value.y, local.value.z));
2006   local.value.z = graphene_simd4f_sub (local.value.z, graphene_simd4f_mul (local.value.y, graphene_simd4f_splat (shear_yz)));
2007 
2008   /* next, get the Z scale and normalize the third row */
2009   scale_z = graphene_simd4f_get_x (graphene_simd4f_length4 (local.value.z));
2010   local.value.z = graphene_simd4f_normalize4 (local.value.z);
2011 
2012   shear_xz /= scale_z;
2013   shear_yz /= scale_z;
2014 
2015   graphene_vec3_init (shear_r, shear_xy, shear_xz, shear_yz);
2016 
2017   /* at this point, the matrix is orthonormal. we check for a
2018    * coordinate system flip. if the determinant is -1, then
2019    * negate the matrix and the scaling factors
2020    */
2021   cross = graphene_simd4f_dot3 (local.value.x, graphene_simd4f_cross3 (local.value.y, local.value.z));
2022   if (graphene_simd4f_get_x (cross) < 0.f)
2023     {
2024       scale_x *= -1.f;
2025       scale_y *= -1.f;
2026       scale_z *= -1.f;
2027 
2028       local.value.x = graphene_simd4f_neg (local.value.x);
2029       local.value.y = graphene_simd4f_neg (local.value.y);
2030       local.value.z = graphene_simd4f_neg (local.value.z);
2031     }
2032 
2033   graphene_vec3_init (scale_r, scale_x, scale_y, scale_z);
2034 
2035   /* get the rotations out */
2036   graphene_quaternion_init_from_matrix (rotate_r, &local);
2037 
2038   return true;
2039 }
2040 
2041 /**
2042  * graphene_matrix_decompose:
2043  * @m: a #graphene_matrix_t
2044  * @translate: (out caller-allocates): the translation vector
2045  * @scale: (out caller-allocates): the scale vector
2046  * @rotate: (out caller-allocates): the rotation quaternion
2047  * @shear: (out caller-allocates): the shear vector
2048  * @perspective: (out caller-allocates): the perspective vector
2049  *
2050  * Decomposes a transformation matrix into its component transformations.
2051  *
2052  * The algorithm for decomposing a matrix is taken from the
2053  * [CSS3 Transforms specification](http://dev.w3.org/csswg/css-transforms/);
2054  * specifically, the decomposition code is based on the equivalent code
2055  * published in "Graphics Gems II", edited by Jim Arvo, and
2056  * [available online](http://tog.acm.org/resources/GraphicsGems/gemsii/unmatrix.c).
2057  *
2058  * Returns: `true` if the matrix could be decomposed
2059  */
2060 bool
graphene_matrix_decompose(const graphene_matrix_t * m,graphene_vec3_t * translate,graphene_vec3_t * scale,graphene_quaternion_t * rotate,graphene_vec3_t * shear,graphene_vec4_t * perspective)2061 graphene_matrix_decompose (const graphene_matrix_t *m,
2062                            graphene_vec3_t         *translate,
2063                            graphene_vec3_t         *scale,
2064                            graphene_quaternion_t   *rotate,
2065                            graphene_vec3_t         *shear,
2066                            graphene_vec4_t         *perspective)
2067 {
2068   if (graphene_matrix_is_2d (m))
2069     {
2070       graphene_vec2_t translate_res;
2071       graphene_vec2_t scale_res;
2072       double rotate_res;
2073       float m_res[4];
2074 
2075       if (!matrix_decompose_2d (m, &translate_res, &scale_res, &rotate_res, m_res))
2076         return false;
2077 
2078       translate->value = translate_res.value;
2079       scale->value = scale_res.value;
2080       graphene_quaternion_init_from_angles (rotate, 0.f, 0.f, (float) rotate_res);
2081       graphene_vec3_init_from_vec3 (shear, graphene_vec3_zero ());
2082       graphene_vec4_init_from_vec4 (perspective, graphene_vec4_zero ());
2083     }
2084   else if (!matrix_decompose_3d (m, scale, shear, rotate, translate, perspective))
2085     return false;
2086 
2087   return true;
2088 }
2089 
2090 /**
2091  * graphene_matrix_interpolate:
2092  * @a: a #graphene_matrix_t
2093  * @b: a #graphene_matrix_t
2094  * @factor: the linear interpolation factor
2095  * @res: (out caller-allocates): return location for the
2096  *   interpolated matrix
2097  *
2098  * Linearly interpolates the two given #graphene_matrix_t by
2099  * interpolating the decomposed transformations separately.
2100  *
2101  * If either matrix cannot be reduced to their transformations
2102  * then the interpolation cannot be performed, and this function
2103  * will return an identity matrix.
2104  *
2105  * Since: 1.0
2106  */
2107 void
graphene_matrix_interpolate(const graphene_matrix_t * a,const graphene_matrix_t * b,double factor,graphene_matrix_t * res)2108 graphene_matrix_interpolate (const graphene_matrix_t *a,
2109                              const graphene_matrix_t *b,
2110                              double                   factor,
2111                              graphene_matrix_t       *res)
2112 {
2113   bool success = false;
2114 
2115   /* Always provide a valid fallback in case we can't decompose either
2116    * or both matrices
2117    */
2118   graphene_matrix_init_identity (res);
2119 
2120   /* Special case the decomposition if we're interpolating between two
2121    * affine transformations.
2122    */
2123   if (graphene_matrix_is_2d (a) &&
2124       graphene_matrix_is_2d (b))
2125     {
2126       graphene_vec2_t translate_a, translate_b, translate_res;
2127       graphene_vec2_t scale_a, scale_b, scale_res;
2128       double rotate_a, rotate_b, rotate_res;
2129       float m_a[4], m_b[4], m_res[4];
2130 
2131       success |= matrix_decompose_2d (a, &translate_a, &scale_a, &rotate_a, m_a);
2132       success |= matrix_decompose_2d (b, &translate_b, &scale_b, &rotate_b, m_b);
2133 
2134       /* If we cannot decompose either matrix we bail out with an identity */
2135       if (!success)
2136         return;
2137 
2138       /* Flip the scaling factor and angle so they are consistent */
2139       float scale_ax = graphene_vec2_get_x (&scale_a);
2140       float scale_ay = graphene_vec2_get_y (&scale_a);
2141       float scale_bx = graphene_vec2_get_x (&scale_b);
2142       float scale_by = graphene_vec2_get_y (&scale_b);
2143       if ((scale_ax < 0 && scale_by < 0) || (scale_ay < 0 && scale_bx < 0))
2144         {
2145           graphene_vec2_negate (&scale_a, &scale_a);
2146 
2147           rotate_a += (rotate_a < 0) ? 180 : -180;
2148         }
2149 
2150       /* Do not rotate "the long way around" */
2151       if (fabs (rotate_a) <= DBL_EPSILON)
2152         rotate_a = 360;
2153       if (fabs (rotate_b) <= DBL_EPSILON)
2154         rotate_b = 360;
2155 
2156       if (fabs (rotate_a - rotate_b) > 180)
2157         {
2158           if (rotate_a > rotate_b)
2159             rotate_a -= 360;
2160           else
2161             rotate_b -= 360;
2162         }
2163 
2164       graphene_vec2_interpolate (&translate_a, &translate_b, factor, &translate_res);
2165       graphene_vec2_interpolate (&scale_a, &scale_b, factor, &scale_res);
2166       rotate_res = graphene_flerp (rotate_a, rotate_b, factor);
2167 
2168       /* Interpolate each component of the (2,2) matrices */
2169       graphene_simd4f_t tmp_va = graphene_simd4f_init_4f (m_a);
2170       graphene_simd4f_t tmp_vb = graphene_simd4f_init_4f (m_b);
2171       graphene_simd4f_t tmp_vres = graphene_simd4f_interpolate (tmp_va, tmp_vb, (float) factor);
2172 
2173       graphene_simd4f_dup_4f (tmp_vres, m_res);
2174 
2175       /* Initialize using the transposed (2,2) matrix */
2176       res->value.x = graphene_simd4f_init (m_res[M_11], m_res[M_21], 0.f, 0.f);
2177       res->value.y = graphene_simd4f_init (m_res[M_12], m_res[M_22], 0.f, 0.f);
2178       res->value.z = graphene_simd4f_init (        0.f,         0.f, 1.f, 0.f);
2179 
2180       /* Translate */
2181       float translate_x = graphene_vec2_get_x (&translate_res);
2182       float translate_y = graphene_vec2_get_y (&translate_res);
2183       res->value.w = graphene_simd4f_init (translate_x * m_res[M_11] + translate_y * m_res[M_21],
2184                                            translate_x * m_res[M_12] + translate_y * m_res[M_22],
2185                                            0.f,
2186                                            1.f);
2187 
2188       /* Rotate using a (2,2) rotation matrix */
2189       float rot_sin, rot_cos;
2190       graphene_sincos (GRAPHENE_DEG_TO_RAD ((float) rotate_res), &rot_sin, &rot_cos);
2191 
2192       graphene_simd4x4f_t tmp_m;
2193       tmp_m = graphene_simd4x4f_init (graphene_simd4f_init (rot_cos, -rot_sin, 0.f, 0.f),
2194                                       graphene_simd4f_init (rot_sin,  rot_cos, 0.f, 0.f),
2195                                       graphene_simd4f_init (    0.f,      0.f, 1.f, 0.f),
2196                                       graphene_simd4f_init (    0.f,      0.f, 0.f, 1.f));
2197       graphene_simd4x4f_matrix_mul (&res->value, &tmp_m, &res->value);
2198 
2199       /* Scale */
2200       float scale_x = graphene_vec2_get_x (&scale_res);
2201       float scale_y = graphene_vec2_get_y (&scale_res);
2202       graphene_simd4x4f_scale (&tmp_m, scale_x, scale_y, 1.f);
2203       graphene_simd4x4f_matrix_mul (&res->value, &tmp_m, &res->value);
2204     }
2205   else
2206     {
2207       graphene_vec3_t scale_a, translate_a;
2208       graphene_quaternion_t rotate_a;
2209       graphene_vec3_t shear_a;
2210       graphene_vec4_t perspective_a;
2211 
2212       graphene_vec3_t scale_b, translate_b;
2213       graphene_quaternion_t rotate_b;
2214       graphene_vec3_t shear_b;
2215       graphene_vec4_t perspective_b;
2216 
2217       graphene_vec3_t scale_r, translate_r;
2218       graphene_quaternion_t rotate_r;
2219       graphene_vec3_t shear_r;
2220 
2221       graphene_simd4f_t tmp;
2222 
2223       success |= matrix_decompose_3d (a, &scale_a, &shear_a, &rotate_a, &translate_a, &perspective_a);
2224       success |= matrix_decompose_3d (b, &scale_b, &shear_b, &rotate_b, &translate_b, &perspective_b);
2225 
2226       /* If we cannot decompose either matrix we bail out with an identity */
2227       if (!success)
2228         return;
2229 
2230       /* Interpolate the perspective row */
2231       tmp = graphene_simd4f_interpolate (perspective_a.value, perspective_b.value, (float) factor);
2232       res->value.x = graphene_simd4f_init (1.f, 0.f, 0.f, graphene_simd4f_get_x (tmp));
2233       res->value.y = graphene_simd4f_init (0.f, 1.f, 0.f, graphene_simd4f_get_y (tmp));
2234       res->value.z = graphene_simd4f_init (0.f, 0.f, 1.f, graphene_simd4f_get_z (tmp));
2235       res->value.w = graphene_simd4f_init (0.f, 0.f, 0.f, graphene_simd4f_get_w (tmp));
2236 
2237       /* Translate */
2238       graphene_point3d_t t;
2239       graphene_vec3_interpolate (&translate_a, &translate_b, factor, &translate_r);
2240       graphene_point3d_init_from_vec3 (&t, &translate_r);
2241       graphene_matrix_translate (res, &t);
2242 
2243       /* Rotate */
2244       graphene_quaternion_slerp (&rotate_a, &rotate_b, (float) factor, &rotate_r);
2245       graphene_matrix_rotate_quaternion (res, &rotate_r);
2246 
2247       /* Skew */
2248       float shear;
2249       graphene_vec3_interpolate (&shear_a, &shear_b, factor, &shear_r);
2250       shear = graphene_simd4f_get (shear_r.value, YZ_SHEAR);
2251       if (!graphene_approx_val (shear, 0.f))
2252         graphene_matrix_skew_yz (res, shear);
2253 
2254       shear = graphene_simd4f_get (shear_r.value, XZ_SHEAR);
2255       if (!graphene_approx_val (shear, 0.f))
2256         graphene_matrix_skew_xz (res, shear);
2257 
2258       shear = graphene_simd4f_get (shear_r.value, XY_SHEAR);
2259       if (!graphene_approx_val (shear, 0.f))
2260         graphene_matrix_skew_xy (res, shear);
2261 
2262       /* Scale */
2263       graphene_point3d_t s;
2264       graphene_vec3_interpolate (&scale_a, &scale_b, factor, &scale_r);
2265       graphene_point3d_init_from_vec3 (&s, &scale_r);
2266       if (!graphene_approx_val (s.x, 1.f) ||
2267           !graphene_approx_val (s.y, 1.f) ||
2268           !graphene_approx_val (s.z, 1.f))
2269         graphene_matrix_scale (res, s.x, s.y, s.z);
2270     }
2271 }
2272 
2273 #undef M_11
2274 #undef M_12
2275 #undef M_21
2276 #undef M_22
2277 #undef XY_SHEAR
2278 #undef XZ_SHEAR
2279 #undef YZ_SHEAR
2280 
2281 /**
2282  * graphene_matrix_print:
2283  * @m: The matrix to print
2284  *
2285  * Prints the contents of a matrix to the standard error stream.
2286  *
2287  * This function is only useful for debugging; there are no guarantees
2288  * made on the format of the output.
2289  *
2290  * Since: 1.0
2291  */
2292 void
graphene_matrix_print(const graphene_matrix_t * m)2293 graphene_matrix_print (const graphene_matrix_t *m)
2294 {
2295   for (int i = 0; i < 4; i++)
2296     {
2297       fprintf (stderr,
2298                "| %+.6f %+.6f %+.6f %+.6f |\n",
2299                graphene_matrix_get_value (m, i, 0),
2300                graphene_matrix_get_value (m, i, 1),
2301                graphene_matrix_get_value (m, i, 2),
2302                graphene_matrix_get_value (m, i, 3));
2303     }
2304 }
2305 
2306 /**
2307  * graphene_matrix_near:
2308  * @a: a #graphene_matrix_t
2309  * @b: a #graphene_matrix_t
2310  * @epsilon: the threshold between the two matrices
2311  *
2312  * Compares the two given #graphene_matrix_t matrices and checks
2313  * whether their values are within the given @epsilon of each
2314  * other.
2315  *
2316  * Returns: `true` if the two matrices are near each other, and
2317  *   `false` otherwise
2318  *
2319  * Since: 1.10
2320  */
2321 bool
graphene_matrix_near(const graphene_matrix_t * a,const graphene_matrix_t * b,float epsilon)2322 graphene_matrix_near (const graphene_matrix_t *a,
2323                       const graphene_matrix_t *b,
2324                       float                    epsilon)
2325 {
2326   if (a == b)
2327     return true;
2328 
2329   if (a == NULL || b == NULL)
2330     return false;
2331 
2332   for (unsigned i = 0; i < 4; i++)
2333     {
2334       graphene_vec4_t row_a, row_b;
2335 
2336       graphene_matrix_get_row (a, i, &row_a);
2337       graphene_matrix_get_row (b, i, &row_b);
2338 
2339       if (!graphene_vec4_near (&row_a, &row_b, epsilon))
2340         return false;
2341     }
2342 
2343   return true;
2344 }
2345 
2346 /**
2347  * graphene_matrix_equal:
2348  * @a: a #graphene_matrix_t
2349  * @b: a #graphene_matrix_t
2350  *
2351  * Checks whether the two given #graphene_matrix_t matrices are equal.
2352  *
2353  * Returns: `true` if the two matrices are equal, and `false` otherwise
2354  *
2355  * Since: 1.10
2356  */
2357 bool
graphene_matrix_equal(const graphene_matrix_t * a,const graphene_matrix_t * b)2358 graphene_matrix_equal (const graphene_matrix_t *a,
2359                        const graphene_matrix_t *b)
2360 {
2361   return graphene_matrix_near (a, b, FLT_EPSILON);
2362 }
2363 
2364 /**
2365  * graphene_matrix_equal_fast:
2366  * @a: a #graphene_matrix_t
2367  * @b: a #graphene_matrix_t
2368  *
2369  * Checks whether the two given #graphene_matrix_t matrices are
2370  * byte-by-byte equal.
2371  *
2372  * While this function is faster than graphene_matrix_equal(), it
2373  * can also return false negatives, so it should be used in
2374  * conjuction with either graphene_matrix_equal() or
2375  * graphene_matrix_near(). For instance:
2376  *
2377  * |[<!-- language="C" -->
2378  *   if (graphene_matrix_equal_fast (a, b))
2379  *     {
2380  *       // matrices are definitely the same
2381  *     }
2382  *   else
2383  *     {
2384  *       if (graphene_matrix_equal (a, b))
2385  *         // matrices contain the same values within an epsilon of FLT_EPSILON
2386  *       else if (graphene_matrix_near (a, b, 0.0001))
2387  *         // matrices contain the same values within an epsilon of 0.0001
2388  *       else
2389  *         // matrices are not equal
2390  *     }
2391  * ]|
2392  *
2393  * Returns: `true` if the matrices are equal. and `false` otherwise
2394  *
2395  * Since: 1.10
2396  */
2397 bool
graphene_matrix_equal_fast(const graphene_matrix_t * a,const graphene_matrix_t * b)2398 graphene_matrix_equal_fast (const graphene_matrix_t *a,
2399                             const graphene_matrix_t *b)
2400 {
2401   return memcmp (a, b, sizeof (graphene_matrix_t)) == 0;
2402 }
2403