1use 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 let expected_iter = expected.iter();
150 let actual_iter = actual.iter();
151
152 assert_eq!(expected_iter.count(), actual_iter.count());
153
154 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 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}