base_2 0.1.0

Exact fixed-point geometry. Float in, float out, zero drift inside.
Documentation
//! Exact coordinate type. 1 unit = 2^-32 micrometers.
//!
//! Resolution: 0.233 picometers — finer than any manufacturing process.
//! Range (i64): ±2km — covers any mechanical assembly or game world.
//! Range (i128): ±9 million km — covers the solar system.
//!
//! No floating point. No epsilon. No drift. Ever.

use std::ops::{Add, Sub, Neg};

// ── CoordInt trait ────────────────────────────────────────────────────────────

/// Marker trait for valid coordinate backing types.
pub trait CoordInt:
    Copy + Clone +
    PartialEq + Eq +
    PartialOrd + Ord +
    Add<Output = Self> +
    Sub<Output = Self> +
    Neg<Output = Self> +
    Send + Sync +
    std::fmt::Debug
{
    const ZERO: Self;
    fn from_i64(v: i64) -> Self;
    fn shift_left(self, bits: u32) -> Self;
    fn shift_right(self, bits: u32) -> Self;
    fn into_i128(self) -> i128;
}

impl CoordInt for i64 {
    const ZERO: Self = 0;
    fn from_i64(v: i64) -> Self { v }
    fn shift_left(self, bits: u32) -> Self { self << bits }
    fn shift_right(self, bits: u32) -> Self { self >> bits }
    fn into_i128(self) -> i128 { i128::from(self) }
}

impl CoordInt for i128 {
    const ZERO: Self = 0;
    fn from_i64(v: i64) -> Self { Self::from(v) }
    fn shift_left(self, bits: u32) -> Self { self << bits }
    fn shift_right(self, bits: u32) -> Self { self >> bits }
    fn into_i128(self) -> i128 { self }
}

// ── Coord ─────────────────────────────────────────────────────────────────────

/// Exact coordinate. 1 unit = 2^-32 micrometers.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Coord<T: CoordInt>(T);

/// 1 unit = 2^-32 µm. Range ±2km. Default for all geometry.
pub type Coord64  = Coord<i64>;

/// 1 unit = 2^-32 µm. Range ±9M km. For astronomical or very large scenes.
pub type Coord128 = Coord<i128>;

impl<T: CoordInt> Coord<T> {
    pub const ZERO: Self = Self(T::ZERO);

    /// Construct from raw integer value.
    pub const fn from_raw(raw: T) -> Self { Self(raw) }

    /// Construct from micrometers.
    pub fn from_um(um: i64) -> Self { Self(T::from_i64(um).shift_left(32)) }

    /// Construct from millimeters.
    pub fn from_mm(mm: i64) -> Self { Self::from_um(mm * 1_000) }

    /// Construct from meters.
    pub fn from_m(m: i64) -> Self { Self::from_um(m * 1_000_000) }

    /// Raw integer value.
    pub const fn raw(self) -> T { self.0 }

    /// Whole micrometers (truncated).
    pub fn whole_um(self) -> T { self.0.shift_right(32) }

    /// Widen to i128 for intermediate arithmetic.
    pub fn into_i128(self) -> i128 { self.0.into_i128() }
}

// ── Float conversions (Coord64 only) ─────────────────────────────────────────

impl Coord64 {
    /// Freeze a float value (in micrometers) to exact `Coord64`.
    /// Error: ≤ 0.233pm, introduced once, never accumulates.
    pub fn from_f32(um: f32) -> Self {
        Self::from_raw((f64::from(um) * (1i64 << 32) as f64) as i64)
    }

    /// Freeze a f64 value (in micrometers) to exact `Coord64`.
    pub fn from_f64(um: f64) -> Self {
        Self::from_raw((um * (1i64 << 32) as f64) as i64)
    }

    /// Derive a float from the exact `Coord64`.
    /// Fresh every call — never accumulates drift.
    pub fn to_f32(self) -> f32 {
        self.0 as f32 / (1i64 << 32) as f32
    }

    /// Derive a f64 from the exact `Coord64`.
    pub fn to_f64(self) -> f64 {
        self.0 as f64 / (1i64 << 32) as f64
    }
}

// ── Arithmetic ────────────────────────────────────────────────────────────────

impl<T: CoordInt> Add for Coord<T> {
    type Output = Self;
    fn add(self, rhs: Self) -> Self { Self(self.0 + rhs.0) }
}

impl<T: CoordInt> Sub for Coord<T> {
    type Output = Self;
    fn sub(self, rhs: Self) -> Self { Self(self.0 - rhs.0) }
}

impl<T: CoordInt> Neg for Coord<T> {
    type Output = Self;
    fn neg(self) -> Self { Self(-self.0) }
}