summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md8
-rw-r--r--Cargo.toml2
-rw-r--r--source/benoit/benoit.rs14
-rw-r--r--source/benoit/benoit/app.rs22
-rw-r--r--source/benoit/benoit/app/animate.rs206
-rw-r--r--source/benoit/benoit/app/configure.rs53
-rw-r--r--source/benoit/benoit/app/dump.rs66
-rw-r--r--source/benoit/benoit/app/handle_keys.rs14
-rw-r--r--source/benoit/benoit/app/initialise.rs95
-rw-r--r--source/benoit/benoit/app/interactive.rs168
-rw-r--r--source/benoit/benoit/app/poll_events.rs2
-rw-r--r--source/benoit/benoit/app/run.rs130
-rw-r--r--source/benoit/benoit/colour_data.rs (renamed from source/benoit/benoit/render/colour_data.rs)27
-rw-r--r--source/benoit/benoit/complex.rs8
-rw-r--r--source/benoit/benoit/configuration.rs14
-rw-r--r--source/benoit/benoit/configuration/load.rs15
-rw-r--r--source/benoit/benoit/fractal.rs58
-rw-r--r--source/benoit/benoit/fractal/iterate.rs (renamed from source/benoit/benoit/render/iterate.rs)2
-rw-r--r--source/benoit/benoit/fractal/iterate/burning_ship.rs (renamed from source/benoit/benoit/render/iterate/burning_ship.rs)0
-rw-r--r--source/benoit/benoit/fractal/iterate/mandelbrot.rs (renamed from source/benoit/benoit/render/iterate/mandelbrot.rs)0
-rw-r--r--source/benoit/benoit/fractal/iterate/multibrot3.rs (renamed from source/benoit/benoit/render/iterate/multibrot3.rs)5
-rw-r--r--source/benoit/benoit/fractal/iterate/multibrot4.rs64
-rw-r--r--source/benoit/benoit/fractal/iterate/tricorn.rs (renamed from source/benoit/benoit/render/iterate/tricorn.rs)6
-rw-r--r--source/benoit/benoit/image.rs78
-rw-r--r--source/benoit/benoit/image/allocate.rs41
-rw-r--r--source/benoit/benoit/image/colour.rs (renamed from source/benoit/benoit/renderer/colour.rs)63
-rw-r--r--source/benoit/benoit/image/dump.rs65
-rw-r--r--source/benoit/benoit/palette.rs75
-rw-r--r--source/benoit/benoit/palette/paint/hsv.rs4
-rw-r--r--source/benoit/benoit/render.rs36
-rw-r--r--source/benoit/benoit/render/allocate.rs (renamed from source/benoit/benoit/app/allocate_buffers.rs)24
-rw-r--r--source/benoit/benoit/render/render.rs (renamed from source/benoit/benoit/renderer/render.rs)46
-rw-r--r--source/benoit/benoit/render_data.rs (renamed from source/benoit/benoit/render/render_data.rs)36
-rw-r--r--source/benoit/benoit/renderer.rs18
-rw-r--r--source/benoit/benoit/renderer/render_point.rs (renamed from source/benoit/benoit/render/render_point.rs)0
-rw-r--r--source/benoit/benoit/renderer/render_point/julia.rs (renamed from source/benoit/benoit/render/render_point/julia.rs)4
-rw-r--r--source/benoit/benoit/renderer/render_point/normal.rs (renamed from source/benoit/benoit/render/render_point/normal.rs)4
-rw-r--r--source/benoit/benoit/script.rs61
-rw-r--r--source/benoit/benoit/script/animate.rs114
-rw-r--r--source/benoit/benoit/script/configure.rs54
-rw-r--r--source/benoit/benoit/script/dump.rs79
-rw-r--r--source/benoit/benoit/script/run.rs36
-rw-r--r--source/benoit/benoit/script/still.rs54
-rw-r--r--source/benoit/benoit/video/draw.rs16
-rw-r--r--source/benoit/benoit/video/initialise.rs1
-rw-r--r--source/benoit/main.rs51
46 files changed, 1144 insertions, 795 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 30a4fc3..0c77393 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+# 2.4.0
+
+* Clean up and restructure code
+* Add multibrot d=4 fractal
+* Update messages
+* Improve safety
+* Add image and render types
+
# 2.3.0
* Bump minor version
diff --git a/Cargo.toml b/Cargo.toml
index a0dce81..305156b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "benoit"
-version = "2.3.0"
+version = "2.4.0"
authors = ["Gabriel Bjørnager Jensen"]
edition = "2021"
description = "Mandelbrot renderer."
diff --git a/source/benoit/benoit.rs b/source/benoit/benoit.rs
index 715a311..86fa5d0 100644
--- a/source/benoit/benoit.rs
+++ b/source/benoit/benoit.rs
@@ -22,17 +22,21 @@
*/
pub mod app;
+pub mod colour_data;
pub mod complex;
pub mod configuration;
+pub mod image;
pub mod fractal;
pub mod palette;
-pub mod renderer;
pub mod render;
+pub mod render_data;
+pub mod renderer;
+pub mod script;
pub mod video;
pub const VERSION: [u32; 0x3] = [
0x2, // Major
- 0x3, // Minor
+ 0x4, // Minor
0x0, // Patch
];
@@ -40,12 +44,6 @@ pub const PRECISION: u32 = 0x80;
pub const BAILOUT: f32 = 256.0;
-#[derive(Clone, Copy)]
-pub enum ImageFormat {
- Png,
- Webp,
-}
-
pub fn width_height_ratio(width: u32, height: u32) -> (f32, f32) {
return if width > height {
(1.0, height as f32 / width as f32)
diff --git a/source/benoit/benoit/app.rs b/source/benoit/benoit/app.rs
index a281304..3eb54d0 100644
--- a/source/benoit/benoit/app.rs
+++ b/source/benoit/benoit/app.rs
@@ -21,38 +21,29 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::ImageFormat;
use crate::benoit::complex::Complex;
use crate::benoit::fractal::Fractal;
use crate::benoit::palette::Palette;
use crate::benoit::renderer::Renderer;
-use crate::benoit::video::Video;
extern crate rug;
use rug::Float;
-pub mod allocate_buffers;
-pub mod animate;
+pub mod configure;
pub mod drop;
-pub mod dump;
pub mod handle_keys;
-pub mod initialise;
-pub mod interactive;
pub mod poll_events;
pub mod run;
pub struct App {
- thread_count: u32,
-
- fractal: Fractal,
+ // Configuration:
+ fractal: Fractal,
renderer: Renderer,
canvas_width: u32,
canvas_height: u32,
scale: u32,
- frame_start: u32,
- frame_stop: u32,
centre: Complex,
extra: Complex,
@@ -63,12 +54,7 @@ pub struct App {
palette: Palette,
colour_range: f32,
- dump_path: String,
- image_format: ImageFormat,
-
- interactive: bool,
+ // Flags:
do_render: bool,
do_textual_feedback: bool,
-
- video: Option<Video>,
}
diff --git a/source/benoit/benoit/app/animate.rs b/source/benoit/benoit/app/animate.rs
deleted file mode 100644
index 157c213..0000000
--- a/source/benoit/benoit/app/animate.rs
+++ /dev/null
@@ -1,206 +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::{ImageFormat, PRECISION};
-use crate::benoit::app::App;
-use crate::benoit::complex::Complex;
-use crate::benoit::fractal::Fractal;
-use crate::benoit::palette::Palette;
-use crate::benoit::renderer::Renderer;
-
-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 frame_count = self.frame_stop - self.frame_start;
-
- if frame_count == 0x0 { return self.still() };
-
- let (mut iter_count_buffer, mut square_dist_buffer, mut image) = App::allocate_buffers(self.canvas_width, self.canvas_height);
-
- // 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 = get_zoom_factor(&zoom, &zoom_stop, self.frame_stop);
-
- zoom = if self.frame_start > 0x0 {
- let mut zoom = zoom_factor.clone();
- zoom.pow_assign(frame_count);
-
- zoom
- } else {
- zoom
- };
-
- eprintln!("animating from #{} to #{} ({} frame(s)) at {}{:+}i to {:.3}x (fac. ~{:.3})", self.frame_start, self.frame_stop, frame_count + 0x1, self.centre.real.to_f32(), self.centre.imag.to_f32(), zoom_stop.to_f32(), zoom_factor.to_f32());
-
- for frame in 0x0..=frame_count {
- let frame_name = format!("frame{frame:010}");
-
- dump_frame(
- self.dump_path.as_str(),
- frame_name.as_str(),
- &mut image[..],
- &mut iter_count_buffer[..],
- &mut square_dist_buffer[..],
- self.renderer,
- &self.fractal,
- self.palette,
- self.canvas_width,
- self.canvas_height,
- &self.centre,
- &self.extra,
- &zoom,
- self.max_iter_count,
- self.colour_range,
- self.image_format,
- );
-
- zoom *= &zoom_factor;
- }
-
- return 0x0;
- }
-
- fn still(&self) -> i32 {
- let (mut iter_count_buffer, mut square_dist_buffer, mut image) = App::allocate_buffers(self.canvas_width, self.canvas_height);
-
- const FRAME_NAME: &str = "render";
-
- dump_frame(
- self.dump_path.as_str(),
- FRAME_NAME,
- &mut image[..],
- &mut iter_count_buffer[..],
- &mut square_dist_buffer[..],
- self.renderer,
- &self.fractal,
- self.palette,
- self.canvas_width,
- self.canvas_height,
- &self.centre,
- &self.extra,
- &self.zoom,
- self.max_iter_count,
- self.colour_range,
- self.image_format,
- );
-
- return 0x0;
- }
-}
-
-fn dump_frame(
- dump_path: &str,
- name: &str,
- image: &mut [u8],
- iter_count_buffer: &mut [u32],
- square_dist_buffer: &mut [f32],
- renderer: Renderer,
- fractal: &Fractal,
- palette: Palette,
- canvas_width: u32,
- canvas_height: u32,
- centre: &Complex,
- extra: &Complex,
- zoom: &Float,
- max_iter_count: u32,
- colour_range: f32,
- image_format: ImageFormat,
-) {
- eprint!("\"{name}\" (2^{:.9}x)...", zoom.to_f64().log2());
-
- let time_start = Instant::now();
-
- renderer.render(
- iter_count_buffer,
- square_dist_buffer,
- fractal,
- canvas_width,
- canvas_height,
- centre,
- zoom,
- extra,
- max_iter_count,
- );
-
- let render_time = time_start.elapsed();
- eprint!(" {:.3}ms, colouring...", render_time.as_micros() as f32 / 1000.0);
-
- renderer.colour(
- image,
- palette,
- canvas_width,
- canvas_height,
- fractal.exponent(),
- max_iter_count,
- colour_range,
- iter_count_buffer,
- square_dist_buffer,
- );
-
- let colour_time = time_start.elapsed() - render_time;
- eprint!(" {:.3}ms...", colour_time.as_micros() as f32 / 1000.0);
-
- let path = format!("{dump_path}/{name}");
-
- App::dump(path.as_str(), &image, canvas_width, canvas_height, image_format);
- eprintln!(" done");
-}
-
-fn get_zoom_factor(zoom_start: &Float, zoom_stop: &Float, frame_count: u32) -> Float {
- assert!(frame_count > 0x0);
-
- // 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)).
-
- let frame_start: f32 = 0.0;
- let frame_stop: f32 = frame_count as f32;
-
- let exponent = Float::with_val(PRECISION, 1.0 / (frame_stop - frame_start));
-
- let mut factor = Float::with_val(PRECISION, zoom_stop / zoom_start);
- factor.pow_assign(exponent);
-
- factor
-}
diff --git a/source/benoit/benoit/app/configure.rs b/source/benoit/benoit/app/configure.rs
new file mode 100644
index 0000000..64db009
--- /dev/null
+++ b/source/benoit/benoit/app/configure.rs
@@ -0,0 +1,53 @@
+/*
+ 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::configuration::Configuration;
+
+impl App {
+ #[must_use]
+ pub fn configure(configuration: Configuration) -> App {
+ return App {
+ fractal: configuration.fractal,
+ renderer: configuration.renderer,
+
+ canvas_width: configuration.canvas_width,
+ canvas_height: configuration.canvas_height,
+ scale: configuration.scale,
+
+ centre: Complex::new(configuration.centre_real, configuration.centre_imag),
+ zoom: configuration.zoom,
+
+ extra: Complex::new(configuration.extra_real, configuration.extra_imag),
+
+ max_iter_count: configuration.max_iter_count,
+
+ palette: configuration.palette,
+ colour_range: configuration.colour_range,
+
+ do_render: true,
+ do_textual_feedback: false,
+ };
+ }
+}
diff --git a/source/benoit/benoit/app/dump.rs b/source/benoit/benoit/app/dump.rs
deleted file mode 100644
index fe92558..0000000
--- a/source/benoit/benoit/app/dump.rs
+++ /dev/null
@@ -1,66 +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::ImageFormat;
-use crate::benoit::app::App;
-
-extern crate png;
-extern crate webp;
-
-use std::fs::{File, write};
-use std::io::BufWriter;
-
-impl App {
- pub fn dump(path: &str, image: &[u8], canvas_width: u32, canvas_height: u32, image_format: ImageFormat) {
- match image_format {
- ImageFormat::Png => dump_png( path, image, canvas_width, canvas_height),
- ImageFormat::Webp => dump_webp(path, image, canvas_width, canvas_height),
- }
- }
-}
-
-fn dump_png(path: &str, image: &[u8], canvas_width: u32, canvas_height: u32) {
- let path = path.to_owned() + ".png";
-
- let file = File::create(path).expect("unable to create file");
- let file_buffer = BufWriter::new(file);
-
- let mut encoder = png::Encoder::new(file_buffer, canvas_width, canvas_height);
- encoder.set_color(png::ColorType::Rgb);
- encoder.set_depth(png::BitDepth::Eight);
- encoder.set_compression(png::Compression::Fast);
- encoder.set_srgb(png::SrgbRenderingIntent::Perceptual);
-
- let mut writer = encoder.write_header().expect("unable to write image");
- writer.write_image_data(image).expect("unable to write image");
-}
-
-fn dump_webp(path: &str, image: &[u8], canvas_width: u32, canvas_height: u32) {
- let path = path.to_owned() + ".webp";
-
- let encoder = webp::Encoder::from_rgb(&image[..], canvas_width, canvas_height);
-
- let data = encoder.encode_lossless();
-
- write(path, &*data).expect("unable to write image");
-}
diff --git a/source/benoit/benoit/app/handle_keys.rs b/source/benoit/benoit/app/handle_keys.rs
index 73d47f7..b0fdcb9 100644
--- a/source/benoit/benoit/app/handle_keys.rs
+++ b/source/benoit/benoit/app/handle_keys.rs
@@ -36,7 +36,7 @@ pub const MIN_COLOUR_RANGE: f32 = 2.0;
impl App {
#[must_use]
- pub fn handle_keys(&mut self, scan_code: Scancode, state: KeyboardState) -> bool {
+ pub(super) fn handle_keys(&mut self, scan_code: Scancode, state: KeyboardState) -> bool {
if scan_code == Scancode::C { self.do_render = true };
if state.is_scancode_pressed(Scancode::LShift) { return self.handle_shift_keys(scan_code) };
@@ -49,7 +49,7 @@ impl App {
Scancode::Left => self.cycle_palette(-0x1),
Scancode::RAlt => self.cycle_fractal(0x1),
Scancode::Right => self.cycle_palette(0x1),
- Scancode::Tab => self.cycle_rendering(),
+ Scancode::Tab => self.toggle_julia(),
Scancode::X => self.reset_viewport(),
Scancode::Z => self.dump_info(),
_ => {},
@@ -123,15 +123,13 @@ impl App {
eprintln!("renderer the {}", self.fractal.kind().name());
}
- fn cycle_rendering(&mut self) {
- let renderer = self.renderer.cycle();
+ fn toggle_julia(&mut self) {
+ self.renderer.toggle();
- match renderer {
+ match self.renderer {
Renderer::Julia => eprintln!("enabled the julia set"),
Renderer::Normal => eprintln!("disabled the julia set"),
};
-
- self.renderer = renderer;
}
fn toggle_inverse(&mut self) {
@@ -167,7 +165,7 @@ impl App {
}
fn dump_info(&self) {
- eprintln!("info dump:");
+ eprintln!("info dump: the {}", self.fractal.kind().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/initialise.rs b/source/benoit/benoit/app/initialise.rs
deleted file mode 100644
index 2876e97..0000000
--- a/source/benoit/benoit/app/initialise.rs
+++ /dev/null
@@ -1,95 +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::app::App;
-use crate::benoit::complex::Complex;
-use crate::benoit::configuration::Configuration;
-use crate::benoit::video::Video;
-
-extern crate rayon;
-
-use rayon::ThreadPoolBuilder;
-use std::env::args;
-use std::thread::available_parallelism;
-
-impl App {
- #[must_use]
- pub fn initialise() -> App {
- let mut arguments = args();
-
- let configuration = match arguments.nth(0x1) {
- Some(path) => Configuration::load(path.as_str()),
- None => Configuration::default(),
- };
-
- let thread_count: u32 = if configuration.thread_count == 0x0 {
- match available_parallelism() {
- Ok(ammount) => ammount.get() as u32,
- _ => 0x2,
- }
- } else {
- configuration.thread_count
- };
-
- eprintln!("using {thread_count} threads");
-
- let video = match configuration.interactive {
- true => Some(Video::initialise(configuration.canvas_width, configuration.canvas_height, configuration.scale)),
- false => None,
- };
-
- ThreadPoolBuilder::new().num_threads(configuration.thread_count as usize).build_global().unwrap();
-
- return App {
- thread_count: thread_count,
-
- fractal: configuration.fractal,
- renderer: configuration.renderer,
-
- canvas_width: configuration.canvas_width,
- canvas_height: configuration.canvas_height,
- scale: configuration.scale,
- frame_start: configuration.frame_start,
- frame_stop: configuration.frame_stop,
-
- centre: Complex { real: configuration.centre_real, imag: configuration.centre_imag },
- zoom: configuration.zoom,
-
- extra: Complex { real: configuration.extra_real, imag: configuration.extra_imag },
-
- max_iter_count: configuration.max_iter_count,
-
- palette: configuration.palette,
- colour_range: configuration.colour_range,
-
- dump_path: configuration.dump_path,
- image_format: configuration.image_format,
-
- interactive: configuration.interactive,
- do_render: true,
- do_textual_feedback: false,
-
- video: video,
- };
- }
-}
diff --git a/source/benoit/benoit/app/interactive.rs b/source/benoit/benoit/app/interactive.rs
deleted file mode 100644
index 94db797..0000000
--- a/source/benoit/benoit/app/interactive.rs
+++ /dev/null
@@ -1,168 +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::app::App;
-use crate::benoit::complex::Complex;
-use crate::benoit::video::Video;
-
-extern crate rug;
-
-use rug::{Assign, Float};
-use std::time::Instant;
-
-impl App {
- #[must_use]
- pub fn interactive(&mut self) -> i32 {
- assert_eq!(self.interactive, true);
- let mut video = self.video.take().unwrap();
-
- App::print_controls();
-
- let mut event_pump = video.sdl.event_pump().expect("unable to get event pump");
-
- let (mut iter_count_buffer, mut square_dist_buffer, mut image) = App::allocate_buffers(self.canvas_width, self.canvas_height);
-
- // Used for colouring:
- let mut prev_max_iter_count = self.max_iter_count;
- let mut prev_multibrot_exponent = self.fractal.exponent();
-
- // Used for translation feedback:
- let mut prev_centre = self.centre.clone();
- let mut prev_zoom = self.zoom.clone();
-
- loop {
- let frame_start = Instant::now();
-
- if self.poll_events(&mut event_pump) { break }
-
- if self.do_render {
- self.render(&mut iter_count_buffer[..], &mut square_dist_buffer[..]);
-
- prev_max_iter_count = self.max_iter_count;
- prev_multibrot_exponent = self.fractal.exponent();
-
- prev_centre.assign(&self.centre);
- prev_zoom.assign( &self.zoom);
-
- self.do_render = false;
- };
-
- self.renderer.colour(
- &mut image[..],
- self.palette,
- self.canvas_width,
- self.canvas_height,
- prev_multibrot_exponent,
- prev_max_iter_count.min(self.max_iter_count),
- self.colour_range,
- &iter_count_buffer[..],
- &square_dist_buffer[..],
- );
-
- video.draw(&image[..], self.canvas_width, self.canvas_height, self.scale);
- self.draw_feedback(&mut video, &prev_centre, &prev_zoom);
-
- video.update();
-
- video.sync(&frame_start);
- }
-
- return 0x0;
- }
-
- pub fn render(&self, iter_count_buffer: &mut [u32], square_dist_buffer: &mut [f32]) {
- eprint!("rendering...");
-
- let time_start = Instant::now();
-
- self.renderer.render(
- iter_count_buffer,
- square_dist_buffer,
- &self.fractal,
- self.canvas_width,
- self.canvas_height,
- &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);
- }
-
- pub 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) };
- }
-
- pub 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/app/poll_events.rs b/source/benoit/benoit/app/poll_events.rs
index af200ec..df24b0e 100644
--- a/source/benoit/benoit/app/poll_events.rs
+++ b/source/benoit/benoit/app/poll_events.rs
@@ -30,7 +30,7 @@ use sdl2::event::Event;
impl App {
#[must_use]
- pub fn poll_events(&mut self, pump: &mut EventPump) -> bool {
+ pub(super) fn poll_events(&mut self, pump: &mut EventPump) -> bool {
loop {
let event = match pump.poll_event() {
Some(event) => event,
diff --git a/source/benoit/benoit/app/run.rs b/source/benoit/benoit/app/run.rs
index 8d6ee0d..cefe5f7 100644
--- a/source/benoit/benoit/app/run.rs
+++ b/source/benoit/benoit/app/run.rs
@@ -21,24 +21,130 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::VERSION;
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 sdl2;
+extern crate rug;
+
+use rug::{Assign, Float};
+use std::time::Instant;
impl App {
#[must_use]
- pub fn run(&mut self) -> i32 {
+ pub fn run(mut self) -> i32 {
+ let mut video = Video::initialise(self.canvas_width, self.canvas_height, self.scale);
+
+ App::print_controls();
+
+ let mut event_pump = video.sdl.event_pump().expect("unable to get event pump");
+
+ let mut image = Image::allocate( self.canvas_width, self.canvas_height);
+ let mut render = Render::allocate(self.canvas_width, self.canvas_height);
+
+ // Used for translation feedback:
+ let mut prev_centre = self.centre.clone();
+ let mut prev_zoom = self.zoom.clone();
+
+ loop {
+ let frame_start = Instant::now();
+
+ if self.poll_events(&mut event_pump) { break }
+
+ if self.do_render {
+ self.render(&mut render);
+
+ prev_centre.assign(&self.centre);
+ prev_zoom.assign( &self.zoom);
+
+ self.do_render = false;
+ };
+
+ image.colour(&render, self.palette, self.max_iter_count, self.colour_range);
+
+ video.draw(&image, self.scale);
+ self.draw_feedback(&mut video, &prev_centre, &prev_zoom);
+
+ video.update();
+
+ video.sync(&frame_start);
+ }
+
+ 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}[1mBENO\u{CE}T\u{1B}[0m {:X}.{:X}.{:X}", VERSION[0x0], VERSION[0x1], VERSION[0x2]);
- println!("Copyright 2021, 2023 Gabriel Bj\u{F8}rnager Jensen.");
+ println!("- \u{1B}[1mQ\u{1B}[0m Zoom out");
+ println!("- \u{1B}[1mE\u{1B}[0m Zoom in");
println!();
- println!("COCITAVIT\u{B7}ERCO\u{B7}FVIT");
+ 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!();
-
- return match self.interactive {
- true => self.interactive(),
- false => self.animate(),
- };
}
-}
+} \ No newline at end of file
diff --git a/source/benoit/benoit/render/colour_data.rs b/source/benoit/benoit/colour_data.rs
index 114a8b0..6e994fb 100644
--- a/source/benoit/benoit/render/colour_data.rs
+++ b/source/benoit/benoit/colour_data.rs
@@ -21,7 +21,8 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::palette::{Palette, PaletteData};
+use crate::benoit::image::Image;
+use crate::benoit::palette::PaletteData;
use std::slice::{from_raw_parts, from_raw_parts_mut};
@@ -37,12 +38,22 @@ pub struct ColourData {
iter_count_buffer: *const u32,
square_dist_buffer: *const f32,
- image: *mut u8,
+ image: *mut (u8, u8, u8),
}
impl ColourData {
#[must_use]
- pub fn new(image: &mut [u8], canvas_width: u32, canvas_height: u32, exponent: f32, max_iter_count: u32, colour_range: f32, palette: Palette, iter_count_buffer: &[u32], square_dist_buffer: &[f32]) -> ColourData {
+ 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],
+ ) -> ColourData {
return ColourData {
canvas_size: canvas_height as usize * canvas_width as usize,
@@ -50,12 +61,12 @@ impl ColourData {
max_iter_count: max_iter_count,
colour_range: colour_range,
- palette_data: palette.get_data(),
+ palette_data: palette_data,
iter_count_buffer: iter_count_buffer.as_ptr(),
square_dist_buffer: square_dist_buffer.as_ptr(),
- image: image.as_mut_ptr(),
+ image: image.mut_data().as_mut_ptr(),
};
}
@@ -68,10 +79,8 @@ impl ColourData {
}
#[must_use]
- pub fn output_buffers(&self) -> &mut [u8] {
- let image = unsafe { from_raw_parts_mut(self.image, self.canvas_size * 0x3) };
-
- return image;
+ pub unsafe fn image<'a>(&'a self) -> &'a mut [(u8, u8, u8)] {
+ return from_raw_parts_mut(self.image, self.canvas_size);
}
#[must_use]
diff --git a/source/benoit/benoit/complex.rs b/source/benoit/benoit/complex.rs
index 146bd18..204b586 100644
--- a/source/benoit/benoit/complex.rs
+++ b/source/benoit/benoit/complex.rs
@@ -25,16 +25,18 @@ extern crate rug;
use rug::{Assign, Float};
+#[derive(Clone)]
pub struct Complex {
pub real: Float,
pub imag: Float,
}
impl Complex {
- pub fn clone(&self) -> Self {
+ #[must_use]
+ pub fn new(real: Float, imag: Float) -> Complex {
return Complex {
- real: self.real.clone(),
- imag: self.imag.clone(),
+ real: real,
+ imag: imag,
};
}
diff --git a/source/benoit/benoit/configuration.rs b/source/benoit/benoit/configuration.rs
index 7e0c97a..2975430 100644
--- a/source/benoit/benoit/configuration.rs
+++ b/source/benoit/benoit/configuration.rs
@@ -21,8 +21,9 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::{fractal, ImageFormat, PRECISION};
-use crate::benoit::fractal::Fractal;
+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;
@@ -58,17 +59,14 @@ pub struct Configuration {
pub dump_path: String,
pub image_format: ImageFormat,
-
- pub interactive: bool,
}
impl Configuration {
pub const DEFAULT_CENTRE_REAL: f64 = 0.0;
pub const DEFAULT_CENTRE_IMAG: f64 = 0.0;
- pub const DEFAULT_ZOOM: f64 = 1.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_MAX_ITER_COUNT: u32 = 0x100;
pub const DEFAULT_COLOUR_RANGE: f32 = 64.0;
@@ -78,7 +76,7 @@ impl Configuration {
return Configuration {
thread_count: 0x0,
- fractal: Fractal::new(fractal::Kind::Mandelbrot, false),
+ fractal: Fractal::new(FractalKind::Multibrot3, false),
renderer: Renderer::Normal,
canvas_width: 0x100,
@@ -101,8 +99,6 @@ impl Configuration {
dump_path: "./render".to_string(),
image_format: ImageFormat::Png,
-
- interactive: true,
};
}
}
diff --git a/source/benoit/benoit/configuration/load.rs b/source/benoit/benoit/configuration/load.rs
index 3f5ec11..6229e58 100644
--- a/source/benoit/benoit/configuration/load.rs
+++ b/source/benoit/benoit/configuration/load.rs
@@ -21,8 +21,10 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::{fractal, ImageFormat, PRECISION};
+use crate::benoit::PRECISION;
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;
@@ -41,8 +43,6 @@ impl Configuration {
let mut configuration = Configuration::default();
- configuration.interactive = false;
-
let configuration_text = match read(path) {
Ok(content) => String::from_utf8_lossy(&content).to_string(),
Err(..) => panic!("unable to read configuration file"),
@@ -62,10 +62,11 @@ impl Configuration {
if let Some(name) = get_string(&configuration_table, "fractal") {
configuration.fractal.set_kind(match name.as_str() {
- "burningship" => fractal::Kind::BurningShip,
- "mandelbrot" => fractal::Kind::Mandelbrot,
- "multibrot3" => fractal::Kind::Multibrot3,
- "tricorn" => fractal::Kind::Tricorn,
+ "burningship" => FractalKind::BurningShip,
+ "mandelbrot" => FractalKind::Mandelbrot,
+ "multibrot3" => FractalKind::Multibrot3,
+ "multibrot4" => FractalKind::Multibrot4,
+ "tricorn" => FractalKind::Tricorn,
name => panic!("invalid fractal kind \"{name}\""),
});
}
diff --git a/source/benoit/benoit/fractal.rs b/source/benoit/benoit/fractal.rs
index 7ca6035..1496450 100644
--- a/source/benoit/benoit/fractal.rs
+++ b/source/benoit/benoit/fractal.rs
@@ -21,27 +21,33 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::render::IteratorFunction;
-use crate::benoit::render::iterate;
+use crate::benoit::complex::Complex;
use std::mem::transmute;
+mod iterate;
+
+#[derive(Clone, Copy)]
pub struct Fractal {
- kind: Kind,
+ kind: FractalKind,
inverse: bool,
}
#[derive(Clone, Copy)]
#[repr(u8)]
-pub enum Kind {
+pub enum FractalKind {
BurningShip,
Mandelbrot,
Multibrot3,
+ Multibrot4,
Tricorn,
}
+pub type IteratorFunction = fn(&mut Complex, &Complex);
+
impl Fractal {
- pub const fn new(kind: Kind, inverse: bool) -> Self {
+ #[must_use]
+ pub const fn new(kind: FractalKind, inverse: bool) -> Self {
let fractal = Fractal {
kind: kind,
inverse: inverse,
@@ -51,7 +57,7 @@ impl Fractal {
}
#[must_use]
- pub fn kind(&self) -> Kind {
+ pub fn kind(&self) -> FractalKind {
return self.kind;
}
@@ -63,23 +69,26 @@ impl Fractal {
#[must_use]
pub fn exponent(&self) -> f32 {
return match self.kind {
- Kind::BurningShip => 2.0,
- Kind::Mandelbrot => 2.0,
- Kind::Multibrot3 => 3.0,
- Kind::Tricorn => 2.0,
+ FractalKind::BurningShip => 2.0,
+ FractalKind::Mandelbrot => 2.0,
+ FractalKind::Multibrot3 => 3.0,
+ FractalKind::Multibrot4 => 4.0,
+ FractalKind::Tricorn => 2.0,
};
}
+ #[must_use]
pub fn iterator(&self) -> IteratorFunction {
return match self.kind {
- Kind::BurningShip => iterate::burning_ship,
- Kind::Mandelbrot => iterate::mandelbrot,
- Kind::Multibrot3 => iterate::multibrot3,
- Kind::Tricorn => iterate::tricorn,
+ FractalKind::BurningShip => iterate::burning_ship,
+ FractalKind::Mandelbrot => iterate::mandelbrot,
+ FractalKind::Multibrot3 => iterate::multibrot3,
+ FractalKind::Multibrot4 => iterate::multibrot4,
+ FractalKind::Tricorn => iterate::tricorn,
};
}
- pub fn set_kind(&mut self, kind: Kind) {
+ pub fn set_kind(&mut self, kind: FractalKind) {
self.kind = kind;
}
@@ -93,28 +102,29 @@ impl Fractal {
let raw = self.kind as i8 + direction;
let raw: u8 = if raw < 0x0 {
- Kind::MAX
- } else if raw > Kind::MAX as i8 {
+ FractalKind::MAX
+ } else if raw > FractalKind::MAX as i8 {
0x0
} else {
raw as u8
};
- let new: Kind = unsafe { transmute(raw) };
+ let new: FractalKind = unsafe { transmute(raw) };
self.kind = new;
}
}
-impl Kind {
- const MAX: u8 = Kind::Tricorn as u8;
+impl FractalKind {
+ const MAX: u8 = FractalKind::Tricorn as u8;
pub fn name(self) -> &'static str {
return match self {
- Kind::BurningShip => "burning ship",
- Kind::Mandelbrot => "mandelbrot set",
- Kind::Multibrot3 => "multibrot (d=3) set",
- Kind::Tricorn => "tricorn",
+ FractalKind::BurningShip => "burning ship",
+ FractalKind::Mandelbrot => "mandelbrot set",
+ FractalKind::Multibrot3 => "multibrot (d=3) set",
+ FractalKind::Multibrot4 => "multibrot (d=4) set",
+ FractalKind::Tricorn => "tricorn",
};
}
}
diff --git a/source/benoit/benoit/render/iterate.rs b/source/benoit/benoit/fractal/iterate.rs
index 723da26..59c5a77 100644
--- a/source/benoit/benoit/render/iterate.rs
+++ b/source/benoit/benoit/fractal/iterate.rs
@@ -24,9 +24,11 @@
pub mod burning_ship;
pub mod mandelbrot;
pub mod multibrot3;
+pub mod multibrot4;
pub mod tricorn;
pub use burning_ship::*;
pub use mandelbrot::*;
pub use multibrot3::*;
+pub use multibrot4::*;
pub use tricorn::*;
diff --git a/source/benoit/benoit/render/iterate/burning_ship.rs b/source/benoit/benoit/fractal/iterate/burning_ship.rs
index 9e4f029..9e4f029 100644
--- a/source/benoit/benoit/render/iterate/burning_ship.rs
+++ b/source/benoit/benoit/fractal/iterate/burning_ship.rs
diff --git a/source/benoit/benoit/render/iterate/mandelbrot.rs b/source/benoit/benoit/fractal/iterate/mandelbrot.rs
index 078cc18..078cc18 100644
--- a/source/benoit/benoit/render/iterate/mandelbrot.rs
+++ b/source/benoit/benoit/fractal/iterate/mandelbrot.rs
diff --git a/source/benoit/benoit/render/iterate/multibrot3.rs b/source/benoit/benoit/fractal/iterate/multibrot3.rs
index fc6f872..da58a5e 100644
--- a/source/benoit/benoit/render/iterate/multibrot3.rs
+++ b/source/benoit/benoit/fractal/iterate/multibrot3.rs
@@ -38,8 +38,8 @@ pub fn multibrot3(z: &mut Complex, c: &Complex) {
// = a^3+(a^2)bi-ab^2-(b^3)i+2(a^2)bi-2ab^2
// = a^3+3(a^2)bi-3ab^2-(b^3)i
//
- // <=> z_a = a^3-3ab^2
- // z_b = 3(a^2)b-b^3
+ // <=> a = a^3-3ab^2
+ // b = 3(a^2)b-b^3
let mut temporary0 = Float::with_val(PRECISION, &z.imag * &z.imag); // b^2
@@ -59,4 +59,5 @@ pub fn multibrot3(z: &mut Complex, c: &Complex) {
z.imag *= 3.0; // 3(a^2)b
z.imag -= &temporary1; // 3(a^2)b-b^3
z.imag += &c.imag; // 3(a^2)b-b^3+Im(c)
+
}
diff --git a/source/benoit/benoit/fractal/iterate/multibrot4.rs b/source/benoit/benoit/fractal/iterate/multibrot4.rs
new file mode 100644
index 0000000..d040cf3
--- /dev/null
+++ b/source/benoit/benoit/fractal/iterate/multibrot4.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::complex::Complex;
+
+use crate::benoit::PRECISION;
+
+extern crate rug;
+
+use rug::{Assign, Float};
+
+pub fn multibrot4(z: &mut Complex, c: &Complex) {
+ // (a+bi)^4
+ // = (a+bi)^2*(a+bi)^2
+ // = (a^2-b^2+2abi)^2
+ // = (a^2-b^2+2abi)(a^2-b^2+2abi)
+ // = a^4-(a^2)b^2+2(a^3)bi-(a^2)b^2+b^4-2a(b^3)i-4(a^2)b^2+2(a^3)bi-2a(b^3)i-4(a^2)b^2
+ // = a^4-6(a^2)b^2+4(a^3)bi+b^4-4a(b^3)i
+ //
+ // <=> a = a^4-6(a^2)b^2+b^4
+ // b = 4(a^3)bi-4a(b^3)i
+
+ let temporary0 = Float::with_val(PRECISION, &z.real * &z.real); // a^2
+ let temporary1 = Float::with_val(PRECISION, &z.imag * &z.imag); // b^2
+
+ let mut temporary2 = Float::with_val(PRECISION, &z.real * &z.imag); // ab
+ temporary2 *= 4.0; // 4ab
+
+ z.real.assign(&temporary0); // a^2
+ z.real /= 6.0; // a^2/6
+ z.real -= &temporary1; // a^2/6-b^2
+ z.real *= &temporary0; // a^4/6-(a^2)b^2
+ z.real *= 6.0; // a^4-6(a^2)b^2
+
+ z.imag.assign(&temporary1); // b^2
+ z.imag *= -1.0; // -b^2
+ z.imag += &temporary0; // a^2-b^2
+ z.imag *= temporary2; // 4(a^3)b-4ab^3
+
+ z.real += temporary1.square(); // a^4-6(a^2)b^2+b^4
+ z.real += &c.real; // a^4-6(a^2)b^2+b^4+Re(c)
+
+ z.imag += &c.imag; // 4(a^3)b-4ab^3+Im(c)
+}
diff --git a/source/benoit/benoit/render/iterate/tricorn.rs b/source/benoit/benoit/fractal/iterate/tricorn.rs
index b562d66..a64b3aa 100644
--- a/source/benoit/benoit/render/iterate/tricorn.rs
+++ b/source/benoit/benoit/fractal/iterate/tricorn.rs
@@ -36,13 +36,13 @@ pub fn tricorn(z: &mut Complex, c: &Complex) {
z.real -= &z.imag * &z.imag; // a^2-b^2
z.real += &c.real; // a^2
- z.imag *= &za_temporary;
+ z.imag *= &za_temporary; // ab
// We can negate the value by multiplying with
// (-1). A multiplication can be saved, as
//
// a*2*(-1) = a*(-2).
//
// Thus, we may combine these two multiplications.
- z.imag *= -2.0;
- z.imag += &c.imag;
+ z.imag *= -2.0; // -2ab
+ z.imag += &c.imag; // -2ab+Im(c)
}
diff --git a/source/benoit/benoit/image.rs b/source/benoit/benoit/image.rs
new file mode 100644
index 0000000..a140a01
--- /dev/null
+++ b/source/benoit/benoit/image.rs
@@ -0,0 +1,78 @@
+/*
+ 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 std::ops::{Index, IndexMut};
+use std::slice::from_raw_parts;
+
+pub mod allocate;
+pub mod colour;
+pub mod dump;
+
+pub struct Image {
+ width: u32,
+ height: u32,
+
+ data: Vec::<(u8, u8, u8)>,
+}
+
+#[derive(Clone, Copy)]
+pub enum ImageFormat {
+ Png,
+ Webp,
+}
+
+impl Image {
+ #[must_use]
+ pub fn size(&self) -> (u32, u32) {
+ return (self.width, self.height);
+ }
+
+ #[must_use]
+ pub fn mut_data<'a>(&'a mut self) -> &'a mut [(u8, u8, u8)] {
+ return &mut self.data[..];
+ }
+
+ #[must_use]
+ pub fn raw<'a>(&'a self) -> &'a [u8] {
+ let data_pointer = self.data.as_ptr() as *const u8;
+
+ let length = self.height as usize * self.width as usize * 0x3;
+ let slice = unsafe { from_raw_parts(data_pointer, length) };
+
+ return slice;
+ }
+}
+
+impl Index<usize> for Image {
+ type Output = (u8, u8, u8);
+
+ fn index<'a>(&'a self, index: usize) -> &'a Self::Output {
+ return &self.data[index];
+ }
+}
+
+impl IndexMut<usize> for Image {
+ fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Self::Output {
+ return &mut self.data[index];
+ }
+}
diff --git a/source/benoit/benoit/image/allocate.rs b/source/benoit/benoit/image/allocate.rs
new file mode 100644
index 0000000..bbdd61b
--- /dev/null
+++ b/source/benoit/benoit/image/allocate.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::image::Image;
+
+impl Image {
+ #[must_use]
+ pub fn allocate(width: u32, height: u32) -> Image {
+ let (canvas_size, overflow) = (height as usize).overflowing_mul(width as usize);
+ if overflow { panic!("overflow when calculating canvas size") };
+
+ let data: Vec::<(u8, u8, u8)> = vec![(0x0, 0x0, 0x0); canvas_size];
+
+ return Image {
+ width: width,
+ height: height,
+
+ data: data,
+ };
+ }
+}
diff --git a/source/benoit/benoit/renderer/colour.rs b/source/benoit/benoit/image/colour.rs
index d4e8e74..18085dd 100644
--- a/source/benoit/benoit/renderer/colour.rs
+++ b/source/benoit/benoit/image/colour.rs
@@ -21,31 +21,36 @@
If not, see <https://www.gnu.org/licenses/>.
*/
+use crate::benoit::colour_data::ColourData;
+use crate::benoit::image::Image;
use crate::benoit::palette::{Palette, PALETTE_DATA_LENGTH};
-use crate::benoit::render::colour_data::ColourData;
-use crate::benoit::renderer::Renderer;
+use crate::benoit::render::Render;
extern crate rayon;
use rayon::prelude::*;
-impl Renderer {
- pub fn colour(
- self,
- buffer: &mut [u8],
- palette: Palette,
- canvas_width: u32,
- canvas_height: u32,
- multibrot_exponent: f32,
- max_iter_count: u32,
- colour_range: f32,
- iter_count_buffer: &[u32],
- square_dist_buffer: &[f32],
- ) {
- let data = ColourData::new(buffer, canvas_width, canvas_height, multibrot_exponent, max_iter_count, colour_range, palette, iter_count_buffer, square_dist_buffer);
-
- let (canvas_size, overflow) = canvas_height.overflowing_mul(canvas_width);
- if overflow { panic!("overflow when calculating canvas size") };
+impl Image {
+ pub fn colour(&mut self, render: &Render, palette: Palette, new_max_iter_count: u32, colour_range: f32) {
+ if render.canvas_size() != self.size() { panic!("canvas size mismatch") };
+
+ 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);
@@ -56,18 +61,18 @@ impl Renderer {
fn colour_point(data: &ColourData, index: usize) {
let (iter_count_buffer, square_dist_buffer) = data.input_buffers();
- let image = data.output_buffers();
+ let image = unsafe { data.image() };
let (exponent, max_iter_count, colour_range, palette_data) = data.consts();
- let iter_count = unsafe { *iter_count_buffer.get_unchecked( index) };
- let distance = unsafe { *square_dist_buffer.get_unchecked(index) }.sqrt();
+ 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.log(exponent).log(exponent)) / colour_range % 1.0;
+ let factor = (iter_count as f32 + 1.0 - distance.ln().log(exponent)) / colour_range;
- let index = (factor * PALETTE_DATA_LENGTH as f32).round() as usize;
- unsafe { *palette_data.get_unchecked(index) }
+ let index = (factor * PALETTE_DATA_LENGTH as f32).round() as usize % PALETTE_DATA_LENGTH;
+ palette_data[index]
} else {
(0.0, 0.0, 0.0)
};
@@ -76,11 +81,5 @@ fn colour_point(data: &ColourData, index: usize) {
let green = (green * 255.0).round() as u8;
let blue = (blue * 255.0).round() as u8;
- unsafe {
- let index = index * 0x3;
-
- *image.get_unchecked_mut(index) = red;
- *image.get_unchecked_mut(index + 0x1) = green;
- *image.get_unchecked_mut(index + 0x2) = blue;
- }
+ image[index] = (red, green, blue);
}
diff --git a/source/benoit/benoit/image/dump.rs b/source/benoit/benoit/image/dump.rs
new file mode 100644
index 0000000..18c166c
--- /dev/null
+++ b/source/benoit/benoit/image/dump.rs
@@ -0,0 +1,65 @@
+/*
+ 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::image::{Image, ImageFormat};
+
+extern crate png;
+extern crate webp;
+
+use std::fs::{File, write};
+use std::io::BufWriter;
+
+impl Image {
+ pub fn dump(&self, path: &str, format: ImageFormat) {
+ match format {
+ ImageFormat::Png => self.dump_png( path),
+ ImageFormat::Webp => self.dump_webp(path),
+ }
+ }
+
+ fn dump_png(&self, path: &str) {
+ let path = path.to_owned() + ".png";
+
+ let file = File::create(path).expect("unable to create file");
+ let file_buffer = BufWriter::new(file);
+
+ let mut encoder = png::Encoder::new(file_buffer, self.width, self.height);
+ encoder.set_color(png::ColorType::Rgb);
+ encoder.set_depth(png::BitDepth::Eight);
+ encoder.set_compression(png::Compression::Fast);
+ encoder.set_srgb(png::SrgbRenderingIntent::Perceptual);
+
+ let mut writer = encoder.write_header().expect("unable to write image");
+ writer.write_image_data(self.raw()).expect("unable to write image");
+ }
+
+ fn dump_webp(&self, path: &str) {
+ let path = path.to_owned() + ".webp";
+
+ let encoder = webp::Encoder::from_rgb(self.raw(), self.width, self.height);
+
+ let data = encoder.encode_lossless();
+
+ write(path, &*data).expect("unable to write image");
+ }
+}
diff --git a/source/benoit/benoit/palette.rs b/source/benoit/benoit/palette.rs
index d270682..b697af7 100644
--- a/source/benoit/benoit/palette.rs
+++ b/source/benoit/benoit/palette.rs
@@ -31,6 +31,7 @@ use std::mem::transmute;
mod paint;
#[derive(Clone, Copy, Sequence)]
+#[repr(u8)]
pub enum Palette {
Ancient,
Fire,
@@ -40,40 +41,6 @@ pub enum Palette {
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.get_data_mut();
- 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);
- }
- }
-}
-
impl Palette {
const MIN: Self = Palette::Ancient;
const MAX: Self = Palette::Sapphire;
@@ -91,8 +58,8 @@ impl Palette {
}
#[must_use]
- pub fn get_data(self) -> &'static PaletteData {
- return &*self.get_data_mut();
+ pub fn data(self) -> &'static PaletteData {
+ return &*self.mut_data();
}
#[must_use]
@@ -123,7 +90,7 @@ impl Palette {
}
#[must_use]
- fn get_data_mut(self) -> &'static mut PaletteData {
+ fn mut_data(self) -> &'static mut PaletteData {
return unsafe { match self {
Palette::Ancient => &mut DATA_ANCIENT,
Palette::Fire => &mut DATA_FIRE,
@@ -134,3 +101,37 @@ impl Palette {
} };
}
}
+
+// 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/paint/hsv.rs b/source/benoit/benoit/palette/paint/hsv.rs
index 7f04261..a9a6d9e 100644
--- a/source/benoit/benoit/palette/paint/hsv.rs
+++ b/source/benoit/benoit/palette/paint/hsv.rs
@@ -21,8 +21,6 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use std::hint::unreachable_unchecked;
-
pub fn hsv(factor: f32) -> (f32, f32, f32) {
return hsv_to_rgb(factor, 7.0 / 8.0, 7.0 / 8.0);
}
@@ -49,7 +47,7 @@ fn hsv_to_rgb(hue: f32, saturation: f32, value: f32) -> (f32, f32, f32) {
0x3 => (p, q, v),
0x4 => (t, p, v),
0x5 => (v, p, q),
- _ => unsafe { unreachable_unchecked() },
+ _ => unreachable!(),
}
};
}
diff --git a/source/benoit/benoit/render.rs b/source/benoit/benoit/render.rs
index b81679c..6e2b6ef 100644
--- a/source/benoit/benoit/render.rs
+++ b/source/benoit/benoit/render.rs
@@ -21,14 +21,34 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::complex::Complex;
-use crate::benoit::render::render_data::RenderData;
+use crate::benoit::fractal::Fractal;
-pub mod colour_data;
-pub mod iterate;
-pub mod render_data;
-pub mod render_point;
+pub mod allocate;
+pub mod render;
-pub type IteratorFunction = fn(&mut Complex, &Complex);
+pub struct Render {
+ canvas_width: u32,
+ canvas_height: u32,
-pub type PointRenderer = fn(&RenderData, u32, u32, IteratorFunction) -> (u32, f32);
+ info: Option<(Fractal, u32)>,
+
+ iter_count_buffer: Vec::<u32>,
+ square_dist_buffer: Vec::<f32>,
+}
+
+impl Render {
+ #[must_use]
+ pub fn canvas_size(&self) -> (u32, u32) {
+ return (self.canvas_width, self.canvas_height);
+ }
+
+ #[must_use]
+ 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);
+ }
+}
diff --git a/source/benoit/benoit/app/allocate_buffers.rs b/source/benoit/benoit/render/allocate.rs
index 44bbb28..e76f540 100644
--- a/source/benoit/benoit/app/allocate_buffers.rs
+++ b/source/benoit/benoit/render/allocate.rs
@@ -21,18 +21,28 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::app::App;
+use crate::benoit::render::Render;
-impl App {
- #[must_use]
- pub fn allocate_buffers(canvas_width: u32, canvas_height: u32) -> (Vec::<u32>, Vec::<f32>, Vec::<u8>) {
- let canvas_size = canvas_height as usize * canvas_width as usize;
+impl Render {
+ pub fn allocate(canvas_width: u32, canvas_height: u32) -> Render {
+ let canvas_size = {
+ let (canvas_size, overflow) = canvas_height.overflowing_mul(canvas_width);
+ if overflow { panic!("overflow when calculating canvas size") };
+
+ 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 image: Vec::<u8> = vec![0x0; canvas_size * 0x3];
+ return Render {
+ canvas_width: canvas_width,
+ canvas_height: canvas_height,
+
+ info: None,
- return (iter_count_buffer, square_dist_buffer, image);
+ iter_count_buffer: iter_count_buffer,
+ square_dist_buffer: square_dist_buffer,
+ };
}
}
diff --git a/source/benoit/benoit/renderer/render.rs b/source/benoit/benoit/render/render.rs
index 9ec5e73..731f347 100644
--- a/source/benoit/benoit/renderer/render.rs
+++ b/source/benoit/benoit/render/render.rs
@@ -22,10 +22,10 @@
*/
use crate::benoit::complex::Complex;
-use crate::benoit::fractal::Fractal;
-use crate::benoit::render::{IteratorFunction, PointRenderer};
-use crate::benoit::render::render_data::RenderData;
-use crate::benoit::renderer::Renderer;
+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;
@@ -33,24 +33,23 @@ extern crate rug;
use rayon::prelude::*;
use rug::Float;
-impl Renderer {
+impl Render {
pub fn render(
- self,
- iter_count_buffer: &mut [u32],
- square_dist_buffer: &mut [f32],
- fractal: &Fractal,
- canvas_width: u32,
- canvas_height: u32,
+ &mut self,
+ fractal: Fractal,
+ renderer: Renderer,
centre: &Complex,
zoom: &Float,
extra: &Complex,
max_iter_count: u32,
) {
+ assert!(max_iter_count > 0x0);
+
let data = RenderData::new(
- iter_count_buffer,
- square_dist_buffer,
- canvas_width,
- canvas_height,
+ &mut self.iter_count_buffer[..],
+ &mut self.square_dist_buffer[..],
+ self.canvas_width,
+ self.canvas_height,
centre.clone(),
extra.clone(),
zoom.clone(),
@@ -58,20 +57,21 @@ impl Renderer {
fractal.inverse(),
);
- let (canvas_size, overflow) = canvas_height.overflowing_mul(canvas_width);
- if overflow { panic!("overflow when calculating canvas size") };
+ let canvas_size = self.canvas_height as usize * self.canvas_width as usize;
- let point_renderer = self.point_renderer();
+ 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.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) = data.output_buffers();
+ let (iter_count_buffer, square_dist_buffer) = unsafe { data.output_buffers() };
let (canvas_width, _) = data.canvas_size();
@@ -80,10 +80,6 @@ fn render_point(data: &RenderData, index: usize, point_renderer: PointRenderer,
let (iter_count, square_dist) = point_renderer(&data, x, y, iterator);
- // Sacrifice safety for speed by removing bounds-
- // checking.
- unsafe {
- *iter_count_buffer.get_unchecked_mut( index as usize) = iter_count;
- *square_dist_buffer.get_unchecked_mut(index as usize) = square_dist;
- }
+ iter_count_buffer[ index as usize] = iter_count;
+ square_dist_buffer[index as usize] = square_dist;
}
diff --git a/source/benoit/benoit/render/render_data.rs b/source/benoit/benoit/render_data.rs
index 86a21ee..0dda5d9 100644
--- a/source/benoit/benoit/render/render_data.rs
+++ b/source/benoit/benoit/render_data.rs
@@ -94,19 +94,6 @@ impl RenderData {
}
#[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)
- };
- }
-
- #[must_use]
pub fn canvas_size(&self) -> (u32, u32) {
return (self.canvas_width, self.canvas_height);
}
@@ -117,9 +104,13 @@ impl RenderData {
}
#[must_use]
- pub fn output_buffers(&self) -> (&mut [u32], &mut [f32]) {
- let iter_count = unsafe { from_raw_parts_mut(self.iter_count_buffer, self.canvas_size as usize) };
- let square_dist = unsafe { from_raw_parts_mut(self.square_dist_buffer, self.canvas_size as usize) };
+ 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.
+
+ 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);
return (iter_count, square_dist);
}
@@ -128,6 +119,19 @@ impl RenderData {
pub fn consts(&self) -> (f32, f32, f32, f32) {
return (self.x_offset, self.y_offset, self.x_factor, self.y_factor);
}
+
+ #[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)
+ };
+ }
}
unsafe impl Send for RenderData {}
diff --git a/source/benoit/benoit/renderer.rs b/source/benoit/benoit/renderer.rs
index 6b9aaca..0c02509 100644
--- a/source/benoit/benoit/renderer.rs
+++ b/source/benoit/benoit/renderer.rs
@@ -21,21 +21,24 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::render::PointRenderer;
-use crate::benoit::render::render_point;
+use crate::benoit::fractal::IteratorFunction;
+use crate::benoit::render_data::RenderData;
use std::mem::transmute;
-pub mod colour;
-pub mod render;
+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,
@@ -43,11 +46,10 @@ impl Renderer {
};
}
- pub fn cycle(self) -> Self {
- let raw = !(self as u8) & 0b00000001;
-
+ pub fn toggle(&mut self) {
+ let raw = !(*self as u8) & 0b00000001;
let new: Self = unsafe { transmute(raw) };
- return new;
+ *self = new;
}
}
diff --git a/source/benoit/benoit/render/render_point.rs b/source/benoit/benoit/renderer/render_point.rs
index 7f9bf9a..7f9bf9a 100644
--- a/source/benoit/benoit/render/render_point.rs
+++ b/source/benoit/benoit/renderer/render_point.rs
diff --git a/source/benoit/benoit/render/render_point/julia.rs b/source/benoit/benoit/renderer/render_point/julia.rs
index 63cd4cb..06a478c 100644
--- a/source/benoit/benoit/render/render_point/julia.rs
+++ b/source/benoit/benoit/renderer/render_point/julia.rs
@@ -23,8 +23,8 @@
use crate::benoit::{BAILOUT, PRECISION};
use crate::benoit::complex::Complex;
-use crate::benoit::render::IteratorFunction;
-use crate::benoit::render::render_data::RenderData;
+use crate::benoit::fractal::IteratorFunction;
+use crate::benoit::render_data::RenderData;
extern crate rug;
diff --git a/source/benoit/benoit/render/render_point/normal.rs b/source/benoit/benoit/renderer/render_point/normal.rs
index 167bdb6..e9f848f 100644
--- a/source/benoit/benoit/render/render_point/normal.rs
+++ b/source/benoit/benoit/renderer/render_point/normal.rs
@@ -23,8 +23,8 @@
use crate::benoit::{BAILOUT, PRECISION};
use crate::benoit::complex::Complex;
-use crate::benoit::render::IteratorFunction;
-use crate::benoit::render::render_data::RenderData;
+use crate::benoit::fractal::IteratorFunction;
+use crate::benoit::render_data::RenderData;
extern crate rug;
diff --git a/source/benoit/benoit/script.rs b/source/benoit/benoit/script.rs
new file mode 100644
index 0000000..4337944
--- /dev/null
+++ b/source/benoit/benoit/script.rs
@@ -0,0 +1,61 @@
+/*
+ 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::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;
+
+use rug::Float;
+
+pub mod animate;
+pub mod configure;
+pub mod dump;
+pub mod run;
+pub mod still;
+
+pub struct Script {
+ // Configuration:
+ fractal: Fractal,
+ renderer: Renderer,
+
+ canvas_width: u32,
+ canvas_height: u32,
+ frame_start: u32,
+ frame_stop: u32,
+
+ centre: Complex,
+ extra: Complex,
+ zoom: Float,
+
+ max_iter_count: u32,
+
+ palette: Palette,
+ colour_range: f32,
+
+ dump_path: String,
+ image_format: ImageFormat,
+}
diff --git a/source/benoit/benoit/script/animate.rs b/source/benoit/benoit/script/animate.rs
new file mode 100644
index 0000000..65bbe90
--- /dev/null
+++ b/source/benoit/benoit/script/animate.rs
@@ -0,0 +1,114 @@
+/*
+ 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::image::Image;
+use crate::benoit::render::Render;
+use crate::benoit::script::Script;
+
+extern crate rug;
+
+use rug::Float;
+use rug::ops::PowAssign;
+
+impl Script {
+ #[must_use]
+ pub(super) fn animate(&self) -> i32 {
+ let frame_count = self.frame_stop - self.frame_start + 0x1;
+
+ let mut image = Image::allocate( self.canvas_width, self.canvas_height);
+ let mut render = Render::allocate(self.canvas_width, self.canvas_height);
+
+ // 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 = get_zoom_factor(&zoom, &zoom_stop, self.frame_stop);
+
+ zoom = if self.frame_start > 0x0 {
+ let mut zoom = zoom_factor.clone();
+ zoom.pow_assign(frame_count);
+
+ zoom
+ } else {
+ zoom
+ };
+
+ eprintln!("animating from #{} to #{} ({} frame(s)) at {}{:+}i to {:.3}x (fac. ~{:.3})", self.frame_start, self.frame_stop, frame_count + 0x1, self.centre.real.to_f32(), self.centre.imag.to_f32(), zoom_stop.to_f32(), zoom_factor.to_f32());
+
+ for frame in 0x0..=frame_count {
+ let frame_name = format!("frame{frame:010}");
+
+ Script::dump(
+ self.dump_path.as_str(),
+ frame_name.as_str(),
+ &mut image,
+ &mut render,
+ self.renderer,
+ self.fractal,
+ self.palette,
+ &self.centre,
+ &self.extra,
+ &zoom,
+ self.max_iter_count,
+ self.colour_range,
+ self.image_format,
+ );
+
+ zoom *= &zoom_factor;
+ }
+
+ return 0x0;
+ }
+}
+
+fn get_zoom_factor(zoom_start: &Float, zoom_stop: &Float, frame_count: u32) -> Float {
+ assert!(frame_count > 0x0);
+
+ // 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)).
+
+ let frame_start: f32 = 0.0;
+ let frame_stop: f32 = frame_count as f32;
+
+ let exponent = Float::with_val(PRECISION, 1.0 / (frame_stop - frame_start));
+
+ let mut factor = Float::with_val(PRECISION, zoom_stop / zoom_start);
+ factor.pow_assign(exponent);
+
+ return factor;
+}
diff --git a/source/benoit/benoit/script/configure.rs b/source/benoit/benoit/script/configure.rs
new file mode 100644
index 0000000..acb6071
--- /dev/null
+++ b/source/benoit/benoit/script/configure.rs
@@ -0,0 +1,54 @@
+/*
+ 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::complex::Complex;
+use crate::benoit::configuration::Configuration;
+use crate::benoit::script::Script;
+
+impl Script {
+ #[must_use]
+ pub fn configure(configuration: Configuration) -> Script {
+ return Script {
+ fractal: configuration.fractal,
+ renderer: configuration.renderer,
+
+ canvas_width: configuration.canvas_width,
+ canvas_height: configuration.canvas_height,
+ frame_start: configuration.frame_start,
+ frame_stop: configuration.frame_stop,
+
+ centre: Complex::new(configuration.centre_real, configuration.centre_imag),
+ zoom: configuration.zoom,
+
+ extra: Complex::new(configuration.extra_real, configuration.extra_imag),
+
+ max_iter_count: configuration.max_iter_count,
+
+ palette: configuration.palette,
+ colour_range: configuration.colour_range,
+
+ dump_path: configuration.dump_path,
+ image_format: configuration.image_format,
+ };
+ }
+}
diff --git a/source/benoit/benoit/script/dump.rs b/source/benoit/benoit/script/dump.rs
new file mode 100644
index 0000000..d77b597
--- /dev/null
+++ b/source/benoit/benoit/script/dump.rs
@@ -0,0 +1,79 @@
+/*
+ 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::complex::Complex;
+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;
+
+use rug::Float;
+use std::time::Instant;
+
+impl Script {
+ pub(super) fn dump(
+ dump_path: &str,
+ name: &str,
+ image: &mut Image,
+ render: &mut Render,
+ renderer: Renderer,
+ fractal: Fractal,
+ palette: Palette,
+ centre: &Complex,
+ extra: &Complex,
+ zoom: &Float,
+ max_iter_count: u32,
+ colour_range: f32,
+ image_format: ImageFormat,
+ ) {
+ eprint!("\"{name}\" (2^{:.9}x)...", zoom.to_f64().log2());
+
+ let time_start = Instant::now();
+
+ render.render(
+ fractal,
+ renderer,
+ centre,
+ zoom,
+ extra,
+ max_iter_count,
+ );
+
+ let render_time = time_start.elapsed();
+ eprint!(" {:.3}ms, colouring...", render_time.as_micros() as f32 / 1000.0);
+
+ image.colour(&render, palette, max_iter_count, colour_range);
+
+ let colour_time = time_start.elapsed() - render_time;
+ eprint!(" {:.3}ms...", colour_time.as_micros() as f32 / 1000.0);
+
+ let path = format!("{dump_path}/{name}");
+
+ image.dump(path.as_str(), image_format);
+ eprintln!(" done");
+ }
+}
diff --git a/source/benoit/benoit/script/run.rs b/source/benoit/benoit/script/run.rs
new file mode 100644
index 0000000..24a34b0
--- /dev/null
+++ b/source/benoit/benoit/script/run.rs
@@ -0,0 +1,36 @@
+/*
+ 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::script::Script;
+
+impl Script {
+ #[must_use]
+ pub fn run(self) -> i32 {
+ let code = match self.frame_stop == 0x0 && self.frame_start == 0x0 {
+ false => self.animate(),
+ true => self.still(),
+ };
+
+ return code;
+ }
+}
diff --git a/source/benoit/benoit/script/still.rs b/source/benoit/benoit/script/still.rs
new file mode 100644
index 0000000..73ebcf8
--- /dev/null
+++ b/source/benoit/benoit/script/still.rs
@@ -0,0 +1,54 @@
+/*
+ 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::image::Image;
+use crate::benoit::render::Render;
+use crate::benoit::script::Script;
+
+impl Script {
+ #[must_use]
+ pub(super) fn still(&self) -> i32 {
+ let mut image = Image::allocate( self.canvas_width, self.canvas_height);
+ let mut render = Render::allocate(self.canvas_width, self.canvas_height);
+
+ const FRAME_NAME: &str = "render";
+
+ Script::dump(
+ self.dump_path.as_str(),
+ FRAME_NAME,
+ &mut image,
+ &mut render,
+ self.renderer,
+ self.fractal,
+ self.palette,
+ &self.centre,
+ &self.extra,
+ &self.zoom,
+ self.max_iter_count,
+ self.colour_range,
+ self.image_format,
+ );
+
+ return 0x0;
+ }
+}
diff --git a/source/benoit/benoit/video/draw.rs b/source/benoit/benoit/video/draw.rs
index a3d2460..69b77b2 100644
--- a/source/benoit/benoit/video/draw.rs
+++ b/source/benoit/benoit/video/draw.rs
@@ -21,6 +21,7 @@
If not, see <https://www.gnu.org/licenses/>.
*/
+use crate::benoit::image::Image;
use crate::benoit::video::Video;
extern crate sdl2;
@@ -29,21 +30,16 @@ use sdl2::pixels::Color;
use sdl2::rect::Rect;
impl Video {
- pub fn draw(&mut self, image: &[u8], canvas_width: u32, canvas_height: u32, scale: u32) {
- self.draw_image(image, canvas_width, canvas_height, scale);
- }
-
- fn draw_image(&mut self, image: &[u8], canvas_width: u32, canvas_height: u32, scale: u32) {
- let canvas_size = canvas_height * canvas_width;
+ pub fn draw(&mut self, image: &Image, scale: u32) {
+ let (canvas_width, canvas_height) = image.size();
+ let canvas_size = canvas_height as usize * canvas_width as usize;
for pixel in 0x0..canvas_size {
+ let x = pixel as u32 % 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];
- let green = image[pixel as usize * 0x3 + 0x1];
- let blue = image[pixel as usize * 0x3 + 0x2];
+ let (red, green, blue) = image[pixel as usize];
Color::RGB(red, green, blue)
};
diff --git a/source/benoit/benoit/video/initialise.rs b/source/benoit/benoit/video/initialise.rs
index b06d774..76668bd 100644
--- a/source/benoit/benoit/video/initialise.rs
+++ b/source/benoit/benoit/video/initialise.rs
@@ -30,6 +30,7 @@ use sdl2::pixels::Color;
use sdl2::render::BlendMode;
impl Video {
+ #[must_use]
pub fn initialise(canvas_width: u32, canvas_height: u32, scale: u32) -> Video {
let sdl = sdl2::init().expect("unable to initialise sdl2");
let sdl_video = sdl.video().expect("unable to initialise video");
diff --git a/source/benoit/main.rs b/source/benoit/main.rs
index 3716309..9e8e572 100644
--- a/source/benoit/main.rs
+++ b/source/benoit/main.rs
@@ -23,15 +23,56 @@
mod benoit;
-use benoit::app::App;
+use crate::benoit::VERSION;
+use crate::benoit::app::App;
+use crate::benoit::configuration::Configuration;
+use crate::benoit::script::Script;
-use std::mem::drop;
+extern crate rayon;
+
+use rayon::ThreadPoolBuilder;
+use std::env::args;
use std::process::exit;
+use std::thread::available_parallelism;
fn main() {
- let mut app = App::initialise();
- let code = app.run();
+ println!();
+ println!("\u{1B}[1mBENO\u{CE}T\u{1B}[0m {:X}.{:X}.{:X}", VERSION[0x0], VERSION[0x1], VERSION[0x2]);
+ 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!();
+
+ let mut arguments = args();
+
+ let (mut configuration, interative) = match arguments.nth(0x1) {
+ Some(path) => (Configuration::load(path.as_str()), false),
+ _ => (Configuration::default(), true),
+ };
+
+ configuration.thread_count = if configuration.thread_count == 0x0 {
+ match available_parallelism() {
+ Ok(ammount) => ammount.get() as u32,
+ _ => 0x2, // We assume at least two threads.
+ }
+ } else {
+ configuration.thread_count
+ };
+
+ eprintln!("using {} threads", configuration.thread_count);
+ ThreadPoolBuilder::new().num_threads(configuration.thread_count as usize).build_global().unwrap();
+
+ let code = if interative {
+ eprintln!("running iteractive mode");
+
+ let app = App::configure(configuration);
+ app.run()
+ } else {
+ eprintln!("running script mode");
+
+ let script = Script::configure(configuration);
+ script.run()
+ };
- drop(app);
exit(code);
}