use crate::{Rect, V2i, V2};
use super::grid_combineable::GridCombineable;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Grid {
pub cols: usize,
pub rows: usize,
pub margin: f32,
pub bounding_box: Rect,
cell_size: V2,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct GridCell {
pub x: usize,
pub y: usize,
pub rect: Rect,
}
impl Grid {
pub fn new(bounding_box: Rect, cols: usize, rows: usize, margin: f32) -> Self {
assert!(cols > 0, "Number of columns must be greater than 0");
assert!(rows > 0, "Number of rows must be greater than 0");
let cell_size = V2::new(
(bounding_box.width() - (cols - 1) as f32 * margin) / cols as f32,
(bounding_box.height() - (rows - 1) as f32 * margin) / rows as f32,
);
assert!(
cell_size.x > 0.0,
"Cell width must be greater than 0. Select a larger bounding box or smaller margin."
);
assert!(
cell_size.y > 0.0,
"Cell height must be greater than 0. Select a larger bounding box or smaller margin."
);
Self {
cols,
rows,
margin,
bounding_box,
cell_size,
}
}
pub fn new_square_cells(bounding_box: Rect, cols: usize, rows: usize, margin: f32) -> Self {
let side = Self::new(bounding_box, cols, rows, margin)
.cell_size
.min_axis();
Self::fitted(bounding_box, V2::xy(side), cols, rows, margin)
}
pub fn with_cell_size(bounding_box: Rect, cell_size: f32, margin: f32) -> Self {
let fit =
|length: f32| (((length + margin) / (cell_size + margin)).floor() as usize).max(1);
let cols = fit(bounding_box.width());
let rows = fit(bounding_box.height());
Self::fitted(bounding_box, V2::xy(cell_size), cols, rows, margin)
}
fn fitted(bounding_box: Rect, cell_size: V2, cols: usize, rows: usize, margin: f32) -> Self {
let size = cell_size * V2::new(cols as f32, rows as f32)
+ V2::new((cols - 1) as f32, (rows - 1) as f32) * margin;
let bl = bounding_box.center() - size * 0.5;
Self::new(Rect::new(bl, bl + size), cols, rows, margin)
}
pub fn cell(&self, x: usize, y: usize) -> Rect {
let index = V2::new(x as f32, y as f32);
let bl = self.bounding_box.bl() + (self.cell_size + V2::xy(self.margin)) * index;
Rect::new(bl, bl + self.cell_size)
}
pub fn cell_size(&self) -> V2 {
self.cell_size
}
pub fn num_cells(&self) -> V2i {
V2i::new(self.cols as i32, self.rows as i32)
}
pub fn iter(&self) -> impl Iterator<Item = Rect> + '_ {
self.cells().map(|cell| cell.rect)
}
pub fn cells(&self) -> impl Iterator<Item = GridCell> + '_ {
(0..self.rows).flat_map(move |y| {
(0..self.cols).map(move |x| GridCell {
x,
y,
rect: self.cell(x, y),
})
})
}
pub fn to_combineable(self) -> GridCombineable {
GridCombineable::new_from_grid(self)
}
}