#![no_std]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct Point {
pub x: u16,
pub y: u16,
}
impl From<(u16, u16)> for Point {
fn from(p: (u16, u16)) -> Self {
Self { x: p.0, y: p.1 }
}
}
impl From<[u16; 2]> for Point {
fn from(p: [u16; 2]) -> Self {
Self { x: p[0], y: p[1] }
}
}
impl From<Point> for (u16, u16) {
fn from(p: Point) -> (u16, u16) {
(p.x, p.y)
}
}
impl From<Point> for [u16; 2] {
fn from(p: Point) -> Self {
[p.x, p.y]
}
}
impl Point {
pub const ORIGIN: Self = Self { x: 0, y: 0 };
const fn swap_xy(self) -> Self {
Self {
x: self.y,
y: self.x,
}
}
const fn mirror_xy(self, n: u16) -> Self {
Self {
x: n - 1 - self.x,
y: n - 1 - self.y,
}
}
const fn rot(self, n: u16, rx: bool, ry: bool) -> Self {
match (rx, ry) {
(true, false) => self.mirror_xy(n).swap_xy(),
(false, false) => self.swap_xy(),
_ => self,
}
}
const fn shift(self, s: u8, rx: bool, ry: bool) -> Self {
Self {
x: self.x + ((rx as u16) << s),
y: self.y + ((ry as u16) << s),
}
}
const fn rot_shift(self, s: u8, rx: bool, ry: bool) -> Self {
self.rot(1 << s, rx, ry).shift(s, rx, ry)
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Curve {
num_pnts: u32,
side_len: u16,
order: u8,
}
impl Default for Curve {
fn default() -> Self {
Self::new(0).unwrap()
}
}
impl Curve {
#[must_use]
pub fn new(order: u8) -> Option<Self> {
1_u16.checked_shl(order.into()).map(|side_len| {
let num_pnts = u32::from(side_len).pow(2);
Self {
order,
side_len,
num_pnts,
}
})
}
#[must_use]
pub const fn order(self) -> u8 {
self.order
}
#[must_use]
pub const fn side_len(self) -> u16 {
self.side_len
}
#[must_use]
pub const fn num_pnts(self) -> u32 {
self.num_pnts
}
#[must_use]
pub fn point_at(self, d: u32) -> Option<Point> {
(self.num_pnts - 1).checked_sub(d)?;
Some(self.wrapping_point_at(d))
}
pub fn dist_at<P>(self, p: P) -> Option<u32>
where
P: Into<Point>,
{
self.dist_at_impl(p.into())
}
fn dist_at_impl(self, p: Point) -> Option<u32> {
(self.side_len - 1).checked_sub(p.x)?;
(self.side_len - 1).checked_sub(p.y)?;
Some(self.wrapping_dist_at_impl(p))
}
#[must_use]
pub fn wrapping_point_at(self, d: u32) -> Point {
let (p, _) = (0..self.order).fold((Point::ORIGIN, d), |(p, t), s| {
let half = t / 2;
let rx = half % 2 == 1;
let ry = half % 2 != t % 2;
let p = p.rot_shift(s, rx, ry);
let t = t / 4;
(p, t)
});
p
}
pub fn wrapping_dist_at<P>(self, p: P) -> u32
where
P: Into<Point>,
{
self.wrapping_dist_at_impl(p.into())
}
fn wrapping_dist_at_impl(self, p: Point) -> u32 {
let (d, _) = (0..self.order).rev().fold((0, p), |(d, p), s| {
let rx = p.x & (1 << s) != 0;
let ry = p.y & (1 << s) != 0;
let p = p.rot(self.side_len, rx, ry);
let t = (3 * u32::from(rx)) ^ u32::from(ry);
let d = d + (t << (2 * s));
(d, p)
});
d
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn various_hilbert_orders() {
for order in 0..=8 {
let curve = Curve::new(order).unwrap();
let num_pnts = curve.num_pnts();
let side_len = curve.side_len();
for dist in 0..num_pnts {
let p = curve.point_at(dist).unwrap();
assert_eq!(dist, curve.dist_at(p).unwrap());
assert!(p.x < side_len);
assert!(p.y < side_len);
}
}
}
}