1 | use path::math::Point; |
2 | use path::PathEvent; |
3 | use path::{Path, PathSlice}; |
4 | |
5 | pub type Polygons = Vec<Vec<Point>>; |
6 | pub type PolygonsRef<'a> = &'a [Vec<Point>]; |
7 | |
8 | pub fn path_to_polygons(path: PathSlice) -> Polygons { |
9 | let mut polygons = Vec::new(); |
10 | let mut poly = Vec::new(); |
11 | for evt in path { |
12 | match evt { |
13 | PathEvent::Begin { at } => { |
14 | if !poly.is_empty() { |
15 | polygons.push(poly); |
16 | } |
17 | poly = vec![at]; |
18 | } |
19 | PathEvent::Line { to, .. } => { |
20 | poly.push(to); |
21 | } |
22 | PathEvent::End { .. } => { |
23 | if !poly.is_empty() { |
24 | polygons.push(poly); |
25 | } |
26 | poly = Vec::new(); |
27 | } |
28 | _ => { |
29 | println!( |
30 | " -- path_to_polygons: warning! Unsupported event type {:?}" , |
31 | evt |
32 | ); |
33 | } |
34 | } |
35 | } |
36 | polygons |
37 | } |
38 | |
39 | pub fn polygons_to_path(polygons: PolygonsRef) -> Path { |
40 | let mut builder: NoAttributes> = Path::builder().flattened(tolerance:0.05); |
41 | for poly: &Vec> in polygons.iter() { |
42 | let mut poly_iter: Iter<'_, Point2D> = poly.iter(); |
43 | builder.begin(*poly_iter.next().unwrap()); |
44 | for v: &Point2D in poly_iter { |
45 | builder.line_to(*v); |
46 | } |
47 | builder.close(); |
48 | } |
49 | builder.build() |
50 | } |
51 | |
52 | pub fn find_reduced_test_case<F: Fn(Path) -> bool + panic::UnwindSafe + panic::RefUnwindSafe>( |
53 | path: PathSlice, |
54 | cb: &F, |
55 | ) -> Path { |
56 | let mut polygons = path_to_polygons(path); |
57 | |
58 | println!(" -- removing sub-paths..." ); |
59 | |
60 | polygons = find_reduced_test_case_sp(polygons, cb); |
61 | |
62 | println!(" -- removing vertices..." ); |
63 | |
64 | for p in 0..polygons.len() { |
65 | let mut v = 0; |
66 | loop { |
67 | if v >= polygons[p].len() || polygons[p].len() <= 3 { |
68 | break; |
69 | } |
70 | |
71 | let mut cloned = polygons.clone(); |
72 | cloned[p].remove(v); |
73 | |
74 | let path = polygons_to_path(&cloned); |
75 | |
76 | let failed = panic::catch_unwind(|| cb(path)).unwrap_or(true); |
77 | |
78 | if failed { |
79 | polygons = cloned; |
80 | continue; |
81 | } |
82 | |
83 | v += 1; |
84 | } |
85 | } |
86 | |
87 | let path = polygons_to_path(&polygons); |
88 | println!(" ----------- reduced test case: ----------- \n\n" ); |
89 | println!("#[test]" ); |
90 | println!("fn reduced_test_case() {{" ); |
91 | println!(" let mut builder = Path::builder(); \n" ); |
92 | for poly in &polygons { |
93 | let mut poly_iter = poly.iter(); |
94 | let pos = *poly_iter.next().unwrap(); |
95 | println!(" builder.begin(point( {:.}, {:.}));" , pos.x, pos.y); |
96 | for pos in poly_iter { |
97 | println!(" builder.line_to(point( {:.}, {:.}));" , pos.x, pos.y); |
98 | } |
99 | println!(" builder.close(); \n" ); |
100 | } |
101 | println!(" test_path(builder.build().as_slice()); \n" ); |
102 | println!(" // SVG path syntax:" ); |
103 | println!(" // \"{:?}\"" , path); |
104 | println!(" }}\n\n" ); |
105 | |
106 | path |
107 | } |
108 | |
109 | use std::panic; |
110 | |
111 | fn find_reduced_test_case_sp<F>(mut polygons: Polygons, cb: &F) -> Polygons |
112 | where |
113 | F: Fn(Path) -> bool + panic::UnwindSafe + panic::RefUnwindSafe, |
114 | { |
115 | let mut i: usize = 0; |
116 | loop { |
117 | if i >= polygons.len() { |
118 | return polygons; |
119 | } |
120 | |
121 | let mut cloned: Vec>> = polygons.clone(); |
122 | cloned.remove(index:i); |
123 | let path: Path = polygons_to_path(&cloned); |
124 | |
125 | let failed: bool = panic::catch_unwind(|| cb(path)).unwrap_or(default:true); |
126 | |
127 | if failed { |
128 | polygons = cloned; |
129 | continue; |
130 | } |
131 | |
132 | i += 1; |
133 | } |
134 | } |
135 | |