cairo/
matrices.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::fmt;
4#[cfg(feature = "use_glib")]
5use std::marker::PhantomData;
6
7use crate::{ffi, utils::status_to_result, Error};
8
9#[repr(transparent)]
10#[derive(Clone, Copy, PartialEq)]
11#[doc(alias = "cairo_matrix_t")]
12pub struct Matrix(ffi::cairo_matrix_t);
13
14impl Default for Matrix {
15    fn default() -> Self {
16        Self::identity()
17    }
18}
19
20impl Matrix {
21    #[inline]
22    pub(crate) fn ptr(&self) -> *const ffi::cairo_matrix_t {
23        self as *const Matrix as _
24    }
25
26    #[inline]
27    pub(crate) fn mut_ptr(&mut self) -> *mut ffi::cairo_matrix_t {
28        self as *mut Matrix as _
29    }
30
31    #[inline]
32    pub(crate) fn null() -> Self {
33        Self(ffi::cairo_matrix_t {
34            xx: 0.0,
35            yx: 0.0,
36            xy: 0.0,
37            yy: 0.0,
38            x0: 0.0,
39            y0: 0.0,
40        })
41    }
42
43    #[inline]
44    pub fn identity() -> Self {
45        Self(ffi::cairo_matrix_t {
46            xx: 1.0,
47            yx: 0.0,
48            xy: 0.0,
49            yy: 1.0,
50            x0: 0.0,
51            y0: 0.0,
52        })
53    }
54
55    #[inline]
56    pub fn new(xx: f64, yx: f64, xy: f64, yy: f64, x0: f64, y0: f64) -> Self {
57        Self(ffi::cairo_matrix_t {
58            xx,
59            yx,
60            xy,
61            yy,
62            x0,
63            y0,
64        })
65    }
66
67    #[inline]
68    pub fn xx(&self) -> f64 {
69        self.0.xx
70    }
71    #[inline]
72    pub fn set_xx(&mut self, xx: f64) {
73        self.0.xx = xx;
74    }
75    #[inline]
76    pub fn yx(&self) -> f64 {
77        self.0.yx
78    }
79    #[inline]
80    pub fn set_yx(&mut self, yx: f64) {
81        self.0.yx = yx;
82    }
83    #[inline]
84    pub fn xy(&self) -> f64 {
85        self.0.xy
86    }
87    #[inline]
88    pub fn set_xy(&mut self, xy: f64) {
89        self.0.xy = xy;
90    }
91    #[inline]
92    pub fn yy(&self) -> f64 {
93        self.0.yy
94    }
95    #[inline]
96    pub fn set_yy(&mut self, yy: f64) {
97        self.0.yy = yy;
98    }
99    #[inline]
100    pub fn x0(&self) -> f64 {
101        self.0.x0
102    }
103    #[inline]
104    pub fn set_x0(&mut self, x0: f64) {
105        self.0.x0 = x0;
106    }
107    #[inline]
108    pub fn y0(&self) -> f64 {
109        self.0.y0
110    }
111    #[inline]
112    pub fn set_y0(&mut self, y0: f64) {
113        self.0.y0 = y0;
114    }
115
116    #[doc(alias = "cairo_matrix_multiply")]
117    #[inline]
118    pub fn multiply(left: &Matrix, right: &Matrix) -> Matrix {
119        let mut matrix = Self::null();
120        unsafe {
121            ffi::cairo_matrix_multiply(matrix.mut_ptr(), left.ptr(), right.ptr());
122        }
123        matrix
124    }
125
126    #[doc(alias = "cairo_matrix_translate")]
127    #[inline]
128    pub fn translate(&mut self, tx: f64, ty: f64) {
129        unsafe { ffi::cairo_matrix_translate(self.mut_ptr(), tx, ty) }
130    }
131
132    #[doc(alias = "cairo_matrix_scale")]
133    #[inline]
134    pub fn scale(&mut self, sx: f64, sy: f64) {
135        unsafe { ffi::cairo_matrix_scale(self.mut_ptr(), sx, sy) }
136    }
137
138    #[doc(alias = "cairo_matrix_rotate")]
139    #[inline]
140    pub fn rotate(&mut self, angle: f64) {
141        unsafe { ffi::cairo_matrix_rotate(self.mut_ptr(), angle) }
142    }
143
144    #[doc(alias = "cairo_matrix_invert")]
145    #[inline]
146    pub fn invert(&mut self) {
147        let status = unsafe { ffi::cairo_matrix_invert(self.mut_ptr()) };
148        status_to_result(status).expect("Failed to invert the matrix")
149    }
150
151    #[doc(alias = "cairo_matrix_invert")]
152    pub fn try_invert(&self) -> Result<Matrix, Error> {
153        let mut matrix = *self;
154
155        let status = unsafe { ffi::cairo_matrix_invert(matrix.mut_ptr()) };
156        status_to_result(status)?;
157        Ok(matrix)
158    }
159
160    #[doc(alias = "cairo_matrix_transform_distance")]
161    #[inline]
162    pub fn transform_distance(&self, _dx: f64, _dy: f64) -> (f64, f64) {
163        let mut dx = _dx;
164        let mut dy = _dy;
165
166        unsafe {
167            ffi::cairo_matrix_transform_distance(self.ptr(), &mut dx, &mut dy);
168        }
169        (dx, dy)
170    }
171
172    #[doc(alias = "cairo_matrix_transform_point")]
173    #[inline]
174    pub fn transform_point(&self, _x: f64, _y: f64) -> (f64, f64) {
175        let mut x = _x;
176        let mut y = _y;
177
178        unsafe {
179            ffi::cairo_matrix_transform_point(self.ptr(), &mut x, &mut y);
180        }
181        (x, y)
182    }
183}
184
185impl fmt::Debug for Matrix {
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187        f.debug_struct("Matrix")
188            .field("xx", &self.xx())
189            .field("yx", &self.yx())
190            .field("xy", &self.xy())
191            .field("yy", &self.yy())
192            .field("x0", &self.x0())
193            .field("y0", &self.y0())
194            .finish()
195    }
196}
197
198#[cfg(feature = "use_glib")]
199#[doc(hidden)]
200impl Uninitialized for Matrix {
201    #[inline]
202    unsafe fn uninitialized() -> Self {
203        std::mem::zeroed()
204    }
205}
206
207#[cfg(feature = "use_glib")]
208#[doc(hidden)]
209impl<'a> ToGlibPtr<'a, *const ffi::cairo_matrix_t> for Matrix {
210    type Storage = PhantomData<&'a Self>;
211
212    #[inline]
213    fn to_glib_none(&'a self) -> Stash<'a, *const ffi::cairo_matrix_t, Self> {
214        Stash(
215            self as *const Matrix as *const ffi::cairo_matrix_t,
216            PhantomData,
217        )
218    }
219}
220
221#[cfg(feature = "use_glib")]
222#[doc(hidden)]
223impl<'a> ToGlibPtrMut<'a, *mut ffi::cairo_matrix_t> for Matrix {
224    type Storage = PhantomData<&'a mut Self>;
225
226    #[inline]
227    fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::cairo_matrix_t, Self> {
228        StashMut(self as *mut Matrix as *mut ffi::cairo_matrix_t, PhantomData)
229    }
230}
231
232#[cfg(feature = "use_glib")]
233#[doc(hidden)]
234impl FromGlibPtrNone<*const ffi::cairo_matrix_t> for Matrix {
235    #[inline]
236    unsafe fn from_glib_none(ptr: *const ffi::cairo_matrix_t) -> Self {
237        *(ptr as *const Matrix)
238    }
239}
240
241#[cfg(feature = "use_glib")]
242#[doc(hidden)]
243impl FromGlibPtrBorrow<*mut ffi::cairo_matrix_t> for Matrix {
244    #[inline]
245    unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_matrix_t) -> crate::Borrowed<Self> {
246        crate::Borrowed::new(*(ptr as *mut Matrix))
247    }
248}
249
250#[cfg(feature = "use_glib")]
251#[doc(hidden)]
252impl FromGlibPtrNone<*mut ffi::cairo_matrix_t> for Matrix {
253    #[inline]
254    unsafe fn from_glib_none(ptr: *mut ffi::cairo_matrix_t) -> Self {
255        *(ptr as *mut Matrix)
256    }
257}
258
259#[cfg(feature = "use_glib")]
260gvalue_impl_inline!(
261    Matrix,
262    ffi::cairo_matrix_t,
263    ffi::gobject::cairo_gobject_matrix_get_type
264);
265
266#[cfg(test)]
267mod tests {
268    use super::*;
269
270    #[test]
271    fn invalid_matrix_does_not_invert() {
272        let matrix = Matrix::null();
273        assert!(matrix.try_invert().is_err());
274    }
275
276    #[test]
277    #[should_panic]
278    fn inverting_invalid_matrix_panics() {
279        let mut matrix = Matrix::null();
280        matrix.invert();
281    }
282
283    #[test]
284    fn valid_matrix_try_invert() {
285        let matrix = Matrix::identity();
286        assert_eq!(matrix.try_invert().unwrap(), Matrix::identity());
287    }
288
289    #[test]
290    fn valid_matrix_invert() {
291        let mut matrix = Matrix::identity();
292        matrix.invert();
293        assert_eq!(matrix, Matrix::identity());
294    }
295}