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