1use path::math::Point;
2use path::PathEvent;
3use path::{Path, PathSlice};
4
5pub type Polygons = Vec<Vec<Point>>;
6pub type PolygonsRef<'a> = &'a [Vec<Point>];
7
8pub 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
38pub 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
51pub 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
108use std::panic;
109
110fn find_reduced_test_case_sp<F>(mut polygons: Polygons, cb: &F) -> Polygons
111where
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