diff options
-rw-r--r-- | CHANGELOG.md | 68 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | benoit.svg | 30 | ||||
-rw-r--r-- | benoit_square.svg | 8 | ||||
-rw-r--r-- | source/benoit/benoit.rs | 33 | ||||
-rw-r--r-- | source/benoit/benoit/app.rs (renamed from source/benoit/benoit/application.rs) | 15 | ||||
-rw-r--r-- | source/benoit/benoit/app/animate.rs | 120 | ||||
-rw-r--r-- | source/benoit/benoit/app/colour.rs | 40 | ||||
-rw-r--r-- | source/benoit/benoit/app/colour_row.rs (renamed from source/benoit/benoit/application/colour.rs) | 80 | ||||
-rw-r--r-- | source/benoit/benoit/app/drop.rs (renamed from source/benoit/benoit/render_data/send.rs) | 9 | ||||
-rw-r--r-- | source/benoit/benoit/app/dump.rs (renamed from source/benoit/benoit/application/dump.rs) | 4 | ||||
-rw-r--r-- | source/benoit/benoit/app/get_iterator_function.rs (renamed from source/benoit/benoit/application/get_iterator_function.rs) | 4 | ||||
-rw-r--r-- | source/benoit/benoit/app/get_row_renderer.rs (renamed from source/benoit/benoit/application/get_row_renderer.rs) | 10 | ||||
-rw-r--r-- | source/benoit/benoit/app/handle_keys.rs (renamed from source/benoit/benoit/application/handle_keys.rs) | 66 | ||||
-rw-r--r-- | source/benoit/benoit/app/initialise.rs (renamed from source/benoit/benoit/application/initialise.rs) | 16 | ||||
-rw-r--r-- | source/benoit/benoit/app/loop.rs | 126 | ||||
-rw-r--r-- | source/benoit/benoit/app/poll_events.rs (renamed from source/benoit/benoit/application/poll_events.rs) | 4 | ||||
-rw-r--r-- | source/benoit/benoit/app/render.rs (renamed from source/benoit/benoit/application/render.rs) | 17 | ||||
-rw-r--r-- | source/benoit/benoit/app/render_row_julia.rs (renamed from source/benoit/benoit/application/render_row_julia.rs) | 19 | ||||
-rw-r--r-- | source/benoit/benoit/app/render_row_normal.rs (renamed from source/benoit/benoit/application/render_row_normal.rs) | 21 | ||||
-rw-r--r-- | source/benoit/benoit/app/run.rs (renamed from source/benoit/benoit/application/run.rs) | 8 | ||||
-rw-r--r-- | source/benoit/benoit/application/animate.rs | 90 | ||||
-rw-r--r-- | source/benoit/benoit/application/loop.rs | 96 | ||||
-rw-r--r-- | source/benoit/benoit/configuration.rs | 2 | ||||
-rw-r--r-- | source/benoit/benoit/configuration/default.rs | 2 | ||||
-rw-r--r-- | source/benoit/benoit/configuration/load.rs | 28 | ||||
-rw-r--r-- | source/benoit/benoit/iteration/iterate_burning_ship.rs | 2 | ||||
-rw-r--r-- | source/benoit/benoit/iteration/iterate_mandelbrot.rs | 4 | ||||
-rw-r--r-- | source/benoit/benoit/iteration/iterate_tricorn.rs | 4 | ||||
-rw-r--r-- | source/benoit/benoit/task.rs (renamed from source/benoit/benoit/render_data/sync.rs) | 5 | ||||
-rw-r--r-- | source/benoit/benoit/task/colour_data.rs | 40 | ||||
-rw-r--r-- | source/benoit/benoit/task/colour_data/new.rs | 40 | ||||
-rw-r--r-- | source/benoit/benoit/task/colour_data/slice.rs | 41 | ||||
-rw-r--r-- | source/benoit/benoit/task/render_data.rs (renamed from source/benoit/benoit/render_data.rs) | 5 | ||||
-rw-r--r-- | source/benoit/benoit/task/render_data/new.rs (renamed from source/benoit/benoit/render_data/new.rs) | 9 | ||||
-rw-r--r-- | source/benoit/benoit/task/render_data/slice.rs (renamed from source/benoit/benoit/render_data/slice.rs) | 7 | ||||
-rw-r--r-- | source/benoit/benoit/video.rs | 9 | ||||
-rw-r--r-- | source/benoit/benoit/video/draw.rs (renamed from source/benoit/benoit/application/draw.rs) | 58 | ||||
-rw-r--r-- | source/benoit/benoit/video/initialise.rs | 7 | ||||
-rw-r--r-- | source/benoit/benoit/video/sync.rs | 46 | ||||
-rw-r--r-- | source/benoit/main.rs | 8 |
41 files changed, 772 insertions, 431 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 74c4f6e..bd1278e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,20 @@ -# 2↋ +# 1.0.0 + +* Use hexadecimal versioning (with major.minor.patch) +* Don't animate single frames +* Support enabling of Julia rendering from configuration +* Perform configuration checks +* Colour in thread pools +* Update naming convention +* Synchronise with screen refresh rate +* Rework logs and timings +* Fix zoom animation +* Panic on missing configuration +* Add new logo +* Restructure code +* Update colouring (change using controls) + +# 23 * Optimise and refactor code * Update colouring @@ -6,34 +22,34 @@ * Fix complex-to-cartesian conversions (and controls) * Update translation feedback -# 2↊ +# 22 * Fix Julia toggle messages * Fix configuration parameter names * Bump dependency versions -# 29 +# 21 * Support offsets in viewport feedback * Remove support for non-square canvasses (update configuration) -# 28 +# 20 * Draw positional feedback before renders (does not currently support offsets) * Rename objects (allow some abbreviations) -# 27 +# 1F * Bring back Julia sets (using row renderers) * Update controls (decrease sensitivity of zooms) * Scale by default -# 26 +# 1E * Update colouring for small iteration counts * Revert start zoom for interactive renders -# 25 +# 1D * Update colouring (smooth) * Yield square distances from renders @@ -41,7 +57,7 @@ * Add control for cycling fractals * Add function for getting the name of a fractal -# 24 +# 1C * Optimise rendering * Use Rayon for threading @@ -50,7 +66,7 @@ * Fix render garbage (somehow) * Lower precision -# 23 +# 1B * Support rendering of the Tricorn and Burning Ship fractals * Update configuration @@ -60,28 +76,28 @@ * Update messages * Update commenting -# 22 +# 1A * Support rendering of Julia sets * Update controls * Refactor code * Update configuration -# 21 +# 19 * Update controls guide (fix typo) -# 20 +# 18 * Optimise renderer * Update commenting -# 1↋ +# 17 * Make configuration support more precise numbers (must be parsed as strings now) * Use global constant for precision -# 1↊ +# 16 * Use arbitrary-precision calculations * Depend on Rug @@ -95,19 +111,19 @@ * Refactor application structure * Print controls -# 19 +# 15 * Update controls * Update configuration format * Optimise renderer * Fix thread count not being loaded -# 18 +# 14 * Rename handle_key to handle_keys * Only load configuration if provided -# 17 +# 13 * Modulise code * Check I/O errors @@ -115,34 +131,34 @@ * Depend on toml * Update gitignore -# 16 +# 12 * Clean up code * Support rendering to files * Depend on webp * Modulise code -# 15 +# 11 * Render using multiple threads -# 14 +# 10 * Update colouring * Rename changelog file: changelog.md => CHANGELOG.md -# 13 +# F * Update render message * Add scaling setting -# 12 +# E * Render and draw in different passes * Update colouring * Actually remove old makefile -# 11 +# D * Remove old makefile * Optimise renderer @@ -150,7 +166,7 @@ * Check keyboard input (allow viewpoint movement) * Update colouring -# 10 +# C * Rewrite in Rust again * Update gitignore @@ -158,7 +174,7 @@ * Update changelog format * Use git tagging for versioning -# ↋ +# B * Drop *boost::multiprecision::mpfr_float* in favour of the standard type *::__float128* for multiprecision * Create a prettier colour palette @@ -166,7 +182,7 @@ * Automatically create a configuration file if one doesn't already exist * Greatly improve render time -# ↊ +# A * Fix #3 * Implement a working, multithreaded renderer forked from MandelbrotSDL @@ -1,6 +1,6 @@ [package] name = "benoit" -version = "0.35.0" +version = "1.0.0" authors = ["Gabriel Bjørnager Jensen"] edition = "2021" description = "Mandelbrot renderer." @@ -1,14 +1,18 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg height="11" version="1.1" width="29" xmlns="http://www.w3.org/2000/svg"> - <rect fill="#212022" height="9" rx="1" ry="1" width="27" x="1" y="1" /> - <rect fill="#FDFAF6" height="7" width="25" x="2" y="2"/> - <polygon fill="#212022" points="3,3 5,3 5,4 6,4 6,5 5,5 5,6 6,6 6,7 5,7 5,8 4,8 3,8" /> - <polygon fill="#FDFAF6" points="4,4 5,4 5,5 4,5" /> - <polygon fill="#FDFAF6" points="4,6 5,6 5,7 4,7" /> - <polygon fill="#212022" points="7,3 10,3 10,4 8,4 8,5 9,5 9,6 8,6 8,7 10,7 10,8 7,8" /> - <polygon fill="#212022" points="11,3 12,3 12,4 13,4 13,3 14,3 14,8 13,8 13,7 12,7 12,8 11,8" /> - <polygon fill="#212022" points="15,3 18,3 18,8 15,8" /> - <polygon fill="#FDFAF6" points="16,4 17,4 17,7 16,7" /> - <polygon fill="#212022" points="19,3 22,3 22,4 21,4 21,7 22,7 22,8 19,8 19,7 20,7 20,4 19,4" /> - <polygon fill="#212022" points="23,3 26,3 26,4 25,4 25,8 24,8 24,4 23,4" /> +<svg height="96" width="96" xmlns="http://www.w3.org/2000/svg"> + <!-- gradients --> + <linearGradient id="backgroundColour" x1="0" x2="1" y1="0" y2="1"> + <stop offset="0%" stop-color="#252525" /> + <stop offset="100%" stop-color="#1E1E1E" /> + </linearGradient> + <!-- masks --> + <mask id="background"> + <polygon fill="white" points="21,21 75,21 75,57.243 57.243,75 21,75" /> + </mask> + <mask id="foreground"> + <polygon fill="white" points="24,24 40,24 48,37.856 56,24 72,24 72,56 56,72 56,51.713 48,65.569 40,51.713 40,72 24,72" /> + </mask> + <!-- fills --> + <rect fill="url(#backgroundColour)" height="96" width="96" x="0" y="0" /> + <rect fill="#EAEAEA" height="96" mask="url(#background)" width="96" x="0" y="0" /> + <rect fill="#B2223B" height="48" mask="url(#foreground)" width="48" x="24" y="24" /> </svg> diff --git a/benoit_square.svg b/benoit_square.svg deleted file mode 100644 index ac70db6..0000000 --- a/benoit_square.svg +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg height="11" version="1.1" width="11" xmlns="http://www.w3.org/2000/svg"> - <rect fill="#212022" height="9" rx="1" ry="1" width="9" x="1" y="1" /> - <rect fill="#FDFAF6" height="7" width="7" x="2" y="2"/> - <polygon fill="#212022" points="4,3 6,3 6,4 7,4 7,5 6,5 6,6 7,6 7,7 6,7 6,8 5,8 4,8" /> - <polygon fill="#FDFAF6" points="5,4 6,4 6,5 5,5" /> - <polygon fill="#FDFAF6" points="5,6 6,6 6,7 5,7" /> -</svg> diff --git a/source/benoit/benoit.rs b/source/benoit/benoit.rs index 2812e52..e4a7a8f 100644 --- a/source/benoit/benoit.rs +++ b/source/benoit/benoit.rs @@ -12,8 +12,8 @@ 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 + even the implied warranty of MERCHANTABIL ITY or + FITNESS FOR A PARTICULAR PURPOSE. See theGNU Affero General Public License for more details. You should have received a copy of the GNU @@ -21,13 +21,36 @@ If not, see <https://www.gnu.org/licenses/>. */ -pub mod application; +extern crate rug; + +use rug::Float; + +pub mod app; pub mod configuration; pub mod fractal; pub mod iteration; -pub mod render_data; +pub mod task; pub mod video; -pub const VERSION: u32 = 0x23; +pub struct Version<T> { + major: T, + minor: T, + patch: T, +} + +pub const VERSION: Version::<u32> = Version::<u32> { + major: 0x1, + minor: 0x0, + patch: 0x0, +}; pub const PRECISION: u32 = 0x80; + +pub struct FeedbackInfo<'a> { + prev_centre_real: &'a Float, + prev_centre_imag: &'a Float, + prev_zoom: &'a Float, + next_centre_real: &'a Float, + next_centre_imag: &'a Float, + next_zoom: &'a Float, +} diff --git a/source/benoit/benoit/application.rs b/source/benoit/benoit/app.rs index b05ede2..f7ebb47 100644 --- a/source/benoit/benoit/application.rs +++ b/source/benoit/benoit/app.rs @@ -23,7 +23,7 @@ use crate::benoit::fractal::Fractal; use crate::benoit::iteration::IteratorFunction; -use crate::benoit::render_data::RenderData; +use crate::benoit::task::render_data::RenderData; use crate::benoit::video::Video; extern crate rug; @@ -33,7 +33,8 @@ use std::sync::Arc; pub mod animate; pub mod colour; -pub mod draw; +pub mod colour_row; +pub mod drop; pub mod dump; pub mod get_iterator_function; pub mod get_row_renderer; @@ -48,13 +49,7 @@ pub mod run; pub type RowRenderer = fn(Arc<RenderData>, u32, IteratorFunction); -pub struct PreviousPosition { - centre_real: Float, - centre_imag: Float, - zoom: Float, -} - -pub struct Application { +pub struct App { thread_count: u32, fractal: Fractal, @@ -69,6 +64,8 @@ pub struct Application { zoom: Float, max_iter_count: u32, + colour_range: f32, + dump_path: String, video: Option<Video>, diff --git a/source/benoit/benoit/app/animate.rs b/source/benoit/benoit/app/animate.rs new file mode 100644 index 0000000..f732e61 --- /dev/null +++ b/source/benoit/benoit/app/animate.rs @@ -0,0 +1,120 @@ +/* + 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::app::App; + +extern crate rug; +extern crate sdl2; + +use rug::Float; +use rug::ops::PowAssign; +use std::time::Instant; + +impl App { + pub fn animate(&self) -> i32 { + let canvas_size = self.canvas_width as usize * self.canvas_width as usize; + + let mut iter_count_buffer: Vec::<u32> = vec![0x0; canvas_size]; + let mut square_dist_buffer: Vec::<f32> = vec![0.0; canvas_size]; + + let mut image: Vec::<u8> = vec![0x0; canvas_size * 0x3]; + + if self.frame_count == 0x1 { + // If only a single frame is to be rendered, we + // aren't going to animate it. + + eprint!("rendering at {}{:+}i ({}x)...", self.centre_real.to_f32(), self.centre_imag.to_f32(), self.zoom.to_f32()); + + let time_start = Instant::now(); + + self.render(&mut iter_count_buffer[..], &mut square_dist_buffer[..], &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count); + let render_time = time_start.elapsed(); + + self.colour(&mut image[..], self.max_iter_count, &mut iter_count_buffer[..], &mut square_dist_buffer[..]); + let colour_time = time_start.elapsed() - render_time; + + self.dump(format!("{}/render.webp", self.dump_path), &image, self.canvas_width); + + eprintln!(" rend. {:.3}ms, col. {:.3}ms", render_time.as_micros() as f32 / 1000.0, colour_time.as_micros() as f32 / 1000.0); + + return 0x0; + } + + // zoom_start: + let mut zoom = Float::with_val(PRECISION, 1.0 / 4.0); + + let zoom_stop = Float::with_val(PRECISION, &self.zoom); + + let zoom_factor = { + // To get the zoom factor, we first want the 'a' + // value of the growth function from (0) to + // (frame_count) on the x-dimension and from + // (zoom_start) to (zoom_stop) on the y-dimension: + // + // a = nroot(x1-x0,y1/y0), + // + // but this may be simplified for use with Rug + // because + // + // nroot(a,b) = b^(1/a), + // + // making the final equation + // + // (y1/y0)^(1/(x1-x0)) = (zoom_stop/zoom_start)^(1/(frame_count-1)). + // + // N.b. that we subtract one from frame_count as we + // want the final render to have exactly the + // specified zoom. + + let exponent = Float::with_val(PRECISION, 1.0 / (self.frame_count as f32 - 1.0)); + + let mut factor = Float::with_val(PRECISION, &zoom_stop / &zoom); + factor.pow_assign(exponent); + + factor + }; + + eprintln!("animating {} frames at {}{:+}i to {:.3} (fac. {:.3})", self.frame_count, self.centre_real.to_f64(), self.centre_imag.to_f64(), zoom_stop.to_f64(), zoom_factor.to_f64()); + + for frame in 0x0..self.frame_count { + eprint!("{frame:010} ({:.3}x)...", zoom.to_f32()); + + let time_start = Instant::now(); + + self.render(&mut iter_count_buffer[..], &mut square_dist_buffer[..], &self.centre_real, &self.centre_imag, &zoom, self.max_iter_count); + let render_time = time_start.elapsed(); + + self.colour(&mut image[..], self.max_iter_count, &mut iter_count_buffer[..], &mut square_dist_buffer[..]); + let colour_time = time_start.elapsed() - render_time; + + self.dump(format!("{}/frame{frame:010}.webp", self.dump_path), &image, self.canvas_width); + + eprintln!(" rend. {:.3}ms, col. {:.3}ms", render_time.as_micros() as f32 / 1000.0, colour_time.as_micros() as f32 / 1000.0); + + zoom *= &zoom_factor; + } + + return 0x0; + } +} diff --git a/source/benoit/benoit/app/colour.rs b/source/benoit/benoit/app/colour.rs new file mode 100644 index 0000000..3325b55 --- /dev/null +++ b/source/benoit/benoit/app/colour.rs @@ -0,0 +1,40 @@ +/* + 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::task::colour_data::ColourData; + +extern crate rayon; + +use rayon::prelude::*; +use std::sync::Arc; + +impl App { + pub fn colour(&self, buffer: &mut [u8], max_iter_count: u32, iter_count_buffer: &[u32], square_dist_buffer: &[f32]) { + let data = Arc::new(ColourData::new(buffer, self.canvas_width, max_iter_count, self.colour_range, iter_count_buffer, square_dist_buffer)); + + (0x0..self.canvas_width).into_par_iter().for_each(|row| { + App::colour_row(data.clone(), row as u32); + }); + } +} diff --git a/source/benoit/benoit/application/colour.rs b/source/benoit/benoit/app/colour_row.rs index a4587c6..81896cc 100644 --- a/source/benoit/benoit/application/colour.rs +++ b/source/benoit/benoit/app/colour_row.rs @@ -21,7 +21,42 @@ If not, see <https://www.gnu.org/licenses/>. */ -use crate::benoit::application::Application; +use crate::benoit::app::App; +use crate::benoit::task::colour_data::ColourData; + +use std::hint::unreachable_unchecked; +use std::sync::Arc; + +impl App { + pub fn colour_row(data: Arc<ColourData>, y: u32) { + let (iter_count_buffer, square_dist_buffer, image) = unsafe { data.slice(y) }; + + for x in 0x0..data.canvas_width { + let x = x as usize; + + let iter_count = iter_count_buffer[ x]; + let distance = square_dist_buffer[x].sqrt(); + + let factor = (iter_count as f32 + 1.0 - distance.log2().log2()) / data.colour_range % 1.0; + + let (red, green, blue) = if iter_count != data.max_iter_count { + hsv_to_rgb(factor, 7.0 / 8.0, 7.0 / 8.0) + } else { + (0.0, 0.0, 0.0) + }; + + let red = (red * 255.0).round() as u8; + let green = (green * 255.0).round() as u8; + let blue = (blue * 255.0).round() as u8; + + unsafe { + *image.get_unchecked_mut(x * 0x3) = red; + *image.get_unchecked_mut(x * 0x3 + 0x1) = green; + *image.get_unchecked_mut(x * 0x3 + 0x2) = blue; + } + } + } +} fn hsv_to_rgb(hue: f32, saturation: f32, value: f32) -> (f32, f32, f32) { return if saturation <= 0.0 { @@ -39,42 +74,13 @@ fn hsv_to_rgb(hue: f32, saturation: f32, value: f32) -> (f32, f32, f32) { let t = v * (1.0 - s * (1.0 - f)); match h.trunc() as u8 { - 0x0 => (v, t, p), - 0x1 => (q, v, p), - 0x2 => (p, v, t), - 0x3 => (p, q, v), - 0x4 => (t, p, v), - 0x5 => (v, p, q), - value => unreachable!(), + 0x0 => (v, t, p), + 0x1 => (q, v, p), + 0x2 => (p, v, t), + 0x3 => (p, q, v), + 0x4 => (t, p, v), + 0x5 => (v, p, q), + _ => unsafe { unreachable_unchecked() }, } }; } - -impl Application { - pub fn colour(&self, buffer: &mut [u8], iter_count_buffer: &[u32], square_dist_buffer: &[f32]) { - let canvas_size = self.canvas_width * self.canvas_width; - - for pixel in 0x0..canvas_size { - let iter_count = iter_count_buffer[pixel as usize]; - - let distance = square_dist_buffer[pixel as usize].sqrt(); - - let factor_range = 16.0_f32.min(self.max_iter_count as f32); - let factor = (iter_count as f32 + 1.0 - distance.log2().log2()) / factor_range % 1.0; - - let (red, green, blue) = if iter_count != self.max_iter_count { - hsv_to_rgb(factor, 7.0 / 8.0, 7.0 / 8.0) - } else { - (0.0, 0.0, 0.0) - }; - - let red = (red * 255.0).round() as u8; - let green = (green * 255.0).round() as u8; - let blue = (blue * 255.0).round() as u8; - - buffer[pixel as usize * 0x3] = red; - buffer[pixel as usize * 0x3 + 0x1] = green; - buffer[pixel as usize * 0x3 + 0x2] = blue; - } - } -} diff --git a/source/benoit/benoit/render_data/send.rs b/source/benoit/benoit/app/drop.rs index b351cdd..dfa6de1 100644 --- a/source/benoit/benoit/render_data/send.rs +++ b/source/benoit/benoit/app/drop.rs @@ -21,6 +21,11 @@ If not, see <https://www.gnu.org/licenses/>. */ -use crate::benoit::render_data::RenderData; +use crate::benoit::app::App; -unsafe impl Send for RenderData {} +impl Drop for App { + fn drop(&mut self) { + eprintln!(); + eprintln!("Goodbye!"); + } +} diff --git a/source/benoit/benoit/application/dump.rs b/source/benoit/benoit/app/dump.rs index 4a19a52..6001e31 100644 --- a/source/benoit/benoit/application/dump.rs +++ b/source/benoit/benoit/app/dump.rs @@ -21,14 +21,14 @@ If not, see <https://www.gnu.org/licenses/>. */ -use crate::benoit::application::Application; +use crate::benoit::app::App; extern crate webp; use std::fs::write; use webp::Encoder; -impl Application { +impl App { pub fn dump(&self, path: String, image: &[u8], canvas_width: u32) { let encoder = Encoder::from_rgb(&image[..], canvas_width, canvas_width); diff --git a/source/benoit/benoit/application/get_iterator_function.rs b/source/benoit/benoit/app/get_iterator_function.rs index 1ca2cb7..1454785 100644 --- a/source/benoit/benoit/application/get_iterator_function.rs +++ b/source/benoit/benoit/app/get_iterator_function.rs @@ -22,10 +22,10 @@ */ use crate::benoit::fractal::Fractal; -use crate::benoit::application::Application; +use crate::benoit::app::App; use crate::benoit::iteration::*; -impl Application { +impl App { pub fn get_iterator_function(fractal: Fractal) -> IteratorFunction { return match fractal { Fractal::BurningShip => iterate_burning_ship, diff --git a/source/benoit/benoit/application/get_row_renderer.rs b/source/benoit/benoit/app/get_row_renderer.rs index d8c349a..050d14f 100644 --- a/source/benoit/benoit/application/get_row_renderer.rs +++ b/source/benoit/benoit/app/get_row_renderer.rs @@ -21,14 +21,14 @@ If not, see <https://www.gnu.org/licenses/>. */ -use crate::benoit::application::Application; -use crate::benoit::application::RowRenderer; +use crate::benoit::app::App; +use crate::benoit::app::RowRenderer; -impl Application { +impl App { pub fn get_row_renderer(julia: bool) -> RowRenderer { return match julia { - false => Application::render_row_normal, - true => Application::render_row_julia, + false => App::render_row_normal, + true => App::render_row_julia, }; } } diff --git a/source/benoit/benoit/application/handle_keys.rs b/source/benoit/benoit/app/handle_keys.rs index 1991c36..2b2cff5 100644 --- a/source/benoit/benoit/application/handle_keys.rs +++ b/source/benoit/benoit/app/handle_keys.rs @@ -22,7 +22,7 @@ */ use crate::benoit::PRECISION; -use crate::benoit::application::{Application, RowRenderer}; +use crate::benoit::app::{App, RowRenderer}; use crate::benoit::fractal::Fractal; use crate::benoit::iteration::IteratorFunction; @@ -32,34 +32,7 @@ extern crate sdl2; use rug::Float; use sdl2::keyboard::Scancode; -fn cycle_fractal(fractal: Fractal) -> (Fractal, IteratorFunction) { - let fractal = match fractal { - Fractal::BurningShip => Fractal::Mandelbrot, - Fractal::Mandelbrot => Fractal::Tricorn, - Fractal::Tricorn => Fractal::BurningShip, - }; - - let iterator_function = Application::get_iterator_function(fractal); - - eprintln!("rendering the {}", fractal.get_name()); - - return (fractal, iterator_function); -} - -fn toggle_julia(julia: bool) -> (bool, RowRenderer) { - let julia = !julia; - - let row_renderer = Application::get_row_renderer(julia); - - match julia { - false => eprintln!("disabled the julia set"), - true => eprintln!("enabled the julia set"), - }; - - return (julia, row_renderer); -} - -impl Application { +impl App { pub fn handle_keys(&mut self, scan_code: Scancode) -> bool { match scan_code { Scancode::LAlt => (self.fractal, self.iterator_function) = cycle_fractal(self.fractal), @@ -103,6 +76,41 @@ impl Application { _ => self.max_iter_count, }; + const COLOUR_RANGE_FACTOR: f32 = 1.0 + 1.0 / 16.0; + + self.colour_range = match scan_code { + Scancode::Up => self.colour_range * COLOUR_RANGE_FACTOR, + Scancode::Down => self.colour_range / COLOUR_RANGE_FACTOR, + _ => self.colour_range, + }; + return false; } } + +fn cycle_fractal(fractal: Fractal) -> (Fractal, IteratorFunction) { + let fractal = match fractal { + Fractal::BurningShip => Fractal::Mandelbrot, + Fractal::Mandelbrot => Fractal::Tricorn, + Fractal::Tricorn => Fractal::BurningShip, + }; + + let iterator_function = App::get_iterator_function(fractal); + + eprintln!("rendering the {}", fractal.get_name()); + + return (fractal, iterator_function); +} + +fn toggle_julia(julia: bool) -> (bool, RowRenderer) { + let julia = !julia; + + let row_renderer = App::get_row_renderer(julia); + + match julia { + false => eprintln!("disabled the julia set"), + true => eprintln!("enabled the julia set"), + }; + + return (julia, row_renderer); +} diff --git a/source/benoit/benoit/application/initialise.rs b/source/benoit/benoit/app/initialise.rs index f019f02..15d98ac 100644 --- a/source/benoit/benoit/application/initialise.rs +++ b/source/benoit/benoit/app/initialise.rs @@ -21,7 +21,7 @@ If not, see <https://www.gnu.org/licenses/>. */ -use crate::benoit::application::Application; +use crate::benoit::app::App; use crate::benoit::configuration::Configuration; use crate::benoit::video::Video; @@ -31,8 +31,8 @@ use rayon::ThreadPoolBuilder; use std::env::args; use std::thread::available_parallelism; -impl Application { - pub fn initialise() -> Application { +impl App { + pub fn initialise() -> App { let mut arguments = args(); let configuration = match arguments.nth(0x1) { @@ -58,7 +58,9 @@ impl Application { ThreadPoolBuilder::new().num_threads(configuration.thread_count as usize).build_global().unwrap(); - return Application { + eprintln!("rendering the {}", configuration.fractal.get_name()); + + return App { thread_count: thread_count, fractal: configuration.fractal, @@ -73,6 +75,8 @@ impl Application { zoom: configuration.zoom, max_iter_count: configuration.max_iter_count, + colour_range: configuration.colour_range, + dump_path: configuration.dump_path, video: video, @@ -81,8 +85,8 @@ impl Application { do_render: true, do_dump: false, - row_renderer: Application::get_row_renderer( configuration.julia), - iterator_function: Application::get_iterator_function(configuration.fractal), + row_renderer: App::get_row_renderer( configuration.julia), + iterator_function: App::get_iterator_function(configuration.fractal), }; } } diff --git a/source/benoit/benoit/app/loop.rs b/source/benoit/benoit/app/loop.rs new file mode 100644 index 0000000..b40b509 --- /dev/null +++ b/source/benoit/benoit/app/loop.rs @@ -0,0 +1,126 @@ +/* + 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::FeedbackInfo; +use crate::benoit::app::App; + +extern crate rug; + +use rug::Assign; +use std::time::Instant; + +impl App { + pub fn r#loop(&mut self) -> i32 { + assert_eq!(self.video.is_some(), true); + + eprintln!(); + eprintln!("Controls:"); + eprintln!("- W Translate up"); + eprintln!("- A Translate left"); + eprintln!("- S Translate down"); + eprintln!("- D Translate right"); + eprintln!(); + eprintln!("- Q Zoom out"); + eprintln!("- E Zoom in"); + eprintln!(); + eprintln!("- R Decrease max. iteration count"); + eprintln!("- F Increase max. iteration count"); + eprintln!(); + eprintln!("- Tab Toggle Julia"); + eprintln!("- Alt Cycle between fractals"); + eprintln!(); + eprintln!("- Up Increase colour range"); + eprintln!("- Down Decrease colour range"); + eprintln!(); + eprintln!("- Z Print centre value (c)"); + eprintln!("- X Dump frame"); + eprintln!("- C Render frame"); + eprintln!(); + + let mut event_pump = unsafe { self.video.as_mut().unwrap_unchecked().sdl.event_pump().expect("unable to get event pump") }; + + let canvas_size = self.canvas_width as usize * self.canvas_width as usize; + + let mut iter_count_buffer: Vec::<u32> = vec![0x0; canvas_size]; + let mut square_dist_buffer: Vec::<f32> = vec![0.0; canvas_size]; + + let mut image: Vec::<u8> = vec![0x0; canvas_size * 0x3]; + + let mut prev_centre_real = self.centre_real.clone(); + let mut prev_centre_imag = self.centre_imag.clone(); + let mut prev_zoom = self.zoom.clone(); + let mut prev_max_iter_count = self.max_iter_count; + + loop { + let frame_start = Instant::now(); + + if self.poll_events(&mut event_pump) { break } + + if self.do_render { + eprint!("rendering..."); + + let time_start = Instant::now(); + + self.render(&mut iter_count_buffer[..], &mut square_dist_buffer[..], &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count); + let render_time = time_start.elapsed(); + + eprintln!(" rend. {:.3}ms", render_time.as_micros() as f32 / 1000.0); + + prev_centre_real.assign(&self.centre_real); + prev_centre_imag.assign(&self.centre_imag); + prev_zoom.assign(&self.zoom); + prev_max_iter_count = self.max_iter_count; + + self.do_render = false; + } + + self.colour(&mut image[..], prev_max_iter_count, &mut iter_count_buffer[..], &mut square_dist_buffer[..]); + + { + let feedback_info = FeedbackInfo { + prev_centre_real: &prev_centre_real, + prev_centre_imag: &prev_centre_imag, + prev_zoom: &prev_zoom, + next_centre_real: &self.centre_real, + next_centre_imag: &self.centre_imag, + next_zoom: &self.zoom, + }; + + unsafe { self.video.as_mut().unwrap_unchecked().draw(&image[..], self.canvas_width, self.scale, Some(&feedback_info)) }; + } + + if self.do_dump { + let path = format!("{}/image.webp", self.dump_path); + + eprintln!("dumping image at \"{path}\""); + self.dump(path, &image, self.canvas_width); + + self.do_dump = false; + } + + unsafe { self.video.as_ref().unwrap_unchecked().sync(&frame_start) }; + } + + return 0x0; + } +}
\ No newline at end of file diff --git a/source/benoit/benoit/application/poll_events.rs b/source/benoit/benoit/app/poll_events.rs index ed26b05..2df0c96 100644 --- a/source/benoit/benoit/application/poll_events.rs +++ b/source/benoit/benoit/app/poll_events.rs @@ -21,14 +21,14 @@ If not, see <https://www.gnu.org/licenses/>. */ -use crate::benoit::application::Application; +use crate::benoit::app::App; extern crate sdl2; use sdl2::EventPump; use sdl2::event::Event; -impl Application { +impl App { pub fn poll_events(&mut self, event_pump: &mut EventPump) -> bool { for event in event_pump.poll_iter() { let quit = match event { diff --git a/source/benoit/benoit/application/render.rs b/source/benoit/benoit/app/render.rs index eae4438..aa5ff9b 100644 --- a/source/benoit/benoit/application/render.rs +++ b/source/benoit/benoit/app/render.rs @@ -21,8 +21,8 @@ If not, see <https://www.gnu.org/licenses/>. */ -use crate::benoit::application::Application; -use crate::benoit::render_data::RenderData; +use crate::benoit::app::App; +use crate::benoit::task::render_data::RenderData; extern crate rayon; extern crate rug; @@ -30,25 +30,16 @@ extern crate rug; use rayon::prelude::*; use rug::Float; use std::sync::Arc; -use std::time::Instant; -impl Application { +impl App { pub fn render(&self, iter_count_buffer: &mut [u32], square_dist_buffer: &mut [f32], centre_real: &Float, centre_imag: &Float, zoom: &Float, max_iter_count: u32) { - eprint!("rendering..."); - - let time_start = Instant::now(); - let row_renderer = self.row_renderer; let iterator = self.iterator_function; let data = Arc::new(RenderData::new(iter_count_buffer, square_dist_buffer, self.canvas_width, centre_real.clone(), centre_imag.clone(), zoom.clone(), max_iter_count)); (0x0..self.canvas_width).into_par_iter().for_each(|row| { - row_renderer(data.clone(), row, iterator); + row_renderer(data.clone(), row as u32, iterator); }); - - let duration = time_start.elapsed(); - - eprintln!(" done ({}ms)", duration.as_millis()); } } diff --git a/source/benoit/benoit/application/render_row_julia.rs b/source/benoit/benoit/app/render_row_julia.rs index 3f4bb0c..5c3cae8 100644 --- a/source/benoit/benoit/application/render_row_julia.rs +++ b/source/benoit/benoit/app/render_row_julia.rs @@ -22,17 +22,18 @@ */ use crate::benoit::PRECISION; -use crate::benoit::application::Application; +use crate::benoit::app::App; use crate::benoit::iteration::IteratorFunction; -use crate::benoit::render_data::RenderData; +use crate::benoit::task::render_data::RenderData; extern crate rug; use rug::{Assign, Float}; use rug::float::Special; +use rug::ops::NegAssign; use std::sync::Arc; -impl Application { +impl App { pub fn render_row_julia(data: Arc<RenderData>, y: u32, iterator: IteratorFunction) { let (iter_count_buffer, square_dist_buffer) = unsafe { data.slice(y) }; @@ -53,9 +54,9 @@ impl Application { // position-determined value that (c) would've had. let mut za = { - let tmp0 = Float::with_val(PRECISION, &canvas_width / 2.0); - - let mut za = Float::with_val(PRECISION, &x_float - &tmp0); + let mut za = Float::with_val(PRECISION, &canvas_width / 2.0); + za.neg_assign(); + za += &x_float; za *= 4.0; za /= &canvas_width; @@ -63,9 +64,9 @@ impl Application { }; let mut zb = { - let tmp0 = Float::with_val(PRECISION, &canvas_width / 2.0); - - let mut zb = Float::with_val(PRECISION, &y_float - &tmp0); + let mut zb = Float::with_val(PRECISION, &canvas_width / 2.0); + zb.neg_assign(); + zb += &y_float; zb *= 4.0; zb /= &canvas_width; diff --git a/source/benoit/benoit/application/render_row_normal.rs b/source/benoit/benoit/app/render_row_normal.rs index 157c767..381b8bc 100644 --- a/source/benoit/benoit/application/render_row_normal.rs +++ b/source/benoit/benoit/app/render_row_normal.rs @@ -22,17 +22,18 @@ */ use crate::benoit::PRECISION; -use crate::benoit::application::Application; +use crate::benoit::app::App; use crate::benoit::iteration::IteratorFunction; -use crate::benoit::render_data::RenderData; +use crate::benoit::task::render_data::RenderData; extern crate rug; use rug::{Assign, Float}; use rug::float::Special; +use rug::ops::NegAssign; use std::sync::Arc; -impl Application { +impl App { pub fn render_row_normal(data: Arc<RenderData>, y: u32, iterator: IteratorFunction) { let (iter_count_buffer, square_dist_buffer) = unsafe { data.slice(y) }; @@ -43,9 +44,9 @@ impl Application { let y_float = Float::with_val(PRECISION, y); let ca = { - let tmp0 = Float::with_val(PRECISION, &canvas_width / 2.0); - - let mut ca = Float::with_val(PRECISION, &x_float - &tmp0); + let mut ca = Float::with_val(PRECISION, &canvas_width / 2.0); + ca.neg_assign(); + ca += &x_float; ca *= 4.0; ca /= &canvas_width; ca /= &data.zoom; @@ -55,9 +56,9 @@ impl Application { }; let cb = { - let tmp0 = Float::with_val(PRECISION, &canvas_width / 2.0); - - let mut cb = Float::with_val(PRECISION, &y_float - &tmp0); + let mut cb = Float::with_val(PRECISION, &canvas_width / 2.0); + cb.neg_assign(); + cb += &y_float; cb *= 4.0; cb /= &canvas_width; cb /= &data.zoom; @@ -72,7 +73,7 @@ impl Application { let mut za_prev = Float::with_val(PRECISION, Special::Nan); let mut zb_prev = Float::with_val(PRECISION, Special::Nan); - let mut iter_count: u32 = 0x0; + let mut iter_count: u32 = 0x1; let mut square_dist; while { square_dist = Float::with_val(PRECISION, &za * &za + &zb * &zb).to_f32(); diff --git a/source/benoit/benoit/application/run.rs b/source/benoit/benoit/app/run.rs index b9aa50b..3524cad 100644 --- a/source/benoit/benoit/application/run.rs +++ b/source/benoit/benoit/app/run.rs @@ -22,19 +22,17 @@ */ use crate::benoit::VERSION; -use crate::benoit::application::Application; +use crate::benoit::app::App; extern crate sdl2; -impl Application { +impl App { pub fn run(&mut self) -> i32 { println!(); - println!("Benoit {VERSION:X}"); + println!("Benoit {:X}.{:X}.{:X}", VERSION.major, VERSION.minor, VERSION.patch); println!("Copyright 2021, 2023 Gabriel Bjørnager Jensen."); println!(); - eprintln!("rendering the {}", self.fractal.get_name()); - return match self.interactive { true => self.r#loop(), false => self.animate(), diff --git a/source/benoit/benoit/application/animate.rs b/source/benoit/benoit/application/animate.rs deleted file mode 100644 index 117f430..0000000 --- a/source/benoit/benoit/application/animate.rs +++ /dev/null @@ -1,90 +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::PRECISION; -use crate::benoit::application::Application; - -extern crate rug; -extern crate sdl2; - -use rug::Float; -use rug::ops::PowAssign; -use std::ops::MulAssign; - -impl Application { - pub fn animate(&self) -> i32 { - // zoom_start: - let mut zoom = Float::with_val(PRECISION, 1.0 / 4.0); - - let zoom_stop = Float::with_val(PRECISION, &self.zoom); - - let zoom_factor = { - // To get the zoom factor, we first want the 'a' - // value of the growth function from (0) and to - // (frame_count) on the x-dimension and from (1) to - // (zoom) on the y-dimension: - // - // a = nroot(x1-x0,y1/y0) - // - // but this may be simplified for use with Rug - // because - // - // nroot(a,b) = b^(1/a) - // - // making the final equation - // - // (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, &zoom_stop); - factor /= &zoom; - factor.pow_assign(exponent); - - factor - }; - - eprintln!("animating {} frames at {}{:+}i to {:.3} (fac.: {:.3})", self.frame_count, self.centre_real.to_f64(), self.centre_imag.to_f64(), zoom_stop.to_f64(), zoom_factor.to_f64()); - - let canvas_size = self.canvas_width as usize * self.canvas_width as usize; - - let mut iter_count_buffer: Vec::<u32> = vec![0x0; canvas_size]; - let mut square_dist_buffer: Vec::<f32> = vec![0.0; canvas_size]; - - let mut image: Vec::<u8> = vec![0x0; canvas_size * 0x3]; - - for frame in 0x0..self.frame_count { - eprint!("{frame:010}: "); - self.render(&mut iter_count_buffer[..], &mut square_dist_buffer[..], &self.centre_real, &self.centre_imag, &zoom, self.max_iter_count); - self.colour(&mut image[..], &mut iter_count_buffer[..], &mut square_dist_buffer[..]); - - self.dump(format!("{}/frame{frame:010}.webp", self.dump_path), &image, self.canvas_width); - - zoom.mul_assign(&zoom_factor); - } - - return 0x0; - } -} diff --git a/source/benoit/benoit/application/loop.rs b/source/benoit/benoit/application/loop.rs deleted file mode 100644 index 9c5fc55..0000000 --- a/source/benoit/benoit/application/loop.rs +++ /dev/null @@ -1,96 +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::application::{Application, PreviousPosition}; - -extern crate sdl2; - -impl Application { - pub fn r#loop(&mut self) -> i32 { - eprintln!(); - eprintln!("Controls:"); - eprintln!("- W Translate up"); - eprintln!("- A Translate left"); - eprintln!("- S Translate down"); - eprintln!("- D Translate right"); - eprintln!(); - eprintln!("- Q Zoom out"); - eprintln!("- E Zoom in"); - eprintln!(); - eprintln!("- R Decrease max. iteration count"); - eprintln!("- F Increase max. iteration count"); - eprintln!(); - eprintln!("- Tab Toggle Julia"); - eprintln!("- Alt Cycle between fractals"); - eprintln!(); - eprintln!("- Z Print centre value (c)"); - eprintln!("- X Dump frame"); - eprintln!("- C Render frame"); - eprintln!(); - - let mut event_pump = self.video.as_mut().unwrap().sdl.event_pump().expect("unable to get event pump"); - - let canvas_size = self.canvas_width as usize * self.canvas_width as usize; - - let mut iter_count_buffer: Vec::<u32> = vec![0x0; canvas_size]; - let mut square_dist_buffer: Vec::<f32> = vec![0.0; canvas_size]; - - let mut image: Vec::<u8> = vec![0x0; canvas_size * 0x3]; - - let mut previous_position = PreviousPosition { - centre_real: self.centre_real.clone(), - centre_imag: self.centre_imag.clone(), - zoom: self.zoom.clone(), - }; - - loop { - if self.poll_events(&mut event_pump) { break } - - if self.do_render { - self.render(&mut iter_count_buffer[..], &mut square_dist_buffer[..], &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count); - self.colour(&mut image[..], &iter_count_buffer[..], &square_dist_buffer[..]); - - previous_position = PreviousPosition { - centre_real: self.centre_real.clone(), - centre_imag: self.centre_imag.clone(), - zoom: self.zoom.clone(), - }; - - self.do_render = false; - } - - self.draw(&image[..], &previous_position); - - if self.do_dump { - let path = format!("{}/image.webp", self.dump_path); - - eprintln!("dumping image at \"{path}\""); - self.dump(path, &image, self.canvas_width); - - self.do_dump = false; - } - } - - return 0x0; - } -}
\ No newline at end of file diff --git a/source/benoit/benoit/configuration.rs b/source/benoit/benoit/configuration.rs index ca36400..f472f62 100644 --- a/source/benoit/benoit/configuration.rs +++ b/source/benoit/benoit/configuration.rs @@ -45,6 +45,8 @@ pub struct Configuration { pub zoom: Float, pub max_iter_count: u32, + pub colour_range: f32, + pub dump_path: String, pub interactive: bool, diff --git a/source/benoit/benoit/configuration/default.rs b/source/benoit/benoit/configuration/default.rs index 1d59786..46373cd 100644 --- a/source/benoit/benoit/configuration/default.rs +++ b/source/benoit/benoit/configuration/default.rs @@ -46,6 +46,8 @@ impl Configuration { zoom: Float::with_val(PRECISION, 1.0), max_iter_count: 0x100, + colour_range: 16.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 b06cd71..711c890 100644 --- a/source/benoit/benoit/configuration/load.rs +++ b/source/benoit/benoit/configuration/load.rs @@ -43,14 +43,20 @@ impl Configuration { let configuration_text = match read(path) { Ok(content) => String::from_utf8_lossy(&content).to_string(), - Err(..) => { - eprintln!("unable to read configuration file"); - return configuration; - }, + Err(..) => panic!("unable to read configuration file"), }; let configuration_table = Table::from_str(configuration_text.as_str()).expect("unable to parse configuration"); + let get_boolean = |buffer: &mut bool, table: &Table, name: &str| { + if !table.contains_key(name) { return } + + match &configuration_table[name] { + Value::Boolean(value) => *buffer = *value, + _ => panic!("mismatched type of {name}"), + }; + }; + let get_integer = |buffer: &mut u32, table: &Table, name: &str| { if !table.contains_key(name) { return } @@ -96,6 +102,8 @@ impl Configuration { configuration.fractal }; + get_boolean(&mut configuration.julia, &configuration_table, "julia"); + get_integer(&mut configuration.canvas_width, &configuration_table, "canvas_width"); get_integer(&mut configuration.scale, &configuration_table, "scale"); get_integer(&mut configuration.frame_count, &configuration_table, "frame_count"); @@ -105,6 +113,18 @@ impl Configuration { get_float( &mut configuration.zoom, &configuration_table, "zoom"); get_integer(&mut configuration.max_iter_count, &configuration_table, "maximum_iteration_count"); + // We allow thread counts of zero as those signal + // automatic thread count detection. + if configuration.canvas_width == 0x0 { + panic!("only non-zero values for canvas_width are allowed"); + } else if configuration.scale == 0x0 { + panic!("only non-zero values for scale are allowed"); + } else if configuration.frame_count == 0x0 { + panic!("only non-zero values for frame_count are allowed"); + } else if configuration.max_iter_count == 0x0 { + panic!("only non-zero values for maximum_iteration_count are allowed"); + } + return configuration; } } diff --git a/source/benoit/benoit/iteration/iterate_burning_ship.rs b/source/benoit/benoit/iteration/iterate_burning_ship.rs index 89d2ec1..038367f 100644 --- a/source/benoit/benoit/iteration/iterate_burning_ship.rs +++ b/source/benoit/benoit/iteration/iterate_burning_ship.rs @@ -30,7 +30,7 @@ pub fn iterate_burning_ship(za: &mut Float, zb: &mut Float, ca: &Float, cb: &Flo // iteration - the real and imaginary parts of (z) // are made absolute: // - // z(n+1) = (abs(Re(z(n)))+i*abs(Im(z(n))))^2+c + // z(n+1) = (abs(Re(z(n)))+i*abs(Im(z(n))))^2+c. za.abs_mut(); zb.abs_mut(); diff --git a/source/benoit/benoit/iteration/iterate_mandelbrot.rs b/source/benoit/benoit/iteration/iterate_mandelbrot.rs index 6be612c..9037326 100644 --- a/source/benoit/benoit/iteration/iterate_mandelbrot.rs +++ b/source/benoit/benoit/iteration/iterate_mandelbrot.rs @@ -34,14 +34,14 @@ pub fn iterate_mandelbrot(za: &mut Float, zb: &mut Float, ca: &Float, cb: &Float // // stays bounded: I.e. the absolute value of (z) stays bounded: // - // abs(z) = sqrt(Re(z)^2+Im(z)^2) <= 2^2 = 4 + // abs(z) = sqrt(Re(z)^2+Im(z)^2) <= 2^2 = 4. let za_temporary = za.clone(); // We can calculate the square of a complex number // as: // - // (a+bi)^2 = (a+bi)(a+bi) = a^2+abi+abi-b^2 = a^2-b^2+2abi + // (a+bi)^2 = (a+bi)(a+bi) = a^2+abi+abi-b^2 = a^2-b^2+2abi. za.square_mut(); *za -= &*zb * &*zb; diff --git a/source/benoit/benoit/iteration/iterate_tricorn.rs b/source/benoit/benoit/iteration/iterate_tricorn.rs index 2edea53..3d1bfb9 100644 --- a/source/benoit/benoit/iteration/iterate_tricorn.rs +++ b/source/benoit/benoit/iteration/iterate_tricorn.rs @@ -30,7 +30,7 @@ pub fn iterate_tricorn(za: &mut Float, zb: &mut Float, ca: &Float, cb: &Float) { // Mandelbrot Set in that the conjugate of (z) is // used instead of just (z): // - // z(n+1) = (Re(z(n))-Im(z(n)))^2+c + // z(n+1) = (Re(z(n))-Im(z(n)))^2+c. let za_temporary = za.clone(); @@ -42,7 +42,7 @@ pub fn iterate_tricorn(za: &mut Float, zb: &mut Float, ca: &Float, cb: &Float) { // We can negate the value by multiplying with // (-1). A multiplication can be saved, as // - // a*2*(-1) = a*(-2) + // a*2*(-1) = a*(-2). // // Thus, we may combine these two multiplications. *zb *= -2.0; diff --git a/source/benoit/benoit/render_data/sync.rs b/source/benoit/benoit/task.rs index 454ebb1..ee53b52 100644 --- a/source/benoit/benoit/render_data/sync.rs +++ b/source/benoit/benoit/task.rs @@ -21,6 +21,5 @@ If not, see <https://www.gnu.org/licenses/>. */ -use crate::benoit::render_data::RenderData; - -unsafe impl Sync for RenderData {} +pub mod colour_data; +pub mod render_data; diff --git a/source/benoit/benoit/task/colour_data.rs b/source/benoit/benoit/task/colour_data.rs new file mode 100644 index 0000000..77d561d --- /dev/null +++ b/source/benoit/benoit/task/colour_data.rs @@ -0,0 +1,40 @@ +/* + 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/>. +*/ + +pub mod new; +pub mod slice; + +pub struct ColourData { + pub canvas_width: u32, + + pub max_iter_count: u32, + pub colour_range: f32, + + iter_count_buffer: *const u32, + square_dist_buffer: *const f32, + + image: *mut u8, +} + +unsafe impl Send for ColourData {} +unsafe impl Sync for ColourData {} diff --git a/source/benoit/benoit/task/colour_data/new.rs b/source/benoit/benoit/task/colour_data/new.rs new file mode 100644 index 0000000..2707018 --- /dev/null +++ b/source/benoit/benoit/task/colour_data/new.rs @@ -0,0 +1,40 @@ +/* + 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::task::colour_data::ColourData; + +impl ColourData { + pub fn new(image: &mut [u8], canvas_width: u32, max_iter_count: u32, colour_range: f32, iter_count_buffer: &[u32], square_dist_buffer: &[f32]) -> ColourData { + return ColourData { + canvas_width: canvas_width, + + max_iter_count: max_iter_count, + colour_range: colour_range, + + iter_count_buffer: iter_count_buffer.as_ptr(), + square_dist_buffer: square_dist_buffer.as_ptr(), + + image: image.as_mut_ptr(), + }; + } +} diff --git a/source/benoit/benoit/task/colour_data/slice.rs b/source/benoit/benoit/task/colour_data/slice.rs new file mode 100644 index 0000000..975eda2 --- /dev/null +++ b/source/benoit/benoit/task/colour_data/slice.rs @@ -0,0 +1,41 @@ +/* + 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::task::colour_data::ColourData; + +use std::slice::{from_raw_parts, from_raw_parts_mut}; + +impl ColourData { + pub unsafe fn slice(&self, row: u32) -> (&[u32], &[f32], &mut [u8]) { + let offset = row as isize * self.canvas_width as isize; + + let iter_count = from_raw_parts(self.iter_count_buffer.offset(offset), self.canvas_width as usize); + let dist = from_raw_parts(self.square_dist_buffer.offset(offset), self.canvas_width as usize); + + let offset = offset * 0x3; + + let image = from_raw_parts_mut(self.image.offset(offset), self.canvas_width as usize * 0x3); + + return (iter_count, dist, image); + } +} diff --git a/source/benoit/benoit/render_data.rs b/source/benoit/benoit/task/render_data.rs index 5546e16..e73b514 100644 --- a/source/benoit/benoit/render_data.rs +++ b/source/benoit/benoit/task/render_data.rs @@ -26,9 +26,7 @@ extern crate rug; use rug::Float; pub mod new; -pub mod send; pub mod slice; -pub mod sync; pub struct RenderData { pub canvas_width: u32, @@ -42,3 +40,6 @@ pub struct RenderData { iter_count_buffer: *mut u32, square_dist_buffer: *mut f32, } + +unsafe impl Send for RenderData {} +unsafe impl Sync for RenderData {} diff --git a/source/benoit/benoit/render_data/new.rs b/source/benoit/benoit/task/render_data/new.rs index 4e3f28d..d9509b5 100644 --- a/source/benoit/benoit/render_data/new.rs +++ b/source/benoit/benoit/task/render_data/new.rs @@ -21,7 +21,7 @@ If not, see <https://www.gnu.org/licenses/>. */ -use crate::benoit::render_data::RenderData; +use crate::benoit::task::render_data::RenderData; extern crate rug; @@ -29,9 +29,6 @@ use rug::Float; impl RenderData { pub fn new(iter_count_buffer: &mut [u32], square_dist_buffer: &mut [f32], canvas_width: u32, centre_real: Float, centre_imag: Float, zoom: Float, max_iter_count: u32) -> RenderData { - let iter_count_buffer_pointer = iter_count_buffer.as_mut_ptr(); - let square_dist_buffer_pointer = square_dist_buffer.as_mut_ptr(); - return RenderData { canvas_width: canvas_width, @@ -41,8 +38,8 @@ impl RenderData { max_iter_count: max_iter_count, - iter_count_buffer: iter_count_buffer_pointer, - square_dist_buffer: square_dist_buffer_pointer, + iter_count_buffer: iter_count_buffer.as_mut_ptr(), + square_dist_buffer: square_dist_buffer.as_mut_ptr(), }; } } diff --git a/source/benoit/benoit/render_data/slice.rs b/source/benoit/benoit/task/render_data/slice.rs index ab12d4f..d2660ee 100644 --- a/source/benoit/benoit/render_data/slice.rs +++ b/source/benoit/benoit/task/render_data/slice.rs @@ -21,16 +21,17 @@ If not, see <https://www.gnu.org/licenses/>. */ -use crate::benoit::render_data::RenderData; +use crate::benoit::task::render_data::RenderData; use std::slice::from_raw_parts_mut; impl RenderData { pub unsafe fn slice(&self, row: u32) -> (&mut [u32], &mut [f32]) { let offset = row as isize * self.canvas_width as isize; + let iter_count = from_raw_parts_mut(self.iter_count_buffer.offset(offset), self.canvas_width as usize); - let distance = from_raw_parts_mut(self.square_dist_buffer.offset(offset), self.canvas_width as usize); + let dist = from_raw_parts_mut(self.square_dist_buffer.offset(offset), self.canvas_width as usize); - return (iter_count, distance); + return (iter_count, dist); } } diff --git a/source/benoit/benoit/video.rs b/source/benoit/benoit/video.rs index 2147b27..af060fe 100644 --- a/source/benoit/benoit/video.rs +++ b/source/benoit/benoit/video.rs @@ -23,12 +23,15 @@ extern crate sdl2; -use sdl2::Sdl; +use sdl2::{Sdl, VideoSubsystem}; use sdl2::render::WindowCanvas; +pub mod draw; pub mod initialise; +pub mod sync; pub struct Video { - pub sdl: Sdl, - pub canvas: WindowCanvas, + pub sdl: Sdl, + pub sdl_video: VideoSubsystem, + pub canvas: WindowCanvas, } diff --git a/source/benoit/benoit/application/draw.rs b/source/benoit/benoit/video/draw.rs index 31ba470..bd5a837 100644 --- a/source/benoit/benoit/application/draw.rs +++ b/source/benoit/benoit/video/draw.rs @@ -21,8 +21,8 @@ If not, see <https://www.gnu.org/licenses/>. */ -use crate::benoit::PRECISION; -use crate::benoit::application::{Application, PreviousPosition}; +use crate::benoit::{FeedbackInfo, PRECISION}; +use crate::benoit::video::Video; extern crate rug; extern crate sdl2; @@ -31,13 +31,13 @@ use rug::Float; use sdl2::pixels::Color; use sdl2::rect::Rect; -impl Application { - pub fn draw(&mut self, image: &[u8], previous_position: &PreviousPosition) { - let canvas_size = self.canvas_width * self.canvas_width; +impl Video { + pub fn draw(&mut self, image: &[u8], canvas_width: u32, scale: u32, feedback_info: Option<&FeedbackInfo>) { + let canvas_size = canvas_width * canvas_width; for pixel in 0x0..canvas_size { - let y = pixel as u32 / self.canvas_width; - let x = pixel as u32 - y * self.canvas_width; + let y = pixel as u32 / canvas_width; + let x = pixel as u32 - y * canvas_width; let colour = { let red = image[pixel as usize * 0x3]; @@ -48,27 +48,29 @@ impl Application { }; let rectangle = Rect::new( - (x * self.scale) as i32, - (y * self.scale) as i32, - self.scale, - self.scale + (x * scale) as i32, + (y * scale) as i32, + scale, + scale ); - self.video.as_mut().unwrap().canvas.set_draw_color(colour); - self.video.as_mut().unwrap().canvas.fill_rects(&[rectangle]).unwrap(); + self.canvas.set_draw_color(colour); + self.canvas.fill_rects(&[rectangle]).unwrap(); } - if !self.julia { + if feedback_info.is_some() { + let feedback_info = unsafe { feedback_info.unwrap_unchecked() }; + let canvas_width = { - let mut canvas_width = Float::with_val(PRECISION, self.canvas_width); - canvas_width *= self.scale; + let mut canvas_width = Float::with_val(PRECISION, canvas_width); + canvas_width *= scale; canvas_width }; let viewport = { let ((offset_x, offset_y), width) = { - let zoom_ratio = Float::with_val(PRECISION, &self.zoom / &previous_position.zoom); + let zoom_ratio = Float::with_val(PRECISION, feedback_info.next_zoom / feedback_info.prev_zoom); let mut width = Float::with_val(PRECISION, 1.0 / &zoom_ratio); @@ -76,17 +78,17 @@ impl Application { // inverted vertical axis compared to those of // SDL's coordinate system. - let mut offset_x = self.centre_real.clone(); - let mut offset_y = Float::with_val(PRECISION, -&self.centre_imag); + let mut offset_x = feedback_info.next_centre_real.clone(); + let mut offset_y = Float::with_val(PRECISION, -feedback_info.next_centre_imag); - offset_x -= &previous_position.centre_real; - offset_y += &previous_position.centre_imag; + offset_x -= feedback_info.prev_centre_real; + offset_y += feedback_info.prev_centre_imag; offset_x /= 4.0; offset_y /= 4.0; - offset_x *= &previous_position.zoom; - offset_y *= &previous_position.zoom; + offset_x *= feedback_info.prev_zoom; + offset_y *= feedback_info.prev_zoom; let mut zoom_offset = Float::with_val(PRECISION, 1.0 - &width); zoom_offset /= 2.0; @@ -109,13 +111,13 @@ impl Application { ) }; - self.video.as_mut().unwrap().canvas.set_draw_color(Color::RGBA(0x0, 0x0, 0x0, 0x3F)); - self.video.as_mut().unwrap().canvas.fill_rects(&[viewport]).unwrap(); + self.canvas.set_draw_color(Color::RGBA(0x0, 0x0, 0x0, 0x3F)); + self.canvas.fill_rects(&[viewport]).unwrap(); - self.video.as_mut().unwrap().canvas.set_draw_color(Color::RGB(0xFF, 0xFF, 0xFF)); - self.video.as_mut().unwrap().canvas.draw_rects(&[viewport]).unwrap(); + self.canvas.set_draw_color(Color::RGB(0xFF, 0xFF, 0xFF)); + self.canvas.draw_rects(&[viewport]).unwrap(); } - self.video.as_mut().unwrap().canvas.present(); + self.canvas.present(); } } diff --git a/source/benoit/benoit/video/initialise.rs b/source/benoit/benoit/video/initialise.rs index 40bc68d..f97ae8b 100644 --- a/source/benoit/benoit/video/initialise.rs +++ b/source/benoit/benoit/video/initialise.rs @@ -33,15 +33,16 @@ impl Video { let sdl = sdl2::init().expect("unable to initialise sdl2"); let sdl_video = sdl.video().expect("unable to initialise video"); - let window = sdl_video.window(format!("Benoit {VERSION:X}").as_str(), canvas_width * scale, canvas_width * scale).position_centered().build().expect("unable to open window"); + let window = sdl_video.window(format!("Benoit {:X}.{:X}.{:X}", VERSION.major, VERSION.minor, VERSION.patch).as_str(), canvas_width * scale, canvas_width * scale).position_centered().build().expect("unable to open window"); let mut canvas = window.into_canvas().build().expect("unable to create canvas"); canvas.set_blend_mode(BlendMode::Blend); return Video { - sdl: sdl, - canvas: canvas, + sdl: sdl, + sdl_video: sdl_video, + canvas: canvas, }; } } diff --git a/source/benoit/benoit/video/sync.rs b/source/benoit/benoit/video/sync.rs new file mode 100644 index 0000000..c8c016a --- /dev/null +++ b/source/benoit/benoit/video/sync.rs @@ -0,0 +1,46 @@ +/* + 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::video::Video; + +use std::thread::sleep; +use std::time::{Duration, Instant}; + +impl Video { + pub fn sync(&self, frame_start: &Instant) { + let frame_duration = { + let index = self.canvas.window().display_index().expect("unable to get display index"); + + let mode = self.sdl_video.current_display_mode(index).expect("unable to get display mode"); + + Duration::from_secs(0x1) / mode.refresh_rate as u32 + }; + + let remaining = match frame_duration.checked_sub(frame_start.elapsed()) { + Some(value) => value, + None => Duration::from_secs(0x0), + }; + + sleep(remaining); + } +}
\ No newline at end of file diff --git a/source/benoit/main.rs b/source/benoit/main.rs index ec24cbb..3716309 100644 --- a/source/benoit/main.rs +++ b/source/benoit/main.rs @@ -23,15 +23,15 @@ mod benoit; -use benoit::application::Application; +use benoit::app::App; use std::mem::drop; use std::process::exit; fn main() { - let mut application = Application::initialise(); - let code = application.run(); + let mut app = App::initialise(); + let code = app.run(); - drop(application); + drop(app); exit(code); } |