Skip to main content

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