#[derive(Debug, Copy, Clone)]
pub struct Entry {
id: usize,
width: u32,
height: u32,
}
impl Entry {
pub fn new(id: usize, width: u32, height: u32) -> Entry {
Entry {
id: id,
width: width,
height: height,
}
}
}
#[derive(Debug)]
pub struct EntryWithPosition {
pub id: usize,
pub width: u32,
pub height: u32,
pub x: u32,
pub y: u32,
}
impl EntryWithPosition {
pub fn new_from_entry(entry: &Entry) -> EntryWithPosition {
EntryWithPosition {
id: entry.id,
width: entry.width,
height: entry.height,
x: 0,
y: 0,
}
}
pub fn set_position(&mut self, x: u32, y: u32) {
self.x = x;
self.y = y;
}
}
#[derive(Debug)]
struct Row {
y: u32, width: u32,
height: u32,
end_x: u32, }
impl Row {
fn new(y: u32, width: u32, height: u32) -> Row {
Row {
y: y,
width: width,
height: height,
end_x: 0,
}
}
fn would_fit(&self, w: u32, h: u32) -> bool {
if self.height >= h {
let available_space = self.width - self.end_x;
if available_space >= w {
true
} else {
false
}
} else {
false
}
}
}
#[derive(Debug, Default)]
pub struct Page {
size: u32,
pub entries: Vec<EntryWithPosition>,
rows: Vec<Row>,
used_height: u32,
}
impl Page {
pub fn new(size: u32, _border: u32) -> Page {
Page {
size: size,
..Default::default()
}
}
fn add_row(&mut self, height: u32) -> Option<usize> {
if height <= (self.size - self.used_height) {
let row = Row::new(self.used_height, self.size, height);
self.used_height += height;
let row_index = self.rows.len();
self.rows.push(row);
Some(row_index)
} else {
None
}
}
fn fit_entry_to_row_with_index(&mut self, entry: &Entry, row_index: usize) -> bool {
match self.rows.get_mut(row_index) {
None => false, Some(row) => {
if row.would_fit(entry.width, entry.height) {
let mut e = EntryWithPosition::new_from_entry(entry);
let x = row.end_x;
let y = row.y;
row.end_x += e.width;
e.set_position(x, y);
self.entries.push(e);
true
} else {
false
}
},
}
}
fn fit_entry(&mut self, entry: &Entry) -> bool {
let h = entry.height;
if self.size < entry.width || self.size < entry.height {
false
} else {
let mut candidates = Vec::new();
for ri in 0..self.rows.len() {
let r = &self.rows[ri];
if r.would_fit(entry.width, entry.height) {
if r.height < 2 * entry.height {
candidates.push(ri);
}
}
}
if candidates.len() > 0 {
let best_candidate_index = 0;
self.fit_entry_to_row_with_index(entry, candidates[best_candidate_index])
} else {
match self.add_row(h) {
None => false, Some(row_index) => self.fit_entry_to_row_with_index(entry, row_index),
}
}
}
}
}
#[derive(Debug)]
pub struct AtlasFitter {
entries: Vec<Entry>,
}
impl AtlasFitter {
pub fn new() -> AtlasFitter {
AtlasFitter {
entries: Vec::new(),
}
}
pub fn add_entry(&mut self, id: usize, width: u32, height: u32) {
let e = Entry::new(id, width, height);
self.entries.push(e);
}
pub fn fit(&self, size: u32, border: u32) -> Vec<Page> {
let mut pages: Vec<Page> = Vec::new();
for e in &self.entries {
let mut did_fit = false;
for p in &mut pages {
if p.fit_entry(&e) {
did_fit = true;
break;
}
}
if !did_fit {
let mut p = Page::new(size, border);
if !p.fit_entry(&e) {
println!(
"‼️ Image doesn't fit into empty page of size {} -> {:?}",
size, e
);
}
pages.push(p);
}
}
pages
}
}