graphene/
matrix.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{fmt, ops};
4
5use glib::translate::*;
6
7use crate::{ffi, Matrix, Point, Point3D, Vec3, Vec4};
8
9impl Matrix {
10    /// Initializes a [`Matrix`][crate::Matrix] from the values of an affine
11    /// transformation matrix.
12    ///
13    /// The arguments map to the following matrix layout:
14    ///
15    ///
16    ///
17    /// **⚠️ The following code is in plain ⚠️**
18    ///
19    /// ```plain
20    ///   ⎛ xx  yx ⎞   ⎛  a   b  0 ⎞
21    ///   ⎜ xy  yy ⎟ = ⎜  c   d  0 ⎟
22    ///   ⎝ x0  y0 ⎠   ⎝ tx  ty  1 ⎠
23    /// ```
24    ///
25    /// This function can be used to convert between an affine matrix type
26    /// from other libraries and a [`Matrix`][crate::Matrix].
27    /// ## `xx`
28    /// the xx member
29    /// ## `yx`
30    /// the yx member
31    /// ## `xy`
32    /// the xy member
33    /// ## `yy`
34    /// the yy member
35    /// ## `x_0`
36    /// the x0 member
37    /// ## `y_0`
38    /// the y0 member
39    ///
40    /// # Returns
41    ///
42    /// the initialized matrix
43    #[doc(alias = "graphene_matrix_init_from_2d")]
44    #[doc(alias = "init_from_2d")]
45    pub fn from_2d(xx: f64, yx: f64, xy: f64, yy: f64, x_0: f64, y_0: f64) -> Self {
46        assert_initialized_main_thread!();
47        unsafe {
48            let mut mat = Self::uninitialized();
49            ffi::graphene_matrix_init_from_2d(mat.to_glib_none_mut().0, xx, yx, xy, yy, x_0, y_0);
50            mat
51        }
52    }
53
54    /// Initializes a [`Matrix`][crate::Matrix] with the given array of floating
55    /// point values.
56    /// ## `v`
57    /// an array of at least 16 floating
58    ///  point values
59    ///
60    /// # Returns
61    ///
62    /// the initialized matrix
63    #[doc(alias = "graphene_matrix_init_from_float")]
64    #[doc(alias = "init_from_float")]
65    pub fn from_float(v: [f32; 16]) -> Self {
66        assert_initialized_main_thread!();
67        unsafe {
68            let mut mat = Self::uninitialized();
69            ffi::graphene_matrix_init_from_float(mat.to_glib_none_mut().0, v.as_ptr() as *const _);
70            mat
71        }
72    }
73
74    /// Initializes a [`Matrix`][crate::Matrix] with the given four row
75    /// vectors.
76    /// ## `v0`
77    /// the first row vector
78    /// ## `v1`
79    /// the second row vector
80    /// ## `v2`
81    /// the third row vector
82    /// ## `v3`
83    /// the fourth row vector
84    ///
85    /// # Returns
86    ///
87    /// the initialized matrix
88    #[doc(alias = "graphene_matrix_init_from_vec4")]
89    #[doc(alias = "init_from_vec4")]
90    pub fn from_vec4(v0: &Vec4, v1: &Vec4, v2: &Vec4, v3: &Vec4) -> Self {
91        assert_initialized_main_thread!();
92        unsafe {
93            let mut mat = Self::uninitialized();
94            ffi::graphene_matrix_init_from_vec4(
95                mat.to_glib_none_mut().0,
96                v0.to_glib_none().0,
97                v1.to_glib_none().0,
98                v2.to_glib_none().0,
99                v3.to_glib_none().0,
100            );
101            mat
102        }
103    }
104
105    /// Initializes a [`Matrix`][crate::Matrix] compatible with [`Frustum`][crate::Frustum].
106    ///
107    /// See also: [`Frustum::from_matrix()`][crate::Frustum::from_matrix()]
108    /// ## `left`
109    /// distance of the left clipping plane
110    /// ## `right`
111    /// distance of the right clipping plane
112    /// ## `bottom`
113    /// distance of the bottom clipping plane
114    /// ## `top`
115    /// distance of the top clipping plane
116    /// ## `z_near`
117    /// distance of the near clipping plane
118    /// ## `z_far`
119    /// distance of the far clipping plane
120    ///
121    /// # Returns
122    ///
123    /// the initialized matrix
124    #[doc(alias = "graphene_matrix_init_frustum")]
125    #[doc(alias = "init_frustum")]
126    pub fn new_frustum(
127        left: f32,
128        right: f32,
129        bottom: f32,
130        top: f32,
131        z_near: f32,
132        z_far: f32,
133    ) -> Self {
134        assert_initialized_main_thread!();
135        unsafe {
136            let mut mat = Self::uninitialized();
137            ffi::graphene_matrix_init_frustum(
138                mat.to_glib_none_mut().0,
139                left,
140                right,
141                bottom,
142                top,
143                z_near,
144                z_far,
145            );
146            mat
147        }
148    }
149
150    /// Initializes a [`Matrix`][crate::Matrix] with the identity matrix.
151    ///
152    /// # Returns
153    ///
154    /// the initialized matrix
155    #[doc(alias = "graphene_matrix_init_identity")]
156    #[doc(alias = "init_identity")]
157    pub fn new_identity() -> Self {
158        assert_initialized_main_thread!();
159        unsafe {
160            let mut mat = Self::uninitialized();
161            ffi::graphene_matrix_init_identity(mat.to_glib_none_mut().0);
162            mat
163        }
164    }
165
166    /// Initializes a [`Matrix`][crate::Matrix] so that it positions the "camera"
167    /// at the given `eye` coordinates towards an object at the `center`
168    /// coordinates. The top of the camera is aligned to the direction
169    /// of the `up` vector.
170    ///
171    /// Before the transform, the camera is assumed to be placed at the
172    /// origin, looking towards the negative Z axis, with the top side of
173    /// the camera facing in the direction of the Y axis and the right
174    /// side in the direction of the X axis.
175    ///
176    /// In theory, one could use `self` to transform a model of such a camera
177    /// into world-space. However, it is more common to use the inverse of
178    /// `self` to transform another object from world coordinates to the view
179    /// coordinates of the camera. Typically you would then apply the
180    /// camera projection transform to get from view to screen
181    /// coordinates.
182    /// ## `eye`
183    /// the vector describing the position to look from
184    /// ## `center`
185    /// the vector describing the position to look at
186    /// ## `up`
187    /// the vector describing the world's upward direction; usually,
188    ///  this is the [`Vec3::y_axis()`][crate::Vec3::y_axis()] vector
189    ///
190    /// # Returns
191    ///
192    /// the initialized matrix
193    #[doc(alias = "graphene_matrix_init_look_at")]
194    #[doc(alias = "init_look_at")]
195    pub fn new_look_at(eye: &Vec3, center: &Vec3, up: &Vec3) -> Self {
196        assert_initialized_main_thread!();
197        unsafe {
198            let mut mat = Self::uninitialized();
199            ffi::graphene_matrix_init_look_at(
200                mat.to_glib_none_mut().0,
201                eye.to_glib_none().0,
202                center.to_glib_none().0,
203                up.to_glib_none().0,
204            );
205            mat
206        }
207    }
208
209    /// Initializes a [`Matrix`][crate::Matrix] with an orthographic projection.
210    /// ## `left`
211    /// the left edge of the clipping plane
212    /// ## `right`
213    /// the right edge of the clipping plane
214    /// ## `top`
215    /// the top edge of the clipping plane
216    /// ## `bottom`
217    /// the bottom edge of the clipping plane
218    /// ## `z_near`
219    /// the distance of the near clipping plane
220    /// ## `z_far`
221    /// the distance of the far clipping plane
222    ///
223    /// # Returns
224    ///
225    /// the initialized matrix
226    #[doc(alias = "graphene_matrix_init_ortho")]
227    #[doc(alias = "init_ortho")]
228    pub fn new_ortho(
229        left: f32,
230        right: f32,
231        top: f32,
232        bottom: f32,
233        z_near: f32,
234        z_far: f32,
235    ) -> Self {
236        assert_initialized_main_thread!();
237        unsafe {
238            let mut mat = Self::uninitialized();
239            ffi::graphene_matrix_init_ortho(
240                mat.to_glib_none_mut().0,
241                left,
242                right,
243                top,
244                bottom,
245                z_near,
246                z_far,
247            );
248            mat
249        }
250    }
251
252    /// Initializes a [`Matrix`][crate::Matrix] with a perspective projection.
253    /// ## `fovy`
254    /// the field of view angle, in degrees
255    /// ## `aspect`
256    /// the aspect value
257    /// ## `z_near`
258    /// the near Z plane
259    /// ## `z_far`
260    /// the far Z plane
261    ///
262    /// # Returns
263    ///
264    /// the initialized matrix
265    #[doc(alias = "graphene_matrix_init_perspective")]
266    #[doc(alias = "init_perspective")]
267    pub fn new_perspective(fovy: f32, aspect: f32, z_near: f32, z_far: f32) -> Self {
268        assert_initialized_main_thread!();
269        unsafe {
270            let mut mat = Self::uninitialized();
271            ffi::graphene_matrix_init_perspective(
272                mat.to_glib_none_mut().0,
273                fovy,
274                aspect,
275                z_near,
276                z_far,
277            );
278            mat
279        }
280    }
281
282    /// Initializes `self` to represent a rotation of `angle` degrees on
283    /// the axis represented by the `axis` vector.
284    /// ## `angle`
285    /// the rotation angle, in degrees
286    /// ## `axis`
287    /// the axis vector as a [`Vec3`][crate::Vec3]
288    ///
289    /// # Returns
290    ///
291    /// the initialized matrix
292    #[doc(alias = "graphene_matrix_init_rotate")]
293    #[doc(alias = "init_rotate")]
294    pub fn new_rotate(angle: f32, axis: &Vec3) -> Self {
295        assert_initialized_main_thread!();
296        unsafe {
297            let mut mat = Self::uninitialized();
298            ffi::graphene_matrix_init_rotate(
299                mat.to_glib_none_mut().0,
300                angle,
301                axis.to_glib_none().0,
302            );
303            mat
304        }
305    }
306
307    /// Initializes a [`Matrix`][crate::Matrix] with the given scaling factors.
308    /// ## `x`
309    /// the scale factor on the X axis
310    /// ## `y`
311    /// the scale factor on the Y axis
312    /// ## `z`
313    /// the scale factor on the Z axis
314    ///
315    /// # Returns
316    ///
317    /// the initialized matrix
318    #[doc(alias = "graphene_matrix_init_scale")]
319    #[doc(alias = "init_scale")]
320    pub fn new_scale(x: f32, y: f32, z: f32) -> Self {
321        assert_initialized_main_thread!();
322        unsafe {
323            let mut mat = Self::uninitialized();
324            ffi::graphene_matrix_init_scale(mat.to_glib_none_mut().0, x, y, z);
325            mat
326        }
327    }
328
329    /// Initializes a [`Matrix`][crate::Matrix] with a skew transformation
330    /// with the given factors.
331    /// ## `x_skew`
332    /// skew factor, in radians, on the X axis
333    /// ## `y_skew`
334    /// skew factor, in radians, on the Y axis
335    ///
336    /// # Returns
337    ///
338    /// the initialized matrix
339    #[doc(alias = "graphene_matrix_init_skew")]
340    #[doc(alias = "init_skew")]
341    pub fn new_skew(x_skew: f32, y_skew: f32) -> Self {
342        assert_initialized_main_thread!();
343        unsafe {
344            let mut mat = Self::uninitialized();
345            ffi::graphene_matrix_init_skew(mat.to_glib_none_mut().0, x_skew, y_skew);
346            mat
347        }
348    }
349
350    /// Initializes a [`Matrix`][crate::Matrix] with a translation to the
351    /// given coordinates.
352    /// ## `p`
353    /// the translation coordinates
354    ///
355    /// # Returns
356    ///
357    /// the initialized matrix
358    #[doc(alias = "graphene_matrix_init_translate")]
359    #[doc(alias = "init_translate")]
360    pub fn new_translate(p: &Point3D) -> Self {
361        assert_initialized_main_thread!();
362        unsafe {
363            let mut mat = Self::uninitialized();
364            ffi::graphene_matrix_init_translate(mat.to_glib_none_mut().0, p.to_glib_none().0);
365            mat
366        }
367    }
368
369    /// Converts a [`Matrix`][crate::Matrix] to an array of floating point
370    /// values.
371    ///
372    /// # Returns
373    ///
374    ///
375    /// ## `v`
376    /// return location
377    ///  for an array of floating point values. The array must be capable
378    ///  of holding at least 16 values.
379    #[doc(alias = "graphene_matrix_to_float")]
380    pub fn to_float(&self) -> [f32; 16] {
381        unsafe {
382            let mut out = std::mem::MaybeUninit::uninit();
383            ffi::graphene_matrix_to_float(self.to_glib_none().0, out.as_mut_ptr());
384            out.assume_init()
385        }
386    }
387
388    #[inline]
389    pub fn values(&self) -> &[[f32; 4]; 4] {
390        unsafe { &*(&self.inner.value as *const ffi::graphene_simd4x4f_t as *const [[f32; 4]; 4]) }
391    }
392}
393
394impl fmt::Debug for Matrix {
395    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396        f.debug_struct("Matrix")
397            .field("values", &self.values())
398            .finish()
399    }
400}
401
402impl Default for Matrix {
403    fn default() -> Self {
404        Self::new_identity()
405    }
406}
407
408// Scalar multiplication
409impl ops::Mul<Matrix> for f32 {
410    type Output = Matrix;
411
412    fn mul(self, mut rhs: Matrix) -> Self::Output {
413        rhs.scale(self, self, self);
414        rhs
415    }
416}
417
418// Matrix-matrix/-vector multiplication
419impl ops::Mul<Matrix> for Matrix {
420    type Output = Matrix;
421
422    fn mul(self, rhs: Matrix) -> Self::Output {
423        Matrix::multiply(&self, &rhs)
424    }
425}
426impl ops::MulAssign<Matrix> for Matrix {
427    fn mul_assign(&mut self, rhs: Matrix) {
428        *self = *self * rhs;
429    }
430}
431
432impl ops::Mul<Vec4> for Matrix {
433    type Output = Vec4;
434
435    /// Transforms this `Vec4` using the provided matrix.
436    /// See [Matrix::transform_vec4].
437    fn mul(self, rhs: Vec4) -> Self::Output {
438        Matrix::transform_vec4(&self, &rhs)
439    }
440}
441
442impl ops::Mul<Vec3> for Matrix {
443    type Output = Vec3;
444
445    /// Transforms this `Vec3` using the provided matrix.
446    /// See [Matrix::transform_vec3].
447    fn mul(self, rhs: Vec3) -> Self::Output {
448        Matrix::transform_vec3(&self, &rhs)
449    }
450}
451
452impl ops::Mul<Point> for Matrix {
453    type Output = Point;
454
455    fn mul(self, rhs: Point) -> Self::Output {
456        Matrix::transform_point(&self, &rhs)
457    }
458}
459
460impl ops::Mul<Point3D> for Matrix {
461    type Output = Point3D;
462
463    /// Transforms this point using the provided matrix.
464    /// See [Matrix::transform_point3d].
465    fn mul(self, rhs: Point3D) -> Self::Output {
466        Matrix::transform_point3d(&self, &rhs)
467    }
468}
469
470#[cfg(test)]
471mod tests {
472    use super::Matrix;
473    #[test]
474    fn test_matrix_values() {
475        let matrix = Matrix::new_identity();
476        assert_eq!(
477            matrix.values(),
478            &[
479                [1.0, 0.0, 0.0, 0.0],
480                [0.0, 1.0, 0.0, 0.0],
481                [0.0, 0.0, 1.0, 0.0],
482                [0.0, 0.0, 0.0, 1.0]
483            ],
484        );
485    }
486}