1use crate::boundary::Boundary2D;
19use crate::clamp_placement_to_boundary;
20use crate::geometry::Geometry2D;
21use crate::nfp::{
22 compute_ifp, compute_nfp, find_bottom_left_placement, verify_no_overlap, Nfp, PlacedGeometry,
23};
24use std::sync::atomic::{AtomicBool, Ordering};
25use std::sync::Arc;
26use u_nesting_core::brkga::{BrkgaConfig, BrkgaProblem, BrkgaRunner, RandomKeyChromosome};
27use u_nesting_core::geometry::{Boundary, Geometry};
28use u_nesting_core::solver::Config;
29use u_nesting_core::{Placement, SolveResult};
30
31use crate::placement_utils::{expand_nfp, nesting_fitness, shrink_ifp, InstanceInfo};
32
33pub struct BrkgaNestingProblem {
35 geometries: Vec<Geometry2D>,
37 boundary: Boundary2D,
39 config: Config,
41 instances: Vec<InstanceInfo>,
43 rotation_angles: Vec<Vec<f64>>,
45 cancelled: Arc<AtomicBool>,
47}
48
49impl BrkgaNestingProblem {
50 pub fn new(
52 geometries: Vec<Geometry2D>,
53 boundary: Boundary2D,
54 config: Config,
55 cancelled: Arc<AtomicBool>,
56 ) -> Self {
57 let mut instances = Vec::new();
59 let mut rotation_angles = Vec::new();
60
61 for (geom_idx, geom) in geometries.iter().enumerate() {
62 let angles = geom.rotations();
64 let angles = if angles.is_empty() { vec![0.0] } else { angles };
65 rotation_angles.push(angles);
66
67 for instance_num in 0..geom.quantity() {
69 instances.push(InstanceInfo {
70 geometry_idx: geom_idx,
71 instance_num,
72 });
73 }
74 }
75
76 Self {
77 geometries,
78 boundary,
79 config,
80 instances,
81 rotation_angles,
82 cancelled,
83 }
84 }
85
86 pub fn num_instances(&self) -> usize {
88 self.instances.len()
89 }
90
91 pub fn decode(&self, chromosome: &RandomKeyChromosome) -> (Vec<Placement<f64>>, f64, usize) {
97 let n = self.instances.len();
98 if n == 0 || chromosome.len() < n {
99 return (Vec::new(), 0.0, 0);
100 }
101
102 let order = chromosome.decode_as_permutation();
104 let order: Vec<usize> = order.into_iter().take(n).collect();
106
107 let mut placements = Vec::new();
108 let mut placed_geometries: Vec<PlacedGeometry> = Vec::new();
109 let mut total_placed_area = 0.0;
110 let mut placed_count = 0;
111
112 let margin = self.config.margin;
113 let spacing = self.config.spacing;
114
115 let boundary_polygon = self.get_boundary_polygon_with_margin(margin);
117
118 let sample_step = self.compute_sample_step();
120
121 for &instance_idx in &order {
123 if self.cancelled.load(Ordering::Relaxed) {
124 break;
125 }
126
127 if instance_idx >= self.instances.len() {
128 continue;
129 }
130
131 let info = &self.instances[instance_idx];
132 let geom = &self.geometries[info.geometry_idx];
133
134 let rotation_key_idx = n + instance_idx;
136 let num_rotations = self
137 .rotation_angles
138 .get(info.geometry_idx)
139 .map(|a| a.len())
140 .unwrap_or(1);
141
142 let rotation_idx = if rotation_key_idx < chromosome.len() {
143 chromosome.decode_as_discrete(rotation_key_idx, num_rotations)
144 } else {
145 0
146 };
147
148 let rotation_angle = self
149 .rotation_angles
150 .get(info.geometry_idx)
151 .and_then(|angles| angles.get(rotation_idx))
152 .copied()
153 .unwrap_or(0.0);
154
155 let ifp = match compute_ifp(&boundary_polygon, geom, rotation_angle) {
157 Ok(ifp) => ifp,
158 Err(_) => continue,
159 };
160
161 if ifp.is_empty() {
162 continue;
163 }
164
165 let mut nfps: Vec<Nfp> = Vec::new();
167 for placed in &placed_geometries {
168 let placed_exterior = placed.translated_exterior();
169 let placed_geom = Geometry2D::new(format!("_placed_{}", placed.geometry.id()))
170 .with_polygon(placed_exterior);
171
172 if let Ok(nfp) = compute_nfp(&placed_geom, geom, rotation_angle) {
173 let expanded = self.expand_nfp(&nfp, spacing);
174 nfps.push(expanded);
175 }
176 }
177
178 let ifp_shrunk = self.shrink_ifp(&ifp, spacing);
180
181 let nfp_refs: Vec<&Nfp> = nfps.iter().collect();
185 if let Some((x, y)) = find_bottom_left_placement(&ifp_shrunk, &nfp_refs, sample_step) {
186 let geom_aabb = geom.aabb_at_rotation(rotation_angle);
188 let boundary_aabb = self.boundary.aabb();
189
190 if let Some((clamped_x, clamped_y)) =
191 clamp_placement_to_boundary(x, y, geom_aabb, boundary_aabb)
192 {
193 let was_clamped = (clamped_x - x).abs() > 1e-6 || (clamped_y - y).abs() > 1e-6;
196 if was_clamped {
197 if !verify_no_overlap(
199 geom,
200 (clamped_x, clamped_y),
201 rotation_angle,
202 &placed_geometries,
203 ) {
204 continue; }
206 }
207
208 let placement = Placement::new_2d(
209 geom.id().clone(),
210 info.instance_num,
211 clamped_x,
212 clamped_y,
213 rotation_angle,
214 );
215
216 placements.push(placement);
217 placed_geometries.push(PlacedGeometry::new(
218 geom.clone(),
219 (clamped_x, clamped_y),
220 rotation_angle,
221 ));
222 total_placed_area += geom.measure();
223 placed_count += 1;
224 }
225 }
226 }
227
228 let utilization = total_placed_area / self.boundary.measure();
229 (placements, utilization, placed_count)
230 }
231
232 fn get_boundary_polygon_with_margin(&self, margin: f64) -> Vec<(f64, f64)> {
234 let (b_min, b_max) = self.boundary.aabb();
235 vec![
236 (b_min[0] + margin, b_min[1] + margin),
237 (b_max[0] - margin, b_min[1] + margin),
238 (b_max[0] - margin, b_max[1] - margin),
239 (b_min[0] + margin, b_max[1] - margin),
240 ]
241 }
242
243 fn compute_sample_step(&self) -> f64 {
245 if self.geometries.is_empty() {
246 return 1.0;
247 }
248
249 let mut min_dim = f64::INFINITY;
250 for geom in &self.geometries {
251 let (g_min, g_max) = geom.aabb();
252 let width = g_max[0] - g_min[0];
253 let height = g_max[1] - g_min[1];
254 min_dim = min_dim.min(width).min(height);
255 }
256
257 (min_dim / 4.0).clamp(0.5, 10.0)
258 }
259
260 fn expand_nfp(&self, nfp: &Nfp, spacing: f64) -> Nfp {
262 expand_nfp(nfp, spacing)
263 }
264
265 fn shrink_ifp(&self, ifp: &Nfp, spacing: f64) -> Nfp {
267 shrink_ifp(ifp, spacing)
268 }
269}
270
271impl BrkgaProblem for BrkgaNestingProblem {
272 fn num_keys(&self) -> usize {
273 self.instances.len() * 2
275 }
276
277 fn evaluate(&self, chromosome: &mut RandomKeyChromosome) {
278 let (_, utilization, placed_count) = self.decode(chromosome);
279 let fitness = nesting_fitness(placed_count, self.instances.len(), utilization);
280 chromosome.set_fitness(fitness);
281 }
282
283 fn on_generation(
284 &self,
285 generation: u32,
286 best: &RandomKeyChromosome,
287 _population: &[RandomKeyChromosome],
288 ) {
289 log::debug!(
290 "BRKGA Generation {}: fitness={:.4}",
291 generation,
292 best.fitness()
293 );
294 }
295}
296
297pub fn run_brkga_nesting(
299 geometries: &[Geometry2D],
300 boundary: &Boundary2D,
301 config: &Config,
302 brkga_config: BrkgaConfig,
303 cancelled: Arc<AtomicBool>,
304) -> SolveResult<f64> {
305 let problem = BrkgaNestingProblem::new(
306 geometries.to_vec(),
307 boundary.clone(),
308 config.clone(),
309 cancelled.clone(),
310 );
311
312 let runner = BrkgaRunner::with_cancellation(brkga_config, problem, cancelled.clone());
313
314 let brkga_result = match config.seed {
317 Some(seed) => {
318 use rand::SeedableRng;
319 runner.run_with_rng(&mut rand::rngs::StdRng::seed_from_u64(seed))
320 }
321 None => runner.run(),
322 };
323
324 let problem = BrkgaNestingProblem::new(
326 geometries.to_vec(),
327 boundary.clone(),
328 config.clone(),
329 Arc::new(AtomicBool::new(false)),
330 );
331
332 let (placements, utilization, _placed_count) = problem.decode(&brkga_result.best);
333
334 let mut unplaced = Vec::new();
336 let mut placed_ids: std::collections::HashSet<String> = std::collections::HashSet::new();
337 for p in &placements {
338 placed_ids.insert(p.geometry_id.clone());
339 }
340 for geom in geometries {
341 if !placed_ids.contains(geom.id()) {
342 unplaced.push(geom.id().clone());
343 }
344 }
345
346 let mut result = SolveResult::new();
347 result.placements = placements;
348 result.unplaced = unplaced;
349 result.boundaries_used = 1;
350 result.utilization = utilization;
351 result.computation_time_ms = brkga_result.elapsed.as_millis() as u64;
352 result.generations = Some(brkga_result.generations);
353 result.best_fitness = Some(brkga_result.best.fitness());
354 result.fitness_history = Some(brkga_result.history);
355 result.strategy = Some("BRKGA".to_string());
356 result.cancelled = cancelled.load(Ordering::Relaxed);
357 result.target_reached = brkga_result.target_reached;
358
359 result
360}
361
362#[cfg(test)]
363mod tests {
364 use super::*;
365
366 #[test]
367 fn test_brkga_nesting_basic() {
368 let geometries = vec![
369 Geometry2D::rectangle("R1", 20.0, 10.0).with_quantity(2),
370 Geometry2D::rectangle("R2", 15.0, 15.0).with_quantity(2),
371 ];
372
373 let boundary = Boundary2D::rectangle(100.0, 50.0);
374 let config = Config::default();
375 let brkga_config = BrkgaConfig::default()
376 .with_population_size(30)
377 .with_max_generations(20);
378
379 let result = run_brkga_nesting(
380 &geometries,
381 &boundary,
382 &config,
383 brkga_config,
384 Arc::new(AtomicBool::new(false)),
385 );
386
387 assert!(result.utilization > 0.0);
388 assert!(!result.placements.is_empty());
389 assert_eq!(result.strategy, Some("BRKGA".to_string()));
390 }
391
392 #[test]
393 fn test_brkga_nesting_all_placed() {
394 let geometries = vec![Geometry2D::rectangle("R1", 20.0, 20.0).with_quantity(4)];
395
396 let boundary = Boundary2D::rectangle(100.0, 100.0);
397 let config = Config::default();
398 let brkga_config = BrkgaConfig::default()
399 .with_population_size(30)
400 .with_max_generations(30);
401
402 let result = run_brkga_nesting(
403 &geometries,
404 &boundary,
405 &config,
406 brkga_config,
407 Arc::new(AtomicBool::new(false)),
408 );
409
410 assert_eq!(result.placements.len(), 4);
412 assert!(result.unplaced.is_empty());
413 }
414
415 #[test]
416 fn test_brkga_nesting_with_rotation() {
417 let geometries = vec![Geometry2D::rectangle("R1", 30.0, 10.0)
418 .with_quantity(3)
419 .with_rotations(vec![0.0, 90.0])];
420
421 let boundary = Boundary2D::rectangle(50.0, 50.0);
422 let config = Config::default();
423 let brkga_config = BrkgaConfig::default()
424 .with_population_size(30)
425 .with_max_generations(30);
426
427 let result = run_brkga_nesting(
428 &geometries,
429 &boundary,
430 &config,
431 brkga_config,
432 Arc::new(AtomicBool::new(false)),
433 );
434
435 assert!(result.utilization > 0.0);
436 assert!(!result.placements.is_empty());
437 }
438
439 #[test]
440 fn test_brkga_problem_decode() {
441 use rand::SeedableRng;
442
443 let geometries = vec![Geometry2D::rectangle("R1", 20.0, 10.0).with_quantity(2)];
444
445 let boundary = Boundary2D::rectangle(100.0, 50.0);
446 let config = Config::default();
447 let cancelled = Arc::new(AtomicBool::new(false));
448
449 let problem = BrkgaNestingProblem::new(geometries, boundary, config, cancelled);
450
451 assert_eq!(problem.num_instances(), 2);
452 assert_eq!(problem.num_keys(), 4);
454
455 let mut rng = rand::rngs::StdRng::seed_from_u64(12345);
457 let chromosome = RandomKeyChromosome::random(problem.num_keys(), &mut rng);
458 let (placements, utilization, placed_count) = problem.decode(&chromosome);
459
460 assert_eq!(placements.len(), placed_count);
462 if placed_count > 0 {
463 assert!(utilization > 0.0);
464 }
465 }
466}