diff options
33 files changed, 527 insertions, 581 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c77393..99e632d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 2.4.1 + +* Remove renderers in favour of the fractal type +* Fix default configuration +* Update rendering +* Greatly improve safety +* Remove ctor as dependency +* Modulise and clean up code + # 2.4.0 * Clean up and restructure code @@ -1,6 +1,6 @@ [package] name = "benoit" -version = "2.4.0" +version = "2.4.1" authors = ["Gabriel Bjørnager Jensen"] edition = "2021" description = "Mandelbrot renderer." @@ -16,7 +16,6 @@ codegen-units = 1 lto = "fat" [dependencies] -ctor = "0.2.5" enum-iterator = "1.4.1" png = "0.17.10" rayon = "1.8.0" diff --git a/source/benoit/benoit.rs b/source/benoit/benoit.rs index 86fa5d0..7d3a165 100644 --- a/source/benoit/benoit.rs +++ b/source/benoit/benoit.rs @@ -30,19 +30,18 @@ pub mod fractal; pub mod palette; pub mod render; pub mod render_data; -pub mod renderer; pub mod script; pub mod video; -pub const VERSION: [u32; 0x3] = [ +pub const VERSION: (u32, u32, u32) = ( 0x2, // Major 0x4, // Minor - 0x0, // Patch -]; + 0x1, // Patch +); pub const PRECISION: u32 = 0x80; -pub const BAILOUT: f32 = 256.0; +pub const BAILOUT_DISTANCE: f32 = 256.0; pub fn width_height_ratio(width: u32, height: u32) -> (f32, f32) { return if width > height { diff --git a/source/benoit/benoit/app.rs b/source/benoit/benoit/app.rs index 3eb54d0..61446be 100644 --- a/source/benoit/benoit/app.rs +++ b/source/benoit/benoit/app.rs @@ -24,22 +24,23 @@ use crate::benoit::complex::Complex; use crate::benoit::fractal::Fractal; use crate::benoit::palette::Palette; -use crate::benoit::renderer::Renderer; extern crate rug; use rug::Float; pub mod configure; +pub mod draw_feedback; pub mod drop; pub mod handle_keys; pub mod poll_events; +pub mod print_controls; +pub mod render; pub mod run; pub struct App { // Configuration: fractal: Fractal, - renderer: Renderer, canvas_width: u32, canvas_height: u32, diff --git a/source/benoit/benoit/app/configure.rs b/source/benoit/benoit/app/configure.rs index 64db009..1af3d60 100644 --- a/source/benoit/benoit/app/configure.rs +++ b/source/benoit/benoit/app/configure.rs @@ -29,8 +29,7 @@ impl App { #[must_use] pub fn configure(configuration: Configuration) -> App { return App { - fractal: configuration.fractal, - renderer: configuration.renderer, + fractal: configuration.fractal, canvas_width: configuration.canvas_width, canvas_height: configuration.canvas_height, diff --git a/source/benoit/benoit/app/draw_feedback.rs b/source/benoit/benoit/app/draw_feedback.rs new file mode 100644 index 0000000..e21b343 --- /dev/null +++ b/source/benoit/benoit/app/draw_feedback.rs @@ -0,0 +1,48 @@ +/* + 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::app::App; +use crate::benoit::complex::Complex; +use crate::benoit::video::Video; + +extern crate rug; + +use rug::Float; + +impl App { + pub(super) fn draw_feedback(&self, video: &mut Video, prev_centre: &Complex, prev_zoom: &Float) { + if { + // Don't draw translation feedback if rendering a + // Julia set or if we haven't done any viewport + // translations. + + &self.centre.real != &prev_centre.real + || &self.centre.imag != &prev_centre.imag + || &self.zoom != prev_zoom + }{ + video.draw_translation_feedback(self.canvas_width, self.canvas_height, self.scale, prev_centre, prev_zoom, &self.centre, &self.zoom); + } + + if self.do_textual_feedback { video.draw_textual_feedback(&self.centre, &self.zoom, self.max_iter_count) }; + } +}
\ No newline at end of file diff --git a/source/benoit/benoit/app/handle_keys.rs b/source/benoit/benoit/app/handle_keys.rs index b0fdcb9..5b27897 100644 --- a/source/benoit/benoit/app/handle_keys.rs +++ b/source/benoit/benoit/app/handle_keys.rs @@ -24,7 +24,6 @@ use crate::benoit::PRECISION; use crate::benoit::app::App; use crate::benoit::configuration::Configuration; -use crate::benoit::renderer::Renderer; extern crate rug; extern crate sdl2; @@ -123,15 +122,6 @@ impl App { eprintln!("renderer the {}", self.fractal.kind().name()); } - fn toggle_julia(&mut self) { - self.renderer.toggle(); - - match self.renderer { - Renderer::Julia => eprintln!("enabled the julia set"), - Renderer::Normal => eprintln!("disabled the julia set"), - }; - } - fn toggle_inverse(&mut self) { let inverse = !self.fractal.inverse(); @@ -143,6 +133,17 @@ impl App { self.fractal.set_inverse(inverse); } + fn toggle_julia(&mut self) { + let julia = !self.fractal.julia(); + + match julia { + false => eprintln!("disabled the julia set"), + true => eprintln!("enabled the julia set"), + }; + + self.fractal.set_julia(julia); + } + fn cycle_palette(&mut self, direction: i8) { let palette = self.palette.cycle(direction); @@ -152,12 +153,12 @@ impl App { } fn reset_viewport(&mut self) { - self.centre.real.assign(Configuration::DEFAULT_CENTRE_REAL); - self.centre.imag.assign(Configuration::DEFAULT_CENTRE_IMAG); + self.centre.real.assign(Configuration::DEFAULT_CENTRE.0); + self.centre.imag.assign(Configuration::DEFAULT_CENTRE.1); self.zoom.assign( Configuration::DEFAULT_ZOOM); - self.extra.real.assign(Configuration::DEFAULT_EXTRA_REAL); - self.extra.imag.assign(Configuration::DEFAULT_EXTRA_IMAG); + self.extra.real.assign(Configuration::DEFAULT_EXTRA.0); + self.extra.imag.assign(Configuration::DEFAULT_EXTRA.1); self.max_iter_count = Configuration::DEFAULT_MAX_ITER_COUNT; @@ -165,7 +166,7 @@ impl App { } fn dump_info(&self) { - eprintln!("info dump: the {}", self.fractal.kind().name()); + eprintln!("info dump: the {}", self.fractal.name()); eprintln!(" c = {}{:+}i ({}x)", &self.centre.real, &self.centre.imag, &self.zoom); eprintln!(" w = {}{:+}i", &self.extra.real, &self.extra.imag); eprintln!(" max. iter.: {}, col. range: {}", self.max_iter_count, self.colour_range); diff --git a/source/benoit/benoit/app/print_controls.rs b/source/benoit/benoit/app/print_controls.rs new file mode 100644 index 0000000..936d286 --- /dev/null +++ b/source/benoit/benoit/app/print_controls.rs @@ -0,0 +1,64 @@ +/* + 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::app::App; + +impl App { + pub(super) fn print_controls(&self) { + println!("Controls:"); + println!("- \u{1B}[1mW\u{1B}[0m Translate +Im"); + println!("- \u{1B}[1mA\u{1B}[0m Translate -Re"); + println!("- \u{1B}[1mS\u{1B}[0m Translate -Im"); + println!("- \u{1B}[1mD\u{1B}[0m Translate +Re"); + println!(); + println!("- \u{1B}[1mQ\u{1B}[0m Zoom out"); + println!("- \u{1B}[1mE\u{1B}[0m Zoom in"); + println!(); + println!("- \u{1B}[1mR\u{1B}[0m Decrease max. iteration count"); + println!("- \u{1B}[1mF\u{1B}[0m Increase max. iteration count"); + println!(); + println!("- \u{1B}[1mLEFT ALT\u{1B}[0m Cycle to previous fractal"); + println!("- \u{1B}[1mRIGHT ALT\u{1B}[0m Cycle to next fractal"); + println!("- \u{1B}[1mTAB\u{1B}[0m Toggle Julia"); + println!("- \u{1B}[1mLEFT CTRL\u{1B}[0m Toggle inverse"); + println!(); + println!("- \u{1B}[1mLEFT\u{1B}[0m Cycle to previous palette"); + println!("- \u{1B}[1mRIGHT\u{1B}[0m Cycle to next palette"); + println!("- \u{1B}[1mUP\u{1B}[0m Increase colour range"); + println!("- \u{1B}[1mDOWN\u{1B}[0m Decrease colour range"); + println!(); + println!("- \u{1B}[1mF1\u{1B}[0m Toggle textual feedback"); + println!("- \u{1B}[1mZ\u{1B}[0m Print centre value (c)"); + println!(); + println!("- \u{1B}[1mC\u{1B}[0m Render frame"); + println!(); + println!("Controls (holding \u{1B}[1mSHIFT\u{1B}[0m):"); + println!("- \u{1B}[1mW\u{1B}[0m Perturbate/translate +Im"); + println!("- \u{1B}[1mA\u{1B}[0m Perturbate/translate -Re"); + println!("- \u{1B}[1mS\u{1B}[0m Perturbate/translate -Im"); + println!("- \u{1B}[1mD\u{1B}[0m Perturbate/translate +Re"); + println!(); + println!("- \u{1B}[1mC\u{1B}[0m Render frame"); + println!(); + } +}
\ No newline at end of file diff --git a/source/benoit/benoit/renderer/render_point.rs b/source/benoit/benoit/app/render.rs index 7f9bf9a..7f4b132 100644 --- a/source/benoit/benoit/renderer/render_point.rs +++ b/source/benoit/benoit/app/render.rs @@ -21,8 +21,26 @@ If not, see <https://www.gnu.org/licenses/>. */ -pub mod julia; -pub mod normal; +use crate::benoit::app::App; +use crate::benoit::render::Render; +use std::time::Instant; -pub use julia::*; -pub use normal::*; +impl App { + pub(super) fn render(&self, render: &mut Render) { + eprint!("rendering..."); + + let time_start = Instant::now(); + + render.render( + self.fractal, + &self.centre, + &self.zoom, + &self.extra, + self.max_iter_count, + ); + + let render_time = time_start.elapsed(); + + eprintln!(" {:.3}ms", render_time.as_micros() as f32 / 1000.0); + } +}
\ No newline at end of file diff --git a/source/benoit/benoit/app/run.rs b/source/benoit/benoit/app/run.rs index cefe5f7..b4f8fb4 100644 --- a/source/benoit/benoit/app/run.rs +++ b/source/benoit/benoit/app/run.rs @@ -22,14 +22,13 @@ */ use crate::benoit::app::App; -use crate::benoit::complex::Complex; use crate::benoit::image::Image; use crate::benoit::render::Render; use crate::benoit::video::Video; extern crate rug; -use rug::{Assign, Float}; +use rug::Assign; use std::time::Instant; impl App { @@ -37,7 +36,7 @@ impl App { pub fn run(mut self) -> i32 { let mut video = Video::initialise(self.canvas_width, self.canvas_height, self.scale); - App::print_controls(); + self.print_controls(); let mut event_pump = video.sdl.event_pump().expect("unable to get event pump"); @@ -74,77 +73,4 @@ impl App { return 0x0; } - - fn render(&self, render: &mut Render) { - eprint!("rendering..."); - - let time_start = Instant::now(); - - render.render( - self.fractal, - self.renderer, - &self.centre, - &self.zoom, - &self.extra, - self.max_iter_count, - ); - - let render_time = time_start.elapsed(); - - eprintln!(" {:.3}ms", render_time.as_micros() as f32 / 1000.0); - } - - fn draw_feedback(&self, video: &mut Video, prev_centre: &Complex, prev_zoom: &Float) { - if { - // Don't draw translation feedback if rendering a - // Julia set or if we haven't done any viewport - // translations. - - &self.centre.real != &prev_centre.real - || &self.centre.imag != &prev_centre.imag - || &self.zoom != prev_zoom - }{ - video.draw_translation_feedback(self.canvas_width, self.canvas_height, self.scale, prev_centre, prev_zoom, &self.centre, &self.zoom); - } - - if self.do_textual_feedback { video.draw_textual_feedback(&self.centre, &self.zoom, self.max_iter_count) }; - } - - fn print_controls() { - println!("Controls:"); - println!("- \u{1B}[1mW\u{1B}[0m Translate +Im"); - println!("- \u{1B}[1mA\u{1B}[0m Translate -Re"); - println!("- \u{1B}[1mS\u{1B}[0m Translate -Im"); - println!("- \u{1B}[1mD\u{1B}[0m Translate +Re"); - println!(); - println!("- \u{1B}[1mQ\u{1B}[0m Zoom out"); - println!("- \u{1B}[1mE\u{1B}[0m Zoom in"); - println!(); - println!("- \u{1B}[1mR\u{1B}[0m Decrease max. iteration count"); - println!("- \u{1B}[1mF\u{1B}[0m Increase max. iteration count"); - println!(); - println!("- \u{1B}[1mLEFT ALT\u{1B}[0m Cycle to previous fractal"); - println!("- \u{1B}[1mRIGHT ALT\u{1B}[0m Cycle to next fractal"); - println!("- \u{1B}[1mTAB\u{1B}[0m Toggle Julia"); - println!("- \u{1B}[1mLEFT CTRL\u{1B}[0m Toggle inverse"); - println!(); - println!("- \u{1B}[1mLEFT\u{1B}[0m Cycle to previous palette"); - println!("- \u{1B}[1mRIGHT\u{1B}[0m Cycle to next palette"); - println!("- \u{1B}[1mUP\u{1B}[0m Increase colour range"); - println!("- \u{1B}[1mDOWN\u{1B}[0m Decrease colour range"); - println!(); - println!("- \u{1B}[1mF1\u{1B}[0m Toggle textual feedback"); - println!("- \u{1B}[1mZ\u{1B}[0m Print centre value (c)"); - println!(); - println!("- \u{1B}[1mC\u{1B}[0m Render frame"); - println!(); - println!("Controls (holding \u{1B}[1mSHIFT\u{1B}[0m):"); - println!("- \u{1B}[1mW\u{1B}[0m Perturbate/translate +Im"); - println!("- \u{1B}[1mA\u{1B}[0m Perturbate/translate -Re"); - println!("- \u{1B}[1mS\u{1B}[0m Perturbate/translate -Im"); - println!("- \u{1B}[1mD\u{1B}[0m Perturbate/translate +Re"); - println!(); - println!("- \u{1B}[1mC\u{1B}[0m Render frame"); - println!(); - } }
\ No newline at end of file diff --git a/source/benoit/benoit/colour_data.rs b/source/benoit/benoit/colour_data.rs index 6e994fb..4ea3852 100644 --- a/source/benoit/benoit/colour_data.rs +++ b/source/benoit/benoit/colour_data.rs @@ -24,63 +24,46 @@ use crate::benoit::image::Image; use crate::benoit::palette::PaletteData; -use std::slice::{from_raw_parts, from_raw_parts_mut}; +use std::mem::size_of; +use std::num::NonZeroUsize; +use std::ptr::addr_of; pub struct ColourData { - canvas_size: usize, - exponent: f32, max_iter_count: u32, colour_range: f32, palette_data: &'static PaletteData, - iter_count_buffer: *const u32, - square_dist_buffer: *const f32, - - image: *mut (u8, u8, u8), + image: NonZeroUsize, } impl ColourData { #[must_use] pub fn new( - image: &mut Image, - canvas_width: u32, - canvas_height: u32, - exponent: f32, - max_iter_count: u32, - colour_range: f32, - palette_data: &'static PaletteData, - iter_count_buffer: &[u32], - square_dist_buffer: &[f32], + image: &Image, + exponent: f32, + max_iter_count: u32, + colour_range: f32, + palette_data: &'static PaletteData, ) -> ColourData { return ColourData { - canvas_size: canvas_height as usize * canvas_width as usize, - exponent: exponent, max_iter_count: max_iter_count, colour_range: colour_range, palette_data: palette_data, - iter_count_buffer: iter_count_buffer.as_ptr(), - square_dist_buffer: square_dist_buffer.as_ptr(), - - image: image.mut_data().as_mut_ptr(), + image: NonZeroUsize::new(image.data().as_ptr() as usize).unwrap(), }; } #[must_use] - pub fn input_buffers(&self) -> (&[u32], &[f32]) { - let iter_count = unsafe { from_raw_parts(self.iter_count_buffer, self.canvas_size) }; - let dist = unsafe { from_raw_parts(self.square_dist_buffer, self.canvas_size) }; + pub fn index(&self, element: &(u8, u8, u8)) -> usize { + let element_addr = addr_of!(*element) as usize; - return (iter_count, dist); - } - - #[must_use] - pub unsafe fn image<'a>(&'a self) -> &'a mut [(u8, u8, u8)] { - return from_raw_parts_mut(self.image, self.canvas_size); + let index = (element_addr - self.image.get()) / size_of::<(u8, u8, u8)>(); + return index; } #[must_use] diff --git a/source/benoit/benoit/configuration.rs b/source/benoit/benoit/configuration.rs index 2975430..104ba41 100644 --- a/source/benoit/benoit/configuration.rs +++ b/source/benoit/benoit/configuration.rs @@ -21,23 +21,21 @@ If not, see <https://www.gnu.org/licenses/>. */ -use crate::benoit::{PRECISION}; use crate::benoit::fractal::{Fractal, FractalKind}; use crate::benoit::image::ImageFormat; use crate::benoit::palette::Palette; -use crate::benoit::renderer::Renderer; extern crate rug; use rug::Float; +pub mod default; pub mod load; pub struct Configuration { pub thread_count: u32, - pub fractal: Fractal, - pub renderer: Renderer, + pub fractal: Fractal, pub canvas_width: u32, pub canvas_height: u32, @@ -62,43 +60,14 @@ pub struct Configuration { } impl Configuration { - pub const DEFAULT_CENTRE_REAL: f64 = 0.0; - pub const DEFAULT_CENTRE_IMAG: f64 = 0.0; - pub const DEFAULT_EXTRA_REAL: f64 = 0.0; - pub const DEFAULT_EXTRA_IMAG: f64 = 0.0; - pub const DEFAULT_ZOOM: f64 = 1.0; + pub const DEFAULT_FRACTAL: Fractal = Fractal::new(FractalKind::Mandelbrot, false, false); - pub const DEFAULT_MAX_ITER_COUNT: u32 = 0x100; - pub const DEFAULT_COLOUR_RANGE: f32 = 64.0; - - #[must_use] - pub fn default() -> Configuration { - return Configuration { - thread_count: 0x0, - - fractal: Fractal::new(FractalKind::Multibrot3, false), - renderer: Renderer::Normal, - - canvas_width: 0x100, - canvas_height: 0xC0, - scale: 0x2, - frame_start: 0x10, - frame_stop: 0x10, + pub const DEFAULT_CENTRE: (f64, f64) = (0.0, 0.0); + pub const DEFAULT_EXTRA: (f64, f64) = (0.0, 0.0); + pub const DEFAULT_ZOOM: f64 = 1.0; - centre_real: Float::with_val(PRECISION, Self::DEFAULT_CENTRE_REAL), - centre_imag: Float::with_val(PRECISION, Self::DEFAULT_CENTRE_IMAG), - zoom: Float::with_val(PRECISION, Self::DEFAULT_ZOOM), - - extra_real: Float::with_val(PRECISION, Self::DEFAULT_EXTRA_REAL), - extra_imag: Float::with_val(PRECISION, Self::DEFAULT_EXTRA_IMAG), - - max_iter_count: Self::DEFAULT_MAX_ITER_COUNT, - - palette: Palette::Fire, - colour_range: Self::DEFAULT_COLOUR_RANGE, + pub const DEFAULT_MAX_ITER_COUNT: u32 = 0x100; - dump_path: "./render".to_string(), - image_format: ImageFormat::Png, - }; - } + pub const DEFAULT_PALETTE: Palette = Palette::Fire; + pub const DEFAULT_COLOUR_RANGE: f32 = 64.0; } diff --git a/source/benoit/benoit/configuration/default.rs b/source/benoit/benoit/configuration/default.rs new file mode 100644 index 0000000..24d28b1 --- /dev/null +++ b/source/benoit/benoit/configuration/default.rs @@ -0,0 +1,62 @@ +/* + 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::configuration::Configuration; +use crate::benoit::image::ImageFormat; + +extern crate rug; + +use rug::Float; + +impl Configuration { + #[must_use] + pub fn default() -> Configuration { + return Configuration { + thread_count: 0x0, // Automatic if null. + + fractal: Self::DEFAULT_FRACTAL, + + canvas_width: 0x100, + canvas_height: 0xC0, + scale: 0x2, + frame_start: 0x0, + frame_stop: 0x0, + + centre_real: Float::with_val(PRECISION, Self::DEFAULT_CENTRE.0), + centre_imag: Float::with_val(PRECISION, Self::DEFAULT_CENTRE.1), + zoom: Float::with_val(PRECISION, Self::DEFAULT_ZOOM), + + extra_real: Float::with_val(PRECISION, Self::DEFAULT_EXTRA.0), + extra_imag: Float::with_val(PRECISION, Self::DEFAULT_EXTRA.1), + + max_iter_count: Self::DEFAULT_MAX_ITER_COUNT, + + palette: Self::DEFAULT_PALETTE, + colour_range: Self::DEFAULT_COLOUR_RANGE, + + dump_path: "./render".to_string(), + image_format: ImageFormat::Png, + }; + } +} diff --git a/source/benoit/benoit/configuration/load.rs b/source/benoit/benoit/configuration/load.rs index 6229e58..c03e299 100644 --- a/source/benoit/benoit/configuration/load.rs +++ b/source/benoit/benoit/configuration/load.rs @@ -26,7 +26,6 @@ use crate::benoit::configuration::Configuration; use crate::benoit::fractal::FractalKind; use crate::benoit::image::ImageFormat; use crate::benoit::palette::Palette; -use crate::benoit::renderer::Renderer; extern crate rug; extern crate toml; @@ -52,14 +51,6 @@ impl Configuration { get_integer(&mut configuration.thread_count, &configuration_table, "thread_count"); - if let Some(name) = get_string(&configuration_table, "renderer") { - configuration.renderer = match name.as_str() { - "julia" => Renderer::Julia, - "normal" => Renderer::Normal, - name => panic!("invalid renderer method \"{name}\""), - }; - } - if let Some(name) = get_string(&configuration_table, "fractal") { configuration.fractal.set_kind(match name.as_str() { "burningship" => FractalKind::BurningShip, diff --git a/source/benoit/benoit/fractal.rs b/source/benoit/benoit/fractal.rs index 1496450..7630571 100644 --- a/source/benoit/benoit/fractal.rs +++ b/source/benoit/benoit/fractal.rs @@ -31,31 +31,51 @@ mod iterate; pub struct Fractal { kind: FractalKind, inverse: bool, + julia: bool, } #[derive(Clone, Copy)] #[repr(u8)] pub enum FractalKind { - BurningShip, + // Sorted according to exponent. Mandelbrot, + BurningShip, + Tricorn, Multibrot3, Multibrot4, - Tricorn, } pub type IteratorFunction = fn(&mut Complex, &Complex); impl Fractal { #[must_use] - pub const fn new(kind: FractalKind, inverse: bool) -> Self { + pub const fn new(kind: FractalKind, inverse: bool, julia: bool) -> Self { let fractal = Fractal { kind: kind, inverse: inverse, + julia: julia, }; return fractal; } + pub fn name(&self) -> String { + let kind = self.kind.name(); + + let extra = if self.inverse && !self.julia { + "inverse" + } else if !self.inverse && self.julia { + "julia" + } else if self.inverse && self.julia { + "inverse julia" + } else { + "normal" + }; + + let name = format!("{kind} ({extra})"); + return name; + } + #[must_use] pub fn kind(&self) -> FractalKind { return self.kind; @@ -67,6 +87,11 @@ impl Fractal { } #[must_use] + pub fn julia(&self) -> bool { + return self.julia; + } + + #[must_use] pub fn exponent(&self) -> f32 { return match self.kind { FractalKind::BurningShip => 2.0, @@ -96,34 +121,40 @@ impl Fractal { self.inverse = inverse; } + pub fn set_julia(&mut self, julia: bool) { + self.julia = julia; + } + pub fn cycle(&mut self, direction: i8) { // Not important. debug_assert!(direction != 0x0); let raw = self.kind as i8 + direction; - let raw: u8 = if raw < 0x0 { + + let new: FractalKind = if raw < 0x0 { FractalKind::MAX } else if raw > FractalKind::MAX as i8 { - 0x0 + FractalKind::MIN } else { - raw as u8 + unsafe { transmute(raw) } }; - let new: FractalKind = unsafe { transmute(raw) }; + let new: FractalKind = unsafe { transmute(new) }; self.kind = new; } } impl FractalKind { - const MAX: u8 = FractalKind::Tricorn as u8; + const MIN: FractalKind = FractalKind::Mandelbrot; + const MAX: FractalKind = FractalKind::Multibrot4; pub fn name(self) -> &'static str { return match self { FractalKind::BurningShip => "burning ship", FractalKind::Mandelbrot => "mandelbrot set", - FractalKind::Multibrot3 => "multibrot (d=3) set", - FractalKind::Multibrot4 => "multibrot (d=4) set", + FractalKind::Multibrot3 => "multibrot3 set", + FractalKind::Multibrot4 => "multibrot4 set", FractalKind::Tricorn => "tricorn", }; } diff --git a/source/benoit/benoit/image.rs b/source/benoit/benoit/image.rs index a140a01..dffacb0 100644 --- a/source/benoit/benoit/image.rs +++ b/source/benoit/benoit/image.rs @@ -48,8 +48,8 @@ impl Image { } #[must_use] - pub fn mut_data<'a>(&'a mut self) -> &'a mut [(u8, u8, u8)] { - return &mut self.data[..]; + pub fn data<'a>(&'a self) -> &'a [(u8, u8, u8)] { + return &self.data[..]; } #[must_use] diff --git a/source/benoit/benoit/image/colour.rs b/source/benoit/benoit/image/colour.rs index 18085dd..3eefd09 100644 --- a/source/benoit/benoit/image/colour.rs +++ b/source/benoit/benoit/image/colour.rs @@ -36,40 +36,27 @@ impl Image { let (fractal, max_iter_count) = render.info().expect("cannot colour before render"); - let (iter_count_buffer, square_dist_buffer) = render.data(); - let data = ColourData::new( self, - self.width, - self.height, fractal.exponent(), max_iter_count.min(new_max_iter_count), colour_range, palette.data(), - &iter_count_buffer, - &square_dist_buffer, ); - let canvas_size = self.height as usize * self.width as usize; - - (0x0..canvas_size).into_par_iter().for_each(|index| { - colour_point(&data, index as usize); + self.data.par_iter_mut().for_each(|point| { + let index = data.index(point); + let (iter_count, dist) = render[index]; + *point = colour_point(&data, iter_count, dist); }); } } -fn colour_point(data: &ColourData, index: usize) { - let (iter_count_buffer, square_dist_buffer) = data.input_buffers(); - - let image = unsafe { data.image() }; - +fn colour_point(data: &ColourData, iter_count: u32, dist: f32) -> (u8, u8, u8) { let (exponent, max_iter_count, colour_range, palette_data) = data.consts(); - let iter_count = iter_count_buffer[ index]; - let distance = square_dist_buffer[index].sqrt(); - let (red, green, blue) = if iter_count < max_iter_count { - let factor = (iter_count as f32 + 1.0 - distance.ln().log(exponent)) / colour_range; + let factor = (iter_count as f32 + 1.0 - dist.ln().log(exponent)) / colour_range; let index = (factor * PALETTE_DATA_LENGTH as f32).round() as usize % PALETTE_DATA_LENGTH; palette_data[index] @@ -81,5 +68,5 @@ fn colour_point(data: &ColourData, index: usize) { let green = (green * 255.0).round() as u8; let blue = (blue * 255.0).round() as u8; - image[index] = (red, green, blue); + return (red, green, blue); } diff --git a/source/benoit/benoit/palette.rs b/source/benoit/benoit/palette.rs index b697af7..c5157f6 100644 --- a/source/benoit/benoit/palette.rs +++ b/source/benoit/benoit/palette.rs @@ -21,29 +21,34 @@ If not, see <https://www.gnu.org/licenses/>. */ -extern crate ctor; extern crate enum_iterator; -use ctor::ctor; -use enum_iterator::{all, Sequence}; +use enum_iterator::Sequence; use std::mem::transmute; +mod data; mod paint; +pub use data::fill_palettes; + +pub const PALETTE_DATA_LENGTH: usize = 0x1000; +pub type PaletteData = [(f32, f32, f32); PALETTE_DATA_LENGTH]; + #[derive(Clone, Copy, Sequence)] #[repr(u8)] pub enum Palette { + // Sorted according to date of addition. Ancient, Fire, Greyscale, + Sapphire, Hsv, Lch, - Sapphire, } impl Palette { const MIN: Self = Palette::Ancient; - const MAX: Self = Palette::Sapphire; + const MAX: Self = Palette::Lch; #[must_use] pub fn name(self) -> &'static str { @@ -59,7 +64,8 @@ impl Palette { #[must_use] pub fn data(self) -> &'static PaletteData { - return &*self.mut_data(); + // This is safe as we return it as immutable. + return unsafe { &*self.mut_data() }; } #[must_use] @@ -88,50 +94,4 @@ impl Palette { Palette::Sapphire => paint::sapphire, }; } - - #[must_use] - fn mut_data(self) -> &'static mut PaletteData { - return unsafe { match self { - Palette::Ancient => &mut DATA_ANCIENT, - Palette::Fire => &mut DATA_FIRE, - Palette::Greyscale => &mut DATA_GREYSCALE, - Palette::Hsv => &mut DATA_HSV, - Palette::Lch => &mut DATA_LCH, - Palette::Sapphire => &mut DATA_SAPPHIRE, - } }; - } -} - -// We would like to precalculate the palettes at -// compile-time, but Rust does not support -// floating-point arithmetic there. - -pub const PALETTE_DATA_LENGTH: usize = 0x1000; -pub type PaletteData = [(f32, f32, f32); PALETTE_DATA_LENGTH]; - -const fn default_palette_data() -> PaletteData { - return [(0.0, 0.0, 0.0); PALETTE_DATA_LENGTH]; -} - -static mut DATA_ANCIENT: PaletteData = default_palette_data(); -static mut DATA_FIRE: PaletteData = default_palette_data(); -static mut DATA_GREYSCALE: PaletteData = default_palette_data(); -static mut DATA_HSV: PaletteData = default_palette_data(); -static mut DATA_LCH: PaletteData = default_palette_data(); -static mut DATA_SAPPHIRE: PaletteData = default_palette_data(); - -#[ctor] -fn calculate_palettes() { - for palette in all::<Palette>() { - let data = palette.mut_data(); - let function = palette.function(); - - for index in 0x0..PALETTE_DATA_LENGTH { - let factor = index as f32 / PALETTE_DATA_LENGTH as f32; - - let (red, green, blue) = function(factor); - - data[index as usize] = (red, green, blue); - } - } } diff --git a/source/benoit/benoit/palette/data.rs b/source/benoit/benoit/palette/data.rs new file mode 100644 index 0000000..28bfba4 --- /dev/null +++ b/source/benoit/benoit/palette/data.rs @@ -0,0 +1,77 @@ +/* + 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::palette::{Palette, PALETTE_DATA_LENGTH, PaletteData}; + +use enum_iterator::all; + +impl Palette { + #[must_use] + pub(super) unsafe fn mut_data(self) -> &'static mut PaletteData { + return unsafe { match self { + Palette::Ancient => &mut DATA_ANCIENT, + Palette::Fire => &mut DATA_FIRE, + Palette::Greyscale => &mut DATA_GREYSCALE, + Palette::Hsv => &mut DATA_HSV, + Palette::Lch => &mut DATA_LCH, + Palette::Sapphire => &mut DATA_SAPPHIRE, + } }; + } +} + +const fn default_palette_data() -> PaletteData { + return [(0.0, 0.0, 0.0); PALETTE_DATA_LENGTH]; +} + +static mut DATA_ANCIENT: PaletteData = default_palette_data(); +static mut DATA_FIRE: PaletteData = default_palette_data(); +static mut DATA_GREYSCALE: PaletteData = default_palette_data(); +static mut DATA_HSV: PaletteData = default_palette_data(); +static mut DATA_LCH: PaletteData = default_palette_data(); +static mut DATA_SAPPHIRE: PaletteData = default_palette_data(); + +pub unsafe fn fill_palettes() { + use std::time::Instant; + + // We would like to precalculate the palettes at + // compile-time, but Rust does not allow + // const floating-point arithmetic. + + eprint!("filling palettes..."); + let time = Instant::now(); + + for palette in all::<Palette>() { + let data = palette.mut_data(); + let function = palette.function(); + + for index in 0x0..PALETTE_DATA_LENGTH { + let factor = index as f32 / PALETTE_DATA_LENGTH as f32; + + let (red, green, blue) = function(factor); + + data[index as usize] = (red, green, blue); + } + } + + eprintln!(" {:.3}ms", time.elapsed().as_micros() as f32 / 1000.0); +} diff --git a/source/benoit/benoit/render.rs b/source/benoit/benoit/render.rs index 6e2b6ef..0141f52 100644 --- a/source/benoit/benoit/render.rs +++ b/source/benoit/benoit/render.rs @@ -26,14 +26,15 @@ use crate::benoit::fractal::Fractal; pub mod allocate; pub mod render; +use std::ops::{Index, IndexMut}; + pub struct Render { canvas_width: u32, canvas_height: u32, info: Option<(Fractal, u32)>, - iter_count_buffer: Vec::<u32>, - square_dist_buffer: Vec::<f32>, + data: Vec::<(u32, f32)>, } impl Render { @@ -46,9 +47,18 @@ impl Render { pub fn info(&self) -> Option<(Fractal, u32)> { return self.info.clone(); } +} - #[must_use] - pub fn data<'a>(&'a self) -> (&'a Vec::<u32>, &'a Vec::<f32>) { - return (&self.iter_count_buffer, &self.square_dist_buffer); +impl Index<usize> for Render { + type Output = (u32, f32); + + fn index<'a>(&'a self, index: usize) -> &'a Self::Output { + return &self.data[index]; + } +} + +impl IndexMut<usize> for Render { + fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Self::Output { + return &mut self.data[index]; } } diff --git a/source/benoit/benoit/render/allocate.rs b/source/benoit/benoit/render/allocate.rs index e76f540..4228914 100644 --- a/source/benoit/benoit/render/allocate.rs +++ b/source/benoit/benoit/render/allocate.rs @@ -32,8 +32,7 @@ impl Render { canvas_size as usize }; - let iter_count_buffer: Vec::<u32> = vec![0x0; canvas_size]; - let square_dist_buffer: Vec::<f32> = vec![0.0; canvas_size]; + let data: Vec<(u32, f32)> = vec![(0x0, 0.0); canvas_size]; return Render { canvas_width: canvas_width, @@ -41,8 +40,7 @@ impl Render { info: None, - iter_count_buffer: iter_count_buffer, - square_dist_buffer: square_dist_buffer, + data: data, }; } } diff --git a/source/benoit/benoit/render/render.rs b/source/benoit/benoit/render/render.rs index 731f347..7ce1a3e 100644 --- a/source/benoit/benoit/render/render.rs +++ b/source/benoit/benoit/render/render.rs @@ -21,33 +21,32 @@ If not, see <https://www.gnu.org/licenses/>. */ +use crate::benoit::{BAILOUT_DISTANCE, PRECISION}; use crate::benoit::complex::Complex; use crate::benoit::fractal::{Fractal, IteratorFunction}; use crate::benoit::render::Render; use crate::benoit::render_data::RenderData; -use crate::benoit::renderer::{PointRenderer, Renderer}; extern crate rayon; extern crate rug; use rayon::prelude::*; -use rug::Float; +use rug::{Assign, Float}; +use rug::float::Special; impl Render { pub fn render( &mut self, - fractal: Fractal, - renderer: Renderer, - centre: &Complex, - zoom: &Float, - extra: &Complex, - max_iter_count: u32, + fractal: Fractal, + centre: &Complex, + zoom: &Float, + extra: &Complex, + max_iter_count: u32, ) { assert!(max_iter_count > 0x0); let data = RenderData::new( - &mut self.iter_count_buffer[..], - &mut self.square_dist_buffer[..], + &mut self.data[..], self.canvas_width, self.canvas_height, centre.clone(), @@ -55,31 +54,68 @@ impl Render { zoom.clone(), max_iter_count, fractal.inverse(), + fractal.julia(), ); - let canvas_size = self.canvas_height as usize * self.canvas_width as usize; + let iterator = fractal.iterator(); - let point_renderer = renderer.point_renderer(); - let iterator = fractal.iterator(); - - (0x0..canvas_size).into_par_iter().for_each(|index| { - render_point(&data, index as usize, point_renderer, iterator); + self.data.par_iter_mut().for_each(|point| { + let (x, y) = data.coordinate(point); + *point = render_point(&data, x, y, iterator); }); self.info = Some((fractal, max_iter_count)); } } -fn render_point(data: &RenderData, index: usize, point_renderer: PointRenderer, iterator: IteratorFunction) { - let (iter_count_buffer, square_dist_buffer) = unsafe { data.output_buffers() }; +fn render_point(data: &RenderData, x: u32, y: u32, iterator: IteratorFunction) -> (u32, f32) { + let (centre, zoom, max_iter_count) = data.input(); + + let (x_offset, y_offset, x_factor, y_factor) = data.consts(); + + let x_temporary = (x as f32 + x_offset) * x_factor; + let y_temporary = (y as f32 + y_offset) * y_factor; + + let mut z = { + let mut a = Float::with_val(PRECISION, x_temporary / zoom); + a += ¢re.real; + + let mut b = Float::with_val(PRECISION, y_temporary / zoom); + b -= ¢re.imag; + + Complex {real: a, imag: b} + }; - let (canvas_width, _) = data.canvas_size(); + z = data.factor_inverse(z); + let (mut z, c) = data.setup_julia(z); - let x = (index % canvas_width as usize) as u32; - let y = (index / canvas_width as usize) as u32; + let mut z_prev = Complex { + real: Float::with_val(PRECISION, Special::Nan), + imag: Float::with_val(PRECISION, Special::Nan), + }; - let (iter_count, square_dist) = point_renderer(&data, x, y, iterator); + let mut iter_count: u32 = 0x1; + let mut square_dist = Float::with_val(PRECISION, Special::Nan); + while { + square_dist.assign(&z.real * &z.real + &z.imag * &z.imag); + // Having a larger escape radius gives better + // results with regard to smoothing. + + // Check if the value is periodic, i.e. its + // sequence repeats. + let periodic = z.real == z_prev.real && z.imag == z_prev.imag; + if periodic { iter_count = max_iter_count } + + square_dist <= BAILOUT_DISTANCE && iter_count < max_iter_count + } { + z_prev.assign(&z); + + iterator(&mut z, &c); + + z = data.perturbate(z); + + iter_count += 0x1; + } - iter_count_buffer[ index as usize] = iter_count; - square_dist_buffer[index as usize] = square_dist; + return (iter_count, square_dist.to_f32()); } diff --git a/source/benoit/benoit/render_data.rs b/source/benoit/benoit/render_data.rs index 0dda5d9..4f6eaa9 100644 --- a/source/benoit/benoit/render_data.rs +++ b/source/benoit/benoit/render_data.rs @@ -27,13 +27,12 @@ use crate::benoit::complex::Complex; extern crate rug; use rug::Float; -use std::slice::from_raw_parts_mut; +use std::mem::size_of; +use std::num::NonZeroUsize; +use std::ptr::addr_of; pub struct RenderData { - canvas_width: u32, - canvas_height: u32, - - canvas_size: usize, + canvas_width: u32, centre: Complex, extra: Complex, @@ -42,6 +41,7 @@ pub struct RenderData { max_iter_count: u32, inverse: bool, + julia: bool, x_offset: f32, y_offset: f32, @@ -49,15 +49,13 @@ pub struct RenderData { x_factor: f32, y_factor: f32, - iter_count_buffer: *mut u32, - square_dist_buffer: *mut f32, + buffer: NonZeroUsize, } impl RenderData { #[must_use] pub fn new( - iter_count_buffer: &mut [u32], - square_dist_buffer: &mut [f32], + buffer: &[(u32, f32)], canvas_width: u32, canvas_height: u32, centre: Complex, @@ -65,14 +63,12 @@ impl RenderData { zoom: Float, max_iter_count: u32, inverse: bool, + julia: bool, ) -> RenderData { let (width_ratio, height_ratio) = width_height_ratio(canvas_width, canvas_height); return RenderData { - canvas_width: canvas_width, - canvas_height: canvas_height, - - canvas_size: canvas_height as usize * canvas_width as usize, + canvas_width: canvas_width, centre: centre, extra: extra, @@ -81,6 +77,7 @@ impl RenderData { max_iter_count: max_iter_count, inverse: inverse, + julia: julia, x_offset: canvas_width as f32 / -2.0, y_offset: canvas_height as f32 / -2.0, @@ -88,49 +85,63 @@ impl RenderData { x_factor: 4.0 / canvas_width as f32 * width_ratio, y_factor: 4.0 / canvas_height as f32 * height_ratio, - iter_count_buffer: iter_count_buffer.as_mut_ptr(), - square_dist_buffer: square_dist_buffer.as_mut_ptr(), + buffer: NonZeroUsize::new(buffer.as_ptr() as usize).unwrap(), }; } #[must_use] - pub fn canvas_size(&self) -> (u32, u32) { - return (self.canvas_width, self.canvas_height); + pub fn input(&self) -> (&Complex, &Float, u32) { + return (&self.centre, &self.zoom, self.max_iter_count); } #[must_use] - pub fn input(&self) -> (&Complex, &Complex, &Float, u32) { - return (&self.centre, &self.extra, &self.zoom, self.max_iter_count); + pub fn consts(&self) -> (f32, f32, f32, f32) { + return (self.x_offset, self.y_offset, self.x_factor, self.y_factor); } #[must_use] - pub unsafe fn output_buffers<'a>(&'a self) -> (&'a mut [u32], &'a mut [f32]) { - // All reads and writes to these buffer are unsafe - // AND UB if the same indices are used from - // multiple threads. + pub fn coordinate(&self, element: &(u32, f32)) -> (u32, u32) { + let element_addr = addr_of!(*element) as usize; - let iter_count = from_raw_parts_mut(self.iter_count_buffer, self.canvas_size as usize); - let square_dist = from_raw_parts_mut(self.square_dist_buffer, self.canvas_size as usize); + let index = (element_addr - self.buffer.get()) / size_of::<(u32, f32)>(); - return (iter_count, square_dist); + let x = (index % self.canvas_width as usize) as u32; + let y = (index / self.canvas_width as usize) as u32; + return (x, y); } #[must_use] - pub fn consts(&self) -> (f32, f32, f32, f32) { - return (self.x_offset, self.y_offset, self.x_factor, self.y_factor); + pub fn factor_inverse(&self, mut z: Complex) -> Complex { + if self.inverse { + let mut factor_inverse = Float::with_val(PRECISION, &z.real * &z.real); + factor_inverse += &z.imag * &z.imag; + factor_inverse.recip_mut(); + + z.real *= &factor_inverse; + z.imag *= factor_inverse; + } + + return z; } #[must_use] - pub fn inverse_factor(&self, val: &Complex) -> Float { - return if self.inverse { - let mut inverse_factor = Float::with_val(PRECISION, &val.real * &val.real); - inverse_factor += &val.imag * &val.imag; - inverse_factor.recip_mut(); - - inverse_factor - } else { - Float::with_val(PRECISION, 0x1) + pub fn setup_julia(&self, z: Complex) -> (Complex, Complex) { + let c = match self.julia { + false => z.clone(), + true => self.extra.clone(), }; + + return (z, c); + } + + #[must_use] + pub fn perturbate(&self, mut z: Complex) -> Complex { + if !self.julia { + z.real += &self.extra.real; + z.imag += &self.extra.imag; + } + + return z; } } diff --git a/source/benoit/benoit/renderer.rs b/source/benoit/benoit/renderer.rs deleted file mode 100644 index 0c02509..0000000 --- a/source/benoit/benoit/renderer.rs +++ /dev/null @@ -1,55 +0,0 @@ -/* - 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::fractal::IteratorFunction; -use crate::benoit::render_data::RenderData; - -use std::mem::transmute; - -mod render_point; - -#[derive(Clone, Copy)] -#[repr(u8)] -pub enum Renderer { - Julia, - Normal, -} - -pub type PointRenderer = fn(&RenderData, u32, u32, IteratorFunction) -> (u32, f32); - -impl Renderer { - #[must_use] - pub fn point_renderer(self) -> PointRenderer { - return match self { - Renderer::Julia => render_point::julia, - Renderer::Normal => render_point::normal, - }; - } - - pub fn toggle(&mut self) { - let raw = !(*self as u8) & 0b00000001; - let new: Self = unsafe { transmute(raw) }; - - *self = new; - } -} diff --git a/source/benoit/benoit/renderer/render_point/julia.rs b/source/benoit/benoit/renderer/render_point/julia.rs deleted file mode 100644 index 06a478c..0000000 --- a/source/benoit/benoit/renderer/render_point/julia.rs +++ /dev/null @@ -1,84 +0,0 @@ -/* - 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::{BAILOUT, PRECISION}; -use crate::benoit::complex::Complex; -use crate::benoit::fractal::IteratorFunction; -use crate::benoit::render_data::RenderData; - -extern crate rug; - -use rug::{Assign, Float}; -use rug::float::Special; - -pub fn julia(data: &RenderData, x: u32, y: u32, iterator: IteratorFunction) -> (u32, f32) { - // For more information, see render_point::normal. - - let (centre, extra, zoom, max_iter_count) = data.input(); - - let (x_offset, y_offset, x_factor, y_factor) = data.consts(); - - let x_temporary = (x as f32 + x_offset) * x_factor; - let y_temporary = (y as f32 + y_offset) * y_factor; - - let c = extra; - - let mut z = { - let mut a = Float::with_val(PRECISION, x_temporary / zoom); - a += ¢re.real; - - let mut b = Float::with_val(PRECISION, y_temporary / zoom); - b -= ¢re.imag; - - Complex {real: a, imag: b} - }; - - let inverse_factor = data.inverse_factor(&z); - - z.real *= &inverse_factor; - z.imag *= &inverse_factor; - - let mut z_prev = Complex { - real: Float::with_val(PRECISION, Special::Nan), - imag: Float::with_val(PRECISION, Special::Nan), - }; - - let mut iter_count: u32 = 0x1; - let mut square_dist = Float::with_val(PRECISION, Special::Nan); - while { - square_dist.assign(&z.real * &z.real + &z.imag * &z.imag); - - let periodic = z.real == z_prev.real && z.imag == z_prev.imag; - if periodic { iter_count = max_iter_count } - - square_dist <= BAILOUT && iter_count < max_iter_count - } { - z_prev.assign(&z); - - iterator(&mut z, c); - - iter_count += 0x1; - } - - return (iter_count, square_dist.to_f32()); -} diff --git a/source/benoit/benoit/renderer/render_point/normal.rs b/source/benoit/benoit/renderer/render_point/normal.rs deleted file mode 100644 index e9f848f..0000000 --- a/source/benoit/benoit/renderer/render_point/normal.rs +++ /dev/null @@ -1,89 +0,0 @@ -/* - 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::{BAILOUT, PRECISION}; -use crate::benoit::complex::Complex; -use crate::benoit::fractal::IteratorFunction; -use crate::benoit::render_data::RenderData; - -extern crate rug; - -use rug::{Assign, Float}; -use rug::float::Special; - -pub fn normal(data: &RenderData, x: u32, y: u32, iterator: IteratorFunction) -> (u32, f32) { - let (centre, extra, zoom, max_iter_count) = data.input(); - - let (x_offset, y_offset, x_factor, y_factor) = data.consts(); - - let x_temporary = (x as f32 + x_offset) * x_factor; - let y_temporary = (y as f32 + y_offset) * y_factor; - - let mut z = { - let mut a = Float::with_val(PRECISION, x_temporary / zoom); - a += ¢re.real; - - let mut b = Float::with_val(PRECISION, y_temporary / zoom); - b -= ¢re.imag; - - Complex {real: a, imag: b} - }; - - let inverse_factor = data.inverse_factor(&z); - - z.real *= &inverse_factor; - z.imag *= &inverse_factor; - - let c = z.clone(); - - let mut z_prev = Complex { - real: Float::with_val(PRECISION, Special::Nan), - imag: Float::with_val(PRECISION, Special::Nan), - }; - - let mut iter_count: u32 = 0x1; - let mut square_dist = Float::with_val(PRECISION, Special::Nan); - while { - square_dist.assign(&z.real * &z.real + &z.imag * &z.imag); - // Having a larger escape radius gives better - // results with regard to smoothing. - - // Check if the value is periodic, i.e. its - // sequence repeats. - let periodic = z.real == z_prev.real && z.imag == z_prev.imag; - if periodic { iter_count = max_iter_count } - - square_dist <= BAILOUT && iter_count < max_iter_count - } { - z_prev.assign(&z); - - iterator(&mut z, &c); - - z.real += &extra.real; - z.imag -= &extra.imag; - - iter_count += 0x1; - } - - return (iter_count, square_dist.to_f32()); -} diff --git a/source/benoit/benoit/script.rs b/source/benoit/benoit/script.rs index 4337944..6403bef 100644 --- a/source/benoit/benoit/script.rs +++ b/source/benoit/benoit/script.rs @@ -25,7 +25,6 @@ use crate::benoit::complex::Complex; use crate::benoit::fractal::Fractal; use crate::benoit::image::ImageFormat; use crate::benoit::palette::Palette; -use crate::benoit::renderer::Renderer; extern crate rug; @@ -40,7 +39,6 @@ pub mod still; pub struct Script { // Configuration: fractal: Fractal, - renderer: Renderer, canvas_width: u32, canvas_height: u32, diff --git a/source/benoit/benoit/script/animate.rs b/source/benoit/benoit/script/animate.rs index 65bbe90..48fea58 100644 --- a/source/benoit/benoit/script/animate.rs +++ b/source/benoit/benoit/script/animate.rs @@ -65,7 +65,6 @@ impl Script { frame_name.as_str(), &mut image, &mut render, - self.renderer, self.fractal, self.palette, &self.centre, diff --git a/source/benoit/benoit/script/configure.rs b/source/benoit/benoit/script/configure.rs index acb6071..9966199 100644 --- a/source/benoit/benoit/script/configure.rs +++ b/source/benoit/benoit/script/configure.rs @@ -29,8 +29,7 @@ impl Script { #[must_use] pub fn configure(configuration: Configuration) -> Script { return Script { - fractal: configuration.fractal, - renderer: configuration.renderer, + fractal: configuration.fractal, canvas_width: configuration.canvas_width, canvas_height: configuration.canvas_height, diff --git a/source/benoit/benoit/script/dump.rs b/source/benoit/benoit/script/dump.rs index d77b597..d8c23f2 100644 --- a/source/benoit/benoit/script/dump.rs +++ b/source/benoit/benoit/script/dump.rs @@ -26,7 +26,6 @@ use crate::benoit::fractal::Fractal; use crate::benoit::image::{Image, ImageFormat}; use crate::benoit::palette::Palette; use crate::benoit::render::Render; -use crate::benoit::renderer::Renderer; use crate::benoit::script::Script; extern crate rug; @@ -40,7 +39,6 @@ impl Script { name: &str, image: &mut Image, render: &mut Render, - renderer: Renderer, fractal: Fractal, palette: Palette, centre: &Complex, @@ -56,7 +54,6 @@ impl Script { render.render( fractal, - renderer, centre, zoom, extra, diff --git a/source/benoit/benoit/script/still.rs b/source/benoit/benoit/script/still.rs index 73ebcf8..b93dc11 100644 --- a/source/benoit/benoit/script/still.rs +++ b/source/benoit/benoit/script/still.rs @@ -38,7 +38,6 @@ impl Script { FRAME_NAME, &mut image, &mut render, - self.renderer, self.fractal, self.palette, &self.centre, diff --git a/source/benoit/benoit/video/initialise.rs b/source/benoit/benoit/video/initialise.rs index 76668bd..64a0608 100644 --- a/source/benoit/benoit/video/initialise.rs +++ b/source/benoit/benoit/video/initialise.rs @@ -35,7 +35,7 @@ impl Video { let sdl = sdl2::init().expect("unable to initialise sdl2"); let sdl_video = sdl.video().expect("unable to initialise video"); - let window_title = format!("BENO\u{CE}T {:X}.{:X}.{:X}", VERSION[0x0], VERSION[0x1], VERSION[0x2]); + let window_title = format!("BENO\u{CE}T {:X}.{:X}.{:X}", VERSION.0, VERSION.1, VERSION.2); let mut window_builder = sdl_video.window(window_title.as_str(), canvas_width * scale, canvas_height * scale); window_builder.position_centered(); diff --git a/source/benoit/main.rs b/source/benoit/main.rs index 9e8e572..4263ff4 100644 --- a/source/benoit/main.rs +++ b/source/benoit/main.rs @@ -26,6 +26,7 @@ mod benoit; use crate::benoit::VERSION; use crate::benoit::app::App; use crate::benoit::configuration::Configuration; +use crate::benoit::palette::fill_palettes; use crate::benoit::script::Script; extern crate rayon; @@ -37,12 +38,14 @@ use std::thread::available_parallelism; fn main() { println!(); - println!("\u{1B}[1mBENO\u{CE}T\u{1B}[0m {:X}.{:X}.{:X}", VERSION[0x0], VERSION[0x1], VERSION[0x2]); + println!("\u{1B}[1mBENO\u{CE}T\u{1B}[0m {:X}.{:X}.{:X}", VERSION.0, VERSION.1, VERSION.2); println!("Copyright 2021, 2023 Gabriel Bj\u{F8}rnager Jensen."); println!(); println!("Le p\u{E8}re cogita et c'est pourquoi il fut."); println!(); + unsafe { fill_palettes() }; + let mut arguments = args(); let (mut configuration, interative) = match arguments.nth(0x1) { @@ -63,12 +66,12 @@ fn main() { ThreadPoolBuilder::new().num_threads(configuration.thread_count as usize).build_global().unwrap(); let code = if interative { - eprintln!("running iteractive mode"); + eprintln!("running in iteractive mode"); let app = App::configure(configuration); app.run() } else { - eprintln!("running script mode"); + eprintln!("running in script mode"); let script = Script::configure(configuration); script.run() |