|
@ -0,0 +1,195 @@ |
|
|
|
|
|
use std::cmp::max;
|
|
|
|
|
|
|
|
|
|
|
|
use ::texture::Buffer2d;
|
|
|
|
|
|
use ::geometry::Rect;
|
|
|
|
|
|
use super::Packer;
|
|
|
|
|
|
|
|
|
|
|
|
struct Skyline {
|
|
|
|
|
|
pub x: usize,
|
|
|
|
|
|
pub y: usize,
|
|
|
|
|
|
pub w: usize,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub struct SkylinePacker<B> where B: Buffer2d {
|
|
|
|
|
|
buf: B,
|
|
|
|
|
|
width: usize,
|
|
|
|
|
|
height: usize,
|
|
|
|
|
|
skylines: Vec<Skyline>,
|
|
|
|
|
|
margin: usize,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<B> SkylinePacker<B> where B: Buffer2d {
|
|
|
|
|
|
fn find_skyline(&self, w: usize, h: usize) -> Option<(usize, Rect<usize>)> {
|
|
|
|
|
|
let mut min_height = ::std::usize::MAX;
|
|
|
|
|
|
let mut min_width = ::std::usize::MAX;
|
|
|
|
|
|
let mut index = None;
|
|
|
|
|
|
let mut rect: Rect<usize> = Rect::new(0, 0, 0, 0);
|
|
|
|
|
|
|
|
|
|
|
|
// Note: Try to keep the min_height as small as possible
|
|
|
|
|
|
for i in 0..self.skylines.len() {
|
|
|
|
|
|
if let Some(y) = self.can_place(i, w, h) {
|
|
|
|
|
|
if y + h < min_height ||
|
|
|
|
|
|
(y + h == min_height && self.skylines[i].w < min_width) {
|
|
|
|
|
|
min_height = y+h;
|
|
|
|
|
|
min_width = self.skylines[i].w;
|
|
|
|
|
|
index = Some(i);
|
|
|
|
|
|
rect.x = self.skylines[i].x;
|
|
|
|
|
|
rect.y = y;
|
|
|
|
|
|
rect.w = w;
|
|
|
|
|
|
rect.h = h;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(i) = index {
|
|
|
|
|
|
Some((i, rect))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn can_place(&self, i: usize, w: usize, h: usize) -> Option<usize> {
|
|
|
|
|
|
let x = self.skylines[i].x;
|
|
|
|
|
|
if x + w > self.width {
|
|
|
|
|
|
return None;
|
|
|
|
|
|
}
|
|
|
|
|
|
let mut width_left = w;
|
|
|
|
|
|
let mut i = i;
|
|
|
|
|
|
let mut y = self.skylines[i].y;
|
|
|
|
|
|
loop {
|
|
|
|
|
|
y = max(y, self.skylines[i].y);
|
|
|
|
|
|
if y+h > self.height {
|
|
|
|
|
|
return None;
|
|
|
|
|
|
}
|
|
|
|
|
|
if self.skylines[i].w > width_left {
|
|
|
|
|
|
return Some(y);
|
|
|
|
|
|
}
|
|
|
|
|
|
width_left -= self.skylines[i].w;
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
if i >= self.skylines.len() {
|
|
|
|
|
|
return None;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn split(&mut self, index: usize, rect: &Rect<usize>) {
|
|
|
|
|
|
let skyline = Skyline {
|
|
|
|
|
|
x: rect.x,
|
|
|
|
|
|
y: rect.y + rect.h,
|
|
|
|
|
|
w: rect.w
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
assert!(skyline.x + skyline.w <= self.width);
|
|
|
|
|
|
assert!(skyline.y <= self.height);
|
|
|
|
|
|
|
|
|
|
|
|
self.skylines.insert(index, skyline);
|
|
|
|
|
|
let i = index + 1;
|
|
|
|
|
|
while i < self.skylines.len() {
|
|
|
|
|
|
assert!(self.skylines[i-1].x <= self.skylines[i].x);
|
|
|
|
|
|
if self.skylines[i].x < self.skylines[i-1].x + self.skylines[i-1].w {
|
|
|
|
|
|
let shrink = self.skylines[i-1].x + self.skylines[i-1].w - self.skylines[i].x;
|
|
|
|
|
|
if self.skylines[i].w <= shrink {
|
|
|
|
|
|
self.skylines.remove(i);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
self.skylines[i].x += shrink;
|
|
|
|
|
|
self.skylines[i].w -= shrink;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Merge skylines with the same y value.
|
|
|
|
|
|
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].w += self.skylines[i].w;
|
|
|
|
|
|
self.skylines.remove(i);
|
|
|
|
|
|
i -= 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<B> Packer for SkylinePacker<B> where B: Buffer2d {
|
|
|
|
|
|
type Buffer = B;
|
|
|
|
|
|
|
|
|
|
|
|
fn dimensions(&self) -> (usize, usize) {(self.width, self.height)}
|
|
|
|
|
|
fn set_dimensions(&mut self, w: usize, h: usize) {
|
|
|
|
|
|
let (old_w, _) = self.dimensions();
|
|
|
|
|
|
|
|
|
|
|
|
self.width = w;
|
|
|
|
|
|
self.height = h;
|
|
|
|
|
|
|
|
|
|
|
|
// create a "ceiling" which fills in the skyline data structure
|
|
|
|
|
|
// with a line at y=0 from the old width to the new width.
|
|
|
|
|
|
self.skylines.push(Skyline {
|
|
|
|
|
|
x: old_w, y: 0,
|
|
|
|
|
|
w: w-old_w,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn set_margin(&mut self, margin: usize) {
|
|
|
|
|
|
self.margin = margin;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn new(buf: B) -> SkylinePacker<B> {
|
|
|
|
|
|
let (w, h) = buf.dimensions();
|
|
|
|
|
|
let mut skylines = Vec::new();
|
|
|
|
|
|
|
|
|
|
|
|
// Fill with a single skyline.
|
|
|
|
|
|
skylines.push(Skyline {
|
|
|
|
|
|
x: 0, y: 0,
|
|
|
|
|
|
w: w
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
SkylinePacker {
|
|
|
|
|
|
buf: buf,
|
|
|
|
|
|
width: w, height: h,
|
|
|
|
|
|
skylines: skylines,
|
|
|
|
|
|
margin: 0,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn pack<O>(&mut self, buf: &O) -> Option<Rect<usize>>
|
|
|
|
|
|
where O: Buffer2d<Pixel=B::Pixel>
|
|
|
|
|
|
{
|
|
|
|
|
|
let (mut w, mut h) = buf.dimensions();
|
|
|
|
|
|
w += self.margin;
|
|
|
|
|
|
h += self.margin;
|
|
|
|
|
|
|
|
|
|
|
|
if let Some((i, mut rect)) = self.find_skyline(w, h) {
|
|
|
|
|
|
if w == rect.w {
|
|
|
|
|
|
self.buf.patch(rect.x, rect.y, buf);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
self.buf.patch_rotated(rect.x, rect.y, buf);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
self.split(i, &rect);
|
|
|
|
|
|
self.merge();
|
|
|
|
|
|
|
|
|
|
|
|
rect.w -= self.margin;
|
|
|
|
|
|
rect.h -= self.margin;
|
|
|
|
|
|
|
|
|
|
|
|
Some(rect)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn buf(&self) -> &B {
|
|
|
|
|
|
&self.buf
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn buf_mut(&mut self) -> &mut B {
|
|
|
|
|
|
&mut self.buf
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn into_buf(self) -> B {
|
|
|
|
|
|
self.buf
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|