1use crate::nfp::Nfp;
7
8#[derive(Debug, Clone)]
13pub struct InstanceInfo {
14 pub geometry_idx: usize,
16 pub instance_num: usize,
18}
19
20pub fn polygon_centroid(polygon: &[(f64, f64)]) -> (f64, f64) {
26 if polygon.is_empty() {
27 return (0.0, 0.0);
28 }
29
30 let sum: (f64, f64) = polygon
31 .iter()
32 .fold((0.0, 0.0), |acc, &(x, y)| (acc.0 + x, acc.1 + y));
33 let n = polygon.len() as f64;
34 (sum.0 / n, sum.1 / n)
35}
36
37pub fn expand_nfp(nfp: &Nfp, spacing: f64) -> Nfp {
45 if spacing <= 0.0 {
46 return nfp.clone();
47 }
48
49 let expanded_polygons: Vec<Vec<(f64, f64)>> = nfp
50 .polygons
51 .iter()
52 .map(|polygon| {
53 let (cx, cy) = polygon_centroid(polygon);
54 polygon
55 .iter()
56 .map(|&(x, y)| {
57 let dx = x - cx;
58 let dy = y - cy;
59 let dist = (dx * dx + dy * dy).sqrt();
60 if dist > 1e-10 {
61 let scale = (dist + spacing) / dist;
62 (cx + dx * scale, cy + dy * scale)
63 } else {
64 (x, y)
65 }
66 })
67 .collect()
68 })
69 .collect();
70
71 Nfp::from_polygons(expanded_polygons)
72}
73
74pub fn shrink_ifp(ifp: &Nfp, spacing: f64) -> Nfp {
82 if spacing <= 0.0 {
83 return ifp.clone();
84 }
85
86 let shrunk_polygons: Vec<Vec<(f64, f64)>> = ifp
87 .polygons
88 .iter()
89 .filter_map(|polygon| {
90 let (cx, cy) = polygon_centroid(polygon);
91 let shrunk: Vec<(f64, f64)> = polygon
92 .iter()
93 .map(|&(x, y)| {
94 let dx = x - cx;
95 let dy = y - cy;
96 let dist = (dx * dx + dy * dy).sqrt();
97 if dist > spacing + 1e-10 {
98 let scale = (dist - spacing) / dist;
99 (cx + dx * scale, cy + dy * scale)
100 } else {
101 (cx, cy)
102 }
103 })
104 .collect();
105
106 if shrunk.len() >= 3 {
107 Some(shrunk)
108 } else {
109 None
110 }
111 })
112 .collect();
113
114 Nfp::from_polygons(shrunk_polygons)
115}
116
117pub fn nesting_fitness(placed_count: usize, total_count: usize, utilization: f64) -> f64 {
133 let placement_ratio = placed_count as f64 / total_count.max(1) as f64;
134 placement_ratio * 100.0 + utilization * 10.0
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn test_polygon_centroid_empty() {
143 assert_eq!(polygon_centroid(&[]), (0.0, 0.0));
144 }
145
146 #[test]
147 fn test_polygon_centroid_square() {
148 let square = vec![(0.0, 0.0), (10.0, 0.0), (10.0, 10.0), (0.0, 10.0)];
149 let (cx, cy) = polygon_centroid(&square);
150 assert!((cx - 5.0).abs() < 1e-10);
151 assert!((cy - 5.0).abs() < 1e-10);
152 }
153
154 #[test]
155 fn test_expand_nfp_zero_spacing() {
156 let nfp = Nfp::from_polygons(vec![vec![
157 (0.0, 0.0),
158 (10.0, 0.0),
159 (10.0, 10.0),
160 (0.0, 10.0),
161 ]]);
162 let expanded = expand_nfp(&nfp, 0.0);
163 assert_eq!(expanded.polygons.len(), nfp.polygons.len());
164 assert_eq!(expanded.polygons[0], nfp.polygons[0]);
165 }
166
167 #[test]
168 fn test_expand_nfp_positive_spacing() {
169 let nfp = Nfp::from_polygons(vec![vec![
170 (0.0, 0.0),
171 (10.0, 0.0),
172 (10.0, 10.0),
173 (0.0, 10.0),
174 ]]);
175 let expanded = expand_nfp(&nfp, 1.0);
176 for &(x, y) in &expanded.polygons[0] {
178 let dx = x - 5.0;
179 let dy = y - 5.0;
180 let dist = (dx * dx + dy * dy).sqrt();
181 assert!(dist > 7.0);
183 }
184 }
185
186 #[test]
187 fn test_shrink_ifp_zero_spacing() {
188 let ifp = Nfp::from_polygons(vec![vec![
189 (0.0, 0.0),
190 (10.0, 0.0),
191 (10.0, 10.0),
192 (0.0, 10.0),
193 ]]);
194 let shrunk = shrink_ifp(&ifp, 0.0);
195 assert_eq!(shrunk.polygons.len(), ifp.polygons.len());
196 assert_eq!(shrunk.polygons[0], ifp.polygons[0]);
197 }
198
199 #[test]
200 fn test_shrink_ifp_positive_spacing() {
201 let ifp = Nfp::from_polygons(vec![vec![
202 (0.0, 0.0),
203 (10.0, 0.0),
204 (10.0, 10.0),
205 (0.0, 10.0),
206 ]]);
207 let shrunk = shrink_ifp(&ifp, 1.0);
208 for &(x, y) in &shrunk.polygons[0] {
210 let dx = x - 5.0;
211 let dy = y - 5.0;
212 let dist = (dx * dx + dy * dy).sqrt();
213 assert!(dist < 7.0);
215 }
216 }
217
218 #[test]
219 fn test_shrink_ifp_collapse() {
220 let ifp = Nfp::from_polygons(vec![vec![(0.0, 0.0), (1.0, 0.0), (0.5, 0.5)]]);
222 let shrunk = shrink_ifp(&ifp, 10.0);
223 assert!(shrunk.polygons.is_empty() || shrunk.polygons[0].len() >= 3);
227 }
228
229 #[test]
230 fn test_nesting_fitness_all_placed() {
231 let f = nesting_fitness(10, 10, 0.85);
232 assert!((f - 108.5).abs() < 1e-10);
233 }
234
235 #[test]
236 fn test_nesting_fitness_partial() {
237 let f = nesting_fitness(5, 10, 0.40);
238 assert!((f - 54.0).abs() < 1e-10);
240 }
241
242 #[test]
243 fn test_nesting_fitness_none_placed() {
244 let f = nesting_fitness(0, 10, 0.0);
245 assert!((f - 0.0).abs() < 1e-10);
246 }
247
248 #[test]
249 fn test_nesting_fitness_empty_total() {
250 let f = nesting_fitness(0, 0, 0.0);
251 assert!((f - 0.0).abs() < 1e-10);
252 }
253}