summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md15
-rw-r--r--Cargo.toml6
-rw-r--r--source/benoit/benoit.rs27
-rw-r--r--source/benoit/benoit/app.rs50
-rw-r--r--source/benoit/benoit/app/allocate_buffers.rs (renamed from source/benoit/benoit/app/get_iterator_function.rs)17
-rw-r--r--source/benoit/benoit/app/animate.rs46
-rw-r--r--source/benoit/benoit/app/colour.rs13
-rw-r--r--source/benoit/benoit/app/colour_row.rs51
-rw-r--r--source/benoit/benoit/app/dump.rs14
-rw-r--r--source/benoit/benoit/app/handle_keys.rs74
-rw-r--r--source/benoit/benoit/app/initialise.rs41
-rw-r--r--source/benoit/benoit/app/interactive.rs152
-rw-r--r--source/benoit/benoit/app/loop.rs140
-rw-r--r--source/benoit/benoit/app/poll_events.rs2
-rw-r--r--source/benoit/benoit/app/render.rs6
-rw-r--r--source/benoit/benoit/app/render_row_julia.rs100
-rw-r--r--source/benoit/benoit/app/render_row_normal.rs101
-rw-r--r--source/benoit/benoit/app/run.rs13
-rw-r--r--source/benoit/benoit/app/still.rs56
-rw-r--r--source/benoit/benoit/configuration.rs25
-rw-r--r--source/benoit/benoit/configuration/default.rs29
-rw-r--r--source/benoit/benoit/configuration/load.rs145
-rw-r--r--source/benoit/benoit/factorisation.rs50
-rw-r--r--source/benoit/benoit/fractal.rs60
-rw-r--r--source/benoit/benoit/palette.rs85
-rw-r--r--source/benoit/benoit/render.rs (renamed from source/benoit/benoit/iteration.rs)25
-rw-r--r--source/benoit/benoit/render/colour_data.rs (renamed from source/benoit/benoit/task/colour_data/slice.rs)33
-rw-r--r--source/benoit/benoit/render/factorise.rs28
-rw-r--r--source/benoit/benoit/render/factorise/smooth.rs (renamed from source/benoit/benoit/task/colour_data.rs)18
-rw-r--r--source/benoit/benoit/render/factorise/stepped.rs (renamed from source/benoit/benoit/fractal/get_name.rs)15
-rw-r--r--source/benoit/benoit/render/iterate.rs32
-rw-r--r--source/benoit/benoit/render/iterate/burning_ship.rs (renamed from source/benoit/benoit/iteration/iterate_burning_ship.rs)2
-rw-r--r--source/benoit/benoit/render/iterate/mandelbrot.rs (renamed from source/benoit/benoit/iteration/iterate_mandelbrot.rs)25
-rw-r--r--source/benoit/benoit/render/iterate/multibrot3.rs60
-rw-r--r--source/benoit/benoit/render/iterate/tricorn.rs (renamed from source/benoit/benoit/iteration/iterate_tricorn.rs)12
-rw-r--r--source/benoit/benoit/render/paint.rs36
-rw-r--r--source/benoit/benoit/render/paint/ancient.rs41
-rw-r--r--source/benoit/benoit/render/paint/fire.rs (renamed from source/benoit/benoit/task/render_data/new.rs)36
-rw-r--r--source/benoit/benoit/render/paint/greyscale.rs (renamed from source/benoit/benoit/task/render_data.rs)32
-rw-r--r--source/benoit/benoit/render/paint/hsv.rs61
-rw-r--r--source/benoit/benoit/render/paint/lch.rs108
-rw-r--r--source/benoit/benoit/render/paint/sapphire.rs40
-rw-r--r--source/benoit/benoit/render/render_data.rs97
-rw-r--r--source/benoit/benoit/render/render_row.rs (renamed from source/benoit/benoit/task.rs)7
-rw-r--r--source/benoit/benoit/render/render_row/julia.rs84
-rw-r--r--source/benoit/benoit/render/render_row/normal.rs94
-rw-r--r--source/benoit/benoit/rendering.rs (renamed from source/benoit/benoit/task/colour_data/new.rs)32
-rw-r--r--source/benoit/benoit/task/render_data/slice.rs37
-rw-r--r--source/benoit/benoit/video.rs3
-rw-r--r--source/benoit/benoit/video/draw.rs79
-rw-r--r--source/benoit/benoit/video/draw_textual_feedback.rs328
-rw-r--r--source/benoit/benoit/video/draw_translation_feedback.rs95
-rw-r--r--source/benoit/benoit/video/initialise.rs14
-rw-r--r--source/benoit/benoit/video/update.rs (renamed from source/benoit/benoit/app/get_row_renderer.rs)12
-rw-r--r--source/benoit/benoit/width_height_ratio.rs30
55 files changed, 2021 insertions, 813 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 92142bc..c09d5b4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,18 @@
+# 2.0.0
+
+* Bump major version
+* Update controls
+* Support non-square canvasses again
+* Modulise and refactor code
+* Draw textual feedback to window (enable with F1)
+* Bump dependency versions
+* Support multiple palette functions (reflect in configuration)
+* Add multibrot3 fractal
+* Improve commenting
+* Check interactive input
+* Remove dumping from interactive mode
+* Fix image file extensions
+
# 1.2.1
* Fix readme
diff --git a/Cargo.toml b/Cargo.toml
index 1d698cd..b99670b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "benoit"
-version = "1.2.0"
+version = "2.0.0"
authors = ["Gabriel Bjørnager Jensen"]
edition = "2021"
description = "Mandelbrot renderer."
@@ -17,8 +17,8 @@ lto = "fat"
[dependencies]
png = "0.17.10"
-rayon = "1.7.0"
+rayon = "1.8.0"
rug = "1.22.0"
sdl2 = "0.35.2"
toml = "0.8.0"
-webp = "0.2.5"
+webp = "0.2.6"
diff --git a/source/benoit/benoit.rs b/source/benoit/benoit.rs
index df2d104..bbbc158 100644
--- a/source/benoit/benoit.rs
+++ b/source/benoit/benoit.rs
@@ -27,10 +27,15 @@ use rug::Float;
pub mod app;
pub mod configuration;
+pub mod factorisation;
pub mod fractal;
-pub mod iteration;
-pub mod task;
+pub mod palette;
+pub mod rendering;
+pub mod render;
pub mod video;
+pub mod width_height_ratio;
+
+pub use width_height_ratio::*;
pub struct Version<T> {
major: T,
@@ -39,13 +44,19 @@ pub struct Version<T> {
}
pub const VERSION: Version::<u32> = Version::<u32> {
- major: 0x1,
- minor: 0x2,
- patch: 0x1,
+ major: 0x2,
+ minor: 0x0,
+ patch: 0x0,
};
pub const PRECISION: u32 = 0x80;
+// We would like to precalculate the palettes at
+// compile-time, but Rust does not support
+// floating-point arithmetic there.
+#[allow(dead_code)]
+pub const PALETTE_LENGTH: usize = 0x100;
+
#[derive(Clone, Copy)]
pub enum ImageFormat {
Png,
@@ -56,7 +67,7 @@ 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,
+ next_centre_real: &'a Float,
+ next_centre_imag: &'a Float,
+ next_zoom: &'a Float,
}
diff --git a/source/benoit/benoit/app.rs b/source/benoit/benoit/app.rs
index 255461e..1863a77 100644
--- a/source/benoit/benoit/app.rs
+++ b/source/benoit/benoit/app.rs
@@ -22,62 +22,68 @@
*/
use crate::benoit::ImageFormat;
+use crate::benoit::factorisation::Factorisation;
use crate::benoit::fractal::Fractal;
-use crate::benoit::iteration::IteratorFunction;
-use crate::benoit::task::render_data::RenderData;
+use crate::benoit::palette::Palette;
+use crate::benoit::rendering::Rendering;
+use crate::benoit::render::{FactoriserFunction, IteratorFunction, PaletteFunction, RowRenderer};
use crate::benoit::video::Video;
extern crate rug;
use rug::Float;
-use std::sync::Arc;
+pub mod allocate_buffers;
pub mod animate;
pub mod colour;
pub mod colour_row;
pub mod drop;
pub mod dump;
-pub mod get_iterator_function;
-pub mod get_row_renderer;
pub mod handle_keys;
pub mod image_filename;
pub mod initialise;
-pub mod r#loop;
+pub mod interactive;
pub mod poll_events;
-pub mod render_row_julia;
-pub mod render_row_normal;
pub mod render;
pub mod run;
-
-pub type RowRenderer = fn(Arc<RenderData>, u32, IteratorFunction);
+pub mod still;
pub struct App {
#[allow(dead_code)]
thread_count: u32,
- fractal: Fractal,
- julia: bool,
+ fractal: Fractal,
+ rendering: Rendering,
+
+ canvas_width: u32,
+ canvas_height: u32,
+ scale: u32,
+ frame_count: u32,
- canvas_width: u32,
- scale: u32,
- frame_count: u32,
+ centre_real: Float,
+ centre_imag: Float,
+ zoom: Float,
+
+ multibrot_exponent: f32,
- centre_real: Float,
- centre_imag: Float,
- zoom: Float,
max_iter_count: u32,
- colour_range: f32,
+ factorisation: Factorisation,
+ palette: Palette,
+ colour_range: f32,
dump_path: String,
image_format: ImageFormat,
video: Option<Video>,
- interactive: bool,
- do_render: bool,
- do_dump: bool,
+ interactive: bool,
+ do_render: bool,
+ do_textual_feedback: bool,
row_renderer: RowRenderer,
iterator_function: IteratorFunction,
+
+ factoriser: FactoriserFunction,
+ palette_function: PaletteFunction,
}
diff --git a/source/benoit/benoit/app/get_iterator_function.rs b/source/benoit/benoit/app/allocate_buffers.rs
index 1454785..b8616d5 100644
--- a/source/benoit/benoit/app/get_iterator_function.rs
+++ b/source/benoit/benoit/app/allocate_buffers.rs
@@ -21,16 +21,17 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::fractal::Fractal;
use crate::benoit::app::App;
-use crate::benoit::iteration::*;
impl App {
- pub fn get_iterator_function(fractal: Fractal) -> IteratorFunction {
- return match fractal {
- Fractal::BurningShip => iterate_burning_ship,
- Fractal::Mandelbrot => iterate_mandelbrot,
- Fractal::Tricorn => iterate_tricorn,
- };
+ 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;
+
+ 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 (iter_count_buffer, square_dist_buffer, image);
}
}
diff --git a/source/benoit/benoit/app/animate.rs b/source/benoit/benoit/app/animate.rs
index 3de4a76..7cc3549 100644
--- a/source/benoit/benoit/app/animate.rs
+++ b/source/benoit/benoit/app/animate.rs
@@ -33,37 +33,9 @@ use std::time::Instant;
impl App {
pub fn animate(&self) -> i32 {
- let canvas_size = self.canvas_width as usize * self.canvas_width as usize;
+ assert!(self.frame_count > 0x1);
- 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();
-
- eprint!(" {:.3}ms, colouring...", render_time.as_micros() as f32 / 1000.0);
-
- self.colour(&mut image[..], self.max_iter_count, &mut iter_count_buffer[..], &mut square_dist_buffer[..]);
- let colour_time = time_start.elapsed() - render_time;
-
- eprint!(" {:.3}ms...", colour_time.as_micros() as f32 / 1000.0);
-
- self.dump(format!("{}/render.webp", self.dump_path), &image, self.canvas_width);
-
- eprintln!(" done");
-
- return 0x0;
- }
+ 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);
@@ -107,22 +79,20 @@ impl App {
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();
+ let render_time = time_start.elapsed();
eprint!(" {:.3}ms, colouring...", render_time.as_micros() as f32 / 1000.0);
- 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.colour(&mut image[..], self.multibrot_exponent, self.max_iter_count, &mut iter_count_buffer[..], &mut square_dist_buffer[..]);
+ let colour_time = time_start.elapsed() - render_time;
eprint!(" {:.3}ms...", colour_time.as_micros() as f32 / 1000.0);
- self.dump(format!("{}/render.webp", self.dump_path), &image, self.canvas_width);
+ let path = App::image_filename(format!("{}/frame{frame:010}.webp", self.dump_path).as_str(), self.image_format);
- eprintln!(" done");
-
- self.dump(format!("{}/frame{frame:010}.webp", self.dump_path), &image, self.canvas_width);
+ self.dump(path.as_str(), &image, self.canvas_width, self.canvas_height);
- eprintln!(" rend. {:.3}ms, col. {:.3}ms", render_time.as_micros() as f32 / 1000.0, colour_time.as_micros() as f32 / 1000.0);
+ eprintln!(" done");
zoom *= &zoom_factor;
}
diff --git a/source/benoit/benoit/app/colour.rs b/source/benoit/benoit/app/colour.rs
index 3325b55..03093ac 100644
--- a/source/benoit/benoit/app/colour.rs
+++ b/source/benoit/benoit/app/colour.rs
@@ -22,7 +22,7 @@
*/
use crate::benoit::app::App;
-use crate::benoit::task::colour_data::ColourData;
+use crate::benoit::render::colour_data::ColourData;
extern crate rayon;
@@ -30,11 +30,14 @@ 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));
+ pub fn colour(&self, buffer: &mut [u8], multibrot_exponent: f32, max_iter_count: u32, iter_count_buffer: &[u32], square_dist_buffer: &[f32]) {
+ let data = Arc::new(ColourData::new(buffer, self.canvas_width, multibrot_exponent, 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);
+ let factoriser = self.factoriser;
+ let palette_function = self.palette_function;
+
+ (0x0..self.canvas_height).into_par_iter().for_each(|row| {
+ App::colour_row(data.clone(), row as u32, factoriser, palette_function);
});
}
}
diff --git a/source/benoit/benoit/app/colour_row.rs b/source/benoit/benoit/app/colour_row.rs
index 0348499..a4baca7 100644
--- a/source/benoit/benoit/app/colour_row.rs
+++ b/source/benoit/benoit/app/colour_row.rs
@@ -22,13 +22,13 @@
*/
use crate::benoit::app::App;
-use crate::benoit::task::colour_data::ColourData;
+use crate::benoit::render::{FactoriserFunction, PaletteFunction};
+use crate::benoit::render::colour_data::ColourData;
-use std::hint::unreachable_unchecked;
use std::sync::Arc;
impl App {
- pub fn colour_row(data: Arc<ColourData>, y: u32) {
+ pub fn colour_row(data: Arc<ColourData>, y: u32, factoriser: FactoriserFunction, palette_function: PaletteFunction) {
let (iter_count_buffer, square_dist_buffer, image) = unsafe { data.slice(y) };
for x in 0x0..data.canvas_width {
@@ -37,50 +37,25 @@ impl App {
let iter_count = unsafe { *iter_count_buffer.get_unchecked( x) };
let distance = unsafe { *square_dist_buffer.get_unchecked(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)
+ let factor = if iter_count < data.max_iter_count {
+ factoriser(iter_count, distance, data.colour_range, data.exponent)
} else {
- (0.0, 0.0, 0.0)
+ f32::NAN
};
+ let (red, green, blue) = palette_function(factor);
+
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;
+ let x = x * 0x3;
+
+ *image.get_unchecked_mut(x) = red;
+ *image.get_unchecked_mut(x + 0x1) = green;
+ *image.get_unchecked_mut(x + 0x2) = blue;
}
}
}
}
-
-fn hsv_to_rgb(hue: f32, saturation: f32, value: f32) -> (f32, f32, f32) {
- return if saturation <= 0.0 {
- let value = value.min(1.0);
-
- (value, value, value)
- } else {
- let h = hue % 1.0 * 6.0;
- let s = saturation.min(1.0);
- let v = value.min(1.0);
-
- let f = h % 1.0;
- let p = v * (1.0 - s);
- let q = v * (1.0 - s * f);
- 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),
- _ => unsafe { unreachable_unchecked() },
- }
- };
-}
diff --git a/source/benoit/benoit/app/dump.rs b/source/benoit/benoit/app/dump.rs
index 094650a..a0fb3c5 100644
--- a/source/benoit/benoit/app/dump.rs
+++ b/source/benoit/benoit/app/dump.rs
@@ -31,19 +31,19 @@ use std::fs::{File, write};
use std::io::BufWriter;
impl App {
- pub fn dump(&self, path: String, image: &[u8], canvas_width: u32) {
+ pub fn dump(&self, path: &str, image: &[u8], canvas_width: u32, canvas_height: u32) {
match self.image_format {
- ImageFormat::Png => dump_png( &path, image, canvas_width),
- ImageFormat::Webp => dump_webp(&path, image, canvas_width),
+ ImageFormat::Png => dump_png( path, image, canvas_width, canvas_height),
+ ImageFormat::Webp => dump_webp(path, image, canvas_width, canvas_height),
}
}
}
-fn dump_png(path: &String, image: &[u8], canvas_width: u32) {
+fn dump_png(path: &str, image: &[u8], canvas_width: u32, canvas_height: u32) {
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_width);
+ 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);
@@ -53,8 +53,8 @@ fn dump_png(path: &String, image: &[u8], canvas_width: u32) {
writer.write_image_data(image).expect("unable to write image");
}
-fn dump_webp(path: &String, image: &[u8], canvas_width: u32) {
- let encoder = webp::Encoder::from_rgb(&image[..], canvas_width, canvas_width);
+fn dump_webp(path: &str, image: &[u8], canvas_width: u32, canvas_height: u32) {
+ let encoder = webp::Encoder::from_rgb(&image[..], canvas_width, canvas_height);
let data = encoder.encode_lossless();
diff --git a/source/benoit/benoit/app/handle_keys.rs b/source/benoit/benoit/app/handle_keys.rs
index 50956f8..da88bfc 100644
--- a/source/benoit/benoit/app/handle_keys.rs
+++ b/source/benoit/benoit/app/handle_keys.rs
@@ -22,9 +22,12 @@
*/
use crate::benoit::PRECISION;
-use crate::benoit::app::{App, RowRenderer};
+use crate::benoit::app::App;
+use crate::benoit::factorisation::Factorisation;
use crate::benoit::fractal::Fractal;
-use crate::benoit::iteration::IteratorFunction;
+use crate::benoit::palette::Palette;
+use crate::benoit::render::{FactoriserFunction, IteratorFunction, PaletteFunction, RowRenderer};
+use crate::benoit::rendering::Rendering;
extern crate rug;
extern crate sdl2;
@@ -35,12 +38,16 @@ use sdl2::keyboard::Scancode;
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),
- Scancode::Escape => return true,
Scancode::C => self.do_render = true,
- Scancode::Tab => (self.julia, self.row_renderer) = toggle_julia(self.julia),
- Scancode::X => self.do_dump = true,
- Scancode::Z => eprintln!("c = {}{:+}i -- {}x @ {} iter. (range: {:.3})", &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count, self.colour_range),
+ Scancode::Escape => return true,
+ Scancode::F1 => self.do_textual_feedback = !self.do_textual_feedback,
+ Scancode::LAlt => (self.fractal, self.multibrot_exponent, self.iterator_function) = cycle_fractal(self.fractal, -0x1),
+ Scancode::LCtrl => (self.factorisation, self.factoriser) = toggle_smooth(self.factorisation),
+ Scancode::Left => (self.palette, self.palette_function) = cycle_palette(self.palette, -0x1),
+ Scancode::RAlt => (self.fractal, self.multibrot_exponent, self.iterator_function) = cycle_fractal(self.fractal, 0x1),
+ Scancode::Right => (self.palette, self.palette_function) = cycle_palette(self.palette, 0x1),
+ Scancode::Tab => (self.rendering, self.row_renderer) = toggle_julia(self.rendering),
+ Scancode::Z => eprintln!("c = {}{:+}i, {}x @ {} iter. (range.: {:.3})", &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count, self.colour_range),
_ => {},
}
@@ -48,7 +55,7 @@ impl App {
self.max_iter_count = match scan_code {
Scancode::F => self.max_iter_count * 0x2,
- Scancode::R => self.max_iter_count / 0x2,
+ Scancode::R => (self.max_iter_count / 0x2).max(0x1),
_ => self.max_iter_count,
};
@@ -56,7 +63,7 @@ impl App {
self.colour_range = match scan_code {
Scancode::Up => self.colour_range * COLOUR_RANGE_FACTOR,
- Scancode::Down => self.colour_range / COLOUR_RANGE_FACTOR,
+ Scancode::Down => (self.colour_range / COLOUR_RANGE_FACTOR).max(2.0),
_ => self.colour_range,
};
@@ -94,29 +101,48 @@ impl App {
}
}
-fn cycle_fractal(fractal: Fractal) -> (Fractal, IteratorFunction) {
- let fractal = match fractal {
- Fractal::BurningShip => Fractal::Mandelbrot,
- Fractal::Mandelbrot => Fractal::Tricorn,
- Fractal::Tricorn => Fractal::BurningShip,
- };
+fn cycle_fractal(fractal: Fractal, distance: i8) -> (Fractal, f32, IteratorFunction) {
+ let fractal = fractal + distance;
+ let exponent = fractal.get_exponent();
- let iterator_function = App::get_iterator_function(fractal);
+ let iterator_function = fractal.get_iterator();
eprintln!("rendering the {}", fractal.get_name());
- return (fractal, iterator_function);
+ return (fractal, exponent, iterator_function);
}
-fn toggle_julia(julia: bool) -> (bool, RowRenderer) {
- let julia = !julia;
+fn toggle_julia(rendering: Rendering) -> (Rendering, RowRenderer) {
+ let rendering = rendering.cycle();
- let row_renderer = App::get_row_renderer(julia);
+ let row_renderer = rendering.get_row_renderer();
- match julia {
- false => eprintln!("disabled the julia set"),
- true => eprintln!("enabled the julia set"),
+ match rendering {
+ Rendering::Julia => eprintln!("enabled the julia set"),
+ Rendering::Normal => eprintln!("disabled the julia set"),
};
- return (julia, row_renderer);
+ return (rendering, row_renderer);
+}
+
+fn toggle_smooth(factorisation: Factorisation) -> (Factorisation, FactoriserFunction) {
+ let factorisation = factorisation.cycle();
+
+ let factoriser = factorisation.get_factoriser();
+
+ match factorisation {
+ Factorisation::Smooth => eprintln!("disabled smoothing"),
+ Factorisation::Stepped => eprintln!("enabled smoothing"),
+ };
+
+ return (factorisation, factoriser);
+}
+
+fn cycle_palette(palette: Palette, direction: i8) -> (Palette, PaletteFunction) {
+ let palette = palette + direction;
+ let palette_function = palette.get_function();
+
+ eprintln!("using palette \"{}\"", palette.get_name());
+
+ return (palette, palette_function);
}
diff --git a/source/benoit/benoit/app/initialise.rs b/source/benoit/benoit/app/initialise.rs
index a357940..6a4d385 100644
--- a/source/benoit/benoit/app/initialise.rs
+++ b/source/benoit/benoit/app/initialise.rs
@@ -52,42 +52,49 @@ impl App {
eprintln!("using {thread_count} threads");
let video = match configuration.interactive {
- true => Some(Video::initialise(configuration.canvas_width, configuration.scale)),
+ 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();
- eprintln!("rendering the {}", configuration.fractal.get_name());
-
return App {
thread_count: thread_count,
- fractal: configuration.fractal,
- julia: configuration.julia,
+ fractal: configuration.fractal,
+ rendering: configuration.rendering,
+
+ canvas_width: configuration.canvas_width,
+ canvas_height: configuration.canvas_height,
+ scale: configuration.scale,
+ frame_count: configuration.frame_count,
- canvas_width: configuration.canvas_width,
- scale: configuration.scale,
- frame_count: configuration.frame_count,
+ centre_real: configuration.centre_real,
+ centre_imag: configuration.centre_imag,
+ zoom: configuration.zoom,
+
+ multibrot_exponent: 2.0,
- centre_real: configuration.centre_real,
- centre_imag: configuration.centre_imag,
- zoom: configuration.zoom,
max_iter_count: configuration.max_iter_count,
- colour_range: configuration.colour_range,
+ factorisation: configuration.factorisation,
+ palette: configuration.palette,
+ colour_range: configuration.colour_range,
dump_path: configuration.dump_path,
image_format: configuration.image_format,
video: video,
- interactive: configuration.interactive,
- do_render: true,
- do_dump: false,
+ interactive: configuration.interactive,
+ do_render: true,
+ do_textual_feedback: false,
+
+ row_renderer: configuration.rendering.get_row_renderer(),
+ iterator_function: configuration.fractal.get_iterator(),
- row_renderer: App::get_row_renderer( configuration.julia),
- iterator_function: App::get_iterator_function(configuration.fractal),
+ factoriser: configuration.factorisation.get_factoriser(),
+ palette_function: configuration.palette.get_function(),
};
}
}
diff --git a/source/benoit/benoit/app/interactive.rs b/source/benoit/benoit/app/interactive.rs
new file mode 100644
index 0000000..f03576d
--- /dev/null
+++ b/source/benoit/benoit/app/interactive.rs
@@ -0,0 +1,152 @@
+/*
+ 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;
+use crate::benoit::rendering::Rendering;
+use crate::benoit::video::Video;
+
+extern crate rug;
+
+use rug::{Assign, Float};
+use std::time::Instant;
+
+impl App {
+ 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.multibrot_exponent;
+
+ // Used for translation feedback:
+ 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();
+
+ 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!(" {:.3}ms", render_time.as_micros() as f32 / 1000.0);
+
+ prev_max_iter_count = self.max_iter_count;
+ prev_multibrot_exponent = self.multibrot_exponent;
+
+ prev_centre_real.assign(&self.centre_real);
+ prev_centre_imag.assign(&self.centre_imag);
+ prev_zoom.assign(&self.zoom);
+
+ self.do_render = false;
+ }
+
+ self.colour(&mut image[..], prev_multibrot_exponent, prev_max_iter_count.min(self.max_iter_count), &mut iter_count_buffer[..], &mut square_dist_buffer[..]);
+
+ video.draw(&image[..], self.canvas_width, self.canvas_height, self.scale);
+ self.draw_feedback(&mut video, &prev_centre_real, &prev_centre_imag, &prev_zoom);
+
+ video.update();
+
+ video.sync(&frame_start);
+ }
+
+ return 0x0;
+ }
+
+ pub fn draw_feedback(&self, video: &mut Video, prev_centre_real: &Float, prev_centre_imag: &Float, prev_zoom: &Float) {
+ let julia = match self.rendering {
+ Rendering::Julia => true,
+ _ => false,
+ };
+
+ if {
+ // Don't draw translation feedback if rendering a
+ // Julia set or if we haven't done any viewport
+ // translations.
+
+ !julia
+ && (&self.centre_real != prev_centre_real
+ || &self.centre_imag != prev_centre_imag
+ || &self.zoom != prev_zoom)
+ }{
+ 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,
+ };
+
+ video.draw_translation_feedback(self.canvas_width, self.canvas_height, self.scale, &feedback_info);
+ }
+
+ if self.do_textual_feedback { video.draw_textual_feedback(&self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count) };
+ }
+
+ pub fn print_controls() {
+ println!("Controls:");
+ println!("- \u{1B}[1mW\u{1B}[0m Translate up");
+ println!("- \u{1B}[1mA\u{1B}[0m Translate left");
+ println!("- \u{1B}[1mS\u{1B}[0m Translate down");
+ println!("- \u{1B}[1mD\u{1B}[0m Translate right");
+ 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}[1mTab\u{1B}[0m Toggle Julia");
+ 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!();
+ println!("- \u{1B}[1mUp\u{1B}[0m Increase colour range");
+ println!("- \u{1B}[1mDown\u{1B}[0m Decrease colour range");
+ println!();
+ println!("- \u{1B}[1mLeft\u{1B}[0m Cycle to previous palette");
+ println!("- \u{1B}[1mRight\u{1B}[0m Cycle to next palette");
+ println!();
+ println!("- \u{1B}[1mF1\u{1B}[0m Toggle textual feedback");
+ println!("- \u{1B}[1mC\u{1B}[0m Print centre value (c)");
+ println!();
+ println!("- \u{1B}[1mSpace\u{1B}[0m Render frame");
+ println!();
+ }
+} \ No newline at end of file
diff --git a/source/benoit/benoit/app/loop.rs b/source/benoit/benoit/app/loop.rs
deleted file mode 100644
index 69eb3d4..0000000
--- a/source/benoit/benoit/app/loop.rs
+++ /dev/null
@@ -1,140 +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::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!(" {:.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.min(self.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,
- };
-
- let feedback_info = if {
- // Don't draw feedback if rendering a Julia set or
- // if we haven't done any viewport translations.
-
- !self.julia
- && (self.centre_real != prev_centre_real
- || self.centre_imag != prev_centre_imag
- || self.zoom != prev_zoom)
- } {
- Some(&feedback_info)
- } else {
- None
- };
-
- unsafe { self.video.as_mut().unwrap_unchecked().draw(&image[..], self.canvas_width, self.scale, feedback_info) };
- }
-
- if self.do_dump {
- let path = App::image_filename(format!("{}/image", self.dump_path).as_str(), self.image_format);
-
- 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/app/poll_events.rs b/source/benoit/benoit/app/poll_events.rs
index 2df0c96..f5e38b6 100644
--- a/source/benoit/benoit/app/poll_events.rs
+++ b/source/benoit/benoit/app/poll_events.rs
@@ -44,7 +44,7 @@ impl App {
_ => false,
};
- if quit { return true }
+ if quit { return true };
}
return false;
diff --git a/source/benoit/benoit/app/render.rs b/source/benoit/benoit/app/render.rs
index aa5ff9b..6e4a382 100644
--- a/source/benoit/benoit/app/render.rs
+++ b/source/benoit/benoit/app/render.rs
@@ -22,7 +22,7 @@
*/
use crate::benoit::app::App;
-use crate::benoit::task::render_data::RenderData;
+use crate::benoit::render::render_data::RenderData;
extern crate rayon;
extern crate rug;
@@ -36,9 +36,9 @@ impl App {
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));
+ let data = Arc::new(RenderData::new(iter_count_buffer, square_dist_buffer, self.canvas_width, self.canvas_height, centre_real.clone(), centre_imag.clone(), zoom.clone(), max_iter_count));
- (0x0..self.canvas_width).into_par_iter().for_each(|row| {
+ (0x0..self.canvas_height).into_par_iter().for_each(|row| {
row_renderer(data.clone(), row as u32, iterator);
});
}
diff --git a/source/benoit/benoit/app/render_row_julia.rs b/source/benoit/benoit/app/render_row_julia.rs
deleted file mode 100644
index 2710423..0000000
--- a/source/benoit/benoit/app/render_row_julia.rs
+++ /dev/null
@@ -1,100 +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::app::App;
-use crate::benoit::iteration::IteratorFunction;
-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 App {
- pub fn render_row_julia(data: Arc<RenderData>, y: u32, iterator: IteratorFunction) {
- let (iter_count_buffer, square_dist_buffer) = unsafe { data.slice(y) };
-
- for x in 0x0..data.canvas_width {
- let canvas_width = Float::with_val(PRECISION, data.canvas_width);
-
- // For more information, see render_row_normal.
-
- let ca = &data.centre_real;
- let cb = &data.centre_imag;
-
- // When rendering the Julia fractals, the value of
- // (c) remains constant throughout the entire
- // canvas. The value of (z) - however - takes the
- // position-determined value that (c) would've had.
-
- let mut za = {
- let mut za = Float::with_val(PRECISION, &canvas_width / 2.0);
- za.neg_assign();
- za += x;
- za *= 4.0;
- za /= &canvas_width;
-
- za
- };
-
- let mut zb = {
- let mut zb = Float::with_val(PRECISION, &canvas_width / 2.0);
- zb.neg_assign();
- zb += y;
- zb *= 4.0;
- zb /= &canvas_width;
-
- zb
- };
-
- 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 square_dist;
- while {
- square_dist = Float::with_val(PRECISION, &za * &za + &zb * &zb).to_f32();
-
- let periodic = za == za_prev && zb == zb_prev;
-
- if periodic { iter_count = data.max_iter_count }
- square_dist <= 256.0 && iter_count < data.max_iter_count
- } {
- za_prev.assign(&za);
- zb_prev.assign(&zb);
-
- iterator(&mut za, &mut zb, ca, cb);
-
- iter_count += 0x1;
- }
-
- unsafe {
- *iter_count_buffer.get_unchecked_mut( x as usize) = iter_count;
- *square_dist_buffer.get_unchecked_mut(x as usize) = square_dist;
- }
- }
- }
-}
diff --git a/source/benoit/benoit/app/render_row_normal.rs b/source/benoit/benoit/app/render_row_normal.rs
deleted file mode 100644
index 832d178..0000000
--- a/source/benoit/benoit/app/render_row_normal.rs
+++ /dev/null
@@ -1,101 +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::app::App;
-use crate::benoit::iteration::IteratorFunction;
-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 App {
- pub fn render_row_normal(data: Arc<RenderData>, y: u32, iterator: IteratorFunction) {
- let (iter_count_buffer, square_dist_buffer) = unsafe { data.slice(y) };
-
- for x in 0x0..data.canvas_width {
- let canvas_width = Float::with_val(PRECISION, data.canvas_width);
-
- let ca = {
- let mut ca = Float::with_val(PRECISION, &canvas_width / 2.0);
- ca.neg_assign();
- ca += x;
- ca *= 4.0;
- ca /= &canvas_width;
- ca /= &data.zoom;
- ca += &data.centre_real;
-
- ca
- };
-
- let cb = {
- let mut cb = Float::with_val(PRECISION, &canvas_width / 2.0);
- cb.neg_assign();
- cb += y;
- cb *= 4.0;
- cb /= &canvas_width;
- cb /= &data.zoom;
- cb -= &data.centre_imag;
-
- cb
- };
-
- let mut za = ca.clone();
- let mut zb = cb.clone();
-
- 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 = 0x1;
- let mut square_dist;
- while {
- square_dist = Float::with_val(PRECISION, &za * &za + &zb * &zb).to_f32();
- // Having a larger escape radius gives better
- // results with regard to smoothing.
-
- // Check if the value is periodic, i.e. its
- // sequence repeats.
- let periodic = za == za_prev && zb == zb_prev;
-
- if periodic { iter_count = data.max_iter_count }
- square_dist <= 256.0 && iter_count < data.max_iter_count
- } {
- za_prev.assign(&za);
- zb_prev.assign(&zb);
-
- iterator(&mut za, &mut zb, &ca, &cb);
-
- iter_count += 0x1;
- }
-
- unsafe {
- *iter_count_buffer.get_unchecked_mut( x as usize) = iter_count;
- *square_dist_buffer.get_unchecked_mut(x as usize) = square_dist;
- }
- }
- }
-}
diff --git a/source/benoit/benoit/app/run.rs b/source/benoit/benoit/app/run.rs
index 3524cad..9211716 100644
--- a/source/benoit/benoit/app/run.rs
+++ b/source/benoit/benoit/app/run.rs
@@ -29,13 +29,18 @@ extern crate sdl2;
impl App {
pub fn run(&mut self) -> i32 {
println!();
- println!("Benoit {:X}.{:X}.{:X}", VERSION.major, VERSION.minor, VERSION.patch);
- println!("Copyright 2021, 2023 Gabriel Bjørnager Jensen.");
+ println!("\u{1B}[1mBENO\u{CE}T\u{1B}[0m {:X}.{:X}.{:X}", VERSION.major, VERSION.minor, VERSION.patch);
+ println!("Copyright 2021, 2023 Gabriel Bj\u{F8}rnager Jensen.");
+ println!();
+ println!("COCITAVIT\u{B7}ERCO\u{B7}FVIT");
println!();
return match self.interactive {
- true => self.r#loop(),
- false => self.animate(),
+ true => self.interactive(),
+ false => match self.frame_count {
+ 0x1 => self.still(),
+ _ => self.animate(),
+ },
};
}
}
diff --git a/source/benoit/benoit/app/still.rs b/source/benoit/benoit/app/still.rs
new file mode 100644
index 0000000..bd5ea9e
--- /dev/null
+++ b/source/benoit/benoit/app/still.rs
@@ -0,0 +1,56 @@
+/*
+ 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;
+
+extern crate rug;
+
+use std::time::Instant;
+
+impl App {
+ pub fn still(&self) -> i32 {
+ assert_eq!(self.frame_count, 0x1);
+
+ let (mut iter_count_buffer, mut square_dist_buffer, mut image) = App::allocate_buffers(self.canvas_width, self.canvas_height);
+
+ 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();
+
+ eprint!(" {:.3}ms, colouring...", render_time.as_micros() as f32 / 1000.0);
+
+ self.colour(&mut image[..], self.multibrot_exponent, self.max_iter_count, &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 = App::image_filename(format!("{}/render.webp", self.dump_path).as_str(), self.image_format);
+ self.dump(path.as_str(), &image, self.canvas_width, self.canvas_height);
+
+ eprintln!(" done");
+
+ return 0x0;
+ }
+}
diff --git a/source/benoit/benoit/configuration.rs b/source/benoit/benoit/configuration.rs
index 6e22f01..828ccae 100644
--- a/source/benoit/benoit/configuration.rs
+++ b/source/benoit/benoit/configuration.rs
@@ -22,7 +22,10 @@
*/
use crate::benoit::ImageFormat;
+use crate::benoit::factorisation::Factorisation;
use crate::benoit::fractal::Fractal;
+use crate::benoit::palette::Palette;
+use crate::benoit::rendering::Rendering;
extern crate rug;
@@ -34,19 +37,23 @@ pub mod load;
pub struct Configuration {
pub thread_count: u32,
- pub fractal: Fractal,
- pub julia: bool,
+ pub fractal: Fractal,
+ pub rendering: Rendering,
- pub canvas_width: u32,
- pub scale: u32,
- pub frame_count: u32,
+ pub canvas_width: u32,
+ pub canvas_height: u32,
+ pub scale: u32,
+ pub frame_count: u32,
+
+ pub centre_real: Float,
+ pub centre_imag: Float,
+ pub zoom: Float,
- pub centre_real: Float,
- pub centre_imag: Float,
- pub zoom: Float,
pub max_iter_count: u32,
- pub colour_range: f32,
+ pub factorisation: Factorisation,
+ pub palette: Palette,
+ pub colour_range: f32,
pub dump_path: String,
pub image_format: ImageFormat,
diff --git a/source/benoit/benoit/configuration/default.rs b/source/benoit/benoit/configuration/default.rs
index 2a0ae9f..f7f30d7 100644
--- a/source/benoit/benoit/configuration/default.rs
+++ b/source/benoit/benoit/configuration/default.rs
@@ -22,8 +22,11 @@
*/
use crate::benoit::{ImageFormat, PRECISION};
-use crate::benoit::configuration::Configuration;
+use crate::benoit::factorisation::Factorisation;
use crate::benoit::fractal::Fractal;
+use crate::benoit::palette::Palette;
+use crate::benoit::rendering::Rendering;
+use crate::benoit::configuration::Configuration;
extern crate rug;
@@ -34,21 +37,25 @@ impl Configuration {
return Configuration {
thread_count: 0x0,
- fractal: Fractal::Mandelbrot,
- julia: false,
+ fractal: Fractal::Mandelbrot,
+ rendering: Rendering::Normal,
+
+ canvas_width: 0x100,
+ canvas_height: 0xC0,
+ scale: 0x2,
+ frame_count: 0x10,
- canvas_width: 0x100,
- scale: 0x2,
- frame_count: 0x10,
+ centre_real: Float::with_val(PRECISION, 0.0),
+ centre_imag: Float::with_val(PRECISION, 0.0),
+ zoom: Float::with_val(PRECISION, 1.0),
- centre_real: Float::with_val(PRECISION, 0.0),
- centre_imag: Float::with_val(PRECISION, 0.0),
- zoom: Float::with_val(PRECISION, 1.0),
max_iter_count: 0x100,
- colour_range: 64.0,
+ factorisation: Factorisation::Smooth,
+ palette: Palette::Fire,
+ colour_range: 64.0,
- dump_path: "./render/".to_string(),
+ 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 cd4b812..0b4b20f 100644
--- a/source/benoit/benoit/configuration/load.rs
+++ b/source/benoit/benoit/configuration/load.rs
@@ -22,8 +22,11 @@
*/
use crate::benoit::{ImageFormat, PRECISION};
-use crate::benoit::configuration::Configuration;
+use crate::benoit::factorisation::Factorisation;
use crate::benoit::fractal::Fractal;
+use crate::benoit::palette::Palette;
+use crate::benoit::rendering::Rendering;
+use crate::benoit::configuration::Configuration;
extern crate rug;
extern crate toml;
@@ -48,78 +51,57 @@ impl Configuration {
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 }
-
- match &configuration_table[name] {
- Value::Integer(value) => *buffer = *value as u32,
- _ => panic!("mismatched type of {name}"),
- };
- };
-
- let get_float32 = |buffer: &mut f32, table: &Table, name: &str| {
- if !table.contains_key(name) { return }
-
- match &configuration_table[name] {
- Value::Float(value) => *buffer = *value as f32,
- _ => panic!("mismatched type of {name}"),
- };
- };
-
- let get_float = |buffer: &mut Float, table: &Table, name: &str| {
- if !table.contains_key(name) { return }
-
- match &configuration_table[name] {
- Value::String(string) => {
- *buffer = match Float::parse(string) {
- Ok(value) => Float::with_val(PRECISION, value),
- _ => panic!("invalid format of {name}"),
- }
- },
- _ => panic!("mismatched type of {name}"),
- };
- };
-
- let get_string = |table: &Table, name: &str| -> Option<&String> {
- if !table.contains_key(name) { return None }
-
- match &configuration_table[name] {
- Value::String(value) => return Some(value),
- _ => panic!("mismatched type of {name}"),
- };
- };
-
get_integer(&mut configuration.thread_count, &configuration_table, "thread_count");
if let Some(name) = get_string(&configuration_table, "fractal") {
configuration.fractal = match name.as_str() {
"burningship" => Fractal::BurningShip,
"mandelbrot" => Fractal::Mandelbrot,
+ "multibrot3" => Fractal::Multibrot3,
"tricorn" => Fractal::Tricorn,
- name => panic!("invalid fractal name \"{name}\""),
+ name => panic!("invalid fractal kind \"{name}\""),
}
}
- get_boolean(&mut configuration.julia, &configuration_table, "julia");
+ if let Some(name) = get_string(&configuration_table, "rendering") {
+ configuration.rendering = match name.as_str() {
+ "julia" => Rendering::Julia,
+ "normal" => Rendering::Normal,
+ name => panic!("invalid rendering method \"{name}\""),
+ };
+ }
+
+ get_integer(&mut configuration.canvas_width, &configuration_table, "canvas_width");
+ get_integer(&mut configuration.canvas_height, &configuration_table, "canvas_height");
+ get_integer(&mut configuration.frame_count, &configuration_table, "frame_count");
- get_integer(&mut configuration.canvas_width, &configuration_table, "canvas_width");
- get_integer(&mut configuration.frame_count, &configuration_table, "frame_count");
+ get_bigfloat(&mut configuration.centre_real, &configuration_table, "real");
+ get_bigfloat(&mut configuration.centre_imag, &configuration_table, "imaginary");
+ get_bigfloat(&mut configuration.zoom, &configuration_table, "zoom");
- get_float( &mut configuration.centre_real, &configuration_table, "real");
- get_float( &mut configuration.centre_imag, &configuration_table, "imaginary");
- get_float( &mut configuration.zoom, &configuration_table, "zoom");
get_integer(&mut configuration.max_iter_count, &configuration_table, "maximum_iteration_count");
- get_float32(&mut configuration.colour_range, &configuration_table, "colour_range");
+ if let Some(name) = get_string(&configuration_table, "factorisation") {
+ configuration.factorisation = match name.as_str() {
+ "smooth" => Factorisation::Smooth,
+ "stepped" => Factorisation::Stepped,
+ name => panic!("invalid factorisation method \"{name}\""),
+ };
+ }
+
+ if let Some(name) = get_string(&configuration_table, "palette") {
+ configuration.palette = match name.as_str() {
+ "ancient" => Palette::Ancient,
+ "fire" => Palette::Fire,
+ "greyscale" => Palette::Greyscale,
+ "hsv" => Palette::Hsv,
+ "lch" => Palette::Lch,
+ "sapphire" => Palette::Sapphire,
+ name => panic!("invalid palette \"{name}\""),
+ };
+ }
+
+ get_float(&mut configuration.colour_range, &configuration_table, "colour_range");
if let Some(path) = get_string(&configuration_table, "dump_path") {
configuration.dump_path = path.clone();
@@ -130,7 +112,7 @@ impl Configuration {
"png" => ImageFormat::Png,
"webp" => ImageFormat::Webp,
name => panic!("invalid image format \"{name}\""),
- }
+ };
}
match check_configuration(&configuration) {
@@ -157,3 +139,46 @@ fn check_configuration(configuration: &Configuration) -> Result<(), &str> {
return Ok(());
}
+
+fn get_value<'a>(table: &'a Table, name: &str) -> Option<&'a Value> {
+ if !table.contains_key(name) { return None };
+
+ return Some(&table[name]);
+}
+
+fn get_integer(buffer: &mut u32, table: &Table, name: &str) {
+ match get_value(table, name) {
+ Some(Value::Integer(value)) => *buffer = (*value) as u32,
+ Some(_) => panic!("\"{name}\" should be an integer"),
+ _ => {},
+ };
+}
+
+fn get_float(buffer: &mut f32, table: &Table, name: &str) {
+ match get_value(table, name) {
+ Some(Value::Float(value)) => *buffer = (*value) as f32,
+ Some(_) => panic!("\"{name}\" should be a float"),
+ _ => {},
+ };
+}
+
+fn get_bigfloat(buffer: &mut Float, table: &Table, name: &str) {
+ return match get_value(table, name) {
+ Some(Value::String(string)) => {
+ *buffer = match Float::parse(string) {
+ Ok(value) => Float::with_val(PRECISION, value),
+ _ => panic!("invalid format of \"{name}\""),
+ }
+ },
+ Some(_) => panic!("\"{name}“ should be a quoted float"),
+ _ => {},
+ };
+}
+
+fn get_string(table: &Table, name: &str) -> Option<String> {
+ return match get_value(table, name) {
+ Some(Value::String(value)) => Some(value.clone()),
+ Some(_) => panic!("\"{name}\" should be a string"),
+ _ => None,
+ };
+}
diff --git a/source/benoit/benoit/factorisation.rs b/source/benoit/benoit/factorisation.rs
new file mode 100644
index 0000000..fc0f7ba
--- /dev/null
+++ b/source/benoit/benoit/factorisation.rs
@@ -0,0 +1,50 @@
+/*
+ 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::render::FactoriserFunction;
+use crate::benoit::render::factorise;
+
+use std::mem::transmute;
+
+#[derive(Clone, Copy)]
+pub enum Factorisation {
+ Smooth,
+ Stepped,
+}
+
+impl Factorisation {
+ pub fn get_factoriser(self) -> FactoriserFunction {
+ return match self {
+ Factorisation::Smooth => factorise::smooth,
+ Factorisation::Stepped => factorise::stepped,
+ };
+ }
+
+ pub fn cycle(self) -> Self {
+ let raw = !(self as u8 != 0x0);
+
+ let new: Self = unsafe { transmute(raw) };
+
+ return new;
+ }
+}
diff --git a/source/benoit/benoit/fractal.rs b/source/benoit/benoit/fractal.rs
index 1c7c08d..5cefcfe 100644
--- a/source/benoit/benoit/fractal.rs
+++ b/source/benoit/benoit/fractal.rs
@@ -21,11 +21,69 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-pub mod get_name;
+use crate::benoit::render::IteratorFunction;
+use crate::benoit::render::iterate;
+
+use std::mem::transmute;
+use std::ops::Add;
#[derive(Clone, Copy)]
pub enum Fractal {
BurningShip,
Mandelbrot,
+ Multibrot3,
Tricorn,
}
+
+impl Fractal {
+ const MAX: u8 = Fractal::Tricorn as u8;
+
+ pub fn get_name(self) -> &'static str {
+ return match self {
+ Fractal::BurningShip => "burning ship",
+ Fractal::Mandelbrot => "mandelbrot set",
+ Fractal::Multibrot3 => "multibrot (d=3)",
+ Fractal::Tricorn => "tricorn",
+ };
+ }
+
+ pub fn get_exponent(self) -> f32 {
+ return match self {
+ Fractal::BurningShip => 2.0,
+ Fractal::Mandelbrot => 2.0,
+ Fractal::Multibrot3 => 3.0,
+ Fractal::Tricorn => 2.0,
+ };
+ }
+
+ pub fn get_iterator(self) -> IteratorFunction {
+ return match self {
+ Fractal::BurningShip => iterate::burning_ship,
+ Fractal::Mandelbrot => iterate::mandelbrot,
+ Fractal::Multibrot3 => iterate::multibrot3,
+ Fractal::Tricorn => iterate::tricorn,
+ };
+ }
+
+}
+
+impl Add<i8> for Fractal {
+ type Output = Fractal;
+
+ fn add(self, direction: i8) -> Self {
+ assert!(direction != 0x0);
+
+ let raw = self as i8 + direction;
+ let raw: u8 = if raw < 0x0 {
+ Fractal::MAX
+ } else if raw > Fractal::MAX as i8 {
+ 0x0
+ } else {
+ raw as u8
+ };
+
+ let new: Self = unsafe { transmute(raw) };
+
+ return new;
+ }
+}
diff --git a/source/benoit/benoit/palette.rs b/source/benoit/benoit/palette.rs
new file mode 100644
index 0000000..628c5f4
--- /dev/null
+++ b/source/benoit/benoit/palette.rs
@@ -0,0 +1,85 @@
+/*
+ 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::render::PaletteFunction;
+use crate::benoit::render::paint;
+
+use std::mem::transmute;
+use std::ops::Add;
+
+#[derive(Clone, Copy)]
+pub enum Palette {
+ Ancient,
+ Fire,
+ Greyscale,
+ Hsv,
+ Lch,
+ Sapphire,
+}
+
+impl Palette {
+ const MAX: u8 = Palette::Sapphire as u8;
+
+ pub fn get_name(self) -> &'static str {
+ return match self {
+ Palette::Ancient => "ancient",
+ Palette::Fire => "fire",
+ Palette::Greyscale => "greyscale",
+ Palette::Hsv => "hsv",
+ Palette::Lch => "lch",
+ Palette::Sapphire => "sapphire",
+ };
+ }
+
+ pub fn get_function(self) -> PaletteFunction {
+ return match self {
+ Palette::Ancient => paint::ancient,
+ Palette::Fire => paint::fire,
+ Palette::Greyscale => paint::greyscale,
+ Palette::Hsv => paint::hsv,
+ Palette::Lch => paint::lch,
+ Palette::Sapphire => paint::sapphire,
+ };
+ }
+}
+
+impl Add<i8> for Palette {
+ type Output = Palette;
+
+ fn add(self, direction: i8) -> Self {
+ assert!(direction != 0x0);
+
+ let raw = self as i8 + direction;
+ let raw: u8 = if raw < 0x0 {
+ Palette::MAX
+ } else if raw > Palette::MAX as i8 {
+ 0x0
+ } else {
+ raw as u8
+ };
+
+ let new: Self = unsafe { transmute(raw) };
+
+ return new;
+ }
+}
diff --git a/source/benoit/benoit/iteration.rs b/source/benoit/benoit/render.rs
index 6c81b80..ddea2f6 100644
--- a/source/benoit/benoit/iteration.rs
+++ b/source/benoit/benoit/render.rs
@@ -21,16 +21,27 @@
If not, see <https://www.gnu.org/licenses/>.
*/
+use crate::benoit::render::render_data::RenderData;
+
extern crate rug;
use rug::Float;
+use std::sync::Arc;
-pub mod iterate_burning_ship;
-pub mod iterate_mandelbrot;
-pub mod iterate_tricorn;
-
-pub use iterate_burning_ship::iterate_burning_ship;
-pub use iterate_mandelbrot::iterate_mandelbrot;
-pub use iterate_tricorn::iterate_tricorn;
+pub mod colour_data;
+pub mod factorise;
+pub mod iterate;
+pub mod paint;
+pub mod render_data;
+pub mod render_row;
pub type IteratorFunction = fn(&mut Float, &mut Float, &Float, &Float);
+
+pub type RowRenderer = fn(Arc<RenderData>, u32, IteratorFunction);
+
+// We pass the multibrot exponent to the factoriser
+// as it is important with regard to smoothing, as
+// the distance grows according to this exponent.
+pub type FactoriserFunction = fn(u32, f32, f32, f32) -> f32;
+
+pub type PaletteFunction = fn(f32) -> (f32, f32, f32);
diff --git a/source/benoit/benoit/task/colour_data/slice.rs b/source/benoit/benoit/render/colour_data.rs
index 975eda2..1d8e278 100644
--- a/source/benoit/benoit/task/colour_data/slice.rs
+++ b/source/benoit/benoit/render/colour_data.rs
@@ -21,11 +21,37 @@
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};
+pub struct ColourData {
+ pub canvas_width: u32,
+
+ pub exponent: f32,
+ pub max_iter_count: u32,
+ pub colour_range: f32,
+
+ iter_count_buffer: *const u32,
+ square_dist_buffer: *const f32,
+
+ image: *mut u8,
+}
+
impl ColourData {
+ pub fn new(image: &mut [u8], canvas_width: u32, exponent: f32, max_iter_count: u32, colour_range: f32, iter_count_buffer: &[u32], square_dist_buffer: &[f32]) -> ColourData {
+ return ColourData {
+ canvas_width: canvas_width,
+
+ exponent: exponent,
+ 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(),
+ };
+ }
+
pub unsafe fn slice(&self, row: u32) -> (&[u32], &[f32], &mut [u8]) {
let offset = row as isize * self.canvas_width as isize;
@@ -39,3 +65,6 @@ impl ColourData {
return (iter_count, dist, image);
}
}
+
+unsafe impl Send for ColourData {}
+unsafe impl Sync for ColourData {}
diff --git a/source/benoit/benoit/render/factorise.rs b/source/benoit/benoit/render/factorise.rs
new file mode 100644
index 0000000..ab2f75e
--- /dev/null
+++ b/source/benoit/benoit/render/factorise.rs
@@ -0,0 +1,28 @@
+/*
+ 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 smooth;
+pub mod stepped;
+
+pub use smooth::*;
+pub use stepped::*;
diff --git a/source/benoit/benoit/task/colour_data.rs b/source/benoit/benoit/render/factorise/smooth.rs
index 77d561d..b324af5 100644
--- a/source/benoit/benoit/task/colour_data.rs
+++ b/source/benoit/benoit/render/factorise/smooth.rs
@@ -21,20 +21,10 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-pub mod new;
-pub mod slice;
+// Classic colour palette, fixed from version ↋.
-pub struct ColourData {
- pub canvas_width: u32,
+pub fn smooth(iter_count: u32, distance: f32, colour_range: f32, exponent: f32) -> f32 {
+ let factor = (iter_count as f32 + 1.0 - distance.log(exponent).log(exponent)) / colour_range;
- pub max_iter_count: u32,
- pub colour_range: f32,
-
- iter_count_buffer: *const u32,
- square_dist_buffer: *const f32,
-
- image: *mut u8,
+ return factor;
}
-
-unsafe impl Send for ColourData {}
-unsafe impl Sync for ColourData {}
diff --git a/source/benoit/benoit/fractal/get_name.rs b/source/benoit/benoit/render/factorise/stepped.rs
index cdbadb1..df9ac2d 100644
--- a/source/benoit/benoit/fractal/get_name.rs
+++ b/source/benoit/benoit/render/factorise/stepped.rs
@@ -21,14 +21,11 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::fractal::Fractal;
+// Classic colour palette, fixed (and smoothed)
+// from version ↋.
-impl Fractal {
- pub fn get_name(self) -> &'static str {
- return match self {
- Fractal::BurningShip => "burning ship",
- Fractal::Mandelbrot => "mandelbrot set",
- Fractal::Tricorn => "tricorn",
- };
- }
+pub fn stepped(iter_count: u32, _distance: f32, colour_range: f32, _exponent: f32) -> f32 {
+ let factor = iter_count as f32 / colour_range;
+
+ return factor;
}
diff --git a/source/benoit/benoit/render/iterate.rs b/source/benoit/benoit/render/iterate.rs
new file mode 100644
index 0000000..723da26
--- /dev/null
+++ b/source/benoit/benoit/render/iterate.rs
@@ -0,0 +1,32 @@
+/*
+ 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 burning_ship;
+pub mod mandelbrot;
+pub mod multibrot3;
+pub mod tricorn;
+
+pub use burning_ship::*;
+pub use mandelbrot::*;
+pub use multibrot3::*;
+pub use tricorn::*;
diff --git a/source/benoit/benoit/iteration/iterate_burning_ship.rs b/source/benoit/benoit/render/iterate/burning_ship.rs
index 038367f..43e6916 100644
--- a/source/benoit/benoit/iteration/iterate_burning_ship.rs
+++ b/source/benoit/benoit/render/iterate/burning_ship.rs
@@ -25,7 +25,7 @@ extern crate rug;
use rug::Float;
-pub fn iterate_burning_ship(za: &mut Float, zb: &mut Float, ca: &Float, cb: &Float) {
+pub fn burning_ship(za: &mut Float, zb: &mut Float, ca: &Float, cb: &Float) {
// The Burning Ship is different in that - during
// iteration - the real and imaginary parts of (z)
// are made absolute:
diff --git a/source/benoit/benoit/iteration/iterate_mandelbrot.rs b/source/benoit/benoit/render/iterate/mandelbrot.rs
index 9037326..015e2e7 100644
--- a/source/benoit/benoit/iteration/iterate_mandelbrot.rs
+++ b/source/benoit/benoit/render/iterate/mandelbrot.rs
@@ -25,7 +25,7 @@ extern crate rug;
use rug::Float;
-pub fn iterate_mandelbrot(za: &mut Float, zb: &mut Float, ca: &Float, cb: &Float) {
+pub fn mandelbrot(za: &mut Float, zb: &mut Float, ca: &Float, cb: &Float) {
// The Mandelbrot Set (M) is defined as the set of
// values in the complex plane where the iterating
// function
@@ -36,18 +36,21 @@ pub fn iterate_mandelbrot(za: &mut Float, zb: &mut Float, ca: &Float, cb: &Float
//
// abs(z) = sqrt(Re(z)^2+Im(z)^2) <= 2^2 = 4.
- let za_temporary = za.clone();
+ let za_temporary = za.clone(); // a
// 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.
-
- za.square_mut();
- *za -= &*zb * &*zb;
- *za += ca;
-
- *zb *= za_temporary;
- *zb *= 2.0;
- *zb += cb;
+ // (a+bi)^2
+ // = (a+bi)(a+bi)
+ // = a^2+abi+abi-b^2
+ // = a^2-b^2+2abi.
+
+ za.square_mut(); // a^2
+ *za -= &*zb * &*zb; // a^2-b^2
+ *za += ca; // a^2-b^2+Re(c)
+
+ *zb *= za_temporary; // ab
+ *zb *= 2.0; // 2ab
+ *zb += cb; // 2ab+Im(c)
}
diff --git a/source/benoit/benoit/render/iterate/multibrot3.rs b/source/benoit/benoit/render/iterate/multibrot3.rs
new file mode 100644
index 0000000..1de2e07
--- /dev/null
+++ b/source/benoit/benoit/render/iterate/multibrot3.rs
@@ -0,0 +1,60 @@
+/*
+ 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;
+
+extern crate rug;
+
+use rug::Float;
+
+pub fn multibrot3(za: &mut Float, zb: &mut Float, ca: &Float, cb: &Float) {
+ let za_temporary = za.clone(); // a
+
+ // (a+bi)^3
+ // = (a+bi)(a+bi)(a+bi)
+ // = (a^2-b^2+2abi)(a+bi)
+ // = 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
+
+ let mut tmp0 = Float::with_val(PRECISION, &*zb * &*zb); // b^2
+
+ let tmp1 = Float::with_val(PRECISION, &tmp0 * &*zb); // b^3
+
+ tmp0 *= &*za; // ab^2
+ tmp0 *= 0x3; // 3ab^2
+
+ za.square_mut(); // a^2
+
+ *zb *= &*za; // (a^2)b
+
+ *za *= &za_temporary; // a^3
+ *za -= &tmp0; // a^3-3ab^2
+ *za += ca; // a^3-3ab^2+Re(c)
+
+ *zb *= 3.0; // 3(a^2)b
+ *zb -= &tmp1; // 3(a^2)b-b^3
+ *zb += cb; // 3(a^2)b-b^3+Im(c)
+}
diff --git a/source/benoit/benoit/iteration/iterate_tricorn.rs b/source/benoit/benoit/render/iterate/tricorn.rs
index 3d1bfb9..2f11ecf 100644
--- a/source/benoit/benoit/iteration/iterate_tricorn.rs
+++ b/source/benoit/benoit/render/iterate/tricorn.rs
@@ -25,18 +25,18 @@ extern crate rug;
use rug::Float;
-pub fn iterate_tricorn(za: &mut Float, zb: &mut Float, ca: &Float, cb: &Float) {
+pub fn tricorn(za: &mut Float, zb: &mut Float, ca: &Float, cb: &Float) {
// The Tricorn is only different from the
// 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))i)^2+c.
- let za_temporary = za.clone();
+ let za_temporary = za.clone(); // a
- za.square_mut();
- *za -= &*zb * &*zb;
- *za += ca;
+ za.square_mut(); // a^2
+ *za -= &*zb * &*zb; // a^2-b^2
+ *za += ca; // a^2
*zb *= za_temporary;
// We can negate the value by multiplying with
diff --git a/source/benoit/benoit/render/paint.rs b/source/benoit/benoit/render/paint.rs
new file mode 100644
index 0000000..ff3bd47
--- /dev/null
+++ b/source/benoit/benoit/render/paint.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/>.
+*/
+
+pub mod ancient;
+pub mod fire;
+pub mod greyscale;
+pub mod hsv;
+pub mod lch;
+pub mod sapphire;
+
+pub use ancient::*;
+pub use fire::*;
+pub use greyscale::*;
+pub use hsv::*;
+pub use lch::*;
+pub use sapphire::*;
diff --git a/source/benoit/benoit/render/paint/ancient.rs b/source/benoit/benoit/render/paint/ancient.rs
new file mode 100644
index 0000000..a2c01eb
--- /dev/null
+++ b/source/benoit/benoit/render/paint/ancient.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/>.
+*/
+
+// Palette function from mandelbrotsdl, my first
+// Mandelbrot renderer.
+
+pub fn ancient(factor: f32) -> (f32, f32, f32) {
+ let factor = factor % 1.0;
+
+ let (red, green, blue) = if !factor.is_nan() {
+ let red = 9.0 * (1.0 - factor) * factor * factor * factor;
+ let green = 15.0 * (1.0 - factor) * (1.0 - factor) * factor * factor;
+ let blue = 8.5 * (1.0 - factor) * (1.0 - factor) * (1.0 - factor) * factor;
+
+ (red, green, blue)
+ } else {
+ (0.0, 0.0, 0.0)
+ };
+
+ return (red, green, blue);
+}
diff --git a/source/benoit/benoit/task/render_data/new.rs b/source/benoit/benoit/render/paint/fire.rs
index d9509b5..23ab2b0 100644
--- a/source/benoit/benoit/task/render_data/new.rs
+++ b/source/benoit/benoit/render/paint/fire.rs
@@ -21,25 +21,31 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::task::render_data::RenderData;
+// Classic colour palette, fixed (and smoothed)
+// from version ↋.
-extern crate rug;
+pub fn fire(factor: f32) -> (f32, f32, f32) {
+ let factor = factor % 1.0;
-use rug::Float;
+ let (red, green, blue) = if !factor.is_nan() {
+ if factor <= 1.0 / 4.0 {
+ (factor * 4.0, 0.0, 0.0)
+ } else if factor <= 1.0 / 2.0 {
+ let factor = factor - (1.0 / 4.0);
-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 {
- return RenderData {
- canvas_width: canvas_width,
+ (1.0, factor * 4.0, 0.0)
+ } else if factor <= 3.0 / 4.0 {
+ let factor = factor - (1.0 / 2.0);
- centre_real: centre_real,
- centre_imag: centre_imag,
- zoom: zoom,
+ (1.0, 1.0, factor * 4.0)
+ } else {
+ let factor = 1.0 - factor;
- max_iter_count: max_iter_count,
+ (factor * 4.0, factor * 4.0, factor * 4.0)
+ }
+ } else {
+ (0.0, 0.0, 0.0)
+ };
- iter_count_buffer: iter_count_buffer.as_mut_ptr(),
- square_dist_buffer: square_dist_buffer.as_mut_ptr(),
- };
- }
+ return (red, green, blue);
}
diff --git a/source/benoit/benoit/task/render_data.rs b/source/benoit/benoit/render/paint/greyscale.rs
index e73b514..977edf1 100644
--- a/source/benoit/benoit/task/render_data.rs
+++ b/source/benoit/benoit/render/paint/greyscale.rs
@@ -21,25 +21,23 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-extern crate rug;
+// Original palette function after the Rust-
+// rewrite (version 10).
-use rug::Float;
+pub fn greyscale(factor: f32) -> (f32, f32, f32) {
+ let factor = factor % 1.0;
-pub mod new;
-pub mod slice;
+ let factor = (if factor >= 1.0 / 2.0 {
+ 1.0 - factor
+ } else {
+ factor
+ }) * 2.0;
-pub struct RenderData {
- pub canvas_width: u32,
+ let (red, green, blue) = if !factor.is_nan() {
+ (factor, factor, factor)
+ } else {
+ (0.0, 0.0, 0.0)
+ };
- pub centre_real: Float,
- pub centre_imag: Float,
- pub zoom: Float,
-
- pub max_iter_count: u32,
-
- iter_count_buffer: *mut u32,
- square_dist_buffer: *mut f32,
+ return (red, green, blue);
}
-
-unsafe impl Send for RenderData {}
-unsafe impl Sync for RenderData {}
diff --git a/source/benoit/benoit/render/paint/hsv.rs b/source/benoit/benoit/render/paint/hsv.rs
new file mode 100644
index 0000000..e6b414b
--- /dev/null
+++ b/source/benoit/benoit/render/paint/hsv.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 std::hint::unreachable_unchecked;
+
+pub fn hsv(factor: f32) -> (f32, f32, f32) {
+ let (red, green, blue) = if !factor.is_nan() {
+ hsv_to_rgb(factor, 7.0 / 8.0, 7.0 / 8.0)
+ } else {
+ (0.0, 0.0, 0.0)
+ };
+
+ return (red, green, blue);
+}
+
+fn hsv_to_rgb(hue: f32, saturation: f32, value: f32) -> (f32, f32, f32) {
+ return if saturation <= 0.0 {
+ let value = value.min(1.0);
+
+ (value, value, value)
+ } else {
+ let h = hue % 1.0 * 6.0;
+ let s = saturation.min(1.0);
+ let v = value.min(1.0);
+
+ let f = h % 1.0;
+ let p = v * (1.0 - s);
+ let q = v * (1.0 - s * f);
+ 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),
+ _ => unsafe { unreachable_unchecked() },
+ }
+ };
+}
diff --git a/source/benoit/benoit/render/paint/lch.rs b/source/benoit/benoit/render/paint/lch.rs
new file mode 100644
index 0000000..38dfa0f
--- /dev/null
+++ b/source/benoit/benoit/render/paint/lch.rs
@@ -0,0 +1,108 @@
+/*
+ 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::f32::consts::PI;
+
+pub fn lch(factor: f32) -> (f32, f32, f32) {
+ let (red, green, blue) = if !factor.is_nan() {
+ // Convert turns to radians:
+ // 1 turn = 2pi radians
+
+ let angle = factor * PI;
+
+ let (l, a, b) = lch_to_lab( 200.0 / 3.0, 1053.0 / 20.0, angle);
+ let (x, y, z) = lab_to_xyz( l, a, b);
+ let (r, g, b) = xyz_to_rgb( x, y, z);
+ let (r, g, b) = rgb_to_srgb(r, g, b);
+
+ (r, g, b)
+ } else {
+ (0.0, 0.0, 0.0)
+ };
+
+ return (red, green, blue);
+}
+
+fn rgb_to_srgb(r: f32, g: f32, b: f32) -> (f32, f32, f32) {
+ fn srgb(value: f32) -> f32 {
+ return if value > 7827.0 / 25000000.0 {
+ 211.0 / 200.0 * value.powf(5.0 / 12.0) - 11.0 / 200.0
+ } else {
+ 298.0 / 25.0 * value
+ };
+ }
+
+ let r = srgb(r);
+ let g = srgb(g);
+ let b = srgb(b);
+
+ (r, g, b)
+}
+
+fn xyz_to_rgb(x: f32, y: f32, z: f32) -> (f32, f32, f32) {
+ let m: [[f32; 0x3]; 0x3] = [
+ [
+ 12831.0 / 3959.0, -329.0 / 214.0, -1974.0 / 3959.0,
+ ],
+ [
+ -851781.0 / 878810.0, 1648619.0 / 878810.0, 36519.0 / 878810.0,
+ ],
+ [
+ 705.0 / 12673.0, -2585.0 / 12673.0, 705.0 / 667.0,
+ ],
+ ];
+
+ let r = m[0x0][0x0] * x + m[0x0][0x1] * y + m[0x0][0x2] * z;
+ let g = m[0x1][0x0] * x + m[0x1][0x1] * y + m[0x1][0x2] * z;
+ let b = m[0x2][0x0] * x + m[0x2][0x1] * y + m[0x2][0x2] * z;
+
+ return (r, g, b);
+}
+
+fn lab_to_xyz(l: f32, a: f32, b: f32) -> (f32, f32, f32) {
+ let kappa: f32 = 24389.0 / 27.0;
+ let epsilon: f32 = 216.0 / 24389.0;
+
+ let f1 = (l + 16.0) / 116.0;
+ let f0 = a / 500.0 + f1;
+ let f2 = f1 - b / 200.0;
+
+ let temporary = (l + 16.0) / 116.0;
+
+ let mut x = f0 * f0 * f0;
+ let mut y = temporary * temporary * temporary;
+ let mut z = f2 * f2 * f2;
+
+ if x <= epsilon { x = 1152.0 / 1195.0 * ((116.0 * f0 - 16.0) / kappa) };
+ if l <= kappa * epsilon { y = l / kappa };
+ if z <= epsilon { z = 986.0 / 1195.0 * ((116.0 * f2 - 16.0) / kappa) };
+
+ return (x, y, z);
+}
+
+fn lch_to_lab(l: f32, c: f32, h: f32) -> (f32, f32, f32) {
+ let a = c * h.cos();
+ let b = c * h.sin();
+
+ return (l, a, b);
+}
diff --git a/source/benoit/benoit/render/paint/sapphire.rs b/source/benoit/benoit/render/paint/sapphire.rs
new file mode 100644
index 0000000..4f3e29a
--- /dev/null
+++ b/source/benoit/benoit/render/paint/sapphire.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 fn sapphire(factor: f32) -> (f32, f32, f32) {
+ let factor = factor % 1.0;
+
+ let factor = (if factor >= 1.0 / 2.0 {
+ 1.0 - factor
+ } else {
+ factor
+ }) * 2.0;
+
+ let (red, green, blue) = if !factor.is_nan() {
+ (factor * factor * factor, factor * factor, factor)
+ } else {
+ (0.0, 0.0, 0.0)
+ };
+
+ return (red, green, blue);
+}
diff --git a/source/benoit/benoit/render/render_data.rs b/source/benoit/benoit/render/render_data.rs
new file mode 100644
index 0000000..b0f0851
--- /dev/null
+++ b/source/benoit/benoit/render/render_data.rs
@@ -0,0 +1,97 @@
+/*
+ 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::width_height_ratio;
+
+extern crate rug;
+
+use rug::Float;
+use std::slice::from_raw_parts_mut;
+
+pub struct RenderData {
+ canvas_width: u32,
+ canvas_height: u32,
+
+ centre_real: Float,
+ centre_imag: Float,
+ zoom: Float,
+
+ max_iter_count: u32,
+
+ x_offset: f32,
+ y_offset: f32,
+
+ x_factor: f32,
+ y_factor: f32,
+
+ iter_count_buffer: *mut u32,
+ square_dist_buffer: *mut f32,
+}
+
+impl RenderData {
+ pub fn new(iter_count_buffer: &mut [u32], square_dist_buffer: &mut [f32], canvas_width: u32, canvas_height: u32, centre_real: Float, centre_imag: Float, zoom: Float, max_iter_count: u32) -> RenderData {
+ let (width_ratio, height_ratio) = width_height_ratio(canvas_width, canvas_height);
+
+ return RenderData {
+ canvas_width: canvas_width,
+ canvas_height: canvas_height,
+
+ centre_real: centre_real,
+ centre_imag: centre_imag,
+ zoom: zoom,
+
+ max_iter_count: max_iter_count,
+
+ x_offset: canvas_width as f32 / -2.0,
+ y_offset: canvas_height as f32 / -2.0,
+
+ x_factor: 4.0 / canvas_width as f32 * width_ratio,
+ y_factor: 4.0 / canvas_height as f32 * height_ratio,
+
+ iter_count_buffer: iter_count_buffer.as_mut_ptr(),
+ square_dist_buffer: square_dist_buffer.as_mut_ptr(),
+ };
+ }
+
+ pub fn input(&self) -> (u32, &Float, &Float, &Float, u32) {
+ return (self.canvas_width, &self.centre_real, &self.centre_imag, &self.zoom, self.max_iter_count);
+ }
+
+ pub fn consts(&self) -> (f32, f32, f32, f32) {
+ return (self.x_offset, self.y_offset, self.x_factor, self.y_factor);
+ }
+
+ pub fn output(&self, row: u32) -> (&mut [u32], &mut [f32]) {
+ assert!(row < self.canvas_height);
+
+ let offset = row as isize * self.canvas_width as isize;
+
+ let iter_count = unsafe { from_raw_parts_mut(self.iter_count_buffer.offset(offset), self.canvas_width as usize) };
+ let square_dist = unsafe { from_raw_parts_mut(self.square_dist_buffer.offset(offset), self.canvas_width as usize) };
+
+ return (iter_count, square_dist);
+ }
+}
+
+unsafe impl Send for RenderData {}
+unsafe impl Sync for RenderData {}
diff --git a/source/benoit/benoit/task.rs b/source/benoit/benoit/render/render_row.rs
index ee53b52..7f9bf9a 100644
--- a/source/benoit/benoit/task.rs
+++ b/source/benoit/benoit/render/render_row.rs
@@ -21,5 +21,8 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-pub mod colour_data;
-pub mod render_data;
+pub mod julia;
+pub mod normal;
+
+pub use julia::*;
+pub use normal::*;
diff --git a/source/benoit/benoit/render/render_row/julia.rs b/source/benoit/benoit/render/render_row/julia.rs
new file mode 100644
index 0000000..7b7c2ef
--- /dev/null
+++ b/source/benoit/benoit/render/render_row/julia.rs
@@ -0,0 +1,84 @@
+/*
+ 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::render::IteratorFunction;
+use crate::benoit::render::render_data::RenderData;
+
+extern crate rug;
+
+use rug::{Assign, Float};
+use rug::float::Special;
+use std::sync::Arc;
+
+pub fn julia(data: Arc<RenderData>, y: u32, iterator: IteratorFunction) {
+ let (iter_count_buffer, square_dist_buffer) = data.output(y);
+
+ let (canvas_width, centre_real, centre_imag, _zoom, max_iter_count) = data.input();
+
+ let (x_offset, y_offset, x_factor, y_factor) = data.consts();
+
+ for x in 0x0..canvas_width {
+ // For more information, see render_row::normal.
+
+ let x_temporary = (x as f32 + x_offset) * x_factor;
+ let y_temporary = (y as f32 + y_offset) * y_factor;
+
+ let ca = centre_real;
+ let cb = centre_imag;
+
+ // When rendering the Julia fractals, the value of
+ // (c) remains constant throughout the entire
+ // canvas. The value of (z) - however - takes the
+ // position-determined value that (c) would've had.
+
+ let mut za = Float::with_val(PRECISION, x_temporary);
+ let mut zb = Float::with_val(PRECISION, y_temporary);
+
+ 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 = 0x1;
+ let mut square_dist;
+ while {
+ square_dist = Float::with_val(PRECISION, &za * &za + &zb * &zb).to_f32();
+
+ let periodic = za == za_prev && zb == zb_prev;
+ if periodic { iter_count = max_iter_count }
+
+ square_dist <= 256.0 && iter_count < max_iter_count
+ } {
+ za_prev.assign(&za);
+ zb_prev.assign(&zb);
+
+ iterator(&mut za, &mut zb, ca, cb);
+
+ iter_count += 0x1;
+ }
+
+ unsafe {
+ *iter_count_buffer.get_unchecked_mut( x as usize) = iter_count;
+ *square_dist_buffer.get_unchecked_mut(x as usize) = square_dist;
+ }
+ }
+}
diff --git a/source/benoit/benoit/render/render_row/normal.rs b/source/benoit/benoit/render/render_row/normal.rs
new file mode 100644
index 0000000..b39af7c
--- /dev/null
+++ b/source/benoit/benoit/render/render_row/normal.rs
@@ -0,0 +1,94 @@
+/*
+ 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::render::IteratorFunction;
+use crate::benoit::render::render_data::RenderData;
+
+extern crate rug;
+
+use rug::{Assign, Float};
+use rug::float::Special;
+use std::sync::Arc;
+
+pub fn normal(data: Arc<RenderData>, y: u32, iterator: IteratorFunction) {
+ let (iter_count_buffer, square_dist_buffer) = data.output(y);
+
+ let (canvas_width, centre_real, centre_imag, zoom, max_iter_count) = data.input();
+
+ let (x_offset, y_offset, x_factor, y_factor) = data.consts();
+
+ for x in 0x0..canvas_width {
+ let x_temporary = (x as f32 + x_offset) * x_factor;
+ let y_temporary = (y as f32 + y_offset) * y_factor;
+
+ let ca = {
+ let mut ca = Float::with_val(PRECISION, x_temporary / zoom);
+ ca += centre_real;
+
+ ca
+ };
+
+ let cb = {
+ let mut cb = Float::with_val(PRECISION, y_temporary / zoom);
+ cb -= centre_imag;
+
+ cb
+ };
+
+ let mut za = ca.clone();
+ let mut zb = cb.clone();
+
+ 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 = 0x1;
+ let mut square_dist;
+ while {
+ square_dist = Float::with_val(PRECISION, &za * &za + &zb * &zb).to_f32();
+ // Having a larger escape radius gives better
+ // results with regard to smoothing.
+
+ // Check if the value is periodic, i.e. its
+ // sequence repeats.
+ let periodic = za == za_prev && zb == zb_prev;
+ if periodic { iter_count = max_iter_count }
+
+ square_dist <= 256.0 && iter_count < max_iter_count
+ } {
+ za_prev.assign(&za);
+ zb_prev.assign(&zb);
+
+ iterator(&mut za, &mut zb, &ca, &cb);
+
+ iter_count += 0x1;
+ }
+
+ // Sacrifice safety for speed by removing bounds-
+ // checking.
+ unsafe {
+ *iter_count_buffer.get_unchecked_mut( x as usize) = iter_count;
+ *square_dist_buffer.get_unchecked_mut(x as usize) = square_dist;
+ }
+ }
+}
diff --git a/source/benoit/benoit/task/colour_data/new.rs b/source/benoit/benoit/rendering.rs
index 2707018..125798f 100644
--- a/source/benoit/benoit/task/colour_data/new.rs
+++ b/source/benoit/benoit/rendering.rs
@@ -21,20 +21,30 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::task::colour_data::ColourData;
+use crate::benoit::render::RowRenderer;
+use crate::benoit::render::render_row;
-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,
+use std::mem::transmute;
- 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(),
+#[derive(Clone, Copy)]
+pub enum Rendering {
+ Julia,
+ Normal,
+}
- image: image.as_mut_ptr(),
+impl Rendering {
+ pub fn get_row_renderer(self) -> RowRenderer {
+ return match self {
+ Rendering::Julia => render_row::julia,
+ Rendering::Normal => render_row::normal,
};
}
+
+ pub fn cycle(self) -> Self {
+ let raw = !(self as u8) & 0b00000001;
+
+ let new: Self = unsafe { transmute(raw) };
+
+ return new;
+ }
}
diff --git a/source/benoit/benoit/task/render_data/slice.rs b/source/benoit/benoit/task/render_data/slice.rs
deleted file mode 100644
index d2660ee..0000000
--- a/source/benoit/benoit/task/render_data/slice.rs
+++ /dev/null
@@ -1,37 +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::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 dist = from_raw_parts_mut(self.square_dist_buffer.offset(offset), self.canvas_width as usize);
-
- return (iter_count, dist);
- }
-}
diff --git a/source/benoit/benoit/video.rs b/source/benoit/benoit/video.rs
index af060fe..88c853b 100644
--- a/source/benoit/benoit/video.rs
+++ b/source/benoit/benoit/video.rs
@@ -27,8 +27,11 @@ use sdl2::{Sdl, VideoSubsystem};
use sdl2::render::WindowCanvas;
pub mod draw;
+pub mod draw_textual_feedback;
+pub mod draw_translation_feedback;
pub mod initialise;
pub mod sync;
+pub mod update;
pub struct Video {
pub sdl: Sdl,
diff --git a/source/benoit/benoit/video/draw.rs b/source/benoit/benoit/video/draw.rs
index bd5a837..a3d2460 100644
--- a/source/benoit/benoit/video/draw.rs
+++ b/source/benoit/benoit/video/draw.rs
@@ -21,19 +21,20 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::{FeedbackInfo, PRECISION};
use crate::benoit::video::Video;
-extern crate rug;
extern crate sdl2;
-use rug::Float;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
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;
+ 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;
for pixel in 0x0..canvas_size {
let y = pixel as u32 / canvas_width;
@@ -47,77 +48,15 @@ impl Video {
Color::RGB(red, green, blue)
};
- let rectangle = Rect::new(
+ let square = Rect::new(
(x * scale) as i32,
(y * scale) as i32,
scale,
- scale
+ scale,
);
self.canvas.set_draw_color(colour);
- self.canvas.fill_rects(&[rectangle]).unwrap();
- }
-
- if feedback_info.is_some() {
- let feedback_info = unsafe { feedback_info.unwrap_unchecked() };
-
- let canvas_width = {
- 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, feedback_info.next_zoom / feedback_info.prev_zoom);
-
- let mut width = Float::with_val(PRECISION, 1.0 / &zoom_ratio);
-
- // Remember that cartesian coordinates have an
- // inverted vertical axis compared to those of
- // SDL's coordinate system.
-
- 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 -= feedback_info.prev_centre_real;
- offset_y += feedback_info.prev_centre_imag;
-
- offset_x /= 4.0;
- offset_y /= 4.0;
-
- 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;
-
- offset_x += &zoom_offset;
- offset_y += &zoom_offset;
-
- offset_x *= &canvas_width;
- offset_y *= &canvas_width;
- width *= &canvas_width;
-
- ((offset_x.to_f32().round() as i32, offset_y.to_f32().round() as i32), width.to_f32().round() as u32)
- };
-
- Rect::new(
- offset_x,
- offset_y,
- width,
- width,
- )
- };
-
- self.canvas.set_draw_color(Color::RGBA(0x0, 0x0, 0x0, 0x3F));
- self.canvas.fill_rects(&[viewport]).unwrap();
-
- self.canvas.set_draw_color(Color::RGB(0xFF, 0xFF, 0xFF));
- self.canvas.draw_rects(&[viewport]).unwrap();
+ self.canvas.fill_rect(square).unwrap();
}
-
- self.canvas.present();
}
}
diff --git a/source/benoit/benoit/video/draw_textual_feedback.rs b/source/benoit/benoit/video/draw_textual_feedback.rs
new file mode 100644
index 0000000..cfa2307
--- /dev/null
+++ b/source/benoit/benoit/video/draw_textual_feedback.rs
@@ -0,0 +1,328 @@
+/*
+ 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;
+
+extern crate rug;
+extern crate sdl2;
+
+use rug::Float;
+use sdl2::pixels::Color;
+use sdl2::rect::Rect;
+use sdl2::render::WindowCanvas;
+
+impl Video {
+ pub fn draw_textual_feedback(&mut self, real: &Float, imag: &Float, zoom: &Float, max_iter_count: u32) {
+ let real_text = format!("REAL: {:.18}", real.to_f64());
+ let imag_text = format!("IMAG: {:.18}", imag.to_f64());
+ let zoom_text = format!("ZOOM: 2^{:.9}", zoom.to_f64().log2());
+ let iter_text = format!("ITER: {}", max_iter_count);
+
+ let string = format!("{real_text}\n{imag_text}\n{zoom_text}\n{iter_text}");
+
+ let mut offset_x = 0x1;
+ let mut offset_y = 0x1;
+ for character in string.chars() {
+ if character == '\n' {
+ offset_x = 0x1;
+ offset_y += TEXTURE_HEIGHT;
+ continue;
+ }
+
+ let character = convert_character(character);
+
+ draw_character(&mut self.canvas, offset_x, offset_y, character);
+
+ offset_x += TEXTURE_WIDTH;
+ }
+ }
+}
+
+fn draw_character(canvas: &mut WindowCanvas, x: u32, y: u32, character: u8) {
+ let texture = &FONT[character as usize];
+
+ for pixel in 0x0..TEXTURE_SIZE {
+ let alpha = texture[pixel] as u8 * 0xFF;
+
+ let texture_y = pixel as u32 / TEXTURE_WIDTH;
+ let texture_x = pixel as u32 - texture_y * TEXTURE_WIDTH;
+
+ let square = Rect::new(
+ (x * CHARACTER_SCALE + texture_x * CHARACTER_SCALE) as i32,
+ (y * CHARACTER_SCALE + texture_y * CHARACTER_SCALE) as i32,
+ CHARACTER_SCALE,
+ CHARACTER_SCALE,
+ );
+
+ canvas.set_draw_color(Color::RGBA(0xFF, 0xFF, 0xFF, alpha));
+ canvas.fill_rect(square).unwrap();
+ }
+}
+
+fn convert_character(input: char) -> u8 {
+ return match input {
+ ' ' => 0x00,
+ 'A' => 0x01,
+ 'E' => 0x02,
+ 'G' => 0x03,
+ 'I' => 0x04,
+ 'L' => 0x05,
+ 'M' => 0x06,
+ 'O' => 0x07,
+ 'R' => 0x08,
+ 'T' => 0x09,
+ 'Z' => 0x0A,
+ '0' => 0x0B,
+ '1' => 0x0C,
+ '2' => 0x0D,
+ '3' => 0x0E,
+ '4' => 0x0F,
+ '5' => 0x10,
+ '6' => 0x11,
+ '7' => 0x12,
+ '8' => 0x13,
+ '9' => 0x14,
+ '-' => 0x15,
+ '^' => 0x16,
+ '.' => 0x17,
+ ':' => 0x18,
+ _ => 0x19,
+ };
+}
+
+const CHARACTER_SCALE: u32 = 0x2;
+
+const TEXTURE_WIDTH: u32 = 0x6;
+const TEXTURE_HEIGHT: u32 = 0x6;
+const TEXTURE_SIZE: usize = TEXTURE_HEIGHT as usize * TEXTURE_WIDTH as usize;
+
+const FONT: [[bool; TEXTURE_SIZE]; 0x1A] = [
+ [
+ false, false, false, false, false, false,
+ false, false, false, false, false, false,
+ false, false, false, false, false, false,
+ false, false, false, false, false, false,
+ false, false, false, false, false, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ false, true, true, true, false, false,
+ true, true, true, true, true, false,
+ true, true, false, true, true, false,
+ true, true, true, true, true, false,
+ true, true, false, true, true, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ true, true, true, true, true, false,
+ true, true, false, false, false, false,
+ true, true, true, true, false, false,
+ true, true, false, false, false, false,
+ true, true, true, true, true, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ true, true, true, true, true, false,
+ true, true, false, false, false, false,
+ true, true, false, true, true, false,
+ true, true, false, false, true, false,
+ true, true, true, true, true, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ true, true, true, true, true, false,
+ false, false, true, false, false, false,
+ false, false, true, false, false, false,
+ false, false, true, false, false, false,
+ true, true, true, true, true, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ true, true, false, false, false, false,
+ true, true, false, false, false, false,
+ true, true, false, false, false, false,
+ true, true, false, false, false, false,
+ true, true, true, true, true, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ true, true, false, true, true, false,
+ true, true, true, true, true, false,
+ true, true, true, true, true, false,
+ true, true, false, true, true, false,
+ true, true, false, true, true, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ false, true, true, true, false, false,
+ true, true, false, true, true, false,
+ true, true, false, true, true, false,
+ true, true, false, true, true, false,
+ false, true, true, true, false, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ true, true, true, true, false, false,
+ true, true, false, true, true, false,
+ true, true, true, true, false, false,
+ true, true, false, true, true, false,
+ true, true, false, false, true, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ true, true, true, true, true, false,
+ false, false, true, false, false, false,
+ false, false, true, false, false, false,
+ false, false, true, false, false, false,
+ false, false, true, false, false, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ true, true, true, true, true, false,
+ false, false, true, true, true, false,
+ false, true, true, true, false, false,
+ true, true, true, false, false, false,
+ true, true, true, true, true, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ false, true, true, true, false, false,
+ true, true, false, true, true, false,
+ true, true, false, true, true, false,
+ true, true, false, true, true, false,
+ false, true, true, true, false, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ false, true, true, false, false, false,
+ true, true, true, false, false, false,
+ false, false, true, false, false, false,
+ false, false, true, false, false, false,
+ true, true, true, true, true, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ false, true, true, true, false, false,
+ true, false, false, true, true, false,
+ false, false, true, true, false, false,
+ false, true, true, false, false, false,
+ true, true, true, true, true, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ true, true, true, true, true, false,
+ true, false, false, true, true, false,
+ false, false, true, true, false, false,
+ false, false, false, true, true, false,
+ true, true, true, true, false, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ false, true, false, true, true, false,
+ true, true, false, true, true, false,
+ true, true, true, true, true, false,
+ false, false, false, true, true, false,
+ false, false, false, true, true, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ true, true, true, true, true, false,
+ true, true, false, false, false, false,
+ true, true, true, true, true, false,
+ false, false, false, true, true, false,
+ true, true, true, true, false, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ false, true, true, true, true, false,
+ true, true, false, false, false, false,
+ true, true, true, true, true, false,
+ true, true, false, true, true, false,
+ false, true, true, true, true, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ true, true, true, true, true, false,
+ false, false, false, true, true, false,
+ false, false, true, true, false, false,
+ false, true, true, false, false, false,
+ true, true, false, false, false, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ false, true, true, true, false, false,
+ true, true, false, true, true, false,
+ false, true, true, true, false, false,
+ true, true, false, true, true, false,
+ false, true, true, true, false, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ false, true, true, true, true, false,
+ true, true, false, true, true, false,
+ true, true, true, true, true, false,
+ false, false, false, true, true, false,
+ true, true, true, true, false, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ false, false, false, false, false, false,
+ false, false, false, false, false, false,
+ true, true, true, true, true, false,
+ false, false, false, false, false, false,
+ false, false, false, false, false, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ false, false, true, false, false, false,
+ false, true, true, true, false, false,
+ true, true, false, true, true, false,
+ false, false, false, false, false, false,
+ false, false, false, false, false, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ false, false, false, false, false, false,
+ false, false, false, false, false, false,
+ false, false, false, false, false, false,
+ true, false, false, false, false, false,
+ true, false, false, false, false, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ true, false, false, false, false, false,
+ false, false, false, false, false, false,
+ false, false, false, false, false, false,
+ false, false, false, false, false, false,
+ true, false, false, false, false, false,
+ false, false, false, false, false, false,
+ ],
+ [
+ true, true, false, false, false, false,
+ false, true, false, false, false, false,
+ true, true, false, false, false, false,
+ false, false, false, false, false, false,
+ true, false, false, false, false, false,
+ false, false, false, false, false, false,
+ ],
+];
diff --git a/source/benoit/benoit/video/draw_translation_feedback.rs b/source/benoit/benoit/video/draw_translation_feedback.rs
new file mode 100644
index 0000000..aeb7463
--- /dev/null
+++ b/source/benoit/benoit/video/draw_translation_feedback.rs
@@ -0,0 +1,95 @@
+/*
+ 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, PRECISION, width_height_ratio};
+use crate::benoit::video::Video;
+
+extern crate rug;
+extern crate sdl2;
+
+use rug::Float;
+use sdl2::pixels::Color;
+use sdl2::rect::Rect;
+
+impl Video {
+ pub fn draw_translation_feedback(&mut self, canvas_width: u32, canvas_height: u32, scale: u32, feedback_info: &FeedbackInfo) {
+ let (width_ratio, height_ratio) = width_height_ratio(canvas_width, canvas_height);
+
+ let canvas_width = Float::with_val(PRECISION, canvas_width * scale);
+ let canvas_height = Float::with_val(PRECISION, canvas_height * scale);
+
+ let viewport = {
+ let (offset_x, offset_y, width, height) = {
+ 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);
+ let mut height = Float::with_val(PRECISION, 1.0 / &zoom_ratio);
+
+ // Remember that cartesian coordinates have an
+ // inverted vertical axis compared to those of
+ // SDL's coordinate system.
+
+ 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 -= feedback_info.prev_centre_real;
+ offset_y += feedback_info.prev_centre_imag;
+
+ offset_x /= 4.0;
+ offset_y /= 4.0;
+
+ offset_x *= feedback_info.prev_zoom;
+ offset_y *= feedback_info.prev_zoom;
+
+ offset_x /= width_ratio;
+ offset_y /= height_ratio;
+
+ let mut zoom_offset = Float::with_val(PRECISION, 1.0 - &width);
+ zoom_offset /= 2.0;
+
+ offset_x += &zoom_offset;
+ offset_y += &zoom_offset;
+
+ offset_x *= &canvas_width;
+ offset_y *= &canvas_height;
+ width *= &canvas_width;
+ height *= &canvas_height;
+
+ (offset_x.to_f32().round() as i32, offset_y.to_f32().round() as i32, width.to_f32().round() as u32, height.to_f32().round() as u32)
+ };
+
+ Rect::new(
+ offset_x,
+ offset_y,
+ width,
+ height,
+ )
+ };
+
+ self.canvas.set_draw_color(Color::RGBA(0x0, 0x0, 0x0, 0x7F));
+ self.canvas.fill_rect(viewport).unwrap();
+
+ self.canvas.set_draw_color(Color::RGB(0xFF, 0xFF, 0xFF));
+ self.canvas.draw_rect(viewport).unwrap();
+ }
+}
diff --git a/source/benoit/benoit/video/initialise.rs b/source/benoit/benoit/video/initialise.rs
index ae633b3..dcd92f7 100644
--- a/source/benoit/benoit/video/initialise.rs
+++ b/source/benoit/benoit/video/initialise.rs
@@ -21,19 +21,22 @@
If not, see <https://www.gnu.org/licenses/>.
*/
+use crate::benoit::VERSION;
use crate::benoit::video::Video;
extern crate sdl2;
-use crate::benoit::VERSION;
+use sdl2::pixels::Color;
use sdl2::render::BlendMode;
impl Video {
- pub fn initialise(canvas_width: u32, scale: u32) -> Video {
+ 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");
- let mut window_builder = sdl_video.window(format!("Beno\u{00EE}t {:X}.{:X}.{:X}", VERSION.major, VERSION.minor, VERSION.patch).as_str(), canvas_width * scale, canvas_width * scale);
+ let window_title = format!("BENO\u{CE}T {:X}.{:X}.{:X}", VERSION.major, VERSION.minor, VERSION.patch);
+
+ let mut window_builder = sdl_video.window(window_title.as_str(), canvas_width * scale, canvas_height * scale);
window_builder.borderless();
window_builder.position_centered();
@@ -46,6 +49,11 @@ impl Video {
// We only want to scale the render, not the
// feedback, so we can't use SDL's scaling feature.
+ let clear_colour = Color::RGB(0x00, 0x00, 0x00);
+ canvas.set_draw_color(clear_colour);
+ canvas.clear();
+ canvas.present();
+
return Video {
sdl: sdl,
sdl_video: sdl_video,
diff --git a/source/benoit/benoit/app/get_row_renderer.rs b/source/benoit/benoit/video/update.rs
index 050d14f..eab330a 100644
--- a/source/benoit/benoit/app/get_row_renderer.rs
+++ b/source/benoit/benoit/video/update.rs
@@ -21,14 +21,10 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::app::App;
-use crate::benoit::app::RowRenderer;
+use crate::benoit::video::Video;
-impl App {
- pub fn get_row_renderer(julia: bool) -> RowRenderer {
- return match julia {
- false => App::render_row_normal,
- true => App::render_row_julia,
- };
+impl Video {
+ pub fn update(&mut self) {
+ self.canvas.present();
}
}
diff --git a/source/benoit/benoit/width_height_ratio.rs b/source/benoit/benoit/width_height_ratio.rs
new file mode 100644
index 0000000..c4afe91
--- /dev/null
+++ b/source/benoit/benoit/width_height_ratio.rs
@@ -0,0 +1,30 @@
+/*
+ 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 fn width_height_ratio(width: u32, height: u32) -> (f32, f32) {
+ return if width > height {
+ (1.0, height as f32 / width as f32)
+ } else {
+ (width as f32 / height as f32, 1.0)
+ };
+}