diff --git a/src/lib.rs b/src/lib.rs index 9f0a770..a3c0169 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,7 @@ pub use crate::mandelbrot::Bounds; pub use crate::mandelbrot::Mandelbrot; + +pub const TEXTURE_WIDTH: usize = 1280; +pub const TEXTURE_HEIGHT: usize = 720; + mod mandelbrot; diff --git a/src/main.rs b/src/main.rs index 2069a27..5f22250 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use bevy::{prelude::*, render::texture::TextureFormat}; -use mandelbrot::{Bounds, Mandelbrot}; +use mandelbrot::{Bounds, Mandelbrot, TEXTURE_HEIGHT, TEXTURE_WIDTH}; use std::time::Instant; fn main() { @@ -110,12 +110,12 @@ fn startup( mut textures: ResMut>, mut materials: ResMut>, ) { - let img_size = Vec2::new(Mandelbrot::width() as f32, Mandelbrot::height() as f32); - let img_buf = Mandelbrot::new().image().to_vec(); + let texture_size = Vec2::new(TEXTURE_WIDTH as f32, TEXTURE_HEIGHT as f32); + let texture_data = Mandelbrot::new().image().to_vec(); let texture_handle = textures.add(Texture::new( - img_size, - img_buf, + texture_size, + texture_data, TextureFormat::Rgba8UnormSrgb, )); diff --git a/src/mandelbrot.rs b/src/mandelbrot.rs index 17019d1..47d4ce1 100644 --- a/src/mandelbrot.rs +++ b/src/mandelbrot.rs @@ -1,6 +1,10 @@ +use crate::{TEXTURE_HEIGHT, TEXTURE_WIDTH}; use num_complex::Complex; use rayon::prelude::*; +const DEFAULT_ITERATION_LIMIT: u32 = 64; +const DEFAULT_BOUNDS: Bounds = Bounds::new((-2.5, 1.0), (-1.0, 1.0)); + pub struct Coordinate(usize, usize); impl Coordinate { @@ -36,73 +40,59 @@ impl Bounds { } } -const DEFAULT_MAX_ITERATIONS: u32 = 64; -const DEFAULT_BOUNDS: Bounds = Bounds::new((-2.5, 1.0), (-1.0, 1.0)); - #[derive(Debug, Clone)] pub struct Mandelbrot { - texture_buffer: Vec, + frame_buffer: Box<[u8; (TEXTURE_WIDTH * TEXTURE_HEIGHT) * 4]>, } impl Mandelbrot { - const IMG_WIDTH: usize = 1280; - const IMG_HEIGHT: usize = 720; - pub fn new() -> Self { Mandelbrot { - texture_buffer: vec![0; (Self::IMG_WIDTH * Self::IMG_HEIGHT) * 4], + frame_buffer: Box::new([0; (TEXTURE_WIDTH * TEXTURE_HEIGHT) * 4]), } } - pub fn width() -> usize { - Self::IMG_WIDTH - } - - pub fn height() -> usize { - Self::IMG_HEIGHT - } - pub fn image(&mut self) -> &[u8] { - let max_iters = DEFAULT_MAX_ITERATIONS as f64; + let limit_f64 = DEFAULT_ITERATION_LIMIT as f64; - self.texture_buffer + self.frame_buffer .par_chunks_mut(4) .enumerate() .for_each(|(i, buf)| { - let iters = Self::escape_time(i, DEFAULT_BOUNDS, DEFAULT_MAX_ITERATIONS); - let normalized_iters = iters / max_iters; + let iterations = Self::escape_time(i, DEFAULT_BOUNDS, DEFAULT_ITERATION_LIMIT); + let normalized_iters = iterations / limit_f64; let h = normalized_iters * 350.0; - let v = if iters == max_iters { 0.0 } else { 1.0 }; + let v = if iterations == limit_f64 { 0.0 } else { 1.0 }; buf.copy_from_slice(&Self::hsv_to_rgb(h, 1.0, v)); }); - &self.texture_buffer + self.frame_buffer.as_ref() } - pub fn scaled_image(&mut self, bounds: Bounds, max_iterations: u32) -> &[u8] { - self.texture_buffer + pub fn scaled_image(&mut self, bounds: Bounds, limit: u32) -> &[u8] { + self.frame_buffer .par_chunks_mut(4) .enumerate() .for_each(|(i, buf)| { - let max_iters = max_iterations as f64; + let limit_f64 = limit as f64; - let iters = Self::escape_time(i, bounds, max_iterations); - let normalized_iters = iters / max_iters; + let iterations = Self::escape_time(i, bounds, limit); + let normalized_iters = iterations / limit_f64; let h = normalized_iters * 350.0; - let v = if iters == max_iters { 0.0 } else { 1.0 }; + let v = if iterations == limit_f64 { 0.0 } else { 1.0 }; buf.copy_from_slice(&Self::hsv_to_rgb(h, 1.0, v)) }); - &self.texture_buffer + self.frame_buffer.as_ref() } - fn escape_time(i: usize, bounds: Bounds, max_iterations: u32) -> f64 { - let point = Coordinate::new(i % Self::IMG_WIDTH, i / Self::IMG_WIDTH); + fn escape_time(i: usize, bounds: Bounds, iteration_limit: u32) -> f64 { + let point = Coordinate::new(i % TEXTURE_WIDTH, i / TEXTURE_WIDTH); let c = Self::coords_to_complex(point, bounds); - Self::count_iterations(c, max_iterations) + Self::count_iterations(c, iteration_limit) } #[inline] @@ -137,27 +127,27 @@ impl Mandelbrot { [r, g, b, a] } - fn count_iterations(c: Complex, max_iterations: u32) -> f64 { + fn count_iterations(c: Complex, maximum: u32) -> f64 { let mut z: Complex = Complex::new(0.0, 0.0); - let mut num_iters: u32 = 0; + let mut count: u32 = 0; loop { if z.norm_sqr() > 4.0 { break; } - if num_iters >= max_iterations { + if count >= maximum { break; } z = (z * z) + c; - num_iters += 1; + count += 1; } - if num_iters < max_iterations { - (num_iters as f64 + 1.0) - z.norm().ln().ln() / 2f64.ln() + if count < maximum { + (count as f64 + 1.0) - z.norm().ln().ln() / 2f64.ln() } else { - num_iters as f64 + count as f64 } } @@ -173,7 +163,7 @@ impl Mandelbrot { // const X_MIN: f64 = -2.5; // const X_MAX: f64 = 1.0; - bounds.0 + ((bounds.1 - bounds.0) * (x as f64 - 0.0)) / Self::IMG_WIDTH as f64 - 0.0 + bounds.0 + ((bounds.1 - bounds.0) * (x as f64 - 0.0)) / TEXTURE_WIDTH as f64 - 0.0 } #[inline] @@ -181,6 +171,6 @@ impl Mandelbrot { // const Y_MIN: f64 = -1.0; // const Y_MAX: f64 = 1.0; - bounds.0 + ((bounds.1 - bounds.0) * (y as f64 - 0.0)) / Self::IMG_HEIGHT as f64 - 0.0 + bounds.0 + ((bounds.1 - bounds.0) * (y as f64 - 0.0)) / TEXTURE_HEIGHT as f64 - 0.0 } }