use crate::{Number, Rect};
use num_traits::Zero;
#[derive(Clone)]
struct RectPackNode<T>
where
T: Number,
{
filled: bool,
split: bool,
bounds: Rect<T>,
left: usize,
right: usize,
}
impl<T> RectPackNode<T>
where
T: Number,
{
fn new(bounds: Rect<T>) -> Self {
Self {
bounds,
filled: false,
split: false,
left: usize::MAX,
right: usize::MAX,
}
}
}
#[derive(Clone)]
pub struct RectPacker<T>
where
T: Number,
{
nodes: Vec<RectPackNode<T>>,
root: usize,
width: T,
height: T,
unvisited: Vec<usize>,
}
impl<T> RectPacker<T>
where
T: Number,
{
pub fn new(w: T, h: T) -> Self {
Self {
nodes: vec![RectPackNode::new(Rect::new(
Zero::zero(),
Zero::zero(),
w,
h,
))],
root: 0,
width: w,
height: h,
unvisited: Default::default(),
}
}
pub fn clear(&mut self) {
self.nodes.clear();
self.unvisited.clear();
self.nodes.push(RectPackNode::new(Rect::new(
Zero::zero(),
Zero::zero(),
self.width,
self.height,
)));
self.root = 0;
}
pub fn find_free(&mut self, w: T, h: T) -> Option<Rect<T>> {
if self.unvisited.is_empty() {
self.unvisited.push(self.root);
}
while let Some(node_index) = self.unvisited.pop() {
let node = &mut self.nodes[node_index];
if node.split {
self.unvisited.push(node.right);
self.unvisited.push(node.left);
} else if !node.filled && node.bounds.w() >= w && node.bounds.h() >= h {
if node.bounds.w() == w && node.bounds.h() == h {
node.filled = true;
return Some(node.bounds);
}
node.split = true;
let (left_bounds, right_bounds) = if node.bounds.w() - w > node.bounds.h() - h {
(
Rect::new(node.bounds.x(), node.bounds.y(), w, node.bounds.h()),
Rect::new(
node.bounds.x() + w,
node.bounds.y(),
node.bounds.w() - w,
node.bounds.h(),
),
)
} else {
(
Rect::new(node.bounds.x(), node.bounds.y(), node.bounds.w(), h),
Rect::new(
node.bounds.x(),
node.bounds.y() + h,
node.bounds.w(),
node.bounds.h() - h,
),
)
};
let left = self.nodes.len();
self.nodes.push(RectPackNode::new(left_bounds));
let right = self.nodes.len();
self.nodes.push(RectPackNode::new(right_bounds));
let node = &mut self.nodes[node_index];
node.left = left;
node.right = right;
self.unvisited.push(left);
}
}
None
}
}
#[cfg(test)]
mod test {
use super::{RectPackNode, RectPacker};
use crate::Rect;
#[test]
fn rect_pack_node_new() {
let rect = Rect::new(0.0, 0.0, 1.0, 1.0);
let node = RectPackNode::new(rect);
assert!(!node.filled);
assert!(!node.split);
assert_eq!(node.bounds, rect);
assert_eq!(node.left, usize::MAX);
assert_eq!(node.right, usize::MAX);
}
#[test]
fn rect_packer_new() {
let rp = RectPacker::new(1.0, 1.0);
assert_eq!(rp.width, 1.0);
assert_eq!(rp.height, 1.0);
assert_eq!(rp.unvisited, vec![]);
}
#[test]
fn rect_packer_find_free() {
let mut rp = RectPacker::new(10.0, 10.0);
assert_eq!(rp.find_free(20.0, 20.0), None);
assert_eq!(rp.find_free(1.0, 1.0), Some(Rect::new(0.0, 0.0, 1.0, 1.0)));
assert_eq!(rp.find_free(9.0, 9.0), Some(Rect::new(0.0, 1.0, 9.0, 9.0)));
}
#[test]
fn rect_packer_clear() {
let mut rp = RectPacker::new(10.0, 10.0);
rp.find_free(1.0, 1.0);
rp.find_free(9.0, 9.0);
assert_eq!(rp.nodes.len(), 7);
rp.clear();
assert_eq!(rp.nodes.len(), 1);
}
}