use crate::math::{UVec2, Vec2, Vec4};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Sheet {
columns: u32,
rows: u32,
}
impl Sheet {
pub fn new(size: UVec2) -> Self {
Self {
columns: size.x.max(1),
rows: size.y.max(1),
}
}
pub fn cell(&self, index: u32) -> Frame {
self.cell_at(UVec2::new(index % self.columns, index / self.columns))
}
pub fn cell_at(&self, at: UVec2) -> Frame {
let size = Vec2::new(1.0 / self.columns as f32, 1.0 / self.rows as f32);
let cell = Vec2::new((at.x % self.columns) as f32, (at.y % self.rows) as f32);
Frame {
min: cell * size,
max: (cell + Vec2::ONE) * size,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Frame {
min: Vec2,
max: Vec2,
}
impl Frame {
pub fn rect(min: Vec2, max: Vec2) -> Self {
Self { min, max }
}
pub fn mirrored(self) -> Self {
Self {
min: Vec2::new(self.max.x, self.min.y),
max: Vec2::new(self.min.x, self.max.y),
}
}
pub(crate) fn lane(self) -> Vec4 {
let size = self.max - self.min;
Vec4::new(self.min.x, self.min.y, size.x, size.y)
}
}
impl Default for Frame {
fn default() -> Self {
Self {
min: Vec2::ZERO,
max: Vec2::ONE,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_whole_texture_is_what_a_draw_samples_until_it_asks_for_a_part() {
assert_eq!(Frame::default().lane(), Vec4::new(0.0, 0.0, 1.0, 1.0));
assert_eq!(
Frame::rect(Vec2::new(0.25, 0.5), Vec2::new(0.75, 1.0)).lane(),
Vec4::new(0.25, 0.5, 0.5, 0.5)
);
}
#[test]
fn the_cells_of_a_grid_are_counted_row_by_row_from_the_top_left() {
let sheet = Sheet::new(UVec2::new(4, 2));
let cell = |index| sheet.cell(index).lane();
assert_eq!(cell(0), Vec4::new(0.0, 0.0, 0.25, 0.5));
assert_eq!(cell(3), Vec4::new(0.75, 0.0, 0.25, 0.5));
assert_eq!(cell(4), Vec4::new(0.0, 0.5, 0.25, 0.5));
}
#[test]
fn a_cell_past_the_last_wraps_around_the_grid() {
let sheet = Sheet::new(UVec2::new(4, 2));
assert_eq!(sheet.cell(8), sheet.cell(0));
assert_eq!(sheet.cell(11), sheet.cell(3));
assert_eq!(sheet.cell(u32::MAX), sheet.cell(u32::MAX % 8));
}
#[test]
fn a_cell_named_by_its_two_axes_is_the_one_the_count_reaches() {
let sheet = Sheet::new(UVec2::new(4, 2));
assert_eq!(sheet.cell_at(UVec2::new(0, 0)), sheet.cell(0));
assert_eq!(sheet.cell_at(UVec2::new(3, 0)), sheet.cell(3));
assert_eq!(sheet.cell_at(UVec2::new(1, 1)), sheet.cell(5));
}
#[test]
fn a_column_or_row_past_the_grid_wraps_around_its_own_side() {
let sheet = Sheet::new(UVec2::new(4, 2));
assert_eq!(
sheet.cell_at(UVec2::new(4, 1)),
sheet.cell_at(UVec2::new(0, 1))
);
assert_eq!(
sheet.cell_at(UVec2::new(2, 2)),
sheet.cell_at(UVec2::new(2, 0))
);
assert_eq!(
sheet.cell_at(UVec2::new(u32::MAX, u32::MAX)),
sheet.cell_at(UVec2::new(u32::MAX % 4, u32::MAX % 2))
);
}
#[test]
fn a_grid_with_a_side_of_nothing_still_names_the_whole_texture() {
for sheet in [
Sheet::new(UVec2::new(0, 0)),
Sheet::new(UVec2::new(0, 1)),
Sheet::new(UVec2::new(1, 0)),
] {
assert_eq!(sheet.cell(0), Frame::default());
assert_eq!(sheet.cell(7), Frame::default());
}
}
#[test]
fn a_mirrored_window_names_the_same_two_edges_the_other_way_round() {
let min = Vec2::new(0.25, 0.5);
let max = Vec2::new(0.75, 1.0);
let window = Frame::rect(min, max);
assert_eq!(
window.mirrored(),
Frame::rect(Vec2::new(max.x, min.y), Vec2::new(min.x, max.y))
);
assert_eq!(window.mirrored().mirrored(), window, "and twice is itself");
}
#[test]
fn a_mirrored_cell_starts_where_the_cell_it_came_from_ends() {
let sheet = Sheet::new(UVec2::new(3, 2));
for index in 0..6 {
let cell = sheet.cell(index);
let [x, y, width, height] = cell.lane().to_array();
assert_eq!(
cell.mirrored().lane(),
Vec4::new(x + width, y, -width, height)
);
assert_eq!(cell.mirrored().mirrored(), cell, "and twice is the cell");
}
}
#[test]
fn the_cells_of_a_grid_cover_it_and_nothing_twice() {
let sheet = Sheet::new(UVec2::new(2, 2));
let cells: Vec<Vec4> = (0..4).map(|index| sheet.cell(index).lane()).collect();
assert_eq!(
cells,
vec![
Vec4::new(0.0, 0.0, 0.5, 0.5),
Vec4::new(0.5, 0.0, 0.5, 0.5),
Vec4::new(0.0, 0.5, 0.5, 0.5),
Vec4::new(0.5, 0.5, 0.5, 0.5),
]
);
}
}