use dilate::*;
use super::{CurveCoord, CurveIndex, SpaceFillingCurve, Neighbours, QueryDirection, Siblings};
use crate::internal::NumTraits;
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Morton<DM, const D: usize>(pub(crate) DM::Dilated)
where
DM: DilationMethod,
DM::Undilated: CurveCoord,
DM::Dilated: CurveIndex;
impl<DM, const D: usize> SpaceFillingCurve<D> for Morton<DM, D>
where
DM: DilationMethod,
DM::Undilated: CurveCoord,
DM::Dilated: CurveIndex,
{
type Coord = DM::Undilated;
type Index = DM::Dilated;
const COORD_MAX: Self::Coord = DM::UNDILATED_MAX;
const INDEX_MAX: Self::Index = DM::DILATED_MASK;
const D: usize = D;
#[inline]
fn from_index(index: Self::Index) -> Self {
debug_assert!(
index <= Self::INDEX_MAX,
"Parameter 'index' exceeds maximum"
);
Self(index)
}
#[inline]
fn from_coords(coords: [Self::Coord; D]) -> Self {
debug_assert!(
*coords.iter().max().unwrap() <= Self::COORD_MAX,
"Parameter 'coords' contains a value which exceeds maximum"
);
let mut v = DM::Dilated::zero();
for (axis, coord) in coords.into_iter().enumerate() {
v = v.bit_or(DM::dilate(coord).value().shl(axis));
}
Self(v)
}
#[inline]
fn coords(&self) -> [Self::Coord; D] {
crate::internal::array_from_fn::<_, _, D>(|i| DilatedInt::<DM>::new(self.0.shr(i).bit_and(DM::DILATED_MAX)).undilate())
}
#[inline]
fn index(&self) -> Self::Index {
self.0
}
}
impl<DM, const D: usize> Siblings<D> for Morton<DM, D>
where
DM: DilationMethod,
DM::Undilated: CurveCoord,
DM::Dilated: CurveIndex,
{
#[inline]
fn sibling_on_axis(&self, axis: usize) -> Self {
Self(self.0.bit_xor(DM::Dilated::one().shl(axis)))
}
#[inline]
fn sibling_or_same_on_axis(&self, axis: usize, direction: QueryDirection) -> Self {
debug_assert!(axis < D, "Parameter 'axis' exceeds maximum");
let lower_axis_mask = DM::Dilated::one().shl(axis);
let search_mask = match direction {
QueryDirection::Positive => lower_axis_mask,
QueryDirection::Negative => DM::Dilated::zero(),
};
Self(
self.0
.bit_and(lower_axis_mask.bit_not())
.bit_or(search_mask),
)
}
}
impl<DM, const D: usize> Neighbours<D> for Morton<DM, D>
where
DM: DilationMethod,
DM::Undilated: CurveCoord,
DM::Dilated: CurveIndex,
{
#[inline]
fn neighbour_on_axis(&self, axis: usize, direction: QueryDirection) -> Option<Self> {
debug_assert!(axis < D, "Parameter 'axis' exceeds maximum");
let coord = DilatedInt::<DM>::new(self.0.shr(axis).bit_and(DM::DILATED_MAX));
let coord = match direction {
QueryDirection::Positive => if coord.value() < DM::DILATED_MAX { Some(coord.add_one()) } else { None },
QueryDirection::Negative => if coord.value() > NumTraits::zero() { Some(coord.sub_one()) } else { None },
};
coord.map(|coord| {
let index = self.0.bit_and(DM::DILATED_MAX.shl(axis).bit_not());
Self(index.bit_or(coord.value().shl(axis)))
})
}
#[inline]
fn neighbour_on_axis_wrapping(&self, axis: usize, direction: QueryDirection) -> Self {
debug_assert!(axis < D, "Parameter 'axis' exceeds maximum");
let coord = DilatedInt::<DM>::new(self.0.shr(axis).bit_and(DM::DILATED_MAX));
let coord = match direction {
QueryDirection::Positive => coord.add_one(),
QueryDirection::Negative => coord.sub_one(),
};
let index = self.0.bit_and(DM::DILATED_MAX.shl(axis).bit_not());
Self(index.bit_or(coord.value().shl(axis)))
}
}
#[cfg(test)]
mod tests {
use crate::internal::tests::{test_curve, test_curve_siblings, test_curve_neighbours};
macro_rules! morton_expand {
($t:ty, $d:literal) => {
Morton::<Expand<$t, $d>, $d>
};
}
macro_rules! morton_fixed {
($t:ty, $d:literal) => {
Morton::<Fixed<$t, $d>, $d>
};
}
macro_rules! morton_tests {
($curve:path, $t:ty, $($d:literal),+) => {
test_curve!($curve, false, $t, $($d),+);
test_curve_siblings!($curve, false, $t, $($d),+);
test_curve_neighbours!($curve, false, $t, $($d),+);
};
}
morton_tests!(morton_expand, u8, 2, 3, 4, 5, 6, 7, 8);
morton_tests!(morton_expand, u16, 2, 3, 4, 5, 6, 7, 8);
morton_tests!(morton_expand, u32, 2, 3, 4);
morton_tests!(morton_expand, u64, 2);
morton_tests!(morton_fixed, u8, 2, 3, 4);
morton_tests!(morton_fixed, u16, 2, 3, 4, 5, 6, 7, 8);
morton_tests!(morton_fixed, u32, 2, 3, 4, 5, 6, 7, 8);
morton_tests!(morton_fixed, u64, 2, 3, 4, 5, 6, 7, 8);
morton_tests!(morton_fixed, u128, 2, 3, 4, 5, 6, 7, 8);
}