use guillotiere::{AtlasAllocator, size2};
pub const SIDE: u32 = 8192;
const START: i32 = 1024;
pub fn place(images: &[(u32, u32)]) -> Result<Vec<(u32, u32)>, usize> {
let mut side = START;
let mut atlas = AtlasAllocator::new(size2(side, side));
let mut at = Vec::with_capacity(images.len());
let corner = |a: guillotiere::Allocation| (a.rectangle.min.x as u32, a.rectangle.min.y as u32);
while at.len() < images.len() {
let (w, h) = images[at.len()];
if let Some(a) = atlas.allocate(size2(w as i32, h as i32)) {
at.push(corner(a));
continue;
}
loop {
side *= 2;
if side > SIDE as i32 {
return Err(at.len());
}
let mut bigger = AtlasAllocator::new(size2(side, side));
let repacked: Option<Vec<_>> = images[..at.len()]
.iter()
.map(|&(w, h)| bigger.allocate(size2(w as i32, h as i32)).map(corner))
.collect();
if let Some(repacked) = repacked {
atlas = bigger;
at = repacked;
break;
}
}
}
Ok(at)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn four_quarters_fill_the_atlas_and_a_fifth_does_not_fit() {
assert_eq!(
place(&[(4096, 4096); 4]),
Ok(vec![(0, 0), (0, 4096), (4096, 0), (4096, 4096)])
);
assert_eq!(place(&[(4096, 4096); 5]), Err(4));
assert_eq!(place(&[(8192, 8192)]), Ok(vec![(0, 0)]));
assert_eq!(place(&[(8193, 1)]), Err(0));
assert!(place(&[(2000, 2000); 16]).is_ok());
assert_eq!(place(&[]), Ok(vec![]));
}
}