Skip to main content

cairo/
matrices.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::error::Error;
4use crate::utils::status_to_result;
5use libc::c_double;
6
7#[repr(C)]
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub struct Matrix {
10    pub xx: c_double,
11    pub yx: c_double,
12
13    pub xy: c_double,
14    pub yy: c_double,
15
16    pub x0: c_double,
17    pub y0: c_double,
18}
19
20impl Default for Matrix {
21    fn default() -> Self {
22        Self::identity()
23    }
24}
25
26impl Matrix {
27    pub(crate) fn ptr(&self) -> *const ffi::Matrix {
28        self as *const Matrix as _
29    }
30
31    pub(crate) fn mut_ptr(&mut self) -> *mut ffi::Matrix {
32        self as *mut Matrix as _
33    }
34
35    pub(crate) fn null() -> Self {
36        Self {
37            xx: 0.0,
38            yx: 0.0,
39            xy: 0.0,
40            yy: 0.0,
41            x0: 0.0,
42            y0: 0.0,
43        }
44    }
45
46    pub fn identity() -> Self {
47        Self {
48            xx: 1.0,
49            yx: 0.0,
50            xy: 0.0,
51            yy: 1.0,
52            x0: 0.0,
53            y0: 0.0,
54        }
55    }
56
57    pub fn new(xx: f64, yx: f64, xy: f64, yy: f64, x0: f64, y0: f64) -> Self {
58        Self {
59            xx,
60            yx,
61            xy,
62            yy,
63            x0,
64            y0,
65        }
66    }
67
68    #[doc(alias = "cairo_matrix_multiply")]
69    pub fn multiply(left: &Matrix, right: &Matrix) -> Matrix {
70        let mut matrix = Self::null();
71        unsafe {
72            ffi::cairo_matrix_multiply(matrix.mut_ptr(), left.ptr(), right.ptr());
73        }
74        matrix
75    }
76
77    #[doc(alias = "cairo_matrix_translate")]
78    pub fn translate(&mut self, tx: f64, ty: f64) {
79        unsafe { ffi::cairo_matrix_translate(self.mut_ptr(), tx, ty) }
80    }
81
82    #[doc(alias = "cairo_matrix_scale")]
83    pub fn scale(&mut self, sx: f64, sy: f64) {
84        unsafe { ffi::cairo_matrix_scale(self.mut_ptr(), sx, sy) }
85    }
86
87    #[doc(alias = "cairo_matrix_rotate")]
88    pub fn rotate(&mut self, angle: f64) {
89        unsafe { ffi::cairo_matrix_rotate(self.mut_ptr(), angle) }
90    }
91
92    #[doc(alias = "cairo_matrix_invert")]
93    pub fn invert(&mut self) {
94        let status = unsafe { ffi::cairo_matrix_invert(self.mut_ptr()) };
95        status_to_result(status).expect("Failed to invert the matrix")
96    }
97
98    #[doc(alias = "cairo_matrix_invert")]
99    pub fn try_invert(&self) -> Result<Matrix, Error> {
100        let mut matrix = *self;
101
102        let status = unsafe { ffi::cairo_matrix_invert(matrix.mut_ptr()) };
103        status_to_result(status)?;
104        Ok(matrix)
105    }
106
107    #[doc(alias = "cairo_matrix_transform_distance")]
108    pub fn transform_distance(&self, _dx: f64, _dy: f64) -> (f64, f64) {
109        let mut dx = _dx;
110        let mut dy = _dy;
111
112        unsafe {
113            ffi::cairo_matrix_transform_distance(self.ptr(), &mut dx, &mut dy);
114        }
115        (dx, dy)
116    }
117
118    #[doc(alias = "cairo_matrix_transform_point")]
119    pub fn transform_point(&self, _x: f64, _y: f64) -> (f64, f64) {
120        let mut x = _x;
121        let mut y = _y;
122
123        unsafe {
124            ffi::cairo_matrix_transform_point(self.ptr(), &mut x, &mut y);
125        }
126        (x, y)
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn memory_layout_is_ffi_equivalent() {
136        macro_rules! dummy_values {
137            ($Matrix: ident) => {
138                $Matrix {
139                    xx: 1.0,
140                    yx: 2.0,
141                    xy: 3.0,
142                    yy: 4.0,
143                    x0: 5.0,
144                    y0: 6.0,
145                }
146            };
147        }
148        use crate::ffi::Matrix as FfiMatrix;
149        let transmuted: Matrix = unsafe { std::mem::transmute(dummy_values!(FfiMatrix)) };
150        assert_eq!(transmuted, dummy_values!(Matrix));
151    }
152
153    #[test]
154    fn invalid_matrix_does_not_invert() {
155        let matrix = Matrix::null();
156        assert!(matrix.try_invert().is_err());
157    }
158
159    #[test]
160    #[should_panic]
161    fn inverting_invalid_matrix_panics() {
162        let mut matrix = Matrix::null();
163        matrix.invert();
164    }
165
166    #[test]
167    fn valid_matrix_try_invert() {
168        let matrix = Matrix::identity();
169        assert_eq!(matrix.try_invert().unwrap(), Matrix::identity());
170    }
171
172    #[test]
173    fn valid_matrix_invert() {
174        let mut matrix = Matrix::identity();
175        matrix.invert();
176        assert_eq!(matrix, Matrix::identity());
177    }
178}