#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MortonCode(pub u64);
impl MortonCode {
pub fn new(morton_code: u64) -> Self {
MortonCode(morton_code)
}
pub fn from_nds_coordinates(x: i32, y: i32) -> MortonCode {
let x_base: i64 = 1 << 31;
let y_base: i64 = 1 << 30;
let mut x: i64 = x as i64;
let mut y: i64 = y as i64;
while x >= x_base {
x -= 1 << 32;
}
while x < -x_base {
x += 1 << 32;
}
while y >= y_base {
y -= 1 << 31;
}
while y < -y_base {
y += 1 << 31;
}
let mut x: u64 = x as u64;
let mut y: u64 = y as u64;
let mut bit: u64 = 1;
let mut morton_code: u64 = 0;
y = y.wrapping_shl(1);
for _ in 0..31 {
morton_code |= x & bit;
x = x.wrapping_shl(1);
bit = bit.wrapping_shl(1);
morton_code |= y & bit;
y = y.wrapping_shl(1);
bit = bit.wrapping_shl(1);
}
morton_code |= x & bit;
morton_code &= !(1u64 << 63);
MortonCode(morton_code)
}
pub fn to_nds_coordinates(&self) -> (i32, i32) {
const YBASE: i64 = 1 << 30;
const XBASE: i64 = 1 << 31;
let mut bit: u64 = 1;
let mut morton_code: u64 = self.0;
let mut x: u64 = 0;
let mut y: u64 = 0;
for _ in 0..31 {
x |= morton_code & bit;
morton_code >>= 1;
y |= morton_code & bit;
bit <<= 1;
}
x |= morton_code & bit;
let mut x = x as i64;
let mut y = y as i64;
if y >= YBASE {
y -= 1 << 31;
}
if x >= XBASE {
x -= 1 << 32;
}
(x as i32, y as i32)
}
pub fn value(&self) -> u64 {
self.0
}
}
impl std::fmt::Display for MortonCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MortonCode(value={})", self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn round_trip(x: i32, y: i32, expected: u64) {
let m = MortonCode::from_nds_coordinates(x, y);
assert_eq!(m.value(), expected, "encode ({x},{y})");
let (dx, dy) = m.to_nds_coordinates();
assert_eq!((dx, dy), (x, y), "decode ({x},{y})");
}
#[test]
fn small_values() {
round_trip(0, 0, 0);
round_trip(1, 0, 1);
round_trip(0, 1, 2);
round_trip(1, 1, 3);
round_trip(12345, 6789, 126387555);
}
#[test]
fn negative_values() {
round_trip(-12345, -6789, 9223372036728388255);
round_trip(i32::MIN, -(1i32 << 30), 6917529027641081856);
}
#[test]
fn extremes() {
round_trip(i32::MAX, (1i32 << 30) - 1, 2305843009213693951);
round_trip(1000000000, -1000000000, 2694675840375717888);
}
#[test]
fn bit63_masked() {
for &(x, y) in &[
(0, 0),
(-1, -1),
(i32::MAX, (1i32 << 30) - 1),
(i32::MIN, -(1i32 << 30)),
] {
let m = MortonCode::from_nds_coordinates(x, y);
assert_eq!(m.value() & (1u64 << 63), 0);
}
}
}