use std::ops::{Index, IndexMut};
#[derive(Clone, Debug, PartialEq)]
pub struct Grid<T> {
width: usize,
height: usize,
data: Vec<T>,
}
impl<T: Clone> Grid<T> {
pub fn new(width: usize, height: usize, value: T) -> Self {
assert!(width > 0 && height > 0, "grid dimensions must be positive");
Self {
width,
height,
data: vec![value; width * height],
}
}
}
impl<T> Grid<T> {
pub fn from_vec(width: usize, height: usize, data: Vec<T>) -> Self {
assert!(width > 0 && height > 0, "grid dimensions must be positive");
assert_eq!(
data.len(),
width * height,
"buffer length must equal width * height"
);
Self {
width,
height,
data,
}
}
#[inline]
pub fn width(&self) -> usize {
self.width
}
#[inline]
pub fn height(&self) -> usize {
self.height
}
#[inline]
pub fn len(&self) -> usize {
self.data.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
false
}
#[inline]
pub fn idx(&self, x: usize, y: usize) -> usize {
debug_assert!(x < self.width && y < self.height);
y * self.width + x
}
#[inline]
pub fn coords(&self, idx: usize) -> (usize, usize) {
(idx % self.width, idx / self.width)
}
#[inline]
pub fn as_slice(&self) -> &[T] {
&self.data
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.data
}
pub fn into_vec(self) -> Vec<T> {
self.data
}
pub fn iter(&self) -> std::slice::Iter<'_, T> {
self.data.iter()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
self.data.iter_mut()
}
}
impl<T> Index<(usize, usize)> for Grid<T> {
type Output = T;
#[inline]
fn index(&self, (x, y): (usize, usize)) -> &T {
&self.data[self.idx(x, y)]
}
}
impl<T> IndexMut<(usize, usize)> for Grid<T> {
#[inline]
fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut T {
let idx = self.idx(x, y);
&mut self.data[idx]
}
}
impl<'a, T> IntoIterator for &'a Grid<T> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.data.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trips_between_coords_and_flat_index() {
let grid = Grid::new(7, 5, 0u8);
for y in 0..5 {
for x in 0..7 {
assert_eq!(grid.coords(grid.idx(x, y)), (x, y));
}
}
}
#[test]
fn indexing_is_row_major() {
let mut grid = Grid::new(3, 2, 0i32);
grid[(2, 1)] = 42;
assert_eq!(grid.as_slice(), &[0, 0, 0, 0, 0, 42]);
}
#[test]
#[should_panic]
fn from_vec_rejects_wrong_length() {
let _ = Grid::from_vec(2, 2, vec![0u8; 3]);
}
#[test]
#[should_panic]
fn zero_dimensions_are_rejected() {
let _ = Grid::new(0, 4, 0u8);
}
}