Implement "hilbert spiral" bucket rendering order.

This commit is contained in:
Nathan Vegdahl 2022-08-02 23:58:56 -07:00
parent 7c750dcded
commit 77ac8ef9f2
2 changed files with 96 additions and 12 deletions

View File

@ -118,18 +118,32 @@ impl<'a> Renderer<'a> {
let _ = io::stdout().flush(); let _ = io::stdout().flush();
// Populate job queue // Populate job queue
let bucket_n = { let hilbert_count = (thread_count as f32).sqrt() as u32;
let bucket_count_x = (width / bucket_size) + 1; let (middle_bucket, bucket_n) = {
let bucket_count_y = (height / bucket_size) + 1; let bucket_count_x =
(width / bucket_size) + if (width % bucket_size) != 0 { 1 } else { 0 };
let bucket_count_y =
(height / bucket_size) + if (height % bucket_size) != 0 { 1 } else { 0 };
let larger = cmp::max(bucket_count_x, bucket_count_y); let larger = cmp::max(bucket_count_x, bucket_count_y);
let pow2 = upper_power_of_two(larger); let pow2 = upper_power_of_two(larger);
pow2 * pow2 let buffer = hilbert_count; // For hilbert spiral.
(
(bucket_count_x / 2, bucket_count_y / 2),
(pow2 + buffer) * (pow2 + buffer),
)
}; };
for hilbert_d in 0..bucket_n { for hilbert_spiral_i in 0..bucket_n {
let (bx, by) = hilbert::decode(hilbert_d); let (bx, by) =
crate::space_fill::hilbert_spiral::decode(hilbert_spiral_i, hilbert_count);
let x = bx * bucket_size; let bx = middle_bucket.0 as i32 + bx;
let y = by * bucket_size; let by = middle_bucket.1 as i32 + by;
if bx < 0 || by < 0 {
continue;
}
let x = bx as u32 * bucket_size;
let y = by as u32 * bucket_size;
let w = if width >= x { let w = if width >= x {
min(bucket_size, width - x) min(bucket_size, width - x)
} else { } else {

View File

@ -11,13 +11,13 @@ pub mod hilbert {
/// y: The y 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. /// Returns the hilbert curve index corresponding to the (x,y) coordinates given.
pub fn encode(x: u32, y: u32) -> u32 { pub fn encode(x: u32, y: u32, n: u32) -> u32 {
assert!(x < N); assert!(x < N);
assert!(y < N); assert!(y < N);
let (mut x, mut y) = (x, y); let (mut x, mut y) = (x, y);
let mut d = 0; let mut d = 0;
let mut s = N >> 1; let mut s = n >> 1;
while s > 0 { while s > 0 {
let rx = if (x & s) > 0 { 1 } else { 0 }; let rx = if (x & s) > 0 { 1 } else { 0 };
let ry = if (y & s) > 0 { 1 } else { 0 }; let ry = if (y & s) > 0 { 1 } else { 0 };
@ -35,11 +35,11 @@ pub mod hilbert {
/// d: The hilbert curve index. /// d: The hilbert curve index.
/// ///
/// Returns the (x, y) coords at the given index. /// Returns the (x, y) coords at the given index.
pub fn decode(d: u32) -> (u32, u32) { pub fn decode(d: u32, n: u32) -> (u32, u32) {
let (mut x, mut y) = (0, 0); let (mut x, mut y) = (0, 0);
let mut s = 1; let mut s = 1;
let mut t = d; let mut t = d;
while s < N { while s < n {
let rx = 1 & (t >> 1); let rx = 1 & (t >> 1);
let ry = 1 & (t ^ rx); let ry = 1 & (t ^ rx);
(x, y) = hilbert_rotate(s, rx, ry, x, y); (x, y) = hilbert_rotate(s, rx, ry, x, y);
@ -118,6 +118,76 @@ pub mod morton {
} }
} }
/// Yields coordinates in outward spiral, but incorporating a Hilbert
/// curve at the smaller scales.
pub mod hilbert_spiral {
/// Convert from hilbert-spiral index to (x,y).
///
/// Note: this returns both negative and positive coordinates.
/// It starts at 0,0 and spirals outwards.
///
/// i: The hilbert-spiral index.
/// hilbert_size: the size of the hulbert blocks on a side. Will be
/// rounded down to the nearest power of two.
///
/// Returns the (x, y) coords at the given index.
pub fn decode(i: u32, hilbert_size: u32) -> (i32, i32) {
assert!(hilbert_size > 0);
let hilbert_size = 1 << (31 - u32::leading_zeros(hilbert_size));
let hilbert_cells = hilbert_size * hilbert_size;
let hilbert_i = i % hilbert_cells;
let spiral_i = i / hilbert_cells;
let (mut sx, mut sy, section) = decode_spiral(spiral_i);
sx = (sx * hilbert_size as i32) - (hilbert_size / 2) as i32;
sy = (sy * hilbert_size as i32) - (hilbert_size / 2) as i32;
let (hx, hy) = {
let (hx, hy) = super::hilbert::decode(hilbert_i, hilbert_size);
let a = hilbert_size - 1;
match section {
0 => (hx, a - hy),
1 => (a - hy, hx),
2 => (a - hx, hy),
3 => (hy, a - hx),
_ => unreachable!(),
}
};
(sx + hx as i32, sy + hy as i32)
}
pub fn decode_spiral(i: u32) -> (i32, i32, u32) {
if i == 0 {
return (0, 0, 3);
}
// 0 = first ring outside of center, 1 = second, and so on.
let ring = (((i as f64).sqrt() - 1.0) / 2.0) as u32;
// The size of the ring along one side.
let size = 1 + ((ring + 1) * 2);
let n = i - ((size - 2) * (size - 2)); // The zero-indexed cell of the ring.
let arm = n / (size - 1); // The arm of the ring.
let arm_n = n % (size - 1); // The index within the arm of the ring.
// The two coordinates. They just need to be flipped around depending on the arm.
let radius = ring as i32 + 1;
let d = -(size as i32 / 2) + 1 + arm_n as i32;
match arm {
0 => (d, -radius, 0),
1 => (radius, d, if arm_n == (size - 2) { 2 } else { 1 }),
2 => (-d, radius, 2),
3 => (-radius, -d, 3),
_ => unreachable!(),
}
}
}
//------------------------------------------------------------- //-------------------------------------------------------------
#[cfg(test)] #[cfg(test)]