// Copyright 2017 The Servo Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use euclid::{Point2D, Rect, Size2D}; pub struct Atlas { free_rects: Vec>, available_width: u32, shelf_height: u32, shelf_count: u32, /// The amount of horizontal space allocated in the last shelf. width_of_last_shelf: u32, } impl Atlas { #[inline] pub fn new(available_width: u32, shelf_height: u32) -> Atlas { Atlas { free_rects: vec![], available_width: available_width, shelf_height: shelf_height, shelf_count: 0, width_of_last_shelf: 0, } } pub fn place(&mut self, size: &Size2D) -> Result, ()> { let chosen_index_and_rect = self.free_rects .iter() .enumerate() .filter(|&(_, rect)| { size.width <= rect.size.width && size.height <= rect.size.height }) .min_by(|&(_, a), &(_, b)| area(a).cmp(&area(b))) .map(|(index, rect)| (index, *rect)); let chosen_rect; match chosen_index_and_rect { None => { // Make a new shelf. chosen_rect = Rect::new(Point2D::new(0, self.shelf_height * self.shelf_count), Size2D::new(self.available_width, self.shelf_height)); self.shelf_count += 1; self.width_of_last_shelf = 0 } Some((index, rect)) => { self.free_rects.swap_remove(index); chosen_rect = rect; } } // Guillotine to bottom. let free_below = Rect::new(Point2D::new(chosen_rect.origin.x, chosen_rect.origin.y + size.height), Size2D::new(size.width, chosen_rect.size.height - size.height)); if !free_below.is_empty() { self.free_rects.push(free_below); } // Guillotine to right. let free_to_right = Rect::new(Point2D::new(chosen_rect.origin.x + size.width, chosen_rect.origin.y), Size2D::new(chosen_rect.size.width - size.width, chosen_rect.size.height)); if !free_to_right.is_empty() { self.free_rects.push(free_to_right); } // Update width of last shelf if necessary. let on_last_shelf = chosen_rect.max_y() >= self.shelf_height * (self.shelf_count - 1); if on_last_shelf && self.width_of_last_shelf < chosen_rect.max_x() { self.width_of_last_shelf = chosen_rect.max_x() } Ok(chosen_rect.origin) } #[inline] pub fn available_width(&self) -> u32 { self.available_width } #[inline] pub fn shelf_height(&self) -> u32 { self.shelf_height } #[inline] pub fn shelf_columns(&self) -> u32 { let full_shelf_count = if self.shelf_count == 0 { 0 } else { self.shelf_count - 1 }; full_shelf_count * self.available_width + self.width_of_last_shelf } } #[inline] fn area(rect: &Rect) -> u32 { rect.size.width * rect.size.height }