use crate::*;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct Config {
pub width: i32,
pub height: i32,
pub border_padding: i32,
pub rectangle_padding: i32,
}
pub trait RectTrait {
fn top(&self) -> i32;
fn bottom(&self) -> i32;
fn left(&self) -> i32;
fn right(&self) -> i32;
fn area(&self) -> i32 {
(self.bottom() - self.top()) * (self.right() - self.left())
}
fn intersects(&self, other: &Self) -> bool {
self.contains_point(other.left(), other.top())
|| self.contains_point(other.left(), other.bottom() - 1)
|| self.contains_point(other.right() - 1, other.bottom() - 1)
|| self.contains_point(other.right() - 1, other.top())
|| other.contains_point(self.left(), self.top())
|| other.contains_point(self.left(), self.bottom() - 1)
|| other.contains_point(self.right() - 1, self.bottom() - 1)
|| other.contains_point(self.right() - 1, self.top())
}
fn contains_rect(&self, other: &Self) -> bool {
self.left() <= other.left() && self.right() >= other.right() && self.top() <= other.top() && self.bottom() >= other.bottom()
}
fn contains_point(&self, x: i32, y: i32) -> bool {
self.left() <= x && x < self.right() && self.top() <= y && y < self.bottom()
}
}
impl RectTrait for Recti {
#[inline(always)]
fn top(&self) -> i32 {
self.y
}
#[inline(always)]
fn bottom(&self) -> i32 {
self.y + self.height
}
#[inline(always)]
fn left(&self) -> i32 {
self.x
}
#[inline(always)]
fn right(&self) -> i32 {
self.x + self.width
}
}
#[derive(Clone)]
pub struct Packer {
config: Config,
packer: DensePacker,
}
impl Packer {
pub fn new(config: Config) -> Packer {
let width = std::cmp::max(0, config.width + config.rectangle_padding - 2 * config.border_padding);
let height = std::cmp::max(0, config.height + config.rectangle_padding - 2 * config.border_padding);
Packer {
config: config,
packer: DensePacker::new(width, height),
}
}
pub fn config(&self) -> Config {
self.config
}
pub fn pack(&mut self, width: i32, height: i32, allow_rotation: bool) -> Option<Recti> {
if width <= 0 || height <= 0 {
return None;
}
if let Some(mut rect) = self
.packer
.pack(width + self.config.rectangle_padding, height + self.config.rectangle_padding, allow_rotation)
{
rect.width -= self.config.rectangle_padding;
rect.height -= self.config.rectangle_padding;
rect.x += self.config.border_padding;
rect.y += self.config.border_padding;
Some(rect)
} else {
None
}
}
pub fn can_pack(&self, width: i32, height: i32, allow_rotation: bool) -> bool {
self.packer
.can_pack(width + self.config.rectangle_padding, height + self.config.rectangle_padding, allow_rotation)
}
}
#[derive(Clone)]
struct Skyline {
pub left: i32,
pub y: i32,
pub width: i32,
}
impl Skyline {
#[inline(always)]
pub fn right(&self) -> i32 {
self.left + self.width
}
}
#[derive(Clone)]
pub struct DensePacker {
width: i32,
height: i32,
skylines: Vec<Skyline>,
}
impl DensePacker {
pub fn new(width: i32, height: i32) -> DensePacker {
let width = std::cmp::max(0, width);
let height = std::cmp::max(0, height);
let skylines = vec![Skyline { left: 0, y: 0, width: width }];
DensePacker {
width: width,
height: height,
skylines: skylines,
}
}
pub fn size(&self) -> (i32, i32) {
(self.width, self.height)
}
pub fn resize(&mut self, width: i32, height: i32) {
assert!(width >= self.width && height >= self.height);
self.width = width;
self.height = height;
let left = self.skylines.last().unwrap().right();
self.skylines.push(Skyline { left: left, y: 0, width: width - left });
}
pub fn pack(&mut self, width: i32, height: i32, allow_rotation: bool) -> Option<Recti> {
if width <= 0 || height <= 0 {
return None;
}
if let Some((i, rect)) = self.find_skyline(width, height, allow_rotation) {
self.split(i, &rect);
self.merge();
Some(rect)
} else {
None
}
}
pub fn can_pack(&self, width: i32, height: i32, allow_rotation: bool) -> bool {
self.find_skyline(width, height, allow_rotation).is_some()
}
fn can_put(&self, mut i: usize, w: i32, h: i32) -> Option<Recti> {
let mut rect = Rect::new(self.skylines[i].left, 0, w, h);
let mut width_left = rect.width;
loop {
rect.y = std::cmp::max(rect.y, self.skylines[i].y);
if !Rect::new(0, 0, self.width, self.height).contains_rect(&rect) {
return None;
}
if self.skylines[i].width >= width_left {
return Some(rect);
}
width_left -= self.skylines[i].width;
i += 1;
assert!(i < self.skylines.len());
}
}
fn find_skyline(&self, w: i32, h: i32, allow_rotation: bool) -> Option<(usize, Recti)> {
let mut bottom = std::i32::MAX;
let mut width = std::i32::MAX;
let mut index = None;
let mut rect = Rect::new(0, 0, 0, 0);
for i in 0..self.skylines.len() {
if let Some(r) = self.can_put(i, w, h) {
if r.bottom() < bottom || (r.bottom() == bottom && self.skylines[i].width < width) {
bottom = r.bottom();
width = self.skylines[i].width;
index = Some(i);
rect = r;
}
}
if allow_rotation {
if let Some(r) = self.can_put(i, h, w) {
if r.bottom() < bottom || (r.bottom() == bottom && self.skylines[i].width < width) {
bottom = r.bottom();
width = self.skylines[i].width;
index = Some(i);
rect = r;
}
}
}
}
if let Some(index) = index { Some((index, rect)) } else { None }
}
fn split(&mut self, i: usize, rect: &Recti) {
let skyline = Skyline {
left: rect.left(),
y: rect.bottom(),
width: rect.width,
};
assert!(skyline.right() <= self.width);
assert!(skyline.y <= self.height);
self.skylines.insert(i, skyline);
while i + 1 < self.skylines.len() {
assert!(self.skylines[i].left <= self.skylines[i + 1].left);
if self.skylines[i + 1].left >= self.skylines[i].right() {
break;
}
let shrink = self.skylines[i].right() - self.skylines[i + 1].left;
if self.skylines[i + 1].width <= shrink {
self.skylines.remove(i + 1);
} else {
self.skylines[i + 1].left += shrink;
self.skylines[i + 1].width -= shrink;
break;
}
}
}
fn merge(&mut self) {
let mut i = 1;
while i < self.skylines.len() {
if self.skylines[i - 1].y == self.skylines[i].y {
self.skylines[i - 1].width += self.skylines[i].width;
self.skylines.remove(i);
} else {
i += 1;
}
}
}
}