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