| @ -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 | |||||
| } | |||||
| } | |||||