1use 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}