use super::CellIndex;
use crate::{
Direction, Resolution,
coord::{CoordCube, CoordIJK, LocalIJK},
error::LocalIjError,
index::bits,
};
use core::cmp::max;
pub struct Children {
parent_resolution: Resolution,
target_resolution: Resolution,
scratchpad: u64,
skip_count: i16,
count: u64,
}
impl Children {
pub fn new(index: CellIndex, resolution: Resolution) -> Self {
Self {
parent_resolution: index.resolution(),
target_resolution: resolution,
scratchpad: get_starting_state(index, resolution),
skip_count: if index.is_pentagon() {
i16::from(u8::from(resolution))
} else {
-1
},
count: index.children_count(resolution),
}
}
fn next_direction(&mut self, resolution: Resolution) -> u8 {
let one = 1 << resolution.direction_offset();
self.scratchpad += one;
bits::get_direction(self.scratchpad, resolution)
}
}
impl Iterator for Children {
type Item = CellIndex;
fn next(&mut self) -> Option<CellIndex> {
if self.count == 0 {
return None;
}
let index = CellIndex::new_unchecked(self.scratchpad);
self.count -= 1;
if self.count != 0 {
for resolution in Resolution::range(
self.parent_resolution,
self.target_resolution,
)
.rev()
{
let direction = self.next_direction(resolution);
if self.skip_count == i16::from(resolution)
&& direction == u8::from(Direction::K)
{
self.next_direction(resolution);
self.skip_count -= 1;
}
if Direction::try_from(direction).is_err() {
self.scratchpad =
bits::clr_direction(self.scratchpad, resolution);
continue;
}
break;
}
}
Some(index)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let count = usize::try_from(self.count).unwrap_or(usize::MAX);
(count, Some(count))
}
}
impl ExactSizeIterator for Children {}
fn get_starting_state(index: CellIndex, resolution: Resolution) -> u64 {
let parent_resolution = index.resolution();
let range =
usize::from(resolution).saturating_sub(parent_resolution.into());
let mut scratchpad = u64::from(index);
if range != 0 {
let mask = (1 << (range * h3o_bit::DIRECTION_BITSIZE)) - 1;
let offset = resolution.direction_offset();
scratchpad &= !(mask << offset);
scratchpad = bits::set_resolution(scratchpad, resolution);
}
scratchpad
}
#[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 {}