Put hilbert and morton code into one module.

This commit is contained in:
Nathan Vegdahl 2022-07-23 12:40:44 -07:00
parent 0df18ce908
commit ff6d879dc6
5 changed files with 166 additions and 164 deletions

View File

@ -1,77 +0,0 @@
#![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);
}
}

View File

@ -23,13 +23,11 @@ mod camera;
mod color;
mod fp_utils;
mod hash;
mod hilbert;
mod image;
mod lerp;
mod light;
mod math;
mod mis;
mod morton;
mod parse;
mod ray;
mod renderer;
@ -37,6 +35,7 @@ mod sampling;
mod scene;
mod scramble;
mod shading;
mod space_fill;
mod surface;
mod timer;
mod tracer;

View File

@ -1,81 +0,0 @@
#![allow(dead_code)]
const N: u32 = 1 << 16;
#[inline(always)]
fn part_1_by_1(mut x: u32) -> u32 {
x &= 0x0000ffff;
x = (x ^ (x << 8)) & 0x00ff00ff;
x = (x ^ (x << 4)) & 0x0f0f0f0f;
x = (x ^ (x << 2)) & 0x33333333;
x = (x ^ (x << 1)) & 0x55555555;
x
}
#[inline(always)]
fn part_1_by_2(mut x: u32) -> u32 {
x &= 0x000003ff;
x = (x ^ (x << 16)) & 0xff0000ff;
x = (x ^ (x << 8)) & 0x0300f00f;
x = (x ^ (x << 4)) & 0x030c30c3;
x = (x ^ (x << 2)) & 0x09249249;
x
}
#[inline(always)]
fn compact_1_by_1(mut x: u32) -> u32 {
x &= 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
x = (x ^ (x >> 1)) & 0x33333333;
x = (x ^ (x >> 2)) & 0x0f0f0f0f;
x = (x ^ (x >> 4)) & 0x00ff00ff;
x = (x ^ (x >> 8)) & 0x0000ffff;
x
}
#[inline(always)]
fn compact_1_by_2(mut x: u32) -> u32 {
x &= 0x09249249;
x = (x ^ (x >> 2)) & 0x030c30c3;
x = (x ^ (x >> 4)) & 0x0300f00f;
x = (x ^ (x >> 8)) & 0xff0000ff;
x = (x ^ (x >> 16)) & 0x000003ff;
x
}
/// Convert (x,y) to morton 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 morton curve index corresponding to the (x,y) coordinates given.
pub fn xy2i(x: u32, y: u32) -> u32 {
debug_assert!(x < N);
debug_assert!(y < N);
part_1_by_1(x) | (part_1_by_1(y) << 1)
}
/// Convert morton curve index to (x,y).
///
/// i: The morton curve index.
///
/// Returns the (x, y) coords at the given index.
pub fn i2xy(i: u32) -> (u32, u32) {
(compact_1_by_1(i), compact_1_by_1(i >> 1))
}
// TODO: 3D morton curves.
#[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);
}
}

View File

