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 = runner.run();
315
316 let problem = BrkgaNestingProblem::new(
318 geometries.to_vec(),
319 boundary.clone(),
320 config.clone(),
321 Arc::new(AtomicBool::new(false)),
322 );
323
324 let (placements, utilization, _placed_count) = problem.decode(&brkga_result.best);
325
326 let mut unplaced = Vec::new();
328 let mut placed_ids: std::collections::HashSet<String> = std::collections::HashSet::new();
329 for p in &placements {
330 placed_ids.insert(p.geometry_id.clone());
331 }
332 for geom in geometries {
333 if !placed_ids.contains(geom.id()) {
334 unplaced.push(geom.id().clone());
335 }
336 }
337
338 let mut result = SolveResult::new();
339 result.placements = placements;
340 result.unplaced = unplaced;
341 result.boundaries_used = 1;
342 result.utilization = utilization;
343 result.computation_time_ms = brkga_result.elapsed.as_millis() as u64;
344 result.generations = Some(brkga_result.generations);
345 result.best_fitness = Some(brkga_result.best.fitness());
346 result.fitness_history = Some(brkga_result.history);
347 result.strategy = Some("BRKGA".to_string());
348 result.cancelled = cancelled.load(Ordering::Relaxed);
349 result.target_reached = brkga_result.target_reached;
350
351 result
352}
353
354#[cfg(test)]
355mod tests {
356 use super::*;
357
358 #[test]
359 fn test_brkga_nesting_basic() {
360 let geometries = vec![
361 Geometry2D::rectangle("R1", 20.0, 10.0).with_quantity(2),
362 Geometry2D::rectangle("R2", 15.0, 15.0).with_quantity(2),
363 ];
364
365 let boundary = Boundary2D::rectangle(100.0, 50.0);
366 let config = Config::default();
367 let brkga_config = BrkgaConfig::default()
368 .with_population_size(30)
369 .with_max_generations(20);
370
371 let result = run_brkga_nesting(
372 &geometries,
373 &boundary,
374 &config,
375 brkga_config,
376 Arc::new(AtomicBool::new(false)),
377 );
378
379 assert!(result.utilization > 0.0);
380 assert!(!result.placements.is_empty());
381 assert_eq!(result.strategy, Some("BRKGA".to_string()));
382 }
383
384 #[test]
385 fn test_brkga_nesting_all_placed() {
386 let geometries = vec![Geometry2D::rectangle("R1", 20.0, 20.0).with_quantity(4)];
387
388 let boundary = Boundary2D::rectangle(100.0, 100.0);
389 let config = Config::default();
390 let brkga_config = BrkgaConfig::default()
391 .with_population_size(30)
392 .with_max_generations(30);
393
394 let result = run_brkga_nesting(
395 &geometries,
396 &boundary,
397 &config,
398 brkga_config,
399 Arc::new(AtomicBool::new(false)),
400 );
401
402 assert_eq!(result.placements.len(), 4);
404 assert!(result.unplaced.is_empty());
405 }
406
407 #[test]
408 fn test_brkga_nesting_with_rotation() {
409 let geometries = vec![Geometry2D::rectangle("R1", 30.0, 10.0)
410 .with_quantity(3)
411 .with_rotations(vec![0.0, 90.0])];
412
413 let boundary = Boundary2D::rectangle(50.0, 50.0);
414 let config = Config::default();
415 let brkga_config = BrkgaConfig::default()
416 .with_population_size(30)
417 .with_max_generations(30);
418
419 let result = run_brkga_nesting(
420 &geometries,
421 &boundary,
422 &config,
423 brkga_config,
424 Arc::new(AtomicBool::new(false)),
425 );
426
427 assert!(result.utilization > 0.0);
428 assert!(!result.placements.is_empty());
429 }
430
431 #[test]
432 fn test_brkga_problem_decode() {
433 use rand::SeedableRng;
434
435 let geometries = vec![Geometry2D::rectangle("R1", 20.0, 10.0).with_quantity(2)];
436
437 let boundary = Boundary2D::rectangle(100.0, 50.0);
438 let config = Config::default();
439 let cancelled = Arc::new(AtomicBool::new(false));
440
441 let problem = BrkgaNestingProblem::new(geometries, boundary, config, cancelled);
442
443 assert_eq!(problem.num_instances(), 2);
444 assert_eq!(problem.num_keys(), 4);
446
447 let mut rng = rand::rngs::StdRng::seed_from_u64(12345);
449 let chromosome = RandomKeyChromosome::random(problem.num_keys(), &mut rng);
450 let (placements, utilization, placed_count) = problem.decode(&chromosome);
451
452 assert_eq!(placements.len(), placed_count);
454 if placed_count > 0 {
455 assert!(utilization > 0.0);
456 }
457 }
458}