use ixy::index::{Layout, RowMajor};
use crate::{
core::{Pos, Rect},
grid::{BoundedGrid, GridBase},
};
pub trait GridRead: GridBase {
fn get(&self, pos: Pos) -> Option<&Self::Element>;
fn rect_iter(&self, bounds: Rect) -> impl Iterator<Item = &Self::Element> {
RowMajor::iter_pos(bounds).filter_map(|pos| self.get(pos))
}
}
pub trait GridReadUnchecked: GridBase {
unsafe fn get_unchecked(&self, pos: Pos) -> &Self::Element;
unsafe fn rect_iter_unchecked(&self, bounds: Rect) -> impl Iterator<Item = &Self::Element> {
RowMajor::iter_pos(bounds).map(move |pos| unsafe { self.get_unchecked(pos) })
}
}
impl<T: GridReadUnchecked + BoundedGrid> GridRead for T {
fn get(&self, pos: Pos) -> Option<&Self::Element> {
if self.contains_pos(pos) {
Some(unsafe { self.get_unchecked(pos) })
} else {
None
}
}
fn rect_iter(&self, bounds: Rect) -> impl Iterator<Item = &Self::Element> {
let size = unsafe { Rect::from_ltrb_unchecked(0, 0, self.width(), self.height()) };
let rect = bounds.intersect(size);
unsafe { self.rect_iter_unchecked(rect) }
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use super::*;
use alloc::vec::Vec;
struct CheckedGridTest {
grid: [[u8; 3]; 3],
}
impl GridBase for CheckedGridTest {
type Element = u8;
}
impl GridRead for CheckedGridTest {
fn get(&self, pos: Pos) -> Option<&Self::Element> {
if pos.x < 3 && pos.y < 3 {
Some(&self.grid[pos.y][pos.x])
} else {
None
}
}
}
struct UncheckedTestGrid {
grid: [[u8; 3]; 3],
}
impl GridBase for UncheckedTestGrid {
type Element = u8;
}
unsafe impl BoundedGrid for UncheckedTestGrid {
fn width(&self) -> usize {
3
}
fn height(&self) -> usize {
3
}
}
impl GridReadUnchecked for UncheckedTestGrid {
unsafe fn get_unchecked(&self, pos: Pos) -> &Self::Element {
&self.grid[pos.y][pos.x]
}
}
#[test]
fn test_get_ok() {
let grid = UncheckedTestGrid {
grid: [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
};
assert_eq!(grid.get(Pos::new(1, 1)), Some(&5));
}
#[test]
fn test_get_out_of_bounds_x() {
let grid = UncheckedTestGrid {
grid: [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
};
assert_eq!(grid.get(Pos::new(3, 1)), None);
}
#[test]
fn test_get_out_of_bounds_y() {
let grid = UncheckedTestGrid {
grid: [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
};
assert_eq!(grid.get(Pos::new(1, 3)), None);
}
#[test]
fn test_get_unchecked_ok() {
let grid = UncheckedTestGrid {
grid: [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
};
let val = unsafe { grid.get_unchecked(Pos::new(2, 2)) };
assert_eq!(val, &9);
}
#[test]
fn rect_iter_completely_in_bounds() {
let grid = CheckedGridTest {
grid: [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
};
let cells = grid
.rect_iter(Rect::from_ltwh(1, 1, 2, 2))
.collect::<Vec<_>>();
#[rustfmt::skip]
assert_eq!(cells, &[
&5, &6,
&8, &9,
]);
}
#[test]
fn rect_iter_completely_in_bounds_unchecked_impl() {
let grid = UncheckedTestGrid {
grid: [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
};
let cells = grid
.rect_iter(Rect::from_ltwh(1, 1, 2, 2))
.collect::<Vec<_>>();
#[rustfmt::skip]
assert_eq!(cells, &[
&5, &6,
&8, &9,
]);
}
#[test]
fn rect_iter_partially_out_of_bounds() {
let grid = CheckedGridTest {
grid: [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
};
let cells = grid
.rect_iter(Rect::from_ltwh(0, 0, 4, 4))
.collect::<Vec<_>>();
#[rustfmt::skip]
assert_eq!(cells, &[
&1, &2, &3,
&4, &5, &6,
&7, &8, &9,
]);
}
#[test]
fn rect_iter_partially_out_of_bounds_unchecked_impl() {
let grid = UncheckedTestGrid {
grid: [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
};
let cells = grid
.rect_iter(Rect::from_ltwh(0, 0, 4, 4))
.collect::<Vec<_>>();
#[rustfmt::skip]
assert_eq!(cells, &[
&1, &2, &3,
&4, &5, &6,
&7, &8, &9,
]);
}
#[test]
fn rect_iter_completely_out_of_bounds() {
let grid = CheckedGridTest {
grid: [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
};
let cells = grid
.rect_iter(Rect::from_ltwh(3, 3, 2, 2))
.collect::<Vec<_>>();
assert!(cells.is_empty());
}
#[test]
fn rect_iter_completely_out_of_bounds_unchecked_impl() {
let grid = UncheckedTestGrid {
grid: [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
};
let cells = grid
.rect_iter(Rect::from_ltwh(3, 3, 2, 2))
.collect::<Vec<_>>();
assert!(cells.is_empty());
}
}