extern crate rayon;
use rayon::prelude::*;
use crate::bucket::Bucket;
use crate::packing_box::PackingBox;
pub struct MaxRects {
pub boxes: Vec<PackingBox>,
pub bins: Vec<Bucket>,
}
impl MaxRects {
pub fn new(boxes: Vec<PackingBox>, bins: Vec<Bucket>) -> Self {
Self { boxes, bins }
}
pub fn place(&mut self) -> (Vec<PackingBox>, Vec<PackingBox>, Vec<Bucket>) {
let mut nochange = false;
let mut placed = vec![];
while !nochange {
nochange = true;
let search_result: Option<(i32, usize, usize)> = self
.boxes
.par_iter()
.enumerate()
.filter_map(|(i, box_item)| {
self.bins
.iter()
.enumerate()
.filter_map(|(j, rect)| {
if box_item.width <= rect.width && box_item.height <= rect.height {
let diff = i32::min(
rect.width - box_item.width,
rect.height - box_item.height,
);
Some((diff, i, j))
} else {
None
}
})
.min_by(|a, b| a.0.partial_cmp(&b.0).unwrap())
})
.min_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
if let Some((_, box_idx, idx)) = search_result {
nochange = false;
let mut box_item = self.boxes.remove(box_idx);
let bin_item = self.bins.remove(idx);
let adjacent = Bucket {
width: bin_item.width - box_item.width,
height: bin_item.height,
originx: bin_item.originx + box_item.width,
originy: bin_item.originy,
bucketid: bin_item.bucketid,
};
let above = Bucket {
width: bin_item.width,
height: bin_item.height - box_item.height,
originx: bin_item.originx,
originy: bin_item.originy,
bucketid: bin_item.bucketid,
};
box_item.place(
bin_item.originx,
bin_item.height - box_item.height + bin_item.originy,
bin_item.bucketid,
);
placed.push(box_item.clone());
if adjacent.area() > 0 {
self.bins.push(adjacent);
}
if above.area() > 0 {
self.bins.push(above);
}
let mut overlaps = vec![false; self.bins.len()];
let mut new_buckets = Vec::new();
for (idx, rect) in self.bins.iter().enumerate() {
if placed.last().unwrap().overlap(rect) {
overlaps[idx] = true;
let left = Bucket::new(
placed.last().unwrap().originx.unwrap() - rect.originx,
rect.height,
rect.originx,
rect.originy,
rect.bucketid,
);
let above = Bucket::new(
rect.width,
placed.last().unwrap().originy.unwrap() - rect.originy,
rect.originx,
rect.originy,
rect.bucketid,
);
let right = Bucket::new(
rect.get_coords().1 - placed.last().unwrap().get_coords().1,
rect.height,
placed.last().unwrap().get_coords().1,
rect.originy,
rect.bucketid,
);
let below = Bucket::new(
rect.width,
rect.get_coords().3 - placed.last().unwrap().get_coords().3,
rect.originx,
placed.last().unwrap().get_coords().3,
rect.bucketid,
);
for new_bucket in [left, above, right, below] {
if new_bucket.area() > 0 {
new_buckets.push(new_bucket);
overlaps.push(false);
}
}
}
}
self.bins.extend(new_buckets);
let filtered_bins: Vec<Bucket> = self
.bins
.iter()
.zip(&overlaps)
.filter_map(|(item, &keep)| if !keep { Some(item) } else { None })
.cloned()
.collect();
self.bins = filtered_bins;
let mut overlaps = vec![false; self.bins.len()];
for (i, bin1) in self.bins.iter().enumerate() {
for (j, bin2) in self.bins.iter().enumerate() {
if i != j && bin1.contains(bin2) {
overlaps[j] = true;
}
}
}
let new_bins: Vec<Bucket> = self
.bins
.clone()
.into_iter()
.enumerate()
.filter(|(idx, _)| !overlaps[*idx])
.map(|(_, bin)| bin)
.collect();
self.bins = new_bins;
}
}
(placed, self.boxes.clone(), self.bins.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bucket::Bucket;
use crate::packing_box::PackingBox;
#[test]
fn test_new() {
let boxes = vec![PackingBox::new(5, 6)];
let bins = vec![Bucket::new(10, 20, 0, 0, 1)];
let max_rects = MaxRects::new(boxes, bins);
assert_eq!(max_rects.boxes.len(), 1);
assert_eq!(max_rects.bins.len(), 1);
}
#[test]
fn test_place_single_box() {
let boxes = vec![PackingBox::new(5, 6)];
let bins = vec![Bucket::new(10, 20, 0, 0, 1)];
let mut max_rects = MaxRects::new(boxes, bins);
let (placed, remaining, updated_bins) = max_rects.place();
assert_eq!(placed.len(), 1);
assert!(remaining.is_empty());
assert_eq!(updated_bins.len(), 2); assert_eq!(placed[0].get_coords(), (0, 5, 14, 20)); assert_eq!(updated_bins[0].get_coords(), (5, 10, 0, 20)); assert_eq!(updated_bins[1].get_coords(), (0, 10, 0, 14)); }
#[test]
fn test_place_multiple_boxes() {
let boxes = vec![PackingBox::new(5, 6), PackingBox::new(4, 4)];
let bins = vec![Bucket::new(10, 20, 0, 0, 1)];
let mut max_rects = MaxRects::new(boxes, bins);
let (placed, remaining, updated_bins) = max_rects.place();
assert_eq!(placed.len(), 2);
assert!(remaining.is_empty());
assert_eq!(updated_bins.len(), 3); assert_eq!(placed[0].get_coords(), (0, 5, 14, 20)); assert_eq!(placed[1].get_coords(), (5, 9, 16, 20));
assert_eq!(updated_bins[0].get_coords(), (0, 10, 0, 14)); assert_eq!(updated_bins[1].get_coords(), (9, 10, 0, 20)); assert_eq!(updated_bins[2].get_coords(), (5, 10, 0, 16)); }
#[test]
fn test_place_no_fit() {
let boxes = vec![PackingBox::new(15, 16)]; let bins = vec![Bucket::new(10, 20, 0, 0, 1)];
let mut max_rects = MaxRects::new(boxes, bins);
let (placed, remaining, updated_bins) = max_rects.place();
assert!(placed.is_empty());
assert_eq!(remaining.len(), 1);
assert_eq!(updated_bins.len(), 1); }
}