cell_grid/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
//! A simple fixed-size 2d grid container suitable for `no_std` game development
//!
//! See [`Grid::new`], [`Grid::new_with`] and [`Grid::from_row_major_iter`] for examples on
//! how to create a grid.
//!
//! ## Features
//!
//! * `std`: *(enabled by default)* enable use of the standard library. Must be disabled for `no_std` crates.
//! * `aline`: add conversions between [`Coord`] and [aline](https://docs.rs/aline/1) vectors
extern crate alloc;
mod coord;
pub mod dynamic;
mod rect;
use alloc::vec::Vec;
pub use coord::Coord;
pub use rect::Rect;
/// A 2d fixed-size grid containers for cells of type `T`
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Grid<T> {
cells: Vec<T>,
width: usize,
}
impl<T: Default> Grid<T> {
/// Create a grid using the default value of `T` for each cell
///
/// # Example
///
/// ```
/// # use cell_grid::{Grid, Coord};
/// let grid: Grid<i32> = Grid::new(2, 2);
/// assert_eq!(grid.get([0, 0]), Some(&0));
/// assert_eq!(grid.get([1, 0]), Some(&0));
/// assert_eq!(grid.get([0, 1]), Some(&0));
/// assert_eq!(grid.get([1, 1]), Some(&0));
/// ```
#[must_use]
pub fn new(width: usize, height: usize) -> Self {
Self::new_with(width, height, |_| T::default())
}
}
impl<T> Grid<T> {
/// Create a grid using `init_cell` function for each cell
///
/// # Example
///
/// ```
/// # use cell_grid::{Grid, Coord};
/// let grid = Grid::new_with(2, 2, |coord| coord);
/// assert_eq!(grid.get([0, 0]), Some(&Coord { x: 0, y: 0 }));
/// assert_eq!(grid.get([1, 0]), Some(&Coord { x: 1, y: 0 }));
/// assert_eq!(grid.get([0, 1]), Some(&Coord { x: 0, y: 1 }));
/// assert_eq!(grid.get([1, 1]), Some(&Coord { x: 1, y: 1 }));
/// ```
#[must_use]
pub fn new_with(width: usize, height: usize, init_cell: impl FnMut(Coord) -> T) -> Self {
Self::from_row_major_iter(
width,
(0..(width * height))
.map(|i| Coord::from_index(width, i))
.map(init_cell),
)
}
/// Create a grid using the row-major `iter`
///
/// # Example
///
/// ```
/// # use cell_grid::Grid;
/// let grid = Grid::from_row_major_iter(2, [1, 2, 3, 4]);
/// assert_eq!(grid.get([0, 0]), Some(&1));
/// assert_eq!(grid.get([1, 0]), Some(&2));
/// assert_eq!(grid.get([0, 1]), Some(&3));
/// assert_eq!(grid.get([1, 1]), Some(&4));
/// ```
#[must_use]
pub fn from_row_major_iter(width: usize, iter: impl IntoIterator<Item = T>) -> Self {
let cells = iter.into_iter().collect();
Self { cells, width }
}
/// Access the cell at `coord`
///
/// Returns `None` if the `coord` is out of bounds
#[must_use]
pub fn get(&self, coord: impl Into<Coord>) -> Option<&T> {
let index = coord.into().into_index(self.width)?;
self.cells.get(index)
}
/// Access mutably the cell at `coord`
///
/// Returns `None` if the `coord` is out of bounds
pub fn get_mut(&mut self, coord: impl Into<Coord>) -> Option<&mut T> {
let index = coord.into().into_index(self.width)?;
self.cells.get_mut(index)
}
/// Set the value of the cell at `coord`
///
/// # Panics
///
/// Panics if the `coord` is out-of-bounds
///
/// For a non-panicking version, use `get_mut`
pub fn set(&mut self, coord: impl Into<Coord>, value: T) {
let cell = self.get_mut(coord).expect("coordinate is out-of-bounds");
*cell = value;
}
/// Iterator over all cells
pub fn cells(&self) -> impl Iterator<Item = &T> {
self.cells.iter()
}
/// Iterator over cells in `rect`
pub fn cells_in_rect(&self, rect: impl Into<Rect>) -> impl Iterator<Item = &T> {
rect.into().coords().filter_map(|c| self.get(c))
}
}