#![allow(clippy::needless_range_loop)]
use std::array;
use super::{Face, Region, Side};
#[derive(Debug, Clone, Copy)]
pub struct IndexSpace<const N: usize> {
size: [usize; N],
}
impl<const N: usize> IndexSpace<N> {
pub const fn new(size: [usize; N]) -> Self {
Self { size }
}
pub fn index_count(&self) -> usize {
let mut result = 1;
for i in 0..N {
result *= self.size[i]
}
result
}
pub fn size(self) -> [usize; N] {
self.size
}
pub fn cartesian_from_linear(self, mut linear: usize) -> [usize; N] {
debug_assert!(linear < self.size.iter().product());
let mut result = [0; N];
for i in 0..N {
result[i] = linear % self.size[i];
linear /= self.size[i];
}
result
}
pub fn linear_from_cartesian(self, cartesian: [usize; N]) -> usize {
for axis in 0..N {
debug_assert!(cartesian[axis] < self.size[axis]);
}
let mut result = 0;
let mut stride = 1;
for i in 0..N {
result += stride * cartesian[i];
stride *= self.size[i];
}
result
}
pub const fn iter(self) -> CartesianIter<N> {
CartesianIter {
size: self.size,
cursor: [0; N],
}
}
pub fn window(self) -> IndexWindow<N> {
IndexWindow {
origin: [0; N],
size: self.size,
}
}
pub fn plane_window(self, axis: usize, intercept: usize) -> IndexWindow<N> {
debug_assert!(intercept < self.size[axis]);
let mut origin = [0; N];
origin[axis] = intercept;
let mut size = self.size;
size[axis] = 1;
IndexWindow::new(origin, size)
}
pub fn face_window(self, face: Face<N>) -> IndexWindow<N> {
let intercept = if face.side {
self.size[face.axis] - 1
} else {
0
};
self.plane_window(face.axis, intercept)
}
pub fn region_adjacent_window(self, region: Region<N>) -> IndexWindow<N> {
let origin = array::from_fn(|axis| match region.side(axis) {
Side::Left | Side::Middle => 0,
Side::Right => self.size[axis] - 1,
});
let size = array::from_fn(|axis| match region.side(axis) {
Side::Left | Side::Right => 1,
Side::Middle => self.size[axis],
});
IndexWindow { origin, size }
}
}
impl<const N: usize> IntoIterator for IndexSpace<N> {
type IntoIter = CartesianIter<N>;
type Item = [usize; N];
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Debug, Clone, Copy)]
pub struct IndexWindow<const N: usize> {
pub origin: [usize; N],
pub size: [usize; N],
}
impl<const N: usize> IndexWindow<N> {
pub fn new(origin: [usize; N], size: [usize; N]) -> Self {
Self { origin, size }
}
pub fn iter(&self) -> CartesianWindowIter<N> {
CartesianWindowIter {
origin: self.origin,
inner: IndexSpace::new(self.size).iter(),
}
}
}
impl<const N: usize> IntoIterator for IndexWindow<N> {
type IntoIter = CartesianWindowIter<N>;
type Item = [usize; N];
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Debug, Clone)]
pub struct CartesianIter<const N: usize> {
size: [usize; N],
cursor: [usize; N],
}
impl<const N: usize> Iterator for CartesianIter<N> {
type Item = [usize; N];
fn next(&mut self) -> Option<Self::Item> {
if self.cursor[N - 1] == self.size[N - 1] {
return None;
}
let result = self.cursor;
for i in 0..N {
if self.size[i] == 0 {
return None;
}
self.cursor[i] += 1;
if self.cursor[i] == self.size[i] && i < N - 1 {
self.cursor[i] = 0;
continue;
}
break;
}
Some(result)
}
}
#[derive(Debug, Clone)]
pub struct CartesianWindowIter<const N: usize> {
origin: [usize; N],
inner: CartesianIter<N>,
}
impl<const N: usize> Iterator for CartesianWindowIter<N> {
type Item = [usize; N];
fn next(&mut self) -> Option<Self::Item> {
let offset = self.inner.next()?;
Some(array::from_fn(|i| self.origin[i] + offset[i]))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn index_iteration() {
let space = IndexSpace::new([3, 2]);
let mut indices = space.iter();
assert_eq!(indices.next(), Some([0, 0]));
assert_eq!(indices.next(), Some([1, 0]));
assert_eq!(indices.next(), Some([2, 0]));
assert_eq!(indices.next(), Some([0, 1]));
assert_eq!(indices.next(), Some([1, 1]));
assert_eq!(indices.next(), Some([2, 1]));
assert_eq!(indices.next(), None);
let space = IndexSpace::new([0, 10]);
let mut indices = space.iter();
assert_eq!(indices.next(), None);
let space = IndexSpace::new([2, 3, 10]);
let mut plane = space.plane_window(2, 5).iter();
assert_eq!(plane.next(), Some([0, 0, 5]));
assert_eq!(plane.next(), Some([1, 0, 5]));
assert_eq!(plane.next(), Some([0, 1, 5]));
assert_eq!(plane.next(), Some([1, 1, 5]));
assert_eq!(plane.next(), Some([0, 2, 5]));
assert_eq!(plane.next(), Some([1, 2, 5]));
assert_eq!(plane.next(), None);
let space = IndexSpace::new([2, 3, 4]);
for (i, index) in space.iter().enumerate() {
assert_eq!(i, space.linear_from_cartesian(index));
}
}
#[test]
fn index_conversion() {
let space = IndexSpace::new([2, 4, 3]);
assert_eq!(space.linear_from_cartesian([1, 0, 0]), 1);
assert_eq!(space.linear_from_cartesian([0, 1, 0]), 2);
assert_eq!(space.linear_from_cartesian([0, 0, 2]), 8 * 2);
assert_eq!(space.linear_from_cartesian([1, 1, 2]), 8 * 2 + 2 + 1);
assert_eq!(space.cartesian_from_linear(1), [1, 0, 0]);
assert_eq!(space.cartesian_from_linear(2), [0, 1, 0]);
assert_eq!(space.cartesian_from_linear(8 * 2), [0, 0, 2]);
assert_eq!(space.cartesian_from_linear(8 * 2 + 2 + 1), [1, 1, 2]);
}
}