diff --git a/src/hilbert.rs b/src/hilbert.rs new file mode 100644 index 0000000..d590364 --- /dev/null +++ b/src/hilbert.rs @@ -0,0 +1,87 @@ +#![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) { + if ry == 0 { + if rx == 1 { + *x = (n - 1).wrapping_sub(*x); + *y = (n - 1).wrapping_sub(*y); + } + let t = *x; + *x = *y; + *y = t; + } +} + +/// 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 xy2d(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 d2xy(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) = d2xy(d); + let d2 = xy2d(x, y); + + assert_eq!(d, d2); + } +} diff --git a/src/main.rs b/src/main.rs index 6567e40..96fceff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ extern crate lodepng; extern crate nom; mod math; +mod hilbert; mod algorithm; mod lerp; mod float4; diff --git a/src/math/mod.rs b/src/math/mod.rs index 036a1fb..646b619 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -39,7 +39,18 @@ pub fn fast_ln(x: f32) -> f32 { } +/// Rounds an integer up to the next power of two. +pub fn upper_power_of_two(mut v: u32) -> u32 { + v -= 1; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v + 1 +} +/// Gets the log base 2 of the given integer pub fn log2_64(value: u64) -> u64 { const TAB64: [u64; 64] = [63, 0, 58, 1, 59, 47, 53, 2, 60, 39, 48, 27, 54, 33, 42, 3, 61, 51, 37, 40, 49, 18, 28, 20, 55, 30, 34, 11, 43, 14, 22, 4, 62, 57, 46, diff --git a/src/renderer.rs b/src/renderer.rs index 302e30f..4f82570 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,3 +1,4 @@ +use std::cmp; use std::io::{self, Write}; use std::path::Path; use std::cmp::min; @@ -10,7 +11,8 @@ use algorithm::partition_pair; use ray::Ray; use tracer::Tracer; use halton; -use math::fast_logit; +use hilbert; +use math::{fast_logit, upper_power_of_two}; use image::Image; use surface; use scene::Scene; @@ -184,20 +186,27 @@ impl Renderer { }; // Populate job queue - for by in 0..((img_height / bucket_h) + 1) { - for bx in 0..((img_width / bucket_w) + 1) { - let x = bx * bucket_w; - let y = by * bucket_h; - let w = min(bucket_w, img_width - x); - let h = min(bucket_h, img_height - y); - if w > 0 && h > 0 { - job_queue.push(BucketJob { - x: x as u32, - y: y as u32, - w: w as u32, - h: h as u32, - }); - } + let bucket_n = { + let bucket_count_x = ((img_width / bucket_w) + 1) as u32; + let bucket_count_y = ((img_height / bucket_h) + 1) as u32; + let larger = cmp::max(bucket_count_x, bucket_count_y); + let pow2 = upper_power_of_two(larger); + pow2 * pow2 + }; + for hilbert_d in 0..bucket_n { + let (bx, by) = hilbert::d2xy(hilbert_d); + + let x = bx as usize * bucket_w; + let y = by as usize * bucket_h; + let w = min(bucket_w, img_width - x); + let h = min(bucket_h, img_height - y); + if x < img_width && y < img_height && w > 0 && h > 0 { + job_queue.push(BucketJob { + x: x as u32, + y: y as u32, + w: w as u32, + h: h as u32, + }); } }