1use crate::packed_location::RotatedBy;
2use crate::{BoxSizeHeuristicFn, PackedLocation, RectToInsert, WidthHeightDepth};
3
4use core::{
5 cmp::Ordering,
6 fmt::{Debug, Display, Error as FmtError, Formatter},
7};
8
9mod overlaps;
10
11pub type ComparePotentialContainersFn =
25 dyn Fn([WidthHeightDepth; 3], [WidthHeightDepth; 3], &BoxSizeHeuristicFn) -> Ordering;
26
27pub fn contains_smallest_box(
31 mut container1: [WidthHeightDepth; 3],
32 mut container2: [WidthHeightDepth; 3],
33 heuristic: &BoxSizeHeuristicFn,
34) -> Ordering {
35 container1.sort_by(|a, b| heuristic(*a).cmp(&heuristic(*b)));
36 container2.sort_by(|a, b| heuristic(*a).cmp(&heuristic(*b)));
37
38 match heuristic(container2[0]).cmp(&heuristic(container1[0])) {
39 Ordering::Equal => heuristic(container2[1]).cmp(&heuristic(container1[1])),
40 o => o,
41 }
42}
43
44#[derive(Debug, Eq, PartialEq, Copy, Clone, Default, Ord, PartialOrd)]
46pub struct BinSection {
47 pub(crate) x: u32,
48 pub(crate) y: u32,
49 pub(crate) z: u32,
50 pub(crate) whd: WidthHeightDepth,
51}
52
53#[derive(Debug, Eq, PartialEq)]
55#[allow(missing_docs)]
56pub enum BinSectionError {
57 PlacementWiderThanBinSection,
58 PlacementTallerThanBinSection,
59 PlacementDeeperThanBinSection,
60}
61
62impl Display for BinSectionError {
63 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
64 let err = match self {
65 BinSectionError::PlacementWiderThanBinSection => {
66 "Can not place a rectangle inside of a bin that is wider than that rectangle."
67 }
68 BinSectionError::PlacementTallerThanBinSection => {
69 "Can not place a rectangle inside of a bin that is taller than that rectangle."
70 }
71 BinSectionError::PlacementDeeperThanBinSection => {
72 "Can not place a rectangle inside of a bin that is deeper than that rectangle."
73 }
74 };
75
76 f.write_str(err)
77 }
78}
79
80impl BinSection {
81 pub fn new(x: u32, y: u32, z: u32, whd: WidthHeightDepth) -> Self {
83 BinSection { x, y, z, whd }
84 }
85
86 fn new_spread(x: u32, y: u32, z: u32, width: u32, height: u32, depth: u32) -> Self {
88 BinSection {
89 x,
90 y,
91 z,
92 whd: WidthHeightDepth {
93 width,
94 height,
95 depth,
96 },
97 }
98 }
99}
100
101impl BinSection {
102 pub fn try_place(
155 &self,
156 incoming: &RectToInsert,
157 container_comparison_fn: &ComparePotentialContainersFn,
158 heuristic_fn: &BoxSizeHeuristicFn,
159 ) -> Result<(PackedLocation, [BinSection; 3]), BinSectionError> {
160 self.incoming_can_fit(incoming)?;
161
162 let mut all_combinations = [
163 self.depth_largest_height_second_largest_width_smallest(incoming),
164 self.depth_largest_width_second_largest_height_smallest(incoming),
165 self.height_largest_depth_second_largest_width_smallest(incoming),
166 self.height_largest_width_second_largest_depth_smallest(incoming),
167 self.width_largest_depth_second_largest_height_smallest(incoming),
168 self.width_largest_height_second_largest_depth_smallest(incoming),
169 ];
170
171 all_combinations.sort_by(|a, b| {
172 container_comparison_fn(
173 [a[0].whd, a[1].whd, a[2].whd],
174 [b[0].whd, b[1].whd, b[2].whd],
175 heuristic_fn,
176 )
177 });
178
179 let packed_location = PackedLocation {
180 x: self.x,
181 y: self.y,
182 z: self.z,
183 whd: WidthHeightDepth {
184 width: incoming.width(),
185 height: incoming.height(),
186 depth: incoming.depth(),
187 },
188 x_axis_rotation: RotatedBy::ZeroDegrees,
189 y_axis_rotation: RotatedBy::ZeroDegrees,
190 z_axis_rotation: RotatedBy::ZeroDegrees,
191 };
192
193 Ok((packed_location, all_combinations[5]))
194 }
195
196 fn incoming_can_fit(&self, incoming: &RectToInsert) -> Result<(), BinSectionError> {
197 if incoming.width() > self.whd.width {
198 return Err(BinSectionError::PlacementWiderThanBinSection);
199 }
200 if incoming.height() > self.whd.height {
201 return Err(BinSectionError::PlacementTallerThanBinSection);
202 }
203
204 if incoming.depth() > self.whd.depth {
205 return Err(BinSectionError::PlacementDeeperThanBinSection);
206 }
207
208 Ok(())
209 }
210
211 fn width_largest_height_second_largest_depth_smallest(
212 &self,
213 incoming: &RectToInsert,
214 ) -> [BinSection; 3] {
215 [
216 self.empty_space_directly_right(incoming),
217 self.all_empty_space_above_excluding_behind(incoming),
218 self.all_empty_space_behind(incoming),
219 ]
220 }
221
222 fn width_largest_depth_second_largest_height_smallest(
223 &self,
224 incoming: &RectToInsert,
225 ) -> [BinSection; 3] {
226 [
227 self.empty_space_directly_right(incoming),
228 self.all_empty_space_above(incoming),
229 self.all_empty_space_behind_excluding_above(incoming),
230 ]
231 }
232
233 fn height_largest_width_second_largest_depth_smallest(
234 &self,
235 incoming: &RectToInsert,
236 ) -> [BinSection; 3] {
237 [
238 self.all_empty_space_right_excluding_behind(incoming),
239 self.empty_space_directly_above(incoming),
240 self.all_empty_space_behind(incoming),
241 ]
242 }
243
244 fn height_largest_depth_second_largest_width_smallest(
245 &self,
246 incoming: &RectToInsert,
247 ) -> [BinSection; 3] {
248 [
249 self.all_empty_space_right(incoming),
250 self.empty_space_directly_above(incoming),
251 self.all_empty_space_behind_excluding_right(incoming),
252 ]
253 }
254
255 fn depth_largest_width_second_largest_height_smallest(
256 &self,
257 incoming: &RectToInsert,
258 ) -> [BinSection; 3] {
259 [
260 self.all_empty_space_right_excluding_above(incoming),
261 self.all_empty_space_above(incoming),
262 self.empty_space_directly_behind(incoming),
263 ]
264 }
265
266 fn depth_largest_height_second_largest_width_smallest(
267 &self,
268 incoming: &RectToInsert,
269 ) -> [BinSection; 3] {
270 [
271 self.all_empty_space_right(incoming),
272 self.all_empty_space_above_excluding_right(incoming),
273 self.empty_space_directly_behind(incoming),
274 ]
275 }
276
277 fn all_empty_space_above(&self, incoming: &RectToInsert) -> BinSection {
278 BinSection::new_spread(
279 self.x,
280 self.y + incoming.height(),
281 self.z,
282 self.whd.width,
283 self.whd.height - incoming.height(),
284 self.whd.depth,
285 )
286 }
287
288 fn all_empty_space_right(&self, incoming: &RectToInsert) -> BinSection {
289 BinSection::new_spread(
290 self.x + incoming.width(),
291 self.y,
292 self.z,
293 self.whd.width - incoming.width(),
294 self.whd.height,
295 self.whd.depth,
296 )
297 }
298
299 fn all_empty_space_behind(&self, incoming: &RectToInsert) -> BinSection {
300 BinSection::new_spread(
301 self.x,
302 self.y,
303 self.z + incoming.depth(),
304 self.whd.width,
305 self.whd.height,
306 self.whd.depth - incoming.depth(),
307 )
308 }
309
310 fn empty_space_directly_above(&self, incoming: &RectToInsert) -> BinSection {
311 BinSection::new_spread(
312 self.x,
313 self.y + incoming.height(),
314 self.z,
315 incoming.width(),
316 self.whd.height - incoming.height(),
317 incoming.depth(),
318 )
319 }
320
321 fn empty_space_directly_right(&self, incoming: &RectToInsert) -> BinSection {
322 BinSection::new_spread(
323 self.x + incoming.width(),
324 self.y,
325 self.z,
326 self.whd.width - incoming.width(),
327 incoming.height(),
328 incoming.depth(),
329 )
330 }
331
332 fn empty_space_directly_behind(&self, incoming: &RectToInsert) -> BinSection {
333 BinSection::new(
334 self.x,
335 self.y,
336 self.z + incoming.depth(),
337 WidthHeightDepth {
338 width: incoming.width(),
339 height: incoming.height(),
340 depth: self.whd.depth - incoming.depth(),
341 },
342 )
343 }
344
345 fn all_empty_space_above_excluding_right(&self, incoming: &RectToInsert) -> BinSection {
346 BinSection::new(
347 self.x,
348 self.y + incoming.height(),
349 self.z,
350 WidthHeightDepth {
351 width: incoming.width(),
352 height: self.whd.height - incoming.height(),
353 depth: self.whd.depth,
354 },
355 )
356 }
357
358 fn all_empty_space_above_excluding_behind(&self, incoming: &RectToInsert) -> BinSection {
359 BinSection::new(
360 self.x,
361 self.y + incoming.height(),
362 self.z,
363 WidthHeightDepth {
364 width: self.whd.width,
365 height: self.whd.height - incoming.height(),
366 depth: incoming.depth(),
367 },
368 )
369 }
370
371 fn all_empty_space_right_excluding_above(&self, incoming: &RectToInsert) -> BinSection {
372 BinSection::new(
373 self.x + incoming.width(),
374 self.y,
375 self.z,
376 WidthHeightDepth {
377 width: self.whd.width - incoming.width(),
378 height: incoming.height(),
379 depth: self.whd.depth,
380 },
381 )
382 }
383
384 fn all_empty_space_right_excluding_behind(&self, incoming: &RectToInsert) -> BinSection {
385 BinSection::new(
386 self.x + incoming.width(),
387 self.y,
388 self.z,
389 WidthHeightDepth {
390 width: self.whd.width - incoming.width(),
391 height: self.whd.height,
392 depth: incoming.depth(),
393 },
394 )
395 }
396
397 fn all_empty_space_behind_excluding_above(&self, incoming: &RectToInsert) -> BinSection {
398 BinSection::new(
399 self.x,
400 self.y,
401 self.z + incoming.depth(),
402 WidthHeightDepth {
403 width: self.whd.width,
404 height: incoming.height(),
405 depth: self.whd.depth - incoming.depth(),
406 },
407 )
408 }
409
410 fn all_empty_space_behind_excluding_right(&self, incoming: &RectToInsert) -> BinSection {
411 BinSection::new(
412 self.x,
413 self.y,
414 self.z + incoming.depth(),
415 WidthHeightDepth {
416 width: incoming.width(),
417 height: self.whd.height,
418 depth: self.whd.depth - incoming.depth(),
419 },
420 )
421 }
422}
423
424#[cfg(test)]
425mod tests {
426 use super::*;
427 use crate::{volume_heuristic, RectToInsert};
428
429 const BIGGEST: u32 = 50;
430 const MIDDLE: u32 = 25;
431 const SMALLEST: u32 = 10;
432
433 const FULL: u32 = 100;
434
435 #[test]
437 fn error_if_placement_is_wider_than_bin_section() {
438 let bin_section = bin_section_width_height_depth(5, 20, 1);
439 let placement = RectToInsert::new(6, 20, 1);
440
441 assert_eq!(
442 bin_section
443 .try_place(&placement, &contains_smallest_box, &volume_heuristic)
444 .unwrap_err(),
445 BinSectionError::PlacementWiderThanBinSection
446 );
447 }
448
449 #[test]
451 fn error_if_placement_is_taller_than_bin_section() {
452 let bin_section = bin_section_width_height_depth(5, 20, 1);
453 let placement = RectToInsert::new(5, 21, 1);
454
455 assert_eq!(
456 bin_section
457 .try_place(&placement, &contains_smallest_box, &volume_heuristic)
458 .unwrap_err(),
459 BinSectionError::PlacementTallerThanBinSection
460 );
461 }
462
463 #[test]
465 fn error_if_placement_is_deeper_than_bin_section() {
466 let bin_section = bin_section_width_height_depth(5, 20, 1);
467 let placement = RectToInsert::new(5, 20, 2);
468
469 assert_eq!(
470 bin_section
471 .try_place(&placement, &contains_smallest_box, &volume_heuristic)
472 .unwrap_err(),
473 BinSectionError::PlacementDeeperThanBinSection
474 );
475 }
476
477 fn test_splits(
478 container_dimensions: u32,
479 rect_to_place: WidthHeightDepth,
480 mut expected: [BinSection; 3],
481 ) {
482 let dim = container_dimensions;
483 let bin_section = bin_section_width_height_depth(dim, dim, dim);
484
485 let whd = rect_to_place;
486
487 let placement = RectToInsert::new(whd.width, whd.height, whd.depth);
488
489 let mut packed = bin_section
490 .try_place(&placement, &contains_smallest_box, &volume_heuristic)
491 .unwrap();
492
493 packed.1.sort();
494 expected.sort();
495
496 assert_eq!(packed.1, expected);
497 }
498
499 #[test]
501 fn width_largest_height_second_largest_depth_smallest() {
502 let whd = WidthHeightDepth {
503 width: BIGGEST,
504 height: MIDDLE,
505 depth: SMALLEST,
506 };
507
508 test_splits(
509 FULL,
510 whd,
511 [
512 BinSection::new_spread(whd.width, 0, 0, FULL - whd.width, whd.height, whd.depth),
513 BinSection::new_spread(0, whd.height, 0, FULL, FULL - whd.height, whd.depth),
514 BinSection::new_spread(0, 0, whd.depth, FULL, FULL, FULL - whd.depth),
515 ],
516 );
517 }
518
519 #[test]
521 fn width_largest_depth_second_largest_height_smallest() {
522 let whd = WidthHeightDepth {
523 width: BIGGEST,
524 height: SMALLEST,
525 depth: MIDDLE,
526 };
527
528 test_splits(
529 FULL,
530 whd,
531 [
532 BinSection::new_spread(whd.width, 0, 0, FULL - whd.width, whd.height, whd.depth),
533 BinSection::new_spread(0, whd.height, 0, FULL, FULL - whd.height, FULL),
534 BinSection::new_spread(0, 0, whd.depth, FULL, whd.height, FULL - whd.depth),
535 ],
536 );
537 }
538
539 #[test]
541 fn height_largest_width_second_largest_depth_smallest() {
542 let whd = WidthHeightDepth {
543 width: MIDDLE,
544 height: BIGGEST,
545 depth: SMALLEST,
546 };
547
548 test_splits(
549 FULL,
550 whd,
551 [
552 BinSection::new_spread(whd.width, 0, 0, FULL - whd.width, FULL, whd.depth),
553 BinSection::new_spread(0, whd.height, 0, whd.width, FULL - whd.height, whd.depth),
554 BinSection::new_spread(0, 0, whd.depth, FULL, FULL, FULL - whd.depth),
555 ],
556 );
557 }
558
559 #[test]
561 fn height_largest_depth_second_largest_width_smallest() {
562 let whd = WidthHeightDepth {
563 width: SMALLEST,
564 height: BIGGEST,
565 depth: MIDDLE,
566 };
567
568 test_splits(
569 FULL,
570 whd,
571 [
572 BinSection::new_spread(whd.width, 0, 0, FULL - whd.width, FULL, FULL),
573 BinSection::new_spread(0, whd.height, 0, whd.width, FULL - whd.height, whd.depth),
574 BinSection::new_spread(0, 0, whd.depth, whd.width, FULL, FULL - whd.depth),
575 ],
576 );
577 }
578
579 #[test]
581 fn depth_largest_width_second_largest_height_smallest() {
582 let whd = WidthHeightDepth {
583 width: MIDDLE,
584 height: SMALLEST,
585 depth: BIGGEST,
586 };
587
588 test_splits(
589 FULL,
590 whd,
591 [
592 BinSection::new_spread(whd.width, 0, 0, FULL - whd.width, whd.height, FULL),
593 BinSection::new_spread(0, whd.height, 0, FULL, FULL - whd.height, FULL),
594 BinSection::new_spread(0, 0, whd.depth, whd.width, whd.height, FULL - whd.depth),
595 ],
596 );
597 }
598
599 #[test]
601 fn depth_largest_height_second_largest_width_smallest() {
602 let whd = WidthHeightDepth {
603 width: SMALLEST,
604 height: MIDDLE,
605 depth: BIGGEST,
606 };
607
608 test_splits(
609 FULL,
610 whd,
611 [
612 BinSection::new_spread(whd.width, 0, 0, FULL - whd.width, FULL, FULL),
613 BinSection::new_spread(0, whd.height, 0, whd.width, FULL - whd.height, FULL),
614 BinSection::new_spread(0, 0, whd.depth, whd.width, whd.height, FULL - whd.depth),
615 ],
616 );
617 }
618
619 fn bin_section_width_height_depth(width: u32, height: u32, depth: u32) -> BinSection {
625 BinSection::new(
626 0,
627 0,
628 0,
629 WidthHeightDepth {
630 width,
631 height,
632 depth,
633 },
634 )
635 }
636}