diff --git a/src/lib.rs b/src/lib.rs index da665f1..14b1537 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,7 @@ pub use controller::config::Config; use controller::key_bindings::KeyBindings; use controller::user_input::{ask, pick_option}; use model::complex_plane::{ComplexPlane, View}; +use model::mandelbrot_model::ColoringFunction; pub use model::mandelbrot_model::MandelbrotModel; use model::{pixel_buffer, pixel_plane, rendering}; use pixel_buffer::PixelBuffer; @@ -48,7 +49,6 @@ use view::coloring::ColorChannelMapping; use view::coloring::TrueColor; //Coloring function -type ColoringFunction = fn(iterations: u32, max_iterations: u32) -> TrueColor; static COLORING_FUNCTION: ColoringFunction = TrueColor::new_from_bernstein_polynomials; //Color channel mapping @@ -128,7 +128,7 @@ impl Default for InteractionVariables { } // Handle any key events -fn handle_key_events(window: &Window, k: &KeyBindings, coloring_function: &mut ColoringFunction) { +fn handle_key_events(window: &Window, k: &KeyBindings) { if let Some(key) = window.get_keys_pressed(minifb::KeyRepeat::No).first() { print!("\nKey pressed: "); k.print_key(key); @@ -137,11 +137,11 @@ fn handle_key_events(window: &Window, k: &KeyBindings, coloring_function: &mut C match key { Key::K => k.print(), Key::A => { - *coloring_function = pick_option(&[ + mandelbrot_model.coloring_function = pick_option(&[ ("HSV", TrueColor::new_from_hsv_colors), ("Bernstein polynomials", TrueColor::new_from_bernstein_polynomials), ]); - render(&mut mandelbrot_model, COLORING_FUNCTION); + render(&mut mandelbrot_model); } _ => (), } @@ -161,13 +161,13 @@ fn handle_left_mouse_clicked(mandelbrot_model: &MandelbrotModel, x: f32, y: f32) println!(); } -fn handle_right_mouse_clicked(mandelbrot_model: &mut MandelbrotModel, x: f32, y: f32, coloring_function: ColoringFunction) { +fn handle_right_mouse_clicked(mandelbrot_model: &mut MandelbrotModel, x: f32, y: f32) { println!("\nMouseButton::Right -> Move to ({x}, {y})"); let new_center = mandelbrot_model.c.complex_from_pixel_plane(x.into(), y.into()); println!("mandelbrot_model.c.center: {:?}", mandelbrot_model.c.center()); println!("new_center: {:?}", new_center); - rendering::translate_to_center_and_render_efficiently(mandelbrot_model, &new_center, coloring_function); + rendering::translate_to_center_and_render_efficiently(mandelbrot_model, &new_center); mandelbrot_model.c.print(); println!(); } @@ -199,7 +199,7 @@ impl MouseClickRecorder { } } -fn handle_mouse_events(window: &Window, coloring_function: ColoringFunction) { +fn handle_mouse_events(window: &Window) { let mut mandelbrot_model = MandelbrotModel::get_instance(); static LEFT_MOUSE_RECORDER: MouseClickRecorder = MouseClickRecorder::new(MouseButton::Left); //Static variable with interior mutability to toggle mouse clicks; without such a variable, clicking the screen once would result in multiple actions static RIGHT_MOUSE_RECORDER: MouseClickRecorder = MouseClickRecorder::new(MouseButton::Right); @@ -212,19 +212,20 @@ fn handle_mouse_events(window: &Window, coloring_function: ColoringFunction) { //Right mouse actions if RIGHT_MOUSE_RECORDER.was_clicked(window) { - handle_right_mouse_clicked(&mut mandelbrot_model, x, y, coloring_function); + handle_right_mouse_clicked(&mut mandelbrot_model, x, y); } } } -fn render(mandelbrot_model: &mut MandelbrotModel, coloring_function: ColoringFunction) { - rendering::render_complex_plane_into_buffer(mandelbrot_model, coloring_function); +fn render(mandelbrot_model: &mut MandelbrotModel) { + //TODO: Observer pattern view -> model to update the view, instead of rendering manually + rendering::render_complex_plane_into_buffer(mandelbrot_model); mandelbrot_model.c.print(); } fn set_view(mandelbrot_model: &mut MandelbrotModel, view: &View) { mandelbrot_model.c.set_view(view); - render(mandelbrot_model, COLORING_FUNCTION); + render(mandelbrot_model); } ///Prints Mandelbrot ASCII art :)
@@ -266,9 +267,9 @@ fn print_command_info() { pub fn run() -> Result<(), Box> { let mut mandelbrot_model = MandelbrotModel::get_instance(); //Coloring function - let mut coloring_function = COLORING_FUNCTION; + mandelbrot_model.coloring_function = COLORING_FUNCTION; //Color channel mapping - //MandelbrotModel::get_instance().p.color_channel_mapping = COLOR_CHANNEL_MAPPING; + mandelbrot_model.p.color_channel_mapping = COLOR_CHANNEL_MAPPING; // Create a new window let mut window = Window::new( "Mandelbrot set viewer", @@ -289,31 +290,31 @@ pub fn run() -> Result<(), Box> { key_bindings.add(Key::Up, "Move up translation_amount pixels", || { let mut mandelbrot_model = MandelbrotModel::get_instance(); let rows = mandelbrot_model.vars.translation_amount; - rendering::translate_and_render_efficiently(&mut mandelbrot_model, rows.into(), 0, COLORING_FUNCTION); + rendering::translate_and_render_efficiently(&mut mandelbrot_model, rows.into(), 0); mandelbrot_model.c.print(); }); key_bindings.add(Key::Down, "Move down translation_amount pixels", || { let mut mandelbrot_model = MandelbrotModel::get_instance(); let rows = -i16::from(mandelbrot_model.vars.translation_amount); - rendering::translate_and_render_efficiently(&mut mandelbrot_model, rows, 0, COLORING_FUNCTION); + rendering::translate_and_render_efficiently(&mut mandelbrot_model, rows, 0); mandelbrot_model.c.print(); }); key_bindings.add(Key::Left, "Move left translation_amount pixels", || { let mut mandelbrot_model = MandelbrotModel::get_instance(); let columns = -i16::from(mandelbrot_model.vars.translation_amount); - rendering::translate_and_render_efficiently(&mut mandelbrot_model, 0, columns, COLORING_FUNCTION); + rendering::translate_and_render_efficiently(&mut mandelbrot_model, 0, columns); mandelbrot_model.c.print(); }); key_bindings.add(Key::Right, "Move right translation_amount pixels", || { let mut mandelbrot_model = MandelbrotModel::get_instance(); let columns = mandelbrot_model.vars.translation_amount.into(); - rendering::translate_and_render_efficiently(&mut mandelbrot_model, 0, columns, COLORING_FUNCTION); + rendering::translate_and_render_efficiently(&mut mandelbrot_model, 0, columns); mandelbrot_model.c.print(); }); key_bindings.add(Key::R, "Reset the Mandelbrot set view to the starting view", || { let mut mandelbrot_model = MandelbrotModel::get_instance(); mandelbrot_model.c.reset(); - render(&mut mandelbrot_model, COLORING_FUNCTION); + render(&mut mandelbrot_model); }); key_bindings.add(Key::NumPadPlus, "Increment translation_amount", || { let mut mandelbrot_model = MandelbrotModel::get_instance(); @@ -346,7 +347,7 @@ pub fn run() -> Result<(), Box> { let mut mandelbrot_model = MandelbrotModel::get_instance(); let scaling_factor = mandelbrot_model.vars.scaling_factor(); mandelbrot_model.c.scale(scaling_factor); - render(&mut mandelbrot_model, COLORING_FUNCTION); + render(&mut mandelbrot_model); }); key_bindings.add( Key::RightBracket, @@ -355,7 +356,7 @@ pub fn run() -> Result<(), Box> { let mut mandelbrot_model = MandelbrotModel::get_instance(); let inverse_scaling_factor = mandelbrot_model.vars.inverse_scaling_factor(); mandelbrot_model.c.scale(inverse_scaling_factor); - render(&mut mandelbrot_model, COLORING_FUNCTION); + render(&mut mandelbrot_model); }, ); key_bindings.add(Key::V, "Prints the current Mandelbrot set view; the center and scale", || { @@ -419,7 +420,7 @@ pub fn run() -> Result<(), Box> { ComplexPlane::new(mandelbrot_model.config.image_width, mandelbrot_model.config.image_height); image_p.color_channel_mapping = mandelbrot_model.p.color_channel_mapping; image_c.set_view(&mandelbrot_model.c.get_view()); - rendering::render_complex_plane_into_buffer(&mut mandelbrot_model, COLORING_FUNCTION); + rendering::render_complex_plane_into_buffer(&mut mandelbrot_model); image_p.save_as_png( &time_stamp, &mandelbrot_model.c.get_view(), @@ -432,13 +433,13 @@ pub fn run() -> Result<(), Box> { key_bindings.add(Key::I, "Manually input a Mandelbrot set view", || { let mut mandelbrot_model = MandelbrotModel::get_instance(); mandelbrot_model.c.set_view(&View::new(ask("x"), ask("y"), ask("scale"))); - render(&mut mandelbrot_model, COLORING_FUNCTION); + render(&mut mandelbrot_model); }); key_bindings.add(Key::A, "Pick an algorithm to color the Mandelbrot set view", empty_closure); key_bindings.add(Key::M, "Change the Mandelbrot set view max_iterations", || { let mut mandelbrot_model = MandelbrotModel::get_instance(); mandelbrot_model.m.max_iterations = ask("max_iterations"); - render(&mut mandelbrot_model, COLORING_FUNCTION); + render(&mut mandelbrot_model); }); key_bindings.add( Key::O, @@ -446,7 +447,7 @@ pub fn run() -> Result<(), Box> { || { let mut mandelbrot_model = MandelbrotModel::get_instance(); mandelbrot_model.p.color_channel_mapping = ask("color_channel_mapping"); - render(&mut mandelbrot_model, COLORING_FUNCTION); + render(&mut mandelbrot_model); }, ); key_bindings.add( @@ -455,7 +456,7 @@ pub fn run() -> Result<(), Box> { || { let mut mandelbrot_model = MandelbrotModel::get_instance(); mandelbrot_model.config.supersampling_amount = ask::("supersampling_amount").clamp(1, 64); - render(&mut mandelbrot_model, COLORING_FUNCTION); + render(&mut mandelbrot_model); }, ); key_bindings.add( @@ -485,16 +486,16 @@ pub fn run() -> Result<(), Box> { println!(); println!("Rendering Mandelbrot set default view"); - rendering::render_complex_plane_into_buffer(&mut mandelbrot_model, coloring_function); + rendering::render_complex_plane_into_buffer(&mut mandelbrot_model); drop(mandelbrot_model); // Main loop while window.is_open() && !window.is_key_down(Key::Escape) { // Handle any window events - handle_key_events(&window, &key_bindings, &mut coloring_function); + handle_key_events(&window, &key_bindings); //Handle any mouse events - handle_mouse_events(&window, coloring_function); + handle_mouse_events(&window); let mandelbrot_model = MandelbrotModel::get_instance(); // Update the window with the new buffer @@ -506,6 +507,10 @@ pub fn run() -> Result<(), Box> { ) .unwrap(); } - + if window.is_key_down(Key::Escape) { + println!("Escape pressed, application exited gracefully."); + } else { + println!("Window closed, application exited gracefully.") + } Ok(()) } diff --git a/src/model/mandelbrot_model.rs b/src/model/mandelbrot_model.rs index 787a4ff..ebc5c9c 100644 --- a/src/model/mandelbrot_model.rs +++ b/src/model/mandelbrot_model.rs @@ -4,7 +4,7 @@ use std::{ sync::{Mutex, MutexGuard}, }; -use crate::{Config, InteractionVariables}; +use crate::{view::coloring::TrueColor, Config, InteractionVariables}; use super::{complex_plane::ComplexPlane, mandelbrot_function::MandelbrotFunction, pixel_buffer::PixelBuffer, pixel_plane::PixelPlane}; @@ -12,6 +12,8 @@ lazy_static! { static ref MANDELBROT_MODEL_INSTANCE: Mutex = Mutex::new(MandelbrotModel::new()); } +pub type ColoringFunction = fn(iterations: u32, max_iterations: u32) -> TrueColor; + pub struct MandelbrotModel { pub config: Config, pub c: ComplexPlane, @@ -19,6 +21,7 @@ pub struct MandelbrotModel { pub vars: InteractionVariables, pub amount_of_threads: usize, pub m: MandelbrotFunction, + pub coloring_function: ColoringFunction, } impl MandelbrotModel { @@ -34,6 +37,7 @@ impl MandelbrotModel { vars: InteractionVariables::default(), amount_of_threads: num_cpus::get(), m: MandelbrotFunction::new(config.max_iterations, config.orbit_radius), + coloring_function: TrueColor::new_from_bernstein_polynomials, } } diff --git a/src/model/rendering.rs b/src/model/rendering.rs index fa9a7c0..9eabb57 100644 --- a/src/model/rendering.rs +++ b/src/model/rendering.rs @@ -59,12 +59,9 @@ impl RenderBox { /// `orbit_radius` determines when Zn is considered to have gone to infinity. /// `max_iterations` concerns the maximum amount of times the Mandelbrot formula will be applied to each Complex number. /// Note: This function is computationally intensive, and should not be used for translations -pub fn render_complex_plane_into_buffer( - mandelbrot_model: &mut MandelbrotModel, - coloring_function: fn(iterations: u32, max_iterations: u32) -> TrueColor, -) { +pub fn render_complex_plane_into_buffer(mandelbrot_model: &mut MandelbrotModel) { let render_box = RenderBox::new(0, mandelbrot_model.p.pixel_plane.width, 0, mandelbrot_model.p.pixel_plane.height); - render_box_render_complex_plane_into_buffer(mandelbrot_model, render_box, coloring_function); + render_box_render_complex_plane_into_buffer(mandelbrot_model, render_box); } /// Render the Complex plane c into the 32-bit pixel buffer by applying the Mandelbrot formula iteratively to every Complex point mapped to a pixel in the buffer. @@ -77,11 +74,7 @@ pub fn render_complex_plane_into_buffer( /// * `coloring_function` - e.g. `TrueColor::new_from_hsv` /// # Panics /// If `lock().unwrap()` panics -pub fn render_box_render_complex_plane_into_buffer( - mandelbrot_model: &mut MandelbrotModel, - render_box: RenderBox, - coloring_function: fn(iterations: u32, max_iterations: u32) -> TrueColor, -) { +pub fn render_box_render_complex_plane_into_buffer(mandelbrot_model: &mut MandelbrotModel, render_box: RenderBox) { let time = benchmark_start(); let supersampling_amount = mandelbrot_model.config.supersampling_amount.clamp(1, 64); //Supersampling_amount should be at least 1 and atmost 64 render_box.print(); @@ -104,6 +97,7 @@ pub fn render_box_render_complex_plane_into_buffer( let pixel_buffer = (mandelbrot_model.p).clone(); let ms = (mandelbrot_model.m).clone(); let atm = Arc::clone(¤t_progress_atomic); + let coloring_function = (mandelbrot_model.coloring_function).clone(); let handle = thread::spawn(move || { let mut thread_chunks = Vec::new(); @@ -169,12 +163,7 @@ pub fn render_box_render_complex_plane_into_buffer( benchmark("render_box_render_complex_plane_into_buffer()", time); } -pub fn translate_and_render_complex_plane_buffer( - mandelbrot_model: &mut MandelbrotModel, - rows: i128, - columns: i128, - coloring_function: fn(iterations: u32, max_iterations: u32) -> TrueColor, -) { +pub fn translate_and_render_complex_plane_buffer(mandelbrot_model: &mut MandelbrotModel, rows: i128, columns: i128) { println!("rows: {}, columns: {}", rows, columns); let max_x: usize = if columns > 0 { columns as usize @@ -194,7 +183,7 @@ pub fn translate_and_render_complex_plane_buffer( 0, mandelbrot_model.p.pixel_plane.height, ); - render_box_render_complex_plane_into_buffer(mandelbrot_model, render_box, coloring_function); + render_box_render_complex_plane_into_buffer(mandelbrot_model, render_box); } else if columns == 0 { let render_box = RenderBox::new( 0, @@ -202,7 +191,7 @@ pub fn translate_and_render_complex_plane_buffer( (max_y as i128 - rows.abs()) as usize, max_y, ); - render_box_render_complex_plane_into_buffer(mandelbrot_model, render_box, coloring_function); + render_box_render_complex_plane_into_buffer(mandelbrot_model, render_box); } else { println!("ERROR: translate_and_render_complex_plane_buffer() requires that rows == 0 || columns == 0"); } @@ -210,12 +199,7 @@ pub fn translate_and_render_complex_plane_buffer( ///# Panics /// If `rows_up` != 0 && `columns_right` != 0 -pub fn translate_and_render_efficiently( - mandelbrot_model: &mut MandelbrotModel, - rows_up: i16, - columns_right: i16, - coloring_function: fn(iterations: u32, max_iterations: u32) -> TrueColor, -) { +pub fn translate_and_render_efficiently(mandelbrot_model: &mut MandelbrotModel, rows_up: i16, columns_right: i16) { assert!( rows_up == 0 || columns_right == 0, "translate_and_render_efficiently: rows_up should be 0 or columns_right should be 0!" @@ -227,14 +211,10 @@ pub fn translate_and_render_efficiently( column_sign * mandelbrot_model.c.pixels_to_real(columns_right.unsigned_abs() as u8), row_sign * mandelbrot_model.c.pixels_to_imaginary(rows_up.unsigned_abs() as u8), ); - translate_and_render_complex_plane_buffer(mandelbrot_model, rows_up.into(), (-columns_right).into(), coloring_function); + translate_and_render_complex_plane_buffer(mandelbrot_model, rows_up.into(), (-columns_right).into()); } -pub fn translate_to_center_and_render_efficiently( - mandelbrot_model: &mut MandelbrotModel, - new_center: &Complex, - coloring_function: fn(iterations: u32, max_iterations: u32) -> TrueColor, -) { +pub fn translate_to_center_and_render_efficiently(mandelbrot_model: &mut MandelbrotModel, new_center: &Complex) { let mut translation: Complex = new_center.subtract(&mandelbrot_model.c.center()); //Mirror the y translation because the screen y is mirrored compared to the complex plane y axis translation.y = -translation.y; @@ -243,13 +223,13 @@ pub fn translate_to_center_and_render_efficiently( mandelbrot_model.c.translate(translation.x, 0.0); let columns_right = -mandelbrot_model.c.real_to_pixels(translation.x); dbg!(columns_right); - translate_and_render_complex_plane_buffer(mandelbrot_model, 0, columns_right.into(), coloring_function); + translate_and_render_complex_plane_buffer(mandelbrot_model, 0, columns_right.into()); //Translate y, up mandelbrot_model.c.translate(0.0, translation.y); let rows_up = -mandelbrot_model.c.imaginary_to_pixels(translation.y); dbg!(rows_up); - translate_and_render_complex_plane_buffer(mandelbrot_model, rows_up.into(), 0, coloring_function); + translate_and_render_complex_plane_buffer(mandelbrot_model, rows_up.into(), 0); } fn benchmark_start() -> Instant {