psychopath/src/hilbert.rs
Nathan Vegdahl 83b48f722d Simpler way to implement screen-space blue-noise sampling.
We now do the index scrambling at the top of the sampling loop,
which is also faster since we only have to run it once per pixel
instead of once per sample.
2022-07-20 18:54:38 -07:00

78 lines
1.6 KiB
Rust

#![allow(dead_code)]
const N: u32 = 1 << 16;
// Utility function used by the functions below.
fn hil_rot(n: u32, rx: u32, ry: u32, x: &mut u32, y: &mut u32) {
use std::mem;
if ry == 0 {
if rx == 1 {
*x = (n - 1).wrapping_sub(*x);
*y = (n - 1).wrapping_sub(*y);
}
mem::swap(x, y);
}
}
/// Convert (x,y) to hilbert curve index.
///
/// x: The x coordinate. Must be a positive integer no greater than 2^16-1.
/// y: The y coordinate. Must be a positive integer no greater than 2^16-1.
///
/// Returns the hilbert curve index corresponding to the (x,y) coordinates given.
pub fn xy2i(x: u32, y: u32) -> u32 {
assert!(x < N);
assert!(y < N);
let (mut x, mut y) = (x, y);
let mut d = 0;
let mut s = N >> 1;
while s > 0 {
let rx = if (x & s) > 0 { 1 } else { 0 };
let ry = if (y & s) > 0 { 1 } else { 0 };
d += s * s * ((3 * rx) ^ ry);
hil_rot(s, rx, ry, &mut x, &mut y);
s >>= 1
}
d
}
/// Convert hilbert curve index to (x,y).
///
/// d: The hilbert curve index.
///
/// Returns the (x, y) coords at the given index.
pub fn i2xy(d: u32) -> (u32, u32) {
let (mut x, mut y) = (0, 0);
let mut s = 1;
let mut t = d;
while s < N {
let rx = 1 & (t >> 1);
let ry = 1 & (t ^ rx);
hil_rot(s, rx, ry, &mut x, &mut y);
x += s * rx;
y += s * ry;
t >>= 2;
s <<= 1;
}
(x, y)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reversible() {
let d = 54;
let (x, y) = i2xy(d);
let d2 = xy2i(x, y);
assert_eq!(d, d2);
}
}