fix: refactor parts of mandelbrot.rs

This commit is contained in:
Rekai Nyangadzayi Musuka 2021-03-29 20:22:26 -05:00
parent 42c783448e
commit 3d0f2acea3
3 changed files with 73 additions and 44 deletions

View File

@ -1,2 +1,3 @@
pub use crate::mandelbrot::Bounds;
pub use crate::mandelbrot::Mandelbrot; pub use crate::mandelbrot::Mandelbrot;
mod mandelbrot; mod mandelbrot;

View File

@ -1,5 +1,5 @@
use bevy::{prelude::*, render::texture::TextureFormat}; use bevy::{prelude::*, render::texture::TextureFormat};
use mandelbrot::Mandelbrot; use mandelbrot::{Bounds, Mandelbrot};
use std::time::Instant; use std::time::Instant;
fn main() { fn main() {
@ -88,14 +88,16 @@ fn mandelbrot_render_system(
let z = scale.zoom; let z = scale.zoom;
let h = scale.horizontal; let h = scale.horizontal;
let v = scale.vertical; let v = scale.vertical;
let iters = scale.iterations; let limit = scale.iterations;
let x = ((-2.5 * z) + h, (1.0 * z) + h);
let y = ((-1.0 * z) - v, (1.0 * z) - v);
let bounds = Bounds::new(x, y);
let start = Instant::now(); let start = Instant::now();
texture.data.copy_from_slice(fractal.generate_scaled_image( texture
((-2.5 * z) + h, (1.0 * z) + h), .data
((-1.0 * z) - v, (1.0 * z) - v), .copy_from_slice(fractal.scaled_image(bounds, limit));
iters,
));
let _diff = Instant::now() - start; let _diff = Instant::now() - start;
} }
} }
@ -109,7 +111,7 @@ fn startup(
mut materials: ResMut<Assets<ColorMaterial>>, mut materials: ResMut<Assets<ColorMaterial>>,
) { ) {
let img_size = Vec2::new(Mandelbrot::width() as f32, Mandelbrot::height() as f32); let img_size = Vec2::new(Mandelbrot::width() as f32, Mandelbrot::height() as f32);
let img_buf = Mandelbrot::new().generate_image().to_vec(); let img_buf = Mandelbrot::new().image().to_vec();
let texture_handle = textures.add(Texture::new( let texture_handle = textures.add(Texture::new(
img_size, img_size,

View File

@ -1,7 +1,43 @@
use num_complex::Complex; use num_complex::Complex;
use rayon::prelude::*; use rayon::prelude::*;
const MAX_ITERATIONS: u16 = 512; pub struct Coordinate(usize, usize);
impl Coordinate {
pub fn new(x: usize, y: usize) -> Self {
Self(x, y)
}
pub fn x(&self) -> usize {
self.0
}
pub fn y(&self) -> usize {
self.1
}
}
#[derive(Debug, Clone, Copy)]
pub struct Bounds {
x: (f64, f64),
y: (f64, f64),
}
impl Bounds {
pub const fn new(x: (f64, f64), y: (f64, f64)) -> Self {
Self { x, y }
}
pub fn x(&self) -> (f64, f64) {
self.x
}
pub fn y(&self) -> (f64, f64) {
self.y
}
}
const DEFAULT_MAX_ITERATIONS: u32 = 64;
const DEFAULT_BOUNDS: Bounds = Bounds::new((-2.5, 1.0), (-1.0, 1.0));
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Mandelbrot { pub struct Mandelbrot {
@ -26,33 +62,32 @@ impl Mandelbrot {
Self::IMG_HEIGHT Self::IMG_HEIGHT
} }
pub fn generate_image(&mut self) -> &[u8] { pub fn image(&mut self) -> &[u8] {
let max_iters = DEFAULT_MAX_ITERATIONS as f64;
self.texture_buffer self.texture_buffer
.par_chunks_mut(4) .par_chunks_mut(4)
.enumerate() .enumerate()
.for_each(|(i, buf)| { .for_each(|(i, buf)| {
let iters = Self::new_escape_time(i, (-2.5, 1.0), (-1.0, 1.0), 64); let iters = Self::escape_time(i, DEFAULT_BOUNDS, DEFAULT_MAX_ITERATIONS);
let normalized_iters = iters / MAX_ITERATIONS as f64; let normalized_iters = iters / max_iters;
let h = 0.5 + (10.0 * normalized_iters);
buf.copy_from_slice(&Self::hsv_to_rgb(h, 0.6, 1.0)); let h = normalized_iters * 350.0;
let v = if iters == max_iters { 0.0 } else { 1.0 };
buf.copy_from_slice(&Self::hsv_to_rgb(h, 1.0, v));
}); });
&self.texture_buffer &self.texture_buffer
} }
pub fn generate_scaled_image( pub fn scaled_image(&mut self, bounds: Bounds, max_iterations: u32) -> &[u8] {
&mut self,
x_bounds: (f64, f64),
y_bounds: (f64, f64),
max_iterations: u32,
) -> &[u8] {
self.texture_buffer self.texture_buffer
.par_chunks_mut(4) .par_chunks_mut(4)
.enumerate() .enumerate()
.for_each(|(i, buf)| { .for_each(|(i, buf)| {
let max_iters = max_iterations as f64; let max_iters = max_iterations as f64;
let iters = Self::new_escape_time(i, x_bounds, y_bounds, max_iterations); let iters = Self::escape_time(i, bounds, max_iterations);
let normalized_iters = iters / max_iters; let normalized_iters = iters / max_iters;
let h = normalized_iters * 350.0; let h = normalized_iters * 350.0;
@ -63,17 +98,11 @@ impl Mandelbrot {
&self.texture_buffer &self.texture_buffer
} }
fn new_escape_time( fn escape_time(i: usize, bounds: Bounds, max_iterations: u32) -> f64 {
i: usize, let point = Coordinate::new(i % Self::IMG_WIDTH, i / Self::IMG_WIDTH);
x_bounds: (f64, f64), let c = Self::coords_to_complex(point, bounds);
y_bounds: (f64, f64),
max_iterations: u32,
) -> f64 {
let px = i % Self::IMG_WIDTH;
let py = i / Self::IMG_WIDTH;
let c = Self::coords_to_complex(px, py, x_bounds, y_bounds);
Self::new_calc_num_iters(c, max_iterations) Self::count_iterations(c, max_iterations)
} }
#[inline] #[inline]
@ -108,7 +137,7 @@ impl Mandelbrot {
[r, g, b, a] [r, g, b, a]
} }
fn new_calc_num_iters(c: Complex<f64>, max_iterations: u32) -> f64 { fn count_iterations(c: Complex<f64>, max_iterations: u32) -> f64 {
let mut z: Complex<f64> = Complex::new(0.0, 0.0); let mut z: Complex<f64> = Complex::new(0.0, 0.0);
let mut num_iters: u32 = 0; let mut num_iters: u32 = 0;
@ -132,29 +161,26 @@ impl Mandelbrot {
} }
} }
fn coords_to_complex( fn coords_to_complex(point: Coordinate, bounds: Bounds) -> Complex<f64> {
px: usize,
py: usize,
x_bounds: (f64, f64),
y_bounds: (f64, f64),
) -> Complex<f64> {
Complex::new( Complex::new(
Self::scale_width(px, x_bounds), Self::scale_width(point.x(), bounds.x()),
Self::scale_height(py, y_bounds), Self::scale_height(point.y(), bounds.y()),
) )
} }
fn scale_width(px: usize, x_bounds: (f64, f64)) -> f64 { #[inline]
fn scale_width(x: usize, bounds: (f64, f64)) -> f64 {
// const X_MIN: f64 = -2.5; // const X_MIN: f64 = -2.5;
// const X_MAX: f64 = 1.0; // const X_MAX: f64 = 1.0;
x_bounds.0 + ((x_bounds.1 - x_bounds.0) * (px as f64 - 0.0)) / Self::IMG_WIDTH as f64 - 0.0 bounds.0 + ((bounds.1 - bounds.0) * (x as f64 - 0.0)) / Self::IMG_WIDTH as f64 - 0.0
} }
fn scale_height(py: usize, y_bounds: (f64, f64)) -> f64 { #[inline]
fn scale_height(y: usize, bounds: (f64, f64)) -> f64 {
// const Y_MIN: f64 = -1.0; // const Y_MIN: f64 = -1.0;
// const Y_MAX: f64 = 1.0; // const Y_MAX: f64 = 1.0;
y_bounds.0 + ((y_bounds.1 - y_bounds.0) * (py as f64 - 0.0)) / Self::IMG_HEIGHT as f64 - 0.0 bounds.0 + ((bounds.1 - bounds.0) * (y as f64 - 0.0)) / Self::IMG_HEIGHT as f64 - 0.0
} }
} }