rectangle_pack/target_bin/push_available_bin_section.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
//! Methods for adding a BinSection back into a TargetBin.
//!
//! Useful in an application that needs to be able to remove packed rectangles from bins.
//! After which the [`TargetBin.coalesce`] method can be used to combine smaller adjacent sections
//! into larger sections.
#![allow(missing_docs)]
use crate::bin_section::BinSection;
use crate::TargetBin;
use core::fmt::{Display, Formatter, Result as FmtResult};
impl TargetBin {
/// Push a [`BinSection`] to the list of remaining [`BinSection`]'s that rectangles can be
/// placed in.
///
/// ## Performance
///
/// This checks that your [`BinSection`] does not overlap any other bin sections. In many
/// cases this will be negligible, however it is important to note that this has a worst case
/// time complexity of `O(Width * Height * Depth)`, where the worst case is tht you have a bin
/// full of `1x1x1` rectangles.
///
/// To skip the validity checks use [`TargetBin.push_available_bin_section_unchecked`].
///
/// [`TargetBin.push_available_bin_section_unchecked`]: #method.push_available_bin_section_unchecked
pub fn push_available_bin_section(
&mut self,
bin_section: BinSection,
) -> Result<(), PushBinSectionError> {
if bin_section.x >= self.max_width
|| bin_section.y >= self.max_height
|| bin_section.z >= self.max_depth
{
return Err(PushBinSectionError::OutOfBounds(bin_section));
}
for available in self.available_bin_sections.iter() {
if available.overlaps(&bin_section) {
return Err(PushBinSectionError::Overlaps {
remaining_section: *available,
new_section: bin_section,
});
}
}
self.push_available_bin_section_unchecked(bin_section);
Ok(())
}
/// Push a [`BinSection`] to the list of remaining [`BinSection`]'s that rectangles can be
/// placed in, without checking whether or not it is valid.
///
/// Use [`TargetBin.push_available_bin_section`] if you want to check that the new bin section
/// does not overlap any existing bin sections nad that it is within the [`TargetBin`]'s bounds.
///
/// [`TargetBin.push_available_bin_section`]: #method.push_available_bin_section
pub fn push_available_bin_section_unchecked(&mut self, bin_section: BinSection) {
self.available_bin_sections.push(bin_section);
}
}
/// An error while attempting to push a [`BinSection`] into the remaining bin sections of a
/// [`TargetBin`].
#[derive(Debug)]
pub enum PushBinSectionError {
/// Attempted to push a [`BinSection`] that is not fully contained by the bin.
OutOfBounds(BinSection),
/// Attempted to push a [`BinSection`] that overlaps another empty bin section.
Overlaps {
/// The section that is already stored as empty within the [`TargetBin`];
remaining_section: BinSection,
/// The section that you were trying to add to the [`TargetBin`];
new_section: BinSection,
},
}
impl Display for PushBinSectionError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
PushBinSectionError::OutOfBounds(oob) => {
f.debug_tuple("BinSection").field(oob).finish()
}
PushBinSectionError::Overlaps {
remaining_section,
new_section,
} => f
.debug_struct("Overlaps")
.field("remaining_section", remaining_section)
.field("new_section", new_section)
.finish(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::width_height_depth::WidthHeightDepth;
/// Verify that if the bin section that we are pushing is outside of the TargetBin's bounds we
/// return an error.
#[test]
fn error_if_bin_section_out_of_bounds() {
let mut bin = empty_bin();
let out_of_bounds = BinSection::new(101, 0, 0, WidthHeightDepth::new(1, 1, 1));
match bin.push_available_bin_section(out_of_bounds).err().unwrap() {
PushBinSectionError::OutOfBounds(err_bin_section) => {
assert_eq!(err_bin_section, out_of_bounds)
}
_ => panic!(),
};
}
/// Verify that if the bin section that we are pushing overlaps another bin section we return
/// an error.
#[test]
fn error_if_bin_section_overlaps_another_remaining_section() {
let mut bin = empty_bin();
let overlaps = BinSection::new(0, 0, 0, WidthHeightDepth::new(1, 1, 1));
match bin.push_available_bin_section(overlaps).err().unwrap() {
PushBinSectionError::Overlaps {
remaining_section: err_remaining_section,
new_section: err_new_section,
} => {
assert_eq!(err_new_section, overlaps);
assert_eq!(
err_remaining_section,
BinSection::new(0, 0, 0, WidthHeightDepth::new(100, 100, 1))
);
}
_ => panic!(),
}
}
/// Verify that we can push a valid bin section.
#[test]
fn push_bin_section() {
let mut bin = full_bin();
let valid_section = BinSection::new(1, 2, 0, WidthHeightDepth::new(1, 1, 1));
assert_eq!(bin.available_bin_sections.len(), 0);
bin.push_available_bin_section(valid_section).unwrap();
assert_eq!(bin.available_bin_sections.len(), 1);
assert_eq!(bin.available_bin_sections[0], valid_section);
}
fn empty_bin() -> TargetBin {
TargetBin::new(100, 100, 1)
}
fn full_bin() -> TargetBin {
let mut bin = TargetBin::new(100, 100, 1);
bin.available_bin_sections.clear();
bin
}
}