use crate::{error, ops, point, spacecurve::SpaceCurve, spec::GridSpec};
#[derive(Debug)]
pub struct ZOrder {
pub bitwidth: u32,
pub dimension: u32,
length: u32,
}
impl ZOrder {
pub fn from_dimensions(dimension: u32, size: u32) -> error::Result<Self> {
let spec = GridSpec::power_of_two(dimension, size)?;
spec.require_index_bits_lt(32)?;
let bitwidth = spec.bits_per_axis().unwrap();
Ok(Self {
dimension: spec.dimension(),
bitwidth,
length: spec.length(),
})
}
}
impl SpaceCurve for ZOrder {
fn name(&self) -> &'static str {
"Z-order (Morton)"
}
fn info(&self) -> &'static str {
"Interleaves coordinate bits to form keys (Morton code).\n\
Extremely fast and pairs well with quad/oct-trees, but preserves\n\
neighborhood worse than Hilbert/H-curve and may exhibit long jumps."
}
fn length(&self) -> u32 {
self.length
}
fn dimensions(&self) -> u32 {
self.dimension
}
fn point(&self, index: u32) -> point::Point {
debug_assert!(index < self.length, "index out of range");
point::Point::new_with_dimension(
self.dimension,
ops::deinterleave_lsb(self.dimension, self.bitwidth, index),
)
}
fn index(&self, p: &point::Point) -> u32 {
debug_assert_eq!(p.len(), self.dimension as usize, "point dimension mismatch");
let side = if self.bitwidth == 0 {
1
} else {
1u32 << self.bitwidth
};
debug_assert!(
p.iter().all(|&coord| coord < side),
"point coordinate out of bounds"
);
ops::interleave_lsb(&p[..], self.bitwidth)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_dimensions_guard() {
assert!(ZOrder::from_dimensions(2, 1u32 << 16).is_err());
assert!(ZOrder::from_dimensions(2, 1u32 << 15).is_ok());
assert!(ZOrder::from_dimensions(4, 1u32 << 8).is_err());
assert!(ZOrder::from_dimensions(4, 1u32 << 7).is_ok());
}
#[test]
fn sequence_matches_reference_2d() {
let curve = ZOrder::from_dimensions(2, 4).unwrap();
let expected = vec![
vec![0, 0],
vec![1, 0],
vec![0, 1],
vec![1, 1],
vec![2, 0],
vec![3, 0],
vec![2, 1],
vec![3, 1],
vec![0, 2],
vec![1, 2],
vec![0, 3],
vec![1, 3],
vec![2, 2],
vec![3, 2],
vec![2, 3],
vec![3, 3],
];
for (idx, coords) in expected.iter().enumerate() {
assert_eq!(Vec::<u32>::from(curve.point(idx as u32)), *coords);
}
}
#[test]
fn roundtrip_holds_for_small_cases() {
let curve = ZOrder::from_dimensions(3, 4).unwrap();
for i in 0..curve.length() {
let point = curve.point(i);
assert_eq!(curve.index(&point), i);
}
}
#[test]
fn roundtrip_dims_up_to_four() {
for dim in 1..=4 {
let curve = ZOrder::from_dimensions(dim, 2).unwrap();
for i in 0..curve.length() {
let point = curve.point(i);
assert_eq!(
curve.index(&point),
i,
"roundtrip failed for dim {dim} at {i}"
);
}
}
}
}