use std::{u64, i64, u32, i32};
use std::default::Default;
pub trait CoordSpace {
type Coord;
fn raw(&self, coord: Self::Coord) -> [u64; 3];
}
macro_rules! create_map_space {
($name:ident, $comp:ty, $map:expr) => {
#[derive(Copy, Clone, Debug)]
pub struct $name;
impl CoordSpace for $name {
type Coord = [$comp; 3];
fn raw(&self, coord: [$comp; 3]) -> [u64; 3] {
map_coord(coord, $map)
}
}
}
}
pub trait ZeroCoord {
fn zero_coord() -> Self;
}
macro_rules! impl_zero_coord {
($int:ty, $zero:expr) => {
impl ZeroCoord for [$int; 3] {
fn zero_coord() -> Self {
[$zero; 3]
}
}
}
}
macro_rules! impl_unit_like_default {
($t:ty) => {
impl Default for $t {
fn default() -> Self {
use std::mem;
unsafe { mem::transmute::<(), $t>(()) }
}
}
}
}
pub fn map_coord<A, B>(a: [A; 3], mut map: impl FnMut(A) -> B) -> [B; 3] {
let [a0, a1, a2] = a;
[map(a0), map(a1), map(a2)]
}
const U56_MASK: u64 = 0x0FFFFFFF;
create_map_space!(U56Space, u64, |c| c & U56_MASK);
impl_zero_coord!(u64, 0);
impl_unit_like_default!(U56Space);
create_map_space!(U32Space, u32, |c| c as u64);
impl_zero_coord!(u32, 0);
impl_unit_like_default!(U32Space);
create_map_space!(I56Space, i64, |c| (c as i128 - i64::MIN as i128) as u64 & U56_MASK);
impl_zero_coord!(i64, 0);
impl_unit_like_default!(I56Space);
create_map_space!(I32Space, i32, |c| (c as i64 - i32::MIN as i64) as u64 & U56_MASK);
impl_zero_coord!(i32, 0);
impl_unit_like_default!(I32Space);
#[derive(Copy, Clone, Debug)]
pub struct F64Space {
pub range: f64
}
impl CoordSpace for F64Space {
type Coord = [f64; 3];
fn raw(&self, coord: [f64; 3]) -> [u64; 3] {
map_coord(coord, |c| {
let scaled = c * ((u64::MAX & U56_MASK) as f64 / self.range);
(scaled as i128 - i64::MIN as i128) as u64 & U56_MASK
})
}
}
#[derive(Copy, Clone, Debug)]
pub struct F32Space {
pub range: f32
}
impl CoordSpace for F32Space {
type Coord = [f32; 3];
fn raw(&self, coord: [f32; 3]) -> [u64; 3] {
map_coord(coord, |c| {
let scaled = c * ((u64::MAX & U56_MASK) as f32 / self.range);
(scaled as i128 - i32::MIN as i128) as u64 & U56_MASK
})
}
}