cairo/
paths.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::{PathDataType, Status};
6use ffi;
7use ffi::cairo_path_t;
8use std::fmt;
9use std::iter::Iterator;
10
11#[derive(Debug)]
12pub struct Path(*mut cairo_path_t);
13
14impl Path {
15    pub fn as_ptr(&self) -> *mut cairo_path_t {
16        self.0
17    }
18
19    pub fn ensure_status(&self) {
20        unsafe {
21            let ptr: *mut cairo_path_t = self.as_ptr();
22            Status::from((*ptr).status).ensure_valid()
23        }
24    }
25
26    pub unsafe fn from_raw_full(pointer: *mut cairo_path_t) -> Path {
27        Path(pointer)
28    }
29
30    pub fn iter(&self) -> PathSegments {
31        use std::slice;
32
33        unsafe {
34            let ptr: *mut cairo_path_t = self.as_ptr();
35            let length = (*ptr).num_data as usize;
36            let data_ptr = (*ptr).data;
37            let data_vec = if length != 0 && !data_ptr.is_null() {
38                slice::from_raw_parts(data_ptr, length)
39            } else {
40                &[]
41            };
42
43            PathSegments {
44                data: data_vec,
45                i: 0,
46                num_data: length,
47            }
48        }
49    }
50}
51
52impl Drop for Path {
53    fn drop(&mut self) {
54        unsafe {
55            ffi::cairo_path_destroy(self.as_ptr());
56        }
57    }
58}
59
60impl fmt::Display for Path {
61    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62        write!(f, "Path")
63    }
64}
65
66#[derive(Debug, Clone, Copy, PartialEq)]
67pub enum PathSegment {
68    MoveTo((f64, f64)),
69    LineTo((f64, f64)),
70    CurveTo((f64, f64), (f64, f64), (f64, f64)),
71    ClosePath,
72}
73
74impl fmt::Display for PathSegment {
75    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76        write!(
77            f,
78            "PathSegment::{}",
79            match *self {
80                PathSegment::MoveTo(_) => "MoveTo",
81                PathSegment::LineTo(_) => "LineTo",
82                PathSegment::CurveTo(_, _, _) => "CurveTo",
83                PathSegment::ClosePath => "ClosePath",
84            }
85        )
86    }
87}
88
89pub struct PathSegments<'a> {
90    data: &'a [ffi::cairo_path_data],
91    i: usize,
92    num_data: usize,
93}
94
95impl<'a> Iterator for PathSegments<'a> {
96    type Item = PathSegment;
97
98    fn next(&mut self) -> Option<PathSegment> {
99        if self.i >= self.num_data {
100            return None;
101        }
102
103        unsafe {
104            let res = match PathDataType::from(self.data[self.i].header.data_type) {
105                PathDataType::MoveTo => PathSegment::MoveTo(to_tuple(&self.data[self.i + 1].point)),
106                PathDataType::LineTo => PathSegment::LineTo(to_tuple(&self.data[self.i + 1].point)),
107                PathDataType::CurveTo => PathSegment::CurveTo(
108                    to_tuple(&self.data[self.i + 1].point),
109                    to_tuple(&self.data[self.i + 2].point),
110                    to_tuple(&self.data[self.i + 3].point),
111                ),
112                PathDataType::ClosePath => PathSegment::ClosePath,
113                PathDataType::__Unknown(x) => panic!("Unknown value: {}", x),
114            };
115
116            self.i += self.data[self.i].header.length as usize;
117
118            Some(res)
119        }
120    }
121}
122
123impl<'a> fmt::Display for PathSegments<'a> {
124    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
125        write!(f, "PathSegments")
126    }
127}
128
129fn to_tuple(pair: &[f64; 2]) -> (f64, f64) {
130    (pair[0], pair[1])
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use context::*;
137    use enums::Format;
138    use image_surface::*;
139
140    fn make_cr() -> Context {
141        let surface = ImageSurface::create(Format::Rgb24, 1, 1).unwrap();
142
143        Context::new(&surface)
144    }
145
146    fn assert_path_equals_segments(expected: &Path, actual: &Vec<PathSegment>) {
147        // First ensure the lengths are equal
148
149        let expected_iter = expected.iter();
150        let actual_iter = actual.iter();
151
152        assert_eq!(expected_iter.count(), actual_iter.count());
153
154        // Then actually compare the contents
155
156        let expected_iter = expected.iter();
157        let actual_iter = actual.iter();
158
159        let mut iter = expected_iter.zip(actual_iter);
160
161        while let Some((e, a)) = iter.next() {
162            assert_eq!(e, *a);
163        }
164    }
165
166    #[test]
167    fn empty_path_doesnt_iter() {
168        let cr = make_cr();
169
170        let path = cr.copy_path();
171
172        assert!(path.iter().next().is_none());
173    }
174
175    #[test]
176    fn moveto() {
177        let cr = make_cr();
178
179        cr.move_to(1.0, 2.0);
180
181        let path = cr.copy_path();
182
183        assert_path_equals_segments(&path, &vec![PathSegment::MoveTo((1.0, 2.0))]);
184    }
185
186    #[test]
187    fn moveto_lineto_moveto() {
188        let cr = make_cr();
189
190        cr.move_to(1.0, 2.0);
191        cr.line_to(3.0, 4.0);
192        cr.move_to(5.0, 6.0);
193
194        let path = cr.copy_path();
195
196        assert_path_equals_segments(
197            &path,
198            &vec![
199                PathSegment::MoveTo((1.0, 2.0)),
200                PathSegment::LineTo((3.0, 4.0)),
201                PathSegment::MoveTo((5.0, 6.0)),
202            ],
203        );
204    }
205
206    #[test]
207    fn moveto_closepath() {
208        let cr = make_cr();
209
210        cr.move_to(1.0, 2.0);
211        cr.close_path();
212
213        let path = cr.copy_path();
214
215        // Note that Cairo represents a close_path as closepath+moveto,
216        // so that the next subpath will have a starting point,
217        // from the extra moveto.
218        assert_path_equals_segments(
219            &path,
220            &vec![
221                PathSegment::MoveTo((1.0, 2.0)),
222                PathSegment::ClosePath,
223                PathSegment::MoveTo((1.0, 2.0)),
224            ],
225        );
226    }
227    #[test]
228    fn curveto_closed_subpath_lineto() {
229        let cr = make_cr();
230
231        cr.move_to(1.0, 2.0);
232        cr.curve_to(3.0, 4.0, 5.0, 6.0, 7.0, 8.0);
233        cr.close_path();
234        cr.line_to(9.0, 10.0);
235
236        let path = cr.copy_path();
237
238        assert_path_equals_segments(
239            &path,
240            &vec![
241                PathSegment::MoveTo((1.0, 2.0)),
242                PathSegment::CurveTo((3.0, 4.0), (5.0, 6.0), (7.0, 8.0)),
243                PathSegment::ClosePath,
244                PathSegment::MoveTo((1.0, 2.0)),
245                PathSegment::LineTo((9.0, 10.0)),
246            ],
247        );
248    }
249
250}