1use crate::config::LBFConfig;
2use crate::opt::loss::LBFLoss;
3use crate::samplers::ls_sampler::LSSampler;
4use crate::samplers::uniform_rect_sampler::UniformRectSampler;
5use itertools::Itertools;
6use jagua_rs::collision_detection::CDEngine;
7use jagua_rs::collision_detection::hazards::BasicHazardEntity;
8use jagua_rs::collision_detection::hazards::filter::HazardFilter;
9use jagua_rs::entities::Item;
10use jagua_rs::geometry::DTransformation;
11use jagua_rs::geometry::geo_traits::TransformableFrom;
12use log::debug;
13use ordered_float::OrderedFloat;
14use rand::Rng;
15use std::cmp::{Ordering, Reverse};
16
17pub fn search(
19 cde: &CDEngine,
20 item: &Item,
21 config: &LBFConfig,
22 rng: &mut impl Rng,
23 sample_counter: &mut usize,
24 filter: &impl HazardFilter<BasicHazardEntity>,
25) -> Option<(DTransformation, LBFLoss)> {
26 let surrogate = item.shape_cd().surrogate();
27 let mut buffer = item.shape_cd().as_ref().clone();
29
30 let mut best: Option<(DTransformation, LBFLoss)> = None;
31
32 let ls_sample_budget = (config.n_samples as f32 * config.ls_frac) as usize;
34 let uni_sample_budget = config.n_samples - ls_sample_budget;
35
36 let mut bin_sampler = UniformRectSampler::new(cde.bbox(), item);
37
38 for i in 0..uni_sample_budget {
39 let d_transf = bin_sampler.sample(rng);
40 let transf = d_transf.compose();
41 if !cde.detect_surrogate_collision(surrogate, &transf, filter) {
42 buffer.transform_from(item.shape_cd(), &transf);
44 let cost = LBFLoss::from_shape(&buffer);
45
46 let worth_testing = match (best.as_ref(), &cost) {
48 (Some((_, best_cost)), cost) => {
49 cost.partial_cmp(best_cost).unwrap() == Ordering::Less
50 }
51 (None, _) => true,
52 };
53
54 if worth_testing && !cde.detect_poly_collision(&buffer, filter) {
55 debug!("[UNI: {i}/{uni_sample_budget}] better: {} ", d_transf);
57
58 best = Some((d_transf, cost));
59
60 let tightened_sampling_bbox = cost.tighten_sample_bbox(bin_sampler.bbox);
61 bin_sampler = UniformRectSampler::new(tightened_sampling_bbox, item);
62 }
63 }
64 }
65
66 *sample_counter += uni_sample_budget;
67
68 let (best_sample, best_cost) = best.as_mut()?;
70
71 let mut ls_sampler = LSSampler::from_defaults(item, *best_sample, cde.bbox());
78
79 for i in 0..ls_sample_budget {
80 let d_transf = ls_sampler.sample(rng);
81 let transf = d_transf.compose();
82 if !cde.detect_surrogate_collision(surrogate, &transf, filter) {
83 buffer.transform_from(item.shape_cd(), &transf);
84 let cost = LBFLoss::from_shape(&buffer);
85
86 let worth_testing = cost < *best_cost;
88
89 if worth_testing && !cde.detect_poly_collision(&buffer, filter) {
90 ls_sampler.shift_mean(d_transf);
92 debug!("[LS: {i}/{ls_sample_budget}] better: {}", d_transf);
93 (*best_sample, *best_cost) = (d_transf, cost);
94 }
95 }
96 let progress_pct = i as f32 / ls_sample_budget as f32;
97 ls_sampler.decay_stddev(progress_pct);
98 }
99
100 *sample_counter += ls_sampler.n_samples;
101
102 best
103}
104
105pub fn item_placement_order<'a>(items: impl Iterator<Item = &'a Item>) -> Vec<usize> {
106 items
108 .sorted_by_key(|item| Reverse(OrderedFloat(item.shape_cd().diameter())))
109 .map(|item| item.idx())
110 .collect_vec()
111}