use std::{collections::BTreeMap, ops::Index};
use glam::{IVec2, UVec2};
use crate::Pivot;
#[derive(Default, Debug, Clone)]
pub struct SparseGrid<T: Clone> {
data: BTreeMap<u32, T>,
size: UVec2,
}
impl<T: Clone> SparseGrid<T> {
pub fn new(size: [u32; 2]) -> Self {
let size = UVec2::from(size);
Self {
data: BTreeMap::new(),
size,
}
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = (&u32, &T)> {
self.data.iter()
}
pub fn iter_values(&self) -> impl Iterator<Item = &T> {
self.data.iter().map(move |(_, v)| v)
}
pub fn iter_values_mut(&self) -> impl Iterator<Item = &T> {
self.data.iter().map(move |(_, v)| v)
}
#[inline]
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&u32, &mut T)> {
self.data.iter_mut()
}
#[inline]
pub fn iter_2d(&self) -> impl Iterator<Item = (UVec2, &T)> {
let w = self.width();
self.data.iter().map(move |(i, v)| {
let x = i % w;
let y = i / w;
(UVec2::new(x, y), v)
})
}
#[inline]
pub fn iter_mut_2d(&mut self) -> impl Iterator<Item = (UVec2, &mut T)> {
let w = self.width();
self.data.iter_mut().map(move |(i, v)| {
let x = i % w;
let y = i / w;
(UVec2::new(x, y), v)
})
}
pub fn insert_row(&mut self, y: usize, row: impl IntoIterator<Item = T> + Iterator<Item = T>) {
self.insert_row_at([0, y as i32], row);
}
pub fn insert_row_at(
&mut self,
xy: [i32; 2],
row: impl IntoIterator<Item = T> + Iterator<Item = T>,
) {
let start = self.pos_to_index(xy) as u32;
let max = self.width() as usize - 1 - xy[0] as usize;
for (x, v) in row.take(max).enumerate() {
self.data.insert(start + x as u32, v);
}
}
pub fn insert_column(
&mut self,
x: usize,
column: impl IntoIterator<Item = T> + Iterator<Item = T>,
) {
self.insert_column_at([x as i32, 0], column);
}
pub fn insert_column_at(
&mut self,
xy: [i32; 2],
column: impl IntoIterator<Item = T> + Iterator<Item = T>,
) {
let start = self.pos_to_index(xy) as u32;
let max = self.height() as usize - 1 - xy[1] as usize;
for (y, v) in column.take(max).enumerate() {
let i = start + (y as u32 * self.width());
self.data.insert(i, v);
}
}
pub fn remove(&mut self, pos: [u32; 2]) -> Option<T> {
let i = self.upos_to_index(pos) as u32;
self.data.remove(&i)
}
pub fn remove_index(&mut self, index: usize) -> Option<T> {
let index = index as u32;
self.data.remove(&index)
}
pub fn clear(&mut self) {
self.data.clear();
}
pub fn width(&self) -> u32 {
self.size.x
}
pub fn height(&self) -> u32 {
self.size.y
}
pub fn size(&self) -> UVec2 {
self.size
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
#[inline(always)]
pub fn pos_to_index(&self, pos: [i32; 2]) -> usize {
(pos[1] * self.width() as i32 + pos[0]) as usize
}
#[inline(always)]
pub fn upos_to_index(&self, pos: [u32; 2]) -> usize {
(pos[1] * self.width() as u32 + pos[0]) as usize
}
#[inline(always)]
pub fn index_to_pos(&self, index: usize) -> IVec2 {
let index = index as i32;
let w = self.width() as i32;
let x = index % w;
let y = index / w;
IVec2::new(x, y)
}
#[inline(always)]
pub fn index_to_upos(&self, index: usize) -> UVec2 {
self.index_to_pos(index).as_uvec2()
}
#[inline(always)]
pub fn top_index(&self) -> usize {
(self.height() - 1) as usize
}
#[inline(always)]
pub fn bottom_index(&self) -> usize {
0
}
#[inline(always)]
pub fn left_index(&self) -> usize {
0
}
#[inline(always)]
pub fn right_index(&self) -> usize {
(self.width() - 1) as usize
}
pub fn pivot_position(&self, pivot: Pivot) -> IVec2 {
match pivot {
Pivot::TopLeft => IVec2::new(0, self.top_index() as i32),
Pivot::TopRight => IVec2::new(self.right_index() as i32, self.top_index() as i32),
Pivot::Center => {
let tr = self.pivot_position(Pivot::TopRight);
(tr.as_vec2() / 2.0).as_ivec2()
}
Pivot::BottomLeft => IVec2::ZERO,
Pivot::BottomRight => IVec2::new(self.right_index() as i32, 0),
}
}
pub fn is_in_bounds(&self, pos: IVec2) -> bool {
pos.cmpge(IVec2::ZERO).all() && pos.cmplt(self.size().as_ivec2()).all()
}
#[inline]
pub fn insert_index(&mut self, index: usize, value: T) -> Option<T> {
self.data.insert(index as u32, value)
}
#[inline]
pub fn insert(&mut self, pos: [i32; 2], value: T) -> Option<T> {
let pos = IVec2::from(pos);
let i = self.pos_to_index(pos.into());
self.data.insert(i as u32, value)
}
#[inline]
pub fn get_index(&self, index: usize) -> Option<&T> {
let i = index as u32;
self.data.get(&i)
}
#[inline]
pub fn get_mut_index(&mut self, index: usize) -> Option<&mut T> {
let i = index as u32;
self.data.get_mut(&i)
}
#[inline]
pub fn get(&self, pos: [i32; 2]) -> Option<&T> {
let pos = IVec2::from(pos);
let i = self.pos_to_index(pos.into());
self.get_index(i)
}
#[inline]
pub fn get_mut(&mut self, pos: [i32; 2]) -> Option<&mut T> {
let pos = IVec2::from(pos);
let i = self.pos_to_index(pos.into()) as u32;
self.data.get_mut(&i)
}
#[allow(dead_code)]
pub(crate) fn debug_bounds_check(&self, pos: IVec2) {
debug_assert!(
self.is_in_bounds(pos),
"Position {} is out of grid bounds {}",
pos,
self.size()
);
}
}
impl<T: Clone> Index<[u32; 2]> for SparseGrid<T> {
type Output = T;
#[inline(always)]
fn index(&self, index: [u32; 2]) -> &Self::Output {
let i = self.upos_to_index(index) as u32;
&self.data[&i]
}
}
impl<T: Clone> Index<usize> for SparseGrid<T> {
type Output = T;
#[inline(always)]
fn index(&self, index: usize) -> &Self::Output {
let index = index as u32;
&self.data[&index]
}
}
impl<T: Clone> Index<IVec2> for SparseGrid<T> {
type Output = T;
#[inline(always)]
fn index(&self, index: IVec2) -> &Self::Output {
let index = self.pos_to_index(index.into()) as u32;
&self.data[&index]
}
}
impl<T: Clone> Index<UVec2> for SparseGrid<T> {
type Output = T;
#[inline(always)]
fn index(&self, index: UVec2) -> &Self::Output {
let index = self.upos_to_index(index.into()) as u32;
&self.data[&index]
}
}
#[cfg(test)]
mod test {
use glam::UVec2;
use super::SparseGrid;
#[test]
fn insert_row() {
let mut grid = SparseGrid::new([10, 10]);
grid.insert_row(5, "Hello".chars());
assert_eq!(5, grid.len());
let str: String = grid.iter_2d().map(|(_, v)| v).collect();
assert_eq!("Hello", str);
for (i, (p, _)) in grid.iter_2d().enumerate() {
assert_eq!((i as u32, 5), p.into());
}
}
#[test]
fn insert_row_at() {
let mut grid = SparseGrid::new([10, 10]);
grid.insert_row_at([3, 3], "Hello".chars());
assert_eq!(5, grid.len());
let str: String = grid.iter_values().collect();
assert_eq!("Hello", str);
let kvp: Vec<_> = grid.iter_2d().collect();
assert_eq!((UVec2::new(3, 3), &'H'), kvp[0]);
assert_eq!((UVec2::new(4, 3), &'e'), kvp[1]);
assert_eq!((UVec2::new(5, 3), &'l'), kvp[2]);
assert_eq!((UVec2::new(6, 3), &'l'), kvp[3]);
assert_eq!((UVec2::new(7, 3), &'o'), kvp[4]);
}
#[test]
fn insert_col() {
let mut grid = SparseGrid::new([10, 10]);
grid.insert_column(5, "Hello".chars());
assert_eq!(5, grid.len());
let str: String = grid.iter_2d().map(|(_, v)| v).collect();
assert_eq!("Hello", str);
for (i, (p, _)) in grid.iter_2d().enumerate() {
assert_eq!((5, i as u32,), p.into());
}
}
#[test]
fn insert_col_at() {
let mut grid = SparseGrid::new([10, 10]);
grid.insert_column_at([3, 3], "Hello".chars());
assert_eq!(5, grid.len());
let str: String = grid.iter_2d().map(|(_, v)| v).collect();
assert_eq!("Hello", str);
let kvp: Vec<_> = grid.iter_2d().collect();
assert_eq!((UVec2::new(3, 3), &'H'), kvp[0]);
assert_eq!((UVec2::new(3, 4), &'e'), kvp[1]);
assert_eq!((UVec2::new(3, 5), &'l'), kvp[2]);
assert_eq!((UVec2::new(3, 6), &'l'), kvp[3]);
assert_eq!((UVec2::new(3, 7), &'o'), kvp[4]);
}
#[test]
fn insert() {
let mut grid = SparseGrid::new([10, 10]);
grid.insert([0, 0], 'h');
grid.insert([1, 3], '3');
assert_eq!(2, grid.len());
assert_eq!('h', grid[[0, 0]]);
assert_eq!('3', *grid.get([1, 3]).unwrap());
}
}