diff --git a/Cargo.toml b/Cargo.toml index c91eba2..952a559 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] rand = { version = "0.8.5", features = ["small_rng"] } - +itertools = { version = "0.11.0" } diff --git a/src/main.rs b/src/main.rs index 1c912b2..8625dd1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,10 @@ use rand::{Rng, SeedableRng}; use rand::rngs::SmallRng; use rand::distributions::Uniform; +use itertools; +use itertools::Itertools; +use std::ops; + use std::thread; fn main() { @@ -165,18 +169,88 @@ fn render_line(y: i32, small_rng: &mut SmallRng, context: RenderContext, distr: //TODO: Ensure that the compiler hoists the distribution's out as constants // else, do so manually (0..context.image.0).map(|x| { - (0..context.samples_per_pixel).into_iter().fold( - Vec3::zero(), - |color, _sample| { - let u = ((x as f32) + small_rng.sample(distr.distrib_zero_one)) / ((context.image.0 - 1) as f32); - let v = ((y as f32) + small_rng.sample(distr.distrib_zero_one)) / ((context.image.1 - 1) as f32); - let ray = context.camera.get_ray(u, v, small_rng); - color + ray_color(ray, &context.world, context.max_depth, small_rng, distr.distrib_plusminus_one) - } - ) + sample_pixel(x, y, small_rng, &context, distr) }).collect() } +fn sample_pixel(x: i32, y: i32, small_rng: &mut SmallRng, context: &RenderContext, distr: &DistributionContianer) -> Vec3{ + (0..context.samples_per_pixel).into_iter().fold( + Vec3::zero(), + |color, _sample| { + let u = ((x as f32) + small_rng.sample(distr.distrib_zero_one)) / ((context.image.0 - 1) as f32); + let v = ((y as f32) + small_rng.sample(distr.distrib_zero_one)) / ((context.image.1 - 1) as f32); + let ray = context.camera.get_ray(u, v, small_rng); + color + ray_color(ray, &context.world, context.max_depth, small_rng, distr.distrib_plusminus_one) + } + ) +} + +fn range2d(bounds: (i32, i32, i32, i32)) -> impl Iterator { + let rheight = bounds.1..(bounds.1+bounds.3); + rheight.flat_map(move |y| { + let rwidth = bounds.0..(bounds.0+bounds.2); + rwidth.map( move |x| { + (x, y) + }) + }) +} + +#[derive (Copy, Clone)] +struct Rect { + x: i32, + y: i32, + w: i32, + h: i32, +} + +/* Iterable that produces pixels left-to-right, top-to-bottom. + * `Tile`s represent the render space, not the finished image. + * There is no internal pixel buffer + */ + +type TileCursorIter = itertools::Product, ops::Range>; + +struct Tile { + bounds: Rect, + context: RenderContext, + small_rng: SmallRng, + rand_distr: DistributionContianer, + cursor: TileCursorIter, +} + +impl Tile{ + fn new( + bounds: Rect, + context: RenderContext, + small_rng: SmallRng, + rand_distr: DistributionContianer + ) -> Self + { + Tile { bounds, context, small_rng, rand_distr, + cursor: (bounds.x..(bounds.x + bounds.w)) + .cartesian_product(bounds.y..(bounds.y + bounds.h) + ) + } + + } +} + +impl Iterator for Tile { + type Item = Vec3; + fn next(&mut self) -> Option { + if let Some((x, y)) = self.cursor.next(){ + Some(sample_pixel( + x, y, + &mut self.small_rng, + &self.context, + &self.rand_distr, + )) + } else { + None + } + } +} + fn ray_color(r: Ray, world: &Hittable, depth: u32, srng: &mut SmallRng, distrib: Uniform ) -> Vec3 { // recursion depth guard if depth == 0 {