@ -13,14 +13,13 @@ use crate::{
accel::ACCEL_NODE_RAY_TESTS,
color::{map_0_1_to_wavelength, SpectralSample, XYZ},
fp_utils::robust_ray_origin,
hilbert,
image::Image,
math::{probit, upper_power_of_two, Float4},
mis::power_heuristic,
morton,
ray::{Ray, RayBatch},
scene::{Scene, SceneLightSample},
scramble::owen4,
space_fill::{hilbert, morton},
surface,
timer::Timer,
tracer::Tracer,
@ -154,7 +153,7 @@ impl<'a> Renderer<'a> {
pow2 * pow2
};
for hilbert_d in 0..bucket_n {
let (bx, by) = hilbert::i2xy(hilbert_d);
let (bx, by) = hilbert::decode(hilbert_d);
let x = bx as usize * bucket_w;
let y = by as usize * bucket_h;
@ -249,7 +248,7 @@ impl<'a> Renderer<'a> {
// and golden ratio sampling, we do this up-front here rather than in
// the samplers themselves.
let si_offset =
owen4(morton::xy2i(x, y), self.seed).wrapping_mul(self.spp as u32);
owen4(morton::encode(x, y), self.seed).wrapping_mul(self.spp as u32);
for si in 0..self.spp {
let si = (si as u32).wrapping_add(si_offset);

162
src/space_fill.rs Normal file
View File

@ -0,0 +1,162 @@
//! Space-filling curves and other related functionality.
#![allow(dead_code)]
const N: u32 = 1 << 16;
pub mod hilbert {
use super::hilbert_rotate;
/// Convert (x,y) to hilbert curve index.
///
/// x: The x coordinate. Must be no greater than 2^16-1.
/// y: The y coordinate. Must be no greater than 2^16-1.
///
/// Returns the hilbert curve index corresponding to the (x,y) coordinates given.
pub fn encode(x: u32, y: u32) -> u32 {
assert!(x < super::N);
assert!(y < super::N);
let (mut x, mut y) = (x, y);
let mut d = 0;
let mut s = super::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);
hilbert_rotate(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 decode(d: u32) -> (u32, u32) {
let (mut x, mut y) = (0, 0);
let mut s = 1;
let mut t = d;
while s < super::N {
let rx = 1 & (t >> 1);
let ry = 1 & (t ^ rx);
hilbert_rotate(s, rx, ry, &mut x, &mut y);
x += s * rx;
y += s * ry;
t >>= 2;
s <<= 1;
}
(x, y)
}
}
pub mod morton {
use super::{compact_1_by_1, part_1_by_1};
/// Convert (x,y) to morton curve index.
///
/// x: The x coordinate. Should be no greater than 2^16-1.
/// y: The y coordinate. Should be no greater than 2^16-1.
///
/// Returns the morton curve index corresponding to the (x,y) coordinates given.
pub fn encode(x: u32, y: u32) -> u32 {
debug_assert!(x < super::N);
debug_assert!(y < super::N);
part_1_by_1(x) | (part_1_by_1(y) << 1)
}
/// Convert morton curve index to (x,y).
///
/// i: The morton curve index.
///
/// Returns the (x, y) coords at the given index.
pub fn decode(i: u32) -> (u32, u32) {
(compact_1_by_1(i), compact_1_by_1(i >> 1))
}
}
//-----------------------------------------------------------
// Utility functions used in the sub-modules above.
fn hilbert_rotate(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);
}
}
#[inline(always)]
fn part_1_by_1(mut x: u32) -> u32 {
x &= 0x0000ffff;
x = (x ^ (x << 8)) & 0x00ff00ff;
x = (x ^ (x << 4)) & 0x0f0f0f0f;
x = (x ^ (x << 2)) & 0x33333333;
x = (x ^ (x << 1)) & 0x55555555;
x
}
#[inline(always)]
fn compact_1_by_1(mut x: u32) -> u32 {
x &= 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
x = (x ^ (x >> 1)) & 0x33333333;
x = (x ^ (x >> 2)) & 0x0f0f0f0f;
x = (x ^ (x >> 4)) & 0x00ff00ff;
x = (x ^ (x >> 8)) & 0x0000ffff;
x
}
#[inline(always)]
fn part_1_by_2(mut x: u32) -> u32 {
x &= 0x000003ff;
x = (x ^ (x << 16)) & 0xff0000ff;
x = (x ^ (x << 8)) & 0x0300f00f;
x = (x ^ (x << 4)) & 0x030c30c3;
x = (x ^ (x << 2)) & 0x09249249;
x
}
#[inline(always)]
fn compact_1_by_2(mut x: u32) -> u32 {
x &= 0x09249249;
x = (x ^ (x >> 2)) & 0x030c30c3;
x = (x ^ (x >> 4)) & 0x0300f00f;
x = (x ^ (x >> 8)) & 0xff0000ff;
x = (x ^ (x >> 16)) & 0x000003ff;
x
}
//-------------------------------------------------------------
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hilbert_reversible() {
let i = 0x4c8587a2;
let (x, y) = hilbert::decode(i);
let i2 = hilbert::encode(x, y);
assert_eq!(i, i2);
}
#[test]
fn morton_reversible() {
let i = 0x4c8587a2;
let (x, y) = morton::decode(i);
let i2 = morton::encode(x, y);
assert_eq!(i, i2);
}
}