diff options
18 files changed, 186 insertions, 107 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fcd2ba..20a3f88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +# 23 + +* Support rendering of the Tricorn and Burning Ship fractals +* Update configuration +* Update start zoom value +* Remove Julia fractal(s) +* Bump dependency versions +* Update messages +* Update commenting + # 22 * Support rendering of Julia sets @@ -15,7 +15,7 @@ path = "source/benoit/main.rs" lto = true [dependencies] -rug = "1.20.1" +rug = "1.21.0" sdl2 = "0.35.2" toml = "0.7.6" webp = "0.2.5" diff --git a/source/benoit/benoit.rs b/source/benoit/benoit.rs index 15e8b74..92fe850 100644 --- a/source/benoit/benoit.rs +++ b/source/benoit/benoit.rs @@ -29,12 +29,13 @@ pub mod application; pub mod configuration; pub mod video; -type RowRenderer = fn(&mut [u32], u32, u32, u32, Float, Float, Float, u32, Float, Float); +type RowRenderer = fn(&mut [u32], u32, u32, u32, Float, Float, Float, u32); pub const PRECISION: u32 = 0x100; #[derive(Clone, Copy)] pub enum Fractal { - Julia, + BurningShip, Mandelbrot, + Tricorn, } diff --git a/source/benoit/benoit/application.rs b/source/benoit/benoit/application.rs index 9e679e5..186b069 100644 --- a/source/benoit/benoit/application.rs +++ b/source/benoit/benoit/application.rs @@ -37,8 +37,9 @@ pub mod handle_keys; pub mod initialise; pub mod r#loop; pub mod poll_events; -pub mod render_row_julia; +pub mod render_row_burning_ship; pub mod render_row_mandelbrot; +pub mod render_row_tricorn; pub mod render; pub mod run; @@ -57,9 +58,6 @@ pub struct Application { zoom: Float, maximum_iteration_count: u32, - julia_real: Float, - julia_imaginary: Float, - dump_path: String, video: Option<Video>, diff --git a/source/benoit/benoit/application/animate.rs b/source/benoit/benoit/application/animate.rs index 0f2c796..65b4aeb 100644 --- a/source/benoit/benoit/application/animate.rs +++ b/source/benoit/benoit/application/animate.rs @@ -33,7 +33,10 @@ use std::ops::MulAssign; impl Application { pub fn animate(&self) -> i32 { - let stop_zoom = Float::with_val(PRECISION, &self.zoom); + // zoom_start: + let mut zoom = Float::with_val(PRECISION, 1.0 / 2.0); + + let zoom_stop = Float::with_val(PRECISION, &self.zoom); let zoom_factor = { // To get the zoom factor, we first want the 'a' @@ -41,30 +44,29 @@ impl Application { // (frame_count) on the x-dimension and from (1) to // (zoom) on the y-dimension: // - // a = nroot(x1-x0, y1/y0) + // a = nroot(x1-x0,y1/y0) // // but this may be simplified for use with Rug // because // - // nroot(a, b) = b^(1/a) + // nroot(a,b) = b^(1/a) // // making the final equation // - // (x1-x0)^(1/y1*y0) = (zoom)^(1/frame_count) + // (x1-x0)^(1/(y1*y0)) = (zoom_stop/zoom_start)^(1/frame_count) let x_difference = Float::with_val(PRECISION, self.frame_count); let exponent = Float::with_val(PRECISION, 1.0 / &x_difference); - let mut factor = Float::with_val(PRECISION, &stop_zoom); + let mut factor = Float::with_val(PRECISION, &zoom_stop); + factor /= &zoom; factor.pow_assign(exponent); factor }; - let mut zoom = Float::with_val(PRECISION, 1.0); - - eprintln!("animating {} frames at {}{:+}i to {:.3} (fac.: {:.3})", self.frame_count, self.center_real.to_f64(), self.center_imaginary.to_f64(), stop_zoom.to_f64(), zoom_factor.to_f64()); + eprintln!("animating {} frames at {}{:+}i to {:.3} (fac.: {:.3})", self.frame_count, self.center_real.to_f64(), self.center_imaginary.to_f64(), zoom_stop.to_f64(), zoom_factor.to_f64()); let canvas_size = self.canvas_height as usize * self.canvas_width as usize; @@ -73,7 +75,7 @@ impl Application { for frame in 0x0..self.frame_count { eprint!("{frame:010}: "); - self.render(&mut data[..], &self.center_real, &self.center_imaginary, &zoom, self.maximum_iteration_count, &self.julia_real, &self.julia_imaginary); + self.render(&mut data[..], &self.center_real, &self.center_imaginary, &zoom, self.maximum_iteration_count); self.colour(&mut image[..], &data[..]); self.dump(format!("{}/frame{frame:010}.webp", self.dump_path), &image, self.canvas_width, self.canvas_height); diff --git a/source/benoit/benoit/application/draw.rs b/source/benoit/benoit/application/draw.rs index 1ac3331..715b3da 100644 --- a/source/benoit/benoit/application/draw.rs +++ b/source/benoit/benoit/application/draw.rs @@ -32,7 +32,7 @@ impl Application { pub fn draw(&mut self, data: &mut [u32], image: &mut [u8]) { let canvas_size = self.canvas_height * self.canvas_width; - self.render(&mut data[..], &self.center_real, &self.center_imaginary, &self.zoom, self.maximum_iteration_count, &self.julia_real, &self.julia_imaginary); + self.render(&mut data[..], &self.center_real, &self.center_imaginary, &self.zoom, self.maximum_iteration_count); self.colour(&mut image[..], &data[..]); for pixel in 0x0..canvas_size { diff --git a/source/benoit/benoit/application/get_row_renderer.rs b/source/benoit/benoit/application/get_row_renderer.rs index ef333c4..3418ab7 100644 --- a/source/benoit/benoit/application/get_row_renderer.rs +++ b/source/benoit/benoit/application/get_row_renderer.rs @@ -27,8 +27,9 @@ use crate::benoit::application::Application; impl Application { pub fn get_row_renderer(fractal: Fractal) -> RowRenderer { return match fractal { - Fractal::Julia => Application::render_row_julia, - Fractal::Mandelbrot => Application::render_row_mandelbrot, + Fractal::BurningShip => Application::render_row_burning_ship, + Fractal::Mandelbrot => Application::render_row_mandelbrot, + Fractal::Tricorn => Application::render_row_tricorn, }; } } diff --git a/source/benoit/benoit/application/handle_keys.rs b/source/benoit/benoit/application/handle_keys.rs index a0c10d7..67e32cd 100644 --- a/source/benoit/benoit/application/handle_keys.rs +++ b/source/benoit/benoit/application/handle_keys.rs @@ -21,7 +21,7 @@ If not, see <https://www.gnu.org/licenses/>. */ -use crate::benoit::{Fractal, PRECISION}; +use crate::benoit::PRECISION; use crate::benoit::application::Application; extern crate rug; @@ -32,20 +32,11 @@ use sdl2::keyboard::Scancode; impl Application { pub fn handle_keys(&mut self, scan_code: Scancode) -> bool { - let print_values = |fractal: Fractal, center_real: &Float, center_imaginary: &Float, zoom: &Float, maximum_iteration_count: u32, julia_real: &Float, julia_imaginary: &Float| { - let base = format!("c = {center_real}{center_imaginary:+}i -- {zoom}x @ {maximum_iteration_count} iter."); - - match fractal { - Fractal::Julia => eprintln!("{base}:\n c(M) = {julia_real}{julia_imaginary:+}i"), - Fractal::Mandelbrot => eprintln!("{base}"), - }; - }; - match scan_code { Scancode::Escape => return true, Scancode::C => self.do_draw = true, Scancode::X => self.do_dump = true, - Scancode::Z => print_values(self.fractal, &self.center_real, &self.center_imaginary, &self.zoom, self.maximum_iteration_count, &self.julia_real, &self.julia_imaginary), + Scancode::Z => eprintln!("c = {}{:+}i -- {}x @ {} iter.", &self.center_real, &self.center_imaginary, &self.zoom, self.maximum_iteration_count), _ => {}, } @@ -81,18 +72,6 @@ impl Application { _ => self.maximum_iteration_count, }; - match scan_code { - Scancode::Down => self.julia_real += &translate_ammount, - Scancode::Up => self.julia_real -= &translate_ammount, - _ => {}, - }; - - match scan_code { - Scancode::Left => self.julia_imaginary -= &translate_ammount, - Scancode::Right => self.julia_imaginary += &translate_ammount, - _ => {}, - }; - return false; } } diff --git a/source/benoit/benoit/application/initialise.rs b/source/benoit/benoit/application/initialise.rs index fec750c..ad70156 100644 --- a/source/benoit/benoit/application/initialise.rs +++ b/source/benoit/benoit/application/initialise.rs @@ -72,9 +72,6 @@ impl Application { zoom: Float::with_val(PRECISION, configuration.zoom), maximum_iteration_count: configuration.maximum_iteration_count, - julia_real: configuration.julia_real, - julia_imaginary: configuration.julia_imaginary, - dump_path: configuration.dump_path, video: video, diff --git a/source/benoit/benoit/application/loop.rs b/source/benoit/benoit/application/loop.rs index a4d355b..8261970 100644 --- a/source/benoit/benoit/application/loop.rs +++ b/source/benoit/benoit/application/loop.rs @@ -44,11 +44,6 @@ impl Application { eprintln!("- X Dump frame"); eprintln!("- C Render frame"); eprintln!(); - eprintln!("- \u{2191} Translate Julia center up"); - eprintln!("- \u{2190} Translate Julia center left"); - eprintln!("- \u{2192} Translate Julia center down"); - eprintln!("- \u{2193} Translate Julia center right"); - eprintln!(); let mut event_pump = self.video.as_mut().unwrap().sdl.event_pump().expect("unable to get event pump"); diff --git a/source/benoit/benoit/application/render.rs b/source/benoit/benoit/application/render.rs index 5d1f2eb..07ab7ee 100644 --- a/source/benoit/benoit/application/render.rs +++ b/source/benoit/benoit/application/render.rs @@ -32,7 +32,7 @@ use std::time::Instant; use std::ptr::addr_of_mut; impl Application { - pub fn render(&self, buffer: &mut [u32], center_real: &Float, center_imaginary: &Float, zoom: &Float, maximum_iteration_count: u32, julia_real: &Float, julia_imaginary: &Float) { + pub fn render(&self, buffer: &mut [u32], center_real: &Float, center_imaginary: &Float, zoom: &Float, maximum_iteration_count: u32) { eprint!("rendering..."); let mut threads = Vec::<JoinHandle<()>>::with_capacity(self.thread_count as usize); @@ -68,10 +68,7 @@ impl Application { let center_imaginary = center_imaginary.clone(); let zoom = zoom.clone(); - let julia_real = julia_real.clone(); - let julia_imaginary = julia_imaginary.clone(); - - threads.push(spawn(move || { render_row(buffer_slice, y, canvas_width, canvas_height, center_real, center_imaginary, zoom, maximum_iteration_count, julia_real, julia_imaginary) })); + threads.push(spawn(move || { render_row(buffer_slice, y, canvas_width, canvas_height, center_real, center_imaginary, zoom, maximum_iteration_count) })); y += 0x1; } @@ -92,10 +89,7 @@ impl Application { let center_imaginary = center_imaginary.clone(); let zoom = zoom.clone(); - let julia_real = julia_real.clone(); - let julia_imaginary = julia_imaginary.clone(); - - threads.push(spawn(move || { render_row(buffer_slice, y, canvas_width, canvas_height, center_real, center_imaginary, zoom, maximum_iteration_count, julia_real, julia_imaginary) })); + threads.push(spawn(move || { render_row(buffer_slice, y, canvas_width, canvas_height, center_real, center_imaginary, zoom, maximum_iteration_count) })); } } diff --git a/source/benoit/benoit/application/render_row_julia.rs b/source/benoit/benoit/application/render_row_burning_ship.rs index 5caea3b..1a2fa62 100644 --- a/source/benoit/benoit/application/render_row_julia.rs +++ b/source/benoit/benoit/application/render_row_burning_ship.rs @@ -29,7 +29,7 @@ extern crate rug; use rug::Float; impl Application { - pub fn render_row_julia(data: &mut [u32], y: u32, canvas_width: u32, canvas_height: u32, center_real: Float, center_imaginary: Float, zoom: Float, maximum_iteration_count: u32, julia_real: Float, julia_imaginary: Float) { + pub fn render_row_burning_ship(data: &mut [u32], y: u32, canvas_width: u32, canvas_height: u32, center_real: Float, center_imaginary: Float, zoom: Float, maximum_iteration_count: u32) { for x in 0x0..canvas_width { let canvas_width = Float::with_val(PRECISION, canvas_width); let canvas_height = Float::with_val(PRECISION, canvas_height); @@ -37,64 +37,60 @@ impl Application { let x_float = Float::with_val(PRECISION, x); let y_float = Float::with_val(PRECISION, y); - // The Julia set of the Mandelbrot set is similar - // but not quite identical: The start value of (z) - // is now relative to the canvas and (c) is - // constant corresponds to a point in the - // Mandelbrot Set. + // For more information, see: + // render_row_mandelbrot - let ca = &julia_real; - let cb = &julia_imaginary; - - // Re(z) = (x-canvas_width/2)*4/canvas_width/zoom+Re(z) - let mut za = { + let ca = { let tmp0 = Float::with_val(PRECISION, &canvas_width / 2.0); - let mut za = Float::with_val(PRECISION, &x_float - &tmp0); - za *= 4.0; - za /= &canvas_width; - za /= &zoom; - za += ¢er_real; + let mut ca = Float::with_val(PRECISION, &x_float - &tmp0); + ca *= 4.0; + ca /= &canvas_width; + ca /= &zoom; + ca += ¢er_real; - za + ca }; - // Im(Z) = (x-canvas_height/2)*4/canvas_height/zoom+Im(z) - let mut zb = { + let cb = { let tmp0 = Float::with_val(PRECISION, &canvas_height / 2.0); - let mut zb = Float::with_val(PRECISION, &y_float - &tmp0); - zb *= 4.0; - zb /= &canvas_height; - zb /= &zoom; - zb += ¢er_imaginary; + let mut cb = Float::with_val(PRECISION, &y_float - &tmp0); + cb *= 4.0; + cb /= &canvas_height; + cb /= &zoom; + cb += ¢er_imaginary; - zb + cb }; + let mut za = Float::with_val(PRECISION, &ca); + let mut zb = Float::with_val(PRECISION, &cb); + let mut iteration_count: u32 = 0x0; while { let square_distance = Float::with_val(PRECISION, &za * &za + &zb * &zb); square_distance <= 4.0 && iteration_count < maximum_iteration_count } { { - // The overall iterations of the Julia of M are - // identical to those of M: + // The Burning Ship is different in that - during + // iteration - the real and imaginary parts of (z) + // are made absolute: // - // z = z^2+c - // - // with only the initial value of (z) and (c) - // differing. + // z = (abs(Re(z))+i*abs(Im(z)))^2+c + + za = za.abs(); + zb = zb.abs(); let za_temporary = Float::with_val(PRECISION, &za); za = za.square(); za -= &zb * &zb; - za += ca; + za += &ca; zb *= &za_temporary; zb *= 2.0; - zb += cb; + zb += &cb; } iteration_count += 0x1; diff --git a/source/benoit/benoit/application/render_row_mandelbrot.rs b/source/benoit/benoit/application/render_row_mandelbrot.rs index ec07cdc..f74170b 100644 --- a/source/benoit/benoit/application/render_row_mandelbrot.rs +++ b/source/benoit/benoit/application/render_row_mandelbrot.rs @@ -29,7 +29,7 @@ extern crate rug; use rug::Float; impl Application { - pub fn render_row_mandelbrot(data: &mut [u32], y: u32, canvas_width: u32, canvas_height: u32, center_real: Float, center_imaginary: Float, zoom: Float, maximum_iteration_count: u32, _julia_real: Float, _julia_imaginary: Float) { + pub fn render_row_mandelbrot(data: &mut [u32], y: u32, canvas_width: u32, canvas_height: u32, center_real: Float, center_imaginary: Float, zoom: Float, maximum_iteration_count: u32) { for x in 0x0..canvas_width { let canvas_width = Float::with_val(PRECISION, canvas_width); let canvas_height = Float::with_val(PRECISION, canvas_height); @@ -97,9 +97,9 @@ impl Application { let za_temporary = Float::with_val(PRECISION, &za); // We can calculate the square of a complex number - // (z) as: + // as: // - // z^2 = (a+ib)^2 = (a+ib)(a+ib) = a^2+iab+iab-b^2 = a^2-b^2+2iab + // (a+ib)^2 = (a+ib)(a+ib) = a^2+iab+iab-b^2 = a^2-b^2+2iab za = za.square(); za -= &zb * &zb; diff --git a/source/benoit/benoit/application/render_row_tricorn.rs b/source/benoit/benoit/application/render_row_tricorn.rs new file mode 100644 index 0000000..9cefc6e --- /dev/null +++ b/source/benoit/benoit/application/render_row_tricorn.rs @@ -0,0 +1,105 @@ +/* + Copyright 2021, 2023 Gabriel Bjørnager Jensen. + + This file is part of Benoit. + + Benoit is free software: you can redistribute it + and/or modify it under the terms of the GNU + Affero General Public License as published by + the Free Software Foundation, either version 3 + of the License, or (at your option) any later + version. + + Benoit is distributed in the hope that it will + be useful, but WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU + Affero General Public License along with Benoit. + If not, see <https://www.gnu.org/licenses/>. +*/ + +use crate::benoit::PRECISION; +use crate::benoit::application::Application; + +extern crate rug; + +use rug::Float; + +impl Application { + pub fn render_row_tricorn(data: &mut [u32], y: u32, canvas_width: u32, canvas_height: u32, center_real: Float, center_imaginary: Float, zoom: Float, maximum_iteration_count: u32) { + for x in 0x0..canvas_width { + let canvas_width = Float::with_val(PRECISION, canvas_width); + let canvas_height = Float::with_val(PRECISION, canvas_height); + + let x_float = Float::with_val(PRECISION, x); + let y_float = Float::with_val(PRECISION, y); + + // For more information, see: + // render_row_mandelbrot + + let ca = { + let tmp0 = Float::with_val(PRECISION, &canvas_width / 2.0); + + let mut ca = Float::with_val(PRECISION, &x_float - &tmp0); + ca *= 4.0; + ca /= &canvas_width; + ca /= &zoom; + ca += ¢er_real; + + ca + }; + + let cb = { + let tmp0 = Float::with_val(PRECISION, &canvas_height / 2.0); + + let mut cb = Float::with_val(PRECISION, &y_float - &tmp0); + cb *= 4.0; + cb /= &canvas_height; + cb /= &zoom; + cb += ¢er_imaginary; + + cb + }; + + let mut za = Float::with_val(PRECISION, &ca); + let mut zb = Float::with_val(PRECISION, &cb); + + let mut iteration_count: u32 = 0x0; + while { + let square_distance = Float::with_val(PRECISION, &za * &za + &zb * &zb); + square_distance <= 4.0 && iteration_count < maximum_iteration_count + } { + { + // The Tricorn is only different from the + // Mandelbrot Set in that the conjugate of (z) is + // used instead of just (z): + // + // z = (Re(z)-Im(z))^2+c + + let za_temporary = Float::with_val(PRECISION, &za); + + za = za.square(); + za -= &zb * &zb; + za += &ca; + + zb *= &za_temporary; + // We can negate the value by multiplying with + // (-1). A multiplication can be saved, as + // + // a*2*(-1) = a*(-2) + // + // so we may combine these two multiplications. + zb *= -2.0; + zb += &cb; + } + + iteration_count += 0x1; + } + + unsafe { *data.get_unchecked_mut(x as usize) = iteration_count } + } + } +} diff --git a/source/benoit/benoit/application/run.rs b/source/benoit/benoit/application/run.rs index d764084..7b91c22 100644 --- a/source/benoit/benoit/application/run.rs +++ b/source/benoit/benoit/application/run.rs @@ -21,12 +21,21 @@ If not, see <https://www.gnu.org/licenses/>. */ +use crate::benoit::Fractal; use crate::benoit::application::Application; extern crate sdl2; impl Application { pub fn run(&mut self) -> i32 { + let fractal_name = match self.fractal { + Fractal::BurningShip => "burning ship", + Fractal::Mandelbrot => "mandelbrot set", + Fractal::Tricorn => "tricorn", + }; + + eprintln!("rendering the {fractal_name}"); + return match self.interactive { true => self.r#loop(), false => self.animate(), diff --git a/source/benoit/benoit/configuration.rs b/source/benoit/benoit/configuration.rs index 4190cf4..b330089 100644 --- a/source/benoit/benoit/configuration.rs +++ b/source/benoit/benoit/configuration.rs @@ -45,9 +45,6 @@ pub struct Configuration { pub zoom: Float, pub maximum_iteration_count: u32, - pub julia_real: Float, - pub julia_imaginary: Float, - pub dump_path: String, pub interactive: bool, diff --git a/source/benoit/benoit/configuration/default.rs b/source/benoit/benoit/configuration/default.rs index e524895..19fc5fb 100644 --- a/source/benoit/benoit/configuration/default.rs +++ b/source/benoit/benoit/configuration/default.rs @@ -45,9 +45,6 @@ impl Configuration { zoom: Float::with_val(PRECISION, 1.0), maximum_iteration_count: 0x100, - julia_real: Float::with_val(PRECISION, 0.0), - julia_imaginary: Float::with_val(PRECISION, 0.0), - dump_path: "./render/".to_string(), interactive: true, diff --git a/source/benoit/benoit/configuration/load.rs b/source/benoit/benoit/configuration/load.rs index 7f0e066..6d39c13 100644 --- a/source/benoit/benoit/configuration/load.rs +++ b/source/benoit/benoit/configuration/load.rs @@ -86,9 +86,10 @@ impl Configuration { configuration.fractal = if let Some(name) = get_string(&configuration_table, "fractal") { match name.as_str() { - "julia" => Fractal::Julia, - "mandelbrot" => Fractal::Mandelbrot, - name => panic!("invalid fractal name {name}"), + "burningship" => Fractal::BurningShip, + "mandelbrot" => Fractal::Mandelbrot, + "tricorn" => Fractal::Tricorn, + name => panic!("invalid fractal name {name}"), } } else { configuration.fractal @@ -104,9 +105,6 @@ impl Configuration { get_float( &mut configuration.zoom, &configuration_table, "zoom"); get_integer(&mut configuration.maximum_iteration_count, &configuration_table, "maximum_iteration_count"); - get_float(&mut configuration.julia_real, &configuration_table, "julia_real"); - get_float(&mut configuration.julia_imaginary, &configuration_table, "julia_imaginary"); - return configuration; } } |