Implement the atlas
This commit is contained in:
parent
34cd1ef9b0
commit
ce811c0e48
|
@ -9,3 +9,6 @@ byteorder = "1"
|
||||||
euclid = "0.10"
|
euclid = "0.10"
|
||||||
memmap = "0.5"
|
memmap = "0.5"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
quickcheck = "0.4"
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
extern crate memmap;
|
extern crate memmap;
|
||||||
extern crate pathfinder;
|
extern crate pathfinder;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
// 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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
|
|
||||||
|
/// TODO(pcwalton): Track width of last shelf.
|
||||||
|
pub struct Atlas {
|
||||||
|
free_rects: Vec<Rect<u32>>,
|
||||||
|
available_width: u32,
|
||||||
|
shelf_height: u32,
|
||||||
|
shelf_count: 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn place(&mut self, size: &Size2D<u32>) -> Result<Point2D<u32>, ()> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
|
fn area(rect: &Rect<u32>) -> u32 {
|
||||||
|
rect.size.width * rect.size.height
|
||||||
|
}
|
||||||
|
|
|
@ -8,12 +8,21 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![feature(iter_min_by)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate bitflags;
|
extern crate bitflags;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate euclid;
|
extern crate euclid;
|
||||||
|
#[cfg(test)]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate quickcheck;
|
||||||
|
|
||||||
|
pub mod atlas;
|
||||||
pub mod batch;
|
pub mod batch;
|
||||||
pub mod otf;
|
pub mod otf;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
use atlas::Atlas;
|
||||||
|
use euclid::{Rect, Size2D};
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
fn place_objects(available_width: u32, objects: Vec<(u32, u32)>) -> (Atlas, Vec<Rect<u32>>) {
|
||||||
|
let objects: Vec<_> = objects.iter()
|
||||||
|
.map(|&(width, height)| Size2D::new(width, height))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let available_width = cmp::max(available_width,
|
||||||
|
objects.iter().map(|object| object.width).max().unwrap_or(0));
|
||||||
|
let shelf_height = objects.iter().map(|object| object.height).max().unwrap_or(0);
|
||||||
|
|
||||||
|
let mut atlas = Atlas::new(available_width, shelf_height);
|
||||||
|
let rects = objects.iter()
|
||||||
|
.map(|object| Rect::new(atlas.place(object).unwrap(), *object))
|
||||||
|
.collect();
|
||||||
|
(atlas, rects)
|
||||||
|
}
|
||||||
|
|
||||||
|
quickcheck! {
|
||||||
|
fn objects_dont_overlap(available_width: u32, objects: Vec<(u32, u32)>) -> bool {
|
||||||
|
let (_, rects) = place_objects(available_width, objects);
|
||||||
|
for (i, a) in rects.iter().enumerate() {
|
||||||
|
for b in &rects[(i + 1)..] {
|
||||||
|
assert!(!a.intersects(b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn objects_dont_exceed_available_width(available_width: u32, objects: Vec<(u32, u32)>) -> bool {
|
||||||
|
let (atlas, rects) = place_objects(available_width, objects);
|
||||||
|
rects.iter().all(|rect| rect.max_x() <= atlas.available_width())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn objects_dont_cross_shelves(available_width: u32, objects: Vec<(u32, u32)>) -> bool {
|
||||||
|
let (atlas, rects) = place_objects(available_width, objects);
|
||||||
|
rects.iter().all(|rect| {
|
||||||
|
rect.is_empty() ||
|
||||||
|
rect.origin.y / atlas.shelf_height() == (rect.max_y() - 1) / atlas.shelf_height()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// 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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
mod atlas;
|
||||||
|
|
Loading…
Reference in New Issue