1use std::mem;
2
3use hashbrown::HashSet;
4use rayon::iter::{
5 IntoParallelIterator,
6 IntoParallelRefIterator,
7 ParallelIterator,
8};
9use serde::{
10 Deserialize,
11 Serialize,
12};
13
14use crate::{
15 aabb::{
16 AABBVersion1,
17 AABBVersion1CheckedItem,
18 },
19 algorithmen::Algorithmen3DBinPackaging,
20 bin::{
21 Bin,
22 SpaceLeftBin,
23 },
24 corners::Corners,
25 items::{
26 Item,
27 ItemsPlaced,
28 },
29 sortedbin::SortedBin,
30};
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct SecondAlgorithmen {
35 bin: Bin,
36 items: Vec<Item>,
37 aabb: AABBVersion1,
38 volume_left: u32,
40 corners: HashSet<Corners>,
41}
42impl SecondAlgorithmen {
43 fn is_support_under_it(items_placed: &Vec<ItemsPlaced>, item: &Item, point: &Corners) -> bool {
44 if point.position.y == 0 {
46 return true;
47 }
48 let min_x = point.position.x;
50 let max_x = point.position.x + item.size_cube.x;
51 let min_z = point.position.z;
52 let max_z = point.position.z + item.size_cube.z;
53
54 items_placed.iter().any(|p| {
55 let top_y = p.position.y + p.item.size_cube.y;
57 if top_y != point.position.y {
58 return false;
59 }
60
61 let p_min_x = p.position.x;
63 let p_max_x = p.position.x + p.item.size_cube.x;
64 let p_min_z = p.position.z;
65 let p_max_z = p.position.z + p.item.size_cube.z;
66
67 let overlap_x = min_x < p_max_x && p_min_x < max_x;
68 let overlap_z = min_z < p_max_z && p_min_z < max_z;
69
70 overlap_x && overlap_z
71 })
72 }
73 fn fits_in_bin(bin: &Bin, corner: &Corners, item: &Item) -> bool {
75 corner.position.x + item.size_cube.x <= bin.position.x
76 && corner.position.y + item.size_cube.y <= bin.position.y
77 && corner.position.z + item.size_cube.z <= bin.position.z
78 }
79 fn score(bin: &Bin, item: &Item, point: &Corners) -> f32 {
81 let x =
83 ((point.position.x + item.size_cube.x) as f32 + 1.0) / (bin.position.x as f32) + 1.0;
84 let y =
85 ((point.position.y + item.size_cube.y) as f32 + 1.0) / (bin.position.y as f32) + 1.0;
86 let z =
87 ((point.position.z + item.size_cube.z) as f32 + 1.0) / (bin.position.z as f32) + 1.0;
88 let maximise_space_on_floor = (item.size_cube.x * item.size_cube.z) as f32;
89
90 ((x) + (y * 100.0) + (10.0 * z) - maximise_space_on_floor).clamp(f32::MIN, f32::MAX)
91 }
92 fn find_best_point_to_place<F>(
94 points: &HashSet<Corners>,
95 item: Item,
96 aabb: &AABBVersion1,
97 bin: &Bin,
98 placed_items: &Vec<ItemsPlaced>,
99 custom_score_function: Option<F>,
100 ) -> Option<(AABBVersion1CheckedItem, f32, Corners)>
101 where
102 F: Fn(&Bin, &Item, &Corners) -> f32 + Send + Sync,
103 {
104 let all_items_rotated = item.rotation_v2();
105 all_items_rotated
106 .into_iter()
107 .filter_map(|x_item| {
108 points
110 .par_iter()
111 .cloned()
112 .filter_map(|x_corner| {
113 if !Self::fits_in_bin(bin, &x_corner, &x_item)
115 || !Self::is_support_under_it(placed_items, &x_item, &x_corner)
116 {
117 return None;
118 }
119 let check = aabb.check_item_v2(x_item.clone(), &x_corner)?;
120 let score = match custom_score_function.as_ref() {
121 None => Self::score(bin, &x_item, &x_corner),
122 Some(x) => x(bin, &x_item, &x_corner),
123 };
124 Some((check, score, x_corner))
125 })
126 .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal))
127 })
128 .min_by(|a, b| {
129 a.1.partial_cmp(&b.1)
130 .unwrap_or_else(|| std::cmp::Ordering::Equal)
131 })
132 }
133 fn place_item_in_bin(
135 &mut self,
136 point: Corners,
137 item: crate::aabb::AABBVersion1CheckedItem,
138 aabb: &mut AABBVersion1,
139 ) -> anyhow::Result<(ItemsPlaced, Vec<Corners>)> {
140 let _ = aabb.add_v2(item.clone(), &point);
141 let _ = self.corners.remove(&point);
142 let one_corner = Corners::new(
143 point.position.x + item.0.size_cube.x,
144 point.position.y,
145 point.position.z,
146 );
147 let second_pointer = Corners::new(
148 point.position.x,
149 item.0.size_cube.y + point.position.y,
150 point.position.z,
151 );
152 let three_pointer = Corners::new(
153 point.position.x,
154 point.position.y,
155 item.0.size_cube.z + point.position.z,
156 );
157 let four_point = Corners::new(
159 point.position.x + item.0.size_cube.x,
160 point.position.y + item.0.size_cube.y,
161 point.position.z,
162 );
163 let five_point = Corners::new(
164 point.position.x + item.0.size_cube.x,
165 point.position.y,
166 point.position.z + item.0.size_cube.z,
167 );
168 let sichs_point = Corners::new(
169 point.position.x,
170 point.position.y + item.0.size_cube.y,
171 point.position.z + item.0.size_cube.z,
172 );
173 let seven_point = Corners::new(
174 point.position.x + item.0.size_cube.x,
175 point.position.y + item.0.size_cube.y,
176 point.position.z + item.0.size_cube.z,
177 );
178 let new_corners = vec![
179 one_corner,
180 second_pointer,
181 three_pointer,
182 four_point,
183 five_point,
184 sichs_point,
185 seven_point,
186 ];
187 let new_corners = new_corners
189 .into_iter()
190 .filter_map(|x| aabb.point_is_free(&x).then(|| x))
191 .collect();
192 let new_item = ItemsPlaced::new(point.position, item.0);
193 Ok((new_item, new_corners))
194 }
195}
196impl Algorithmen3DBinPackaging for SecondAlgorithmen {
197 fn create_algorithmen(
198 input: Vec<Item>,
199 bin: Bin,
200 ) -> Result<Self, crate::algorithmen::AlgorithmenError> {
201 let volume = bin
202 .position
203 .x
204 .saturating_mul(bin.position.y)
205 .saturating_mul(bin.position.z);
206 let length = input.len();
207 let mut hashset: HashSet<Corners> = HashSet::with_capacity(length);
208 let _ = hashset.insert(Corners::new(0, 0, 0));
210 Ok(Self {
211 bin,
212 items: input,
213 aabb: AABBVersion1::new(length),
214 volume_left: volume,
215 corners: hashset,
216 })
217 }
218
219 fn add_item(&mut self, input: Vec<Item>) -> Result<(), crate::algorithmen::AlgorithmenError> {
221 self.items.extend(input);
222 Ok(())
223 }
224
225 fn remove_item(&mut self, input: Vec<Item>) -> Result<(), Vec<Item>> {
227 self.items.retain(|x| !input.contains(x));
228 Ok(())
229 }
230
231 fn space_left(&self) -> u32 {
232 self.volume_left
233 }
234
235 fn check_fit_quick(input: &[Item], bin: &Bin) -> (bool, crate::bin::SpaceLeftBin) {
236 let bin_volume =
237 (bin.position.x * bin.position.y * bin.position.z).clamp(u32::MIN, u32::MAX);
238 let item_total_volume: u32 = input
239 .iter()
240 .map(|x| x.size_cube.x * x.size_cube.y * x.size_cube.z)
241 .sum();
242
243 let item_total_volume = item_total_volume.clamp(u32::MIN, u32::MAX);
244 let result = bin_volume
245 .saturating_sub(item_total_volume)
246 .clamp(u32::MIN, u32::MAX);
247 let result = SpaceLeftBin(result);
248 let check = result.0 > 0;
249 (check, result)
250 }
251
252 fn calculate_custom<F>(
253 mut self,
254 custom_score_function: Option<F>,
255 ) -> Result<crate::sortedbin::SortedBin, crate::algorithmen::AlgorithmenError>
256 where
257 F: Fn(&Bin, &Item, &Corners) -> f32 + Send + Sync,
258 {
259 let item = mem::take(&mut self.items);
260 let (mut keep, remove): (Vec<Item>, Vec<Item>) = item.into_par_iter().partition(|x| {
261 x.size_cube.x <= self.bin.position.x
262 && x.size_cube.y <= self.bin.position.y
263 && x.size_cube.z <= self.bin.position.z
264 });
265 keep.sort_unstable_by(|a, b| a.order.cmp(&b.order).then_with(|| a.weight.cmp(&b.weight)));
266 let item: Vec<Item> = keep;
268 let mut removed_items: Vec<Item> = Vec::with_capacity(item.len());
269 removed_items.extend(remove);
270 let mut placed_item: Vec<ItemsPlaced> = Vec::with_capacity(item.len());
271
272 let mut aabb = mem::take(&mut self.aabb);
274 item.into_iter().for_each(|x| {
275 let result = Self::find_best_point_to_place(
276 &self.corners,
277 x.clone(),
278 &aabb,
279 &self.bin,
280 &placed_item,
281 custom_score_function.as_ref(),
282 );
283 if let Some(checked_result) = result {
284 if let Ok((item_finished, new_corners)) = Self::place_item_in_bin(
285 &mut self,
286 checked_result.2,
287 checked_result.0,
288 &mut aabb,
289 ) {
290 self.volume_left = self
291 .volume_left
292 .saturating_sub(item_finished.item.volume_item());
293 placed_item.push(item_finished);
294 self.corners.extend(new_corners);
295 } else {
296 removed_items.push(x);
297 }
298 } else {
299 removed_items.push(x);
300 }
301 });
302 Ok(SortedBin::new(self.bin, placed_item, removed_items))
303 }
304
305 fn calculate(self) -> Result<SortedBin, crate::algorithmen::AlgorithmenError> {
306 Self::calculate_custom(self, None::<fn(&Bin, &Item, &Corners) -> f32>)
307 }
308}
309#[cfg(test)]
310mod tests {
311 use proptest::prelude::*;
312
313 use crate::{
314 bin::Bin,
315 corners::Corners,
316 items::Item,
317 second_algorithmen::SecondAlgorithmen,
318 vector::Vector3,
319 };
320
321 proptest! {
322 #[test]
323 fn second_algorithmen_score(x in 0u32..100,y in 0u32..100,z in 0u32..100) {
324 let result = SecondAlgorithmen::score(&Bin::new(1,Vector3::new(x + 100, y + 100, z + 100), 1000, 0), &Item::new(1,Vector3::new(x, y, z), 10, 1), &Corners::new(0,0,0));
325 dbg!(&result);
326 prop_assert!(result.is_normal());
327 prop_assert!(!result.is_nan());
328 }
329 }
330}