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