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