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