cairo/
matrices.rs

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