use crate::{
CellIndex,
coord::{CoordCube, CoordIJK, LocalIJK},
error::LocalIjError,
};
use core::{cmp::max, iter::FusedIterator};
#[derive(Debug, Clone)]
pub struct GridPathCells {
anchor: CellIndex,
start: CoordCube,
distance: i32,
n: i32,
i_step: f64,
j_step: f64,
k_step: f64,
}
impl GridPathCells {
pub fn new(start: CellIndex, end: CellIndex) -> Result<Self, LocalIjError> {
let anchor = start;
let src = start.to_local_ijk(start)?;
let dst = end.to_local_ijk(start)?;
let distance = src.coord().distance(dst.coord());
let start = CoordCube::from(*src.coord());
let end = CoordCube::from(*dst.coord());
let (i_step, j_step, k_step) = if distance == 0 {
(0., 0., 0.)
} else {
let inv_distance = 1.0 / f64::from(distance);
(
f64::from(end.i - start.i) * inv_distance,
f64::from(end.j - start.j) * inv_distance,
f64::from(end.k - start.k) * inv_distance,
)
};
Ok(Self {
anchor,
start,
distance,
n: 0,
i_step,
j_step,
k_step,
})
}
}
impl Iterator for GridPathCells {
type Item = Result<CellIndex, LocalIjError>;
fn next(&mut self) -> Option<Self::Item> {
(self.n <= self.distance).then(|| {
let coord = self.start.translate((
self.i_step * f64::from(self.n),
self.j_step * f64::from(self.n),
self.k_step * f64::from(self.n),
));
self.n += 1;
let local_ijk = LocalIJK {
anchor: self.anchor,
coord: CoordIJK::from(coord),
};
CellIndex::try_from(local_ijk)
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
let count = usize::try_from(max(self.distance - self.n, 0))
.unwrap_or(usize::MAX);
(count, Some(count))
}
}
impl ExactSizeIterator for GridPathCells {}
impl FusedIterator for GridPathCells {}
#[cfg(test)]
#[path = "./grid_path_tests.rs"]
mod tests;