summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md10
-rw-r--r--Cargo.toml2
-rw-r--r--source/benoit/benoit.rs4
-rw-r--r--source/benoit/benoit/app.rs2
-rw-r--r--source/benoit/benoit/app/handle_keys.rs14
-rw-r--r--source/benoit/benoit/app/new.rs (renamed from source/benoit/benoit/app/configure.rs)28
-rw-r--r--source/benoit/benoit/app/print_controls.rs51
-rw-r--r--source/benoit/benoit/app/render.rs8
-rw-r--r--source/benoit/benoit/app/run.rs25
-rw-r--r--source/benoit/benoit/configuration.rs38
-rw-r--r--source/benoit/benoit/configuration/default.rs36
-rw-r--r--source/benoit/benoit/configuration/load.rs131
-rw-r--r--source/benoit/benoit/fractal/from_str.rs12
-rw-r--r--source/benoit/benoit/fractal/iterate/burning_ship.rs5
-rw-r--r--source/benoit/benoit/image/allocate.rs11
-rw-r--r--source/benoit/benoit/image/colour.rs16
-rw-r--r--source/benoit/benoit/image/dump.rs29
-rw-r--r--source/benoit/benoit/launcher.rs7
-rw-r--r--source/benoit/benoit/launcher/parse_arguments.rs61
-rw-r--r--source/benoit/benoit/launcher/print_help.rs21
-rw-r--r--source/benoit/benoit/launcher/run.rs39
-rw-r--r--source/benoit/benoit/launcher/setup.rs10
-rw-r--r--source/benoit/benoit/render.rs8
-rw-r--r--source/benoit/benoit/render/allocate.rs19
-rw-r--r--source/benoit/benoit/render/render.rs12
-rw-r--r--source/benoit/benoit/script.rs23
-rw-r--r--source/benoit/benoit/script/animate.rs136
-rw-r--r--source/benoit/benoit/script/configure.rs33
-rw-r--r--source/benoit/benoit/script/dump_frame.rs16
-rw-r--r--source/benoit/benoit/script/run.rs6
-rw-r--r--source/benoit/benoit/script/still.rs31
-rw-r--r--source/benoit/benoit/video/initialise.rs27
-rw-r--r--source/benoit/benoit/video/sync.rs16
-rw-r--r--source/benoit/main.rs9
34 files changed, 552 insertions, 344 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index daeaf95..0a04efc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+# 2.7.0
+
+* Bump minor version
+* Rework animations (support multiple variables)
+* Rework terminal arguments
+* Improve error handling (avoid panics and assertions)
+* Don't validate configuration immediately
+* Update messages
+* Rename 'burningship' to 'burning_ship' in configuration
+
# 2.6.3
* Add help
diff --git a/Cargo.toml b/Cargo.toml
index 8294d21..359cdb9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "benoit"
-version = "2.6.3"
+version = "2.7.0"
authors = ["Gabriel Bjørnager Jensen"]
edition = "2021"
description = "Multithreaded Mandelbrot renderer with support for PNG and WebP encoding."
diff --git a/source/benoit/benoit.rs b/source/benoit/benoit.rs
index b7878b2..549e25a 100644
--- a/source/benoit/benoit.rs
+++ b/source/benoit/benoit.rs
@@ -36,8 +36,8 @@ pub mod video;
pub const VERSION: (u32, u32, u32) = (
0x2, // Major
- 0x6, // Minor
- 0x3, // Patch
+ 0x7, // Minor
+ 0x0, // Patch
);
pub const PRECISION: u32 = 0x80;
diff --git a/source/benoit/benoit/app.rs b/source/benoit/benoit/app.rs
index 61446be..3f487f7 100644
--- a/source/benoit/benoit/app.rs
+++ b/source/benoit/benoit/app.rs
@@ -29,10 +29,10 @@ extern crate rug;
use rug::Float;
-pub mod configure;
pub mod draw_feedback;
pub mod drop;
pub mod handle_keys;
+pub mod new;
pub mod poll_events;
pub mod print_controls;
pub mod render;
diff --git a/source/benoit/benoit/app/handle_keys.rs b/source/benoit/benoit/app/handle_keys.rs
index 04e5a54..df35ea5 100644
--- a/source/benoit/benoit/app/handle_keys.rs
+++ b/source/benoit/benoit/app/handle_keys.rs
@@ -31,8 +31,6 @@ extern crate sdl2;
use rug::{Assign, Float};
use sdl2::keyboard::{KeyboardState, Scancode};
-pub const MIN_COLOUR_RANGE: f32 = 2.0;
-
impl App {
#[must_use]
pub(super) fn handle_keys(&mut self, scan_code: Scancode, state: KeyboardState) -> bool {
@@ -66,7 +64,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).max(MIN_COLOUR_RANGE),
+ Scancode::Down => (self.colour_range / COLOUR_RANGE_FACTOR).max(Configuration::MIN_COLOUR_RANGE),
_ => self.colour_range,
};
@@ -161,8 +159,12 @@ impl App {
fn dump_info(&self) {
eprintln!("info dump: the {}", self.fractal.name());
- eprintln!(" c = {}{:+}i ({}x)", &self.centre.real, &self.centre.imag, &self.zoom);
- eprintln!(" w = {}{:+}i", &self.extra.real, &self.extra.imag);
- eprintln!(" max. iter.: {}, col. range: {}", self.max_iter_count, self.colour_range);
+ eprintln!(" re(c): {}", self.centre.real);
+ eprintln!(" im(c): {}", self.centre.imag);
+ eprintln!(" re(w): {}", self.extra.real);
+ eprintln!(" im(w): {}", self.extra.imag);
+ eprintln!(" zoom: {}", self.zoom);
+ eprintln!(" max. iter count: {}", self.max_iter_count);
+ eprintln!(" col. range: {}", self.colour_range);
}
}
diff --git a/source/benoit/benoit/app/configure.rs b/source/benoit/benoit/app/new.rs
index 1af3d60..f19380a 100644
--- a/source/benoit/benoit/app/configure.rs
+++ b/source/benoit/benoit/app/new.rs
@@ -21,29 +21,33 @@
If not, see <https://www.gnu.org/licenses/>.
*/
+use crate::benoit::PRECISION;
use crate::benoit::app::App;
use crate::benoit::complex::Complex;
use crate::benoit::configuration::Configuration;
+extern crate rug;
+
+use rug::Float;
+
impl App {
#[must_use]
- pub fn configure(configuration: Configuration) -> App {
+ pub fn new(canvas_width: u32, canvas_height: u32) -> App {
return App {
- fractal: configuration.fractal,
-
- canvas_width: configuration.canvas_width,
- canvas_height: configuration.canvas_height,
- scale: configuration.scale,
+ fractal: Configuration::DEFAULT_FRACTAL,
- centre: Complex::new(configuration.centre_real, configuration.centre_imag),
- zoom: configuration.zoom,
+ canvas_width: canvas_width,
+ canvas_height: canvas_height,
+ scale: 0x2,
- extra: Complex::new(configuration.extra_real, configuration.extra_imag),
+ centre: Complex::new(Float::with_val(PRECISION, Configuration::DEFAULT_CENTRE.0), Float::with_val(PRECISION, Configuration::DEFAULT_CENTRE.1)),
+ extra: Complex::new(Float::with_val(PRECISION, Configuration::DEFAULT_EXTRA.0), Float::with_val(PRECISION, Configuration::DEFAULT_EXTRA.1)),
+ zoom: Float::with_val(PRECISION, Configuration::DEFAULT_ZOOM),
- max_iter_count: configuration.max_iter_count,
+ max_iter_count: Configuration::DEFAULT_MAX_ITER_COUNT,
- palette: configuration.palette,
- colour_range: configuration.colour_range,
+ palette: Configuration::DEFAULT_PALETTE,
+ colour_range: Configuration::DEFAULT_COLOUR_RANGE,
do_render: true,
do_textual_feedback: false,
diff --git a/source/benoit/benoit/app/print_controls.rs b/source/benoit/benoit/app/print_controls.rs
index f6f2612..99cab61 100644
--- a/source/benoit/benoit/app/print_controls.rs
+++ b/source/benoit/benoit/app/print_controls.rs
@@ -25,40 +25,41 @@ use crate::benoit::app::App;
impl App {
pub(super) fn print_controls() {
- println!("Controls:");
- println!("- \u{1B}[1mW\u{1B}[0m Translate +Im");
- println!("- \u{1B}[1mA\u{1B}[0m Translate -Re");
- println!("- \u{1B}[1mS\u{1B}[0m Translate -Im");
- println!("- \u{1B}[1mD\u{1B}[0m Translate +Re");
+ println!("");
+ println!("controls:");
+ println!("- \u{1B}[1mW\u{1B}[0m translate +Im");
+ println!("- \u{1B}[1mA\u{1B}[0m translate -Re");
+ println!("- \u{1B}[1mS\u{1B}[0m translate -Im");
+ println!("- \u{1B}[1mD\u{1B}[0m translate +Re");
println!();
- println!("- \u{1B}[1mQ\u{1B}[0m Zoom out");
- println!("- \u{1B}[1mE\u{1B}[0m Zoom in");
+ println!("- \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!("- \u{1B}[1mR\u{1B}[0m decrease max. iteration count");
+ println!("- \u{1B}[1mF\u{1B}[0m increase max. iteration count");
println!();
- println!("- \u{1B}[1mLEFT ALT\u{1B}[0m Cycle to previous fractal");
- println!("- \u{1B}[1mRIGHT ALT\u{1B}[0m Cycle to next fractal");
- println!("- \u{1B}[1mTAB\u{1B}[0m Toggle Julia");
- println!("- \u{1B}[1mLEFT CTRL\u{1B}[0m Toggle inverse");
+ println!("- \u{1B}[1mLEFT ALT\u{1B}[0m cycle to previous fractal");
+ println!("- \u{1B}[1mRIGHT ALT\u{1B}[0m cycle to next fractal");
+ println!("- \u{1B}[1mTAB\u{1B}[0m toggle Julia");
+ println!("- \u{1B}[1mLEFT CTRL\u{1B}[0m toggle inverse");
println!();
- println!("- \u{1B}[1mLEFT\u{1B}[0m Cycle to previous palette");
- println!("- \u{1B}[1mRIGHT\u{1B}[0m Cycle to next palette");
- println!("- \u{1B}[1mUP\u{1B}[0m Increase colour range");
- println!("- \u{1B}[1mDOWN\u{1B}[0m Decrease colour range");
+ println!("- \u{1B}[1mLEFT\u{1B}[0m cycle to previous palette");
+ println!("- \u{1B}[1mRIGHT\u{1B}[0m cycle to next palette");
+ println!("- \u{1B}[1mUP\u{1B}[0m increase colour range");
+ println!("- \u{1B}[1mDOWN\u{1B}[0m decrease colour range");
println!();
- println!("- \u{1B}[1mF1\u{1B}[0m Toggle textual feedback");
- println!("- \u{1B}[1mZ\u{1B}[0m Print centre value (c)");
+ println!("- \u{1B}[1mF1\u{1B}[0m toggle textual feedback");
+ println!("- \u{1B}[1mZ\u{1B}[0m print centre value (c)");
println!();
- println!("- \u{1B}[1mC\u{1B}[0m Render frame");
+ println!("- \u{1B}[1mC\u{1B}[0m render frame");
println!();
println!("Controls (holding \u{1B}[1mSHIFT\u{1B}[0m):");
- println!("- \u{1B}[1mW\u{1B}[0m Perturbate/translate +Im");
- println!("- \u{1B}[1mA\u{1B}[0m Perturbate/translate -Re");
- println!("- \u{1B}[1mS\u{1B}[0m Perturbate/translate -Im");
- println!("- \u{1B}[1mD\u{1B}[0m Perturbate/translate +Re");
+ println!("- \u{1B}[1mW\u{1B}[0m perturbate/translate +Im");
+ println!("- \u{1B}[1mA\u{1B}[0m perturbate/translate -Re");
+ println!("- \u{1B}[1mS\u{1B}[0m perturbate/translate -Im");
+ println!("- \u{1B}[1mD\u{1B}[0m perturbate/translate +Re");
println!();
- println!("- \u{1B}[1mC\u{1B}[0m Render frame");
+ println!("- \u{1B}[1mC\u{1B}[0m render frame");
println!();
}
} \ No newline at end of file
diff --git a/source/benoit/benoit/app/render.rs b/source/benoit/benoit/app/render.rs
index 7f4b132..1ba6c01 100644
--- a/source/benoit/benoit/app/render.rs
+++ b/source/benoit/benoit/app/render.rs
@@ -26,7 +26,7 @@ use crate::benoit::render::Render;
use std::time::Instant;
impl App {
- pub(super) fn render(&self, render: &mut Render) {
+ pub(super) fn render(&self, render: &mut Render) -> Result<(), String> {
eprint!("rendering...");
let time_start = Instant::now();
@@ -37,10 +37,12 @@ impl App {
&self.zoom,
&self.extra,
self.max_iter_count,
- );
+ )?;
let render_time = time_start.elapsed();
- eprintln!(" {:.3}ms", render_time.as_micros() as f32 / 1000.0);
+ eprintln!("\rrendered: {:.3}ms", render_time.as_micros() as f32 / 1000.0);
+
+ return Ok(());
}
} \ No newline at end of file
diff --git a/source/benoit/benoit/app/run.rs b/source/benoit/benoit/app/run.rs
index 1810faa..9652b8e 100644
--- a/source/benoit/benoit/app/run.rs
+++ b/source/benoit/benoit/app/run.rs
@@ -33,27 +33,30 @@ use std::time::Instant;
impl App {
#[must_use]
- pub fn run(mut self) -> i32 {
- let mut video = Video::initialise(self.canvas_width, self.canvas_height, self.scale);
+ pub fn run(mut self) -> Result<(), String> {
+ let mut video = Video::initialise(self.canvas_width, self.canvas_height, self.scale)?;
App::print_controls();
- let mut event_pump = video.sdl.event_pump().expect("unable to get event pump");
+ let mut event_pump = match video.sdl.event_pump() {
+ Ok( pump) => pump,
+ Err(_) => return Err("unable to get event pump".to_string()),
+ };
- let mut render = Render::allocate(self.canvas_width, self.canvas_height);
- let mut image = Image::allocate( self.canvas_width, self.canvas_height);
+ let mut render = Render::allocate(self.canvas_width, self.canvas_height)?;
+ let mut image = Image::allocate( self.canvas_width, self.canvas_height)?;
// Used for translation feedback:
let mut prev_centre = self.centre.clone();
let mut prev_zoom = self.zoom.clone();
loop {
- let frame_start = Instant::now();
+ let start_frame = Instant::now();
- if self.poll_events(&mut event_pump) { break }
+ if self.poll_events(&mut event_pump) { break };
if self.do_render {
- self.render(&mut render);
+ self.render(&mut render)?;
prev_centre.assign(&self.centre);
prev_zoom.assign( &self.zoom);
@@ -61,15 +64,15 @@ impl App {
self.do_render = false;
};
- image.colour(&render, self.palette, self.max_iter_count, self.colour_range);
+ image.colour(&render, self.palette, self.max_iter_count, self.colour_range)?;
video.draw_image(&image, self.scale);
self.draw_feedback(&mut video, &prev_centre, &prev_zoom);
video.update();
- video.sync(&frame_start);
+ video.sync(&start_frame)?;
}
- return 0x0;
+ return Ok(());
}
} \ No newline at end of file
diff --git a/source/benoit/benoit/configuration.rs b/source/benoit/benoit/configuration.rs
index ca96a54..511a9aa 100644
--- a/source/benoit/benoit/configuration.rs
+++ b/source/benoit/benoit/configuration.rs
@@ -39,24 +39,28 @@ pub struct Configuration {
pub canvas_width: u32,
pub canvas_height: u32,
- pub scale: u32,
- pub frame_start: u32,
- pub frame_stop: u32,
-
- pub centre_real: Float,
- pub centre_imag: Float,
- pub zoom: Float,
-
- pub extra_real: Float,
- pub extra_imag: Float,
-
- pub max_iter_count: u32,
-
- pub palette: Palette,
- pub colour_range: f32,
+ pub palette: Palette,
pub dump_path: String,
pub image_format: ImageFormat,
+
+ pub start_frame: u32,
+ pub start_centre_real: Float,
+ pub start_centre_imag: Float,
+ pub start_extra_real: Float,
+ pub start_extra_imag: Float,
+ pub start_zoom: Float,
+ pub start_max_iter_count: u32,
+ pub start_colour_range: f32,
+
+ pub stop_frame: u32,
+ pub stop_centre_real: Float,
+ pub stop_centre_imag: Float,
+ pub stop_extra_real: Float,
+ pub stop_extra_imag: Float,
+ pub stop_zoom: Float,
+ pub stop_max_iter_count: u32,
+ pub stop_colour_range: f32,
}
impl Configuration {
@@ -70,4 +74,8 @@ impl Configuration {
pub const DEFAULT_PALETTE: Palette = Palette::Fire;
pub const DEFAULT_COLOUR_RANGE: f32 = 64.0;
+
+ pub const MIN_CANVAS_WIDTH: u32 = 0x2;
+ pub const MIN_MAX_ITER_COUNT: u32 = 0x1;
+ pub const MIN_COLOUR_RANGE: f32 = 2.0;
}
diff --git a/source/benoit/benoit/configuration/default.rs b/source/benoit/benoit/configuration/default.rs
index 24d28b1..ecb6f58 100644
--- a/source/benoit/benoit/configuration/default.rs
+++ b/source/benoit/benoit/configuration/default.rs
@@ -38,25 +38,29 @@ impl Configuration {
fractal: Self::DEFAULT_FRACTAL,
canvas_width: 0x100,
- canvas_height: 0xC0,
- scale: 0x2,
- frame_start: 0x0,
- frame_stop: 0x0,
-
- centre_real: Float::with_val(PRECISION, Self::DEFAULT_CENTRE.0),
- centre_imag: Float::with_val(PRECISION, Self::DEFAULT_CENTRE.1),
- zoom: Float::with_val(PRECISION, Self::DEFAULT_ZOOM),
-
- extra_real: Float::with_val(PRECISION, Self::DEFAULT_EXTRA.0),
- extra_imag: Float::with_val(PRECISION, Self::DEFAULT_EXTRA.1),
-
- max_iter_count: Self::DEFAULT_MAX_ITER_COUNT,
-
- palette: Self::DEFAULT_PALETTE,
- colour_range: Self::DEFAULT_COLOUR_RANGE,
+ canvas_height: 0x100,
+ palette: Self::DEFAULT_PALETTE,
dump_path: "./render".to_string(),
image_format: ImageFormat::Png,
+
+ start_frame: 0x0,
+ start_centre_real: Float::with_val(PRECISION, Self::DEFAULT_CENTRE.0),
+ start_centre_imag: Float::with_val(PRECISION, Self::DEFAULT_CENTRE.1),
+ start_extra_real: Float::with_val(PRECISION, Self::DEFAULT_EXTRA.0),
+ start_extra_imag: Float::with_val(PRECISION, Self::DEFAULT_EXTRA.1),
+ start_zoom: Float::with_val(PRECISION, Self::DEFAULT_ZOOM),
+ start_max_iter_count: Self::DEFAULT_MAX_ITER_COUNT,
+ start_colour_range: Self::DEFAULT_COLOUR_RANGE,
+
+ stop_frame: 0xF,
+ stop_centre_real: Float::with_val(PRECISION, Self::DEFAULT_CENTRE.0),
+ stop_centre_imag: Float::with_val(PRECISION, Self::DEFAULT_CENTRE.1),
+ stop_extra_real: Float::with_val(PRECISION, Self::DEFAULT_EXTRA.0),
+ stop_extra_imag: Float::with_val(PRECISION, Self::DEFAULT_EXTRA.1),
+ stop_zoom: Float::with_val(PRECISION, Self::DEFAULT_ZOOM),
+ stop_max_iter_count: Self::DEFAULT_MAX_ITER_COUNT,
+ stop_colour_range: Self::DEFAULT_COLOUR_RANGE,
};
}
}
diff --git a/source/benoit/benoit/configuration/load.rs b/source/benoit/benoit/configuration/load.rs
index 8c02be9..4995163 100644
--- a/source/benoit/benoit/configuration/load.rs
+++ b/source/benoit/benoit/configuration/load.rs
@@ -43,129 +43,128 @@ impl Configuration {
let mut configuration = Configuration::default();
let configuration_text = match read(path) {
- Ok(content) => String::from_utf8_lossy(&content).to_string(),
- Err(..) => panic!("unable to read configuration file"),
+ Ok( content) => String::from_utf8_lossy(&content).to_string(),
+ Err(_) => return Err("unable to read configuration file".to_string()),
};
- let configuration_table = Table::from_str(configuration_text.as_str()).expect("unable to parse configuration");
+ let base_table = match Table::from_str(configuration_text.as_str()) {
+ Ok( table) => table,
+ Err(_) => return Err("unable to parse configuration".to_string()),
+ };
- get_integer(&mut configuration.thread_count, &configuration_table, "thread_count");
+ let start_table = get_table(&base_table, "start")?;
+ let stop_table = get_table(&base_table, "stop")?;
- get_boolean(&mut configuration.fractal.inverse, &configuration_table, "inverse");
- get_boolean(&mut configuration.fractal.julia, &configuration_table, "julia");
+ get_integer(&mut configuration.thread_count, &base_table, "thread_count")?;
- 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_start, &configuration_table, "frame_start");
- get_integer(&mut configuration.frame_stop, &configuration_table, "frame_stop");
+ get_boolean(&mut configuration.fractal.inverse, &base_table, "inverse")?;
+ get_boolean(&mut configuration.fractal.julia, &base_table, "julia")?;
- get_bigfloat(&mut configuration.centre_real, &configuration_table, "real");
- get_bigfloat(&mut configuration.centre_imag, &configuration_table, "imaginary");
- get_bigfloat(&mut configuration.extra_real, &configuration_table, "extra_real");
- get_bigfloat(&mut configuration.extra_imag, &configuration_table, "extra_imaginary");
- get_bigfloat(&mut configuration.zoom, &configuration_table, "zoom");
+ get_integer(&mut configuration.canvas_width, &base_table, "canvas_width")?;
+ get_integer(&mut configuration.canvas_height, &base_table, "canvas_height")?;
- get_integer(&mut configuration.max_iter_count, &configuration_table, "maximum_iteration_count");
+ if let Some(start_table) = start_table {
+ get_integer( &mut configuration.start_frame, &start_table, "frame")?;
+ get_bigfloat(&mut configuration.start_centre_real, &start_table, "real")?;
+ get_bigfloat(&mut configuration.start_centre_imag, &start_table, "imaginary")?;
+ get_bigfloat(&mut configuration.start_extra_real, &start_table, "extra_real")?;
+ get_bigfloat(&mut configuration.start_extra_imag, &start_table, "extra_imaginary")?;
+ get_bigfloat(&mut configuration.start_zoom, &start_table, "zoom")?;
+ get_integer( &mut configuration.start_max_iter_count, &start_table, "maximum_iteration_count")?;
+ get_float( &mut configuration.start_colour_range, &start_table, "colour_range")?;
+ }
- get_float(&mut configuration.colour_range, &configuration_table, "colour_range");
+ if let Some(stop_table) = stop_table {
+ get_integer( &mut configuration.stop_frame, &stop_table, "frame")?;
+ get_bigfloat(&mut configuration.stop_centre_real, &stop_table, "real")?;
+ get_bigfloat(&mut configuration.stop_centre_imag, &stop_table, "imaginary")?;
+ get_bigfloat(&mut configuration.stop_extra_real, &stop_table, "extra_real")?;
+ get_bigfloat(&mut configuration.stop_extra_imag, &stop_table, "extra_imaginary")?;
+ get_bigfloat(&mut configuration.stop_zoom, &stop_table, "zoom")?;
+ get_integer( &mut configuration.stop_max_iter_count, &stop_table, "maximum_iteration_count")?;
+ get_float( &mut configuration.stop_colour_range, &stop_table, "colour_range")?;
+ }
- // Enumerations:
- if let Some(name) = get_string(&configuration_table, "fractal") {
+ // Strings:
+ if let Some(name) = get_string(&base_table, "fractal")? {
configuration.fractal.kind = FractalKind::from_str(name.as_str())?;
}
- if let Some(name) = get_string(&configuration_table, "palette") {
+ if let Some(name) = get_string(&base_table, "palette")? {
configuration.palette = Palette::from_str(name.as_str())?;
}
- if let Some(path) = get_string(&configuration_table, "dump_path") {
+ if let Some(path) = get_string(&base_table, "dump_path")? {
configuration.dump_path = path.clone();
}
- if let Some(name) = get_string(&configuration_table, "image_format") {
+ if let Some(name) = get_string(&base_table, "image_format")? {
configuration.image_format = ImageFormat::from_str(name.as_str())?;
}
- validate_configuration(&configuration)?;
-
return Ok(configuration);
}
}
-fn validate_configuration(configuration: &Configuration) -> Result<(), &str> {
- // We allow thread counts of zero as those signal
- // automatic thread count detection.
-
- if configuration.canvas_width == 0x0 || configuration.canvas_height == 0x0 {
- return Err("only non-zero values for canvas_width and canvas_height are allowed");
- }
-
- if configuration.scale == 0x0 {
- return Err("only non-zero values for scale are allowed");
- }
-
- if configuration.frame_start > configuration.frame_stop {
- return Err("frame_start may not be greater than frame_stop");
- }
-
- if configuration.max_iter_count == 0x0 {
- return Err("only non-zero values for maximum_iteration_count are allowed");
- }
-
- if configuration.colour_range < 2.0 {
- return Err("only values greater than two are allowed for colour_range");
- }
-
- 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_boolean(buffer: &mut bool, table: &Table, name: &str) {
+fn get_table<'a>(table: &'a Table, name: &str) -> Result<Option<&'a Table>, String> {
+ return match get_value(table, name) {
+ Some(Value::Table(table)) => Ok(Some(table)),
+ Some(_) => Err(format!("\"{name}\" should be a section")),
+ _ => Ok(None),
+ };
+}
+
+fn get_boolean(buffer: &mut bool, table: &Table, name: &str) -> Result<(), String> {
match get_value(table, name) {
Some(Value::Boolean(value)) => *buffer = *value,
- Some(_) => panic!("\"{name}\" should be a boolean"),
+ Some(_) => return Err(format!("\"{name}\" should be a boolean")),
_ => {},
};
+ return Ok(())
}
-fn get_integer(buffer: &mut u32, table: &Table, name: &str) {
+fn get_integer(buffer: &mut u32, table: &Table, name: &str) -> Result<(), String> {
match get_value(table, name) {
Some(Value::Integer(value)) => *buffer = (*value) as u32,
- Some(_) => panic!("\"{name}\" should be an integer"),
+ Some(_) => return Err(format!("\"{name}\" should be an integer")),
_ => {},
};
+ return Ok(())
}
-fn get_float(buffer: &mut f32, table: &Table, name: &str) {
+fn get_float(buffer: &mut f32, table: &Table, name: &str) -> Result<(), String> {
match get_value(table, name) {
Some(Value::Float(value)) => *buffer = (*value) as f32,
- Some(_) => panic!("\"{name}\" should be a float"),
+ Some(_) => return Err(format!("\"{name}\" should be a float")),
_ => {},
};
+ return Ok(())
}
-fn get_bigfloat(buffer: &mut Float, table: &Table, name: &str) {
- return match get_value(table, name) {
+fn get_bigfloat(buffer: &mut Float, table: &Table, name: &str) -> Result<(), String> {
+ 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}\""),
+ _ => return Err(format!("invalid format of \"{name}\"")),
}
},
- Some(_) => panic!("\"{name}“ should be a quoted float"),
- _ => {},
+ Some(_) => return Err(format!("\"{name}“ should be a quoted float")),
+ _ => {},
};
+ return Ok(())
}
-fn get_string(table: &Table, name: &str) -> Option<String> {
+fn get_string(table: &Table, name: &str) -> Result<Option<String>, String> {
return match get_value(table, name) {
- Some(Value::String(value)) => Some(value.clone()),
- Some(_) => panic!("\"{name}\" should be a string"),
- _ => None,
+ Some(Value::String(value)) => Ok(Some(value.clone())),
+ Some(_) => Err(format!("\"{name}\" should be a string")),
+ _ => Ok(None),
};
}
diff --git a/source/benoit/benoit/fractal/from_str.rs b/source/benoit/benoit/fractal/from_str.rs
index 151fc98..05cf22e 100644
--- a/source/benoit/benoit/fractal/from_str.rs
+++ b/source/benoit/benoit/fractal/from_str.rs
@@ -32,12 +32,12 @@ impl FromStr for FractalKind {
use FractalKind::*;
let kind = match string {
- "burningship" => Some(BurningShip),
- "mandelbrot" => Some(Mandelbrot),
- "multibrot3" => Some(Multibrot3),
- "multibrot4" => Some(Multibrot4),
- "tricorn" => Some(Tricorn),
- _ => None,
+ "burning_ship" => Some(BurningShip),
+ "mandelbrot" => Some(Mandelbrot),
+ "multibrot3" => Some(Multibrot3),
+ "multibrot4" => Some(Multibrot4),
+ "tricorn" => Some(Tricorn),
+ _ => None,
};
return match kind {
diff --git a/source/benoit/benoit/fractal/iterate/burning_ship.rs b/source/benoit/benoit/fractal/iterate/burning_ship.rs
index 9e4f029..dae3a12 100644
--- a/source/benoit/benoit/fractal/iterate/burning_ship.rs
+++ b/source/benoit/benoit/fractal/iterate/burning_ship.rs
@@ -30,15 +30,14 @@ pub fn burning_ship(z: &mut Complex, c: &Complex) {
//
// z(n+1) = (abs(Re(z(n)))+i*abs(Im(z(n))))^2+c.
- z.real.abs_mut(); // abs(a)
- z.imag.abs_mut(); // abs(b)
-
+ z.real.abs_mut(); // abs(a)
let za_temporary = z.real.clone(); // abs(a)
z.real.square_mut(); // abs(a)^2
z.real -= &z.imag * &z.imag; // abs(a)^2-abs(b)^2
z.real += &c.real; // abs(a)^2-abs(b)^2+Re(c)
+ z.imag.abs_mut(); // abs(b)
z.imag *= &za_temporary; // abs(a)
z.imag *= 2.0; // 2*abs(a)
z.imag += &c.imag; // 2*abs(a)+Im(c)
diff --git a/source/benoit/benoit/image/allocate.rs b/source/benoit/benoit/image/allocate.rs
index bbdd61b..bfd5a46 100644
--- a/source/benoit/benoit/image/allocate.rs
+++ b/source/benoit/benoit/image/allocate.rs
@@ -21,21 +21,24 @@
If not, see <https://www.gnu.org/licenses/>.
*/
+use crate::benoit::configuration::Configuration;
use crate::benoit::image::Image;
impl Image {
#[must_use]
- pub fn allocate(width: u32, height: u32) -> Image {
+ pub fn allocate(width: u32, height: u32) -> Result<Image, String> {
+ if width < Configuration::MIN_CANVAS_WIDTH || height < Configuration::MIN_CANVAS_WIDTH { return Err(format!("width and height may not be more than {}", Configuration::MIN_CANVAS_WIDTH)) };
+
let (canvas_size, overflow) = (height as usize).overflowing_mul(width as usize);
- if overflow { panic!("overflow when calculating canvas size") };
+ if overflow { return Err("overflow when calculating canvas size".to_string()) };
let data: Vec::<(u8, u8, u8)> = vec![(0x0, 0x0, 0x0); canvas_size];
- return Image {
+ return Ok(Image {
width: width,
height: height,
data: data,
- };
+ });
}
}
diff --git a/source/benoit/benoit/image/colour.rs b/source/benoit/benoit/image/colour.rs
index 3eefd09..9349662 100644
--- a/source/benoit/benoit/image/colour.rs
+++ b/source/benoit/benoit/image/colour.rs
@@ -22,6 +22,7 @@
*/
use crate::benoit::colour_data::ColourData;
+use crate::benoit::configuration::Configuration;
use crate::benoit::image::Image;
use crate::benoit::palette::{Palette, PALETTE_DATA_LENGTH};
use crate::benoit::render::Render;
@@ -31,10 +32,16 @@ extern crate rayon;
use rayon::prelude::*;
impl Image {
- pub fn colour(&mut self, render: &Render, palette: Palette, new_max_iter_count: u32, colour_range: f32) {
- if render.canvas_size() != self.size() { panic!("canvas size mismatch") };
+ pub fn colour(&mut self, render: &Render, palette: Palette, new_max_iter_count: u32, colour_range: f32) -> Result<(), String> {
+ if render.size() != self.size() { return Err(format!("size mismatch - {}*{} (render) vs. {}*{} (image)", render.size().0, render.size().1, self.size().0, self.size().1)) };
- let (fractal, max_iter_count) = render.info().expect("cannot colour before render");
+ if new_max_iter_count < Configuration::MIN_MAX_ITER_COUNT { return Err(format!("maximum_iteration_count must be at least {}", Configuration::MIN_MAX_ITER_COUNT)) };
+ if colour_range < Configuration::MIN_COLOUR_RANGE { return Err(format!("colour range may not be less than {}", Configuration::MIN_COLOUR_RANGE)) };
+
+ let (fractal, max_iter_count) = match render.info() {
+ Some(info) => info,
+ None => return Err("cannot colour before render".to_string()),
+ };
let data = ColourData::new(
self,
@@ -47,8 +54,11 @@ impl Image {
self.data.par_iter_mut().for_each(|point| {
let index = data.index(point);
let (iter_count, dist) = render[index];
+
*point = colour_point(&data, iter_count, dist);
});
+
+ return Ok(());
}
}
diff --git a/source/benoit/benoit/image/dump.rs b/source/benoit/benoit/image/dump.rs
index 1e029c4..cd1f1ea 100644
--- a/source/benoit/benoit/image/dump.rs
+++ b/source/benoit/benoit/image/dump.rs
@@ -30,19 +30,22 @@ use std::fs::{File, write};
use std::io::BufWriter;
impl Image {
- pub fn dump(&self, path: &str, format: ImageFormat) {
+ pub fn dump(&self, path: &str, format: ImageFormat) -> Result<(), String> {
use ImageFormat::*;
- match format {
+ return match format {
Png => self.dump_png( path),
Webp => self.dump_webp(path),
- }
+ };
}
- fn dump_png(&self, path: &str) {
+ fn dump_png(&self, path: &str) -> Result<(), String> {
let path = path.to_owned() + ".png";
- let file = File::create(path).expect("unable to create file");
+ let file = match File::create(path) {
+ Ok( file) => file,
+ Err(_) => return Err("unable to create file".to_string()),
+ };
let file_buffer = BufWriter::new(file);
let mut encoder = png::Encoder::new(file_buffer, self.width, self.height);
@@ -51,17 +54,23 @@ impl Image {
encoder.set_compression(png::Compression::Fast);
encoder.set_srgb(png::SrgbRenderingIntent::Perceptual);
- let mut writer = encoder.write_header().expect("unable to write image");
- writer.write_image_data(self.raw()).expect("unable to write image");
+ let mut writer = match encoder.write_header() {
+ Ok( writer) => writer,
+ Err(_) => return Err("unable to write image header".to_string()),
+ };
+ if writer.write_image_data(self.raw()).is_err() { return Err("unable to write image".to_string()) };
+
+ return Ok(());
}
- fn dump_webp(&self, path: &str) {
+ fn dump_webp(&self, path: &str) -> Result<(), String> {
let path = path.to_owned() + ".webp";
let encoder = webp::Encoder::from_rgb(self.raw(), self.width, self.height);
+ let data = encoder.encode_lossless();
- let data = encoder.encode_lossless();
+ if write(path, &*data).is_err() { return Err("unable to write image".to_string()) };
- write(path, &*data).expect("unable to write image");
+ return Ok(());
}
}
diff --git a/source/benoit/benoit/launcher.rs b/source/benoit/benoit/launcher.rs
index 3a5ea48..f38b6c3 100644
--- a/source/benoit/benoit/launcher.rs
+++ b/source/benoit/benoit/launcher.rs
@@ -21,6 +21,8 @@
If not, see <https://www.gnu.org/licenses/>.
*/
+use crate::benoit::configuration::Configuration;
+
pub mod parse_arguments;
pub mod print_help;
pub mod print_message;
@@ -28,6 +30,11 @@ pub mod run;
pub mod set_title;
pub mod setup;
+pub enum Mode {
+ App( u32, u32),
+ Script(Configuration),
+}
+
pub struct Launcher {}
impl Launcher {
diff --git a/source/benoit/benoit/launcher/parse_arguments.rs b/source/benoit/benoit/launcher/parse_arguments.rs
index 73d2d79..75adee1 100644
--- a/source/benoit/benoit/launcher/parse_arguments.rs
+++ b/source/benoit/benoit/launcher/parse_arguments.rs
@@ -22,27 +22,62 @@
*/
use crate::benoit::configuration::Configuration;
-use crate::benoit::launcher::Launcher;
+use crate::benoit::launcher::{Launcher, Mode};
use std::env::args;
+use std::str::FromStr;
impl Launcher {
#[must_use]
- pub(super) fn parse_arguments(&self) -> (Configuration, bool) {
- let mut arguments = args();
+ pub(super) fn parse_arguments(&self) -> Result<Mode, String> {
+ let arguments = args();
- return match arguments.nth(0x1) {
- Some(argument) => {
- if argument == "--help" { Launcher::print_help() };
+ let mut width: Option<u32> = None;
+ let mut height: Option<u32> = None;
+ let mut configuration: Option<Configuration> = None;
- let configuration = match Configuration::load(argument.as_str()) {
- Ok( configuration) => configuration,
- Err(message) => panic!("error parsing configuration: {message}"),
- };
+ let mut command: Option<String> = None;
- (configuration, false)
- },
- _ => (Configuration::default(), true),
+ for argument in arguments.skip(0x1) {
+ match command {
+ Some(_) => {
+ match command.take().unwrap().as_str() {
+ "height" => height = Some(u32::from_str(argument.as_str()).unwrap()),
+ "width" => width = Some(u32::from_str(argument.as_str()).unwrap()),
+ "path" => configuration = Some(Configuration::load(argument.as_str())?),
+ _ => {},
+ }
+ continue;
+ },
+ _ => {},
+ }
+
+ // Check if command doesn't take a value.
+ match argument.as_str() {
+ "help" => Launcher::print_help(),
+ _ => command = Some(argument.clone()),
+ };
+ }
+
+ if command.is_some() { return Err(format!("missing value to command \"{}\"", command.unwrap())) };
+
+ return if configuration.is_some() {
+ if width.is_some() || height.is_some() { eprintln!("width and height will be overwritten in script mode") };
+
+ Ok(Mode::Script(configuration.unwrap()))
+ } else {
+ // If only one length is specified, the other is
+ // assumed equal.
+
+ let width_val = if let Some(val) = width { val }
+ else if let Some(val) = height { val }
+ else { 0x100 };
+
+ let height_val = if let Some(val) = height { val }
+ else if let Some(val) = width { val }
+ else { 0xC0 };
+
+ Ok(Mode::App(width_val, height_val))
};
}
}
diff --git a/source/benoit/benoit/launcher/print_help.rs b/source/benoit/benoit/launcher/print_help.rs
index 4d03053..13d6805 100644
--- a/source/benoit/benoit/launcher/print_help.rs
+++ b/source/benoit/benoit/launcher/print_help.rs
@@ -37,25 +37,26 @@ impl Launcher {
println!(" inverse false|true");
println!(" julia false|true");
println!();
- println!(" canvas_width 0..=4294967295");
- println!(" canvas_height 0..=4294967295");
- println!(" frame_start 0..=4294967295");
- println!(" frame_stop frame_start..=4294967295");
+ println!(" canvas_width 2..=4294967295");
+ println!(" canvas_height 2..=4294967295");
+ println!(" start_frame 0..=4294967295");
+ println!(" stop_frame start_frame..=4294967295");
+ println!(" palette \"emerald\"|\"fire\"|\"greyscale\"|\"hsv\"|\"lch\"|\"ruby\"|\"sapphire\"|\"simple\"|\"twilight\"");
+ println!();
+ println!(" dump_path");
+ println!(" image_format \"png\"|\"webp\"");
+ println!();
+ println!("Additionally, the following parameters may be set in the \"start\" and \"stop\" sections:");
println!();
println!(" real \"0.0\"\u{B1}");
println!(" imaginary \"0.0\"\u{B1}");
println!(" extra_real \"0.0\"\u{B1}");
println!(" extra_imaginary \"0.0\"\u{B1}");
println!(" zoom \"0.0\"<");
- println!();
println!(" maximum_iteration_count 1..=4294967295");
- println!();
- println!(" palette \"emerald\"|\"fire\"|\"greyscale\"|\"hsv\"|\"lch\"|\"ruby\"|\"sapphire\"|\"simple\"|\"twilight\"");
println!(" colour_range 2.0+");
println!();
- println!(" dump_path");
- println!(" image_format \"png\"|\"webp\"");
- println!();
+ println!("If not animating (stop_frame equals zero), only the latter section is used.");
println!("Note that real, imaginary, extra_real, extra_imaginary, and zoom - if present - must be quoted floats.");
println!("When choosing image format, keep in mind that PNG is faster whilst WebP yields better compression. Both are, however, lossless.");
println!();
diff --git a/source/benoit/benoit/launcher/run.rs b/source/benoit/benoit/launcher/run.rs
index f949e6a..ca71143 100644
--- a/source/benoit/benoit/launcher/run.rs
+++ b/source/benoit/benoit/launcher/run.rs
@@ -22,39 +22,36 @@
*/
use crate::benoit::app::App;
-use crate::benoit::launcher::Launcher;
+use crate::benoit::launcher::{Launcher, Mode};
use crate::benoit::script::Script;
-use std::thread::available_parallelism;
-
impl Launcher {
#[must_use]
- pub fn run(self) -> i32 {
+ pub fn run(self) -> Result<(), String> {
Launcher::print_message();
- let (mut configuration, interative) = self.parse_arguments();
+ let mode = self.parse_arguments()?;
- configuration.thread_count = if configuration.thread_count == 0x0 {
- match available_parallelism() {
- Ok(ammount) => ammount.get() as u32,
- _ => 0x2, // We assume at least two threads.
- }
- } else {
- configuration.thread_count
+ let thread_count = match &mode {
+ Mode::Script(configuration) => configuration.thread_count,
+ _ => 0x0,
};
- self.setup(configuration.thread_count);
+ self.setup(thread_count);
- return if interative {
- eprintln!("running in iteractive mode");
+ return match mode {
+ Mode::App(width, height) => {
+ eprintln!("running in iteractive mode");
- let app = App::configure(configuration);
- app.run()
- } else {
- eprintln!("running in script mode");
+ let app = App::new(width, height);
+ app.run()
+ },
+ Mode::Script(configuration) => {
+ eprintln!("running in script mode");
- let script = Script::configure(configuration);
- script.run()
+ let script = Script::configure(configuration);
+ script.run()
+ },
};
}
}
diff --git a/source/benoit/benoit/launcher/setup.rs b/source/benoit/benoit/launcher/setup.rs
index 3e9be56..da360dd 100644
--- a/source/benoit/benoit/launcher/setup.rs
+++ b/source/benoit/benoit/launcher/setup.rs
@@ -28,6 +28,7 @@ use crate::benoit::palette::fill_palettes;
extern crate rayon;
use rayon::ThreadPoolBuilder;
+use std::thread::available_parallelism;
impl Launcher {
pub(super) fn setup(&self, thread_count: u32) {
@@ -35,6 +36,15 @@ impl Launcher {
fill_palettes();
+ let thread_count = if thread_count == 0x0 {
+ match available_parallelism() {
+ Ok(ammount) => ammount.get() as u32,
+ _ => 0x2, // We assume at least two threads.
+ }
+ } else {
+ thread_count
+ };
+
eprintln!("using {} threads", thread_count);
ThreadPoolBuilder::new().num_threads(thread_count as usize).build_global().unwrap();
}
diff --git a/source/benoit/benoit/render.rs b/source/benoit/benoit/render.rs
index 0141f52..9bd3756 100644
--- a/source/benoit/benoit/render.rs
+++ b/source/benoit/benoit/render.rs
@@ -29,8 +29,8 @@ pub mod render;
use std::ops::{Index, IndexMut};
pub struct Render {
- canvas_width: u32,
- canvas_height: u32,
+ width: u32,
+ height: u32,
info: Option<(Fractal, u32)>,
@@ -39,8 +39,8 @@ pub struct Render {
impl Render {
#[must_use]
- pub fn canvas_size(&self) -> (u32, u32) {
- return (self.canvas_width, self.canvas_height);
+ pub fn size(&self) -> (u32, u32) {
+ return (self.width, self.height);
}
#[must_use]
diff --git a/source/benoit/benoit/render/allocate.rs b/source/benoit/benoit/render/allocate.rs
index 4228914..8e9143d 100644
--- a/source/benoit/benoit/render/allocate.rs
+++ b/source/benoit/benoit/render/allocate.rs
@@ -21,26 +21,25 @@
If not, see <https://www.gnu.org/licenses/>.
*/
+use crate::benoit::configuration::Configuration;
use crate::benoit::render::Render;
impl Render {
- pub fn allocate(canvas_width: u32, canvas_height: u32) -> Render {
- let canvas_size = {
- let (canvas_size, overflow) = canvas_height.overflowing_mul(canvas_width);
- if overflow { panic!("overflow when calculating canvas size") };
+ pub fn allocate(width: u32, height: u32) -> Result<Render, String> {
+ if width < Configuration::MIN_CANVAS_WIDTH || height < Configuration::MIN_CANVAS_WIDTH { return Err(format!("width and height may not be more than {}", Configuration::MIN_CANVAS_WIDTH)) };
- canvas_size as usize
- };
+ let (canvas_size, overflow) = (height as usize).overflowing_mul(width as usize);
+ if overflow { return Err("overflow when calculating canvas size".to_string()) };
let data: Vec<(u32, f32)> = vec![(0x0, 0.0); canvas_size];
- return Render {
- canvas_width: canvas_width,
- canvas_height: canvas_height,
+ return Ok(Render {
+ width: width,
+ height: height,
info: None,
data: data,
- };
+ });
}
}
diff --git a/source/benoit/benoit/render/render.rs b/source/benoit/benoit/render/render.rs
index 21a86a9..4d11d14 100644
--- a/source/benoit/benoit/render/render.rs
+++ b/source/benoit/benoit/render/render.rs
@@ -23,6 +23,7 @@
use crate::benoit::{BAILOUT_DISTANCE, PRECISION};
use crate::benoit::complex::Complex;
+use crate::benoit::configuration::Configuration;
use crate::benoit::fractal::{Fractal, IteratorFunction};
use crate::benoit::render::Render;
use crate::benoit::render_data::RenderData;
@@ -42,13 +43,14 @@ impl Render {
zoom: &Float,
extra: &Complex,
max_iter_count: u32,
- ) {
- assert!(max_iter_count > 0x0);
+ ) -> Result<(), String> {
+ if *zoom <= 0x0 { return Err("zoom may not be negative nor zero".to_string()) };
+ if max_iter_count < Configuration::MIN_MAX_ITER_COUNT { return Err(format!("maximum_iteration_count must be at least {}", Configuration::MIN_MAX_ITER_COUNT)) };
let data = RenderData::new(
&mut self.data[..],
- self.canvas_width,
- self.canvas_height,
+ self.width,
+ self.height,
centre.clone(),
extra.clone(),
zoom.clone(),
@@ -65,6 +67,8 @@ impl Render {
});
self.info = Some((fractal, max_iter_count));
+
+ return Ok(());
}
}
diff --git a/source/benoit/benoit/script.rs b/source/benoit/benoit/script.rs
index 2b1a7d0..fd34ab2 100644
--- a/source/benoit/benoit/script.rs
+++ b/source/benoit/benoit/script.rs
@@ -36,24 +36,27 @@ pub mod dump_frame;
pub mod run;
pub mod still;
+pub struct Keyframe {
+ frame: u32,
+ centre: Complex,
+ extra: Complex,
+ zoom: Float,
+ max_iter_count: u32,
+ colour_range: f32,
+}
+
pub struct Script {
// Configuration:
fractal: Fractal,
canvas_width: u32,
canvas_height: u32,
- frame_start: u32,
- frame_stop: u32,
- centre: Complex,
- extra: Complex,
- zoom: Float,
-
- max_iter_count: u32,
-
- palette: Palette,
- colour_range: f32,
+ palette: Palette,
dump_path: String,
image_format: ImageFormat,
+
+ start: Keyframe,
+ stop: Keyframe,
}
diff --git a/source/benoit/benoit/script/animate.rs b/source/benoit/benoit/script/animate.rs
index 8745090..d30ac8c 100644
--- a/source/benoit/benoit/script/animate.rs
+++ b/source/benoit/benoit/script/animate.rs
@@ -33,31 +33,43 @@ use rug::ops::PowAssign;
impl Script {
#[must_use]
- pub(super) fn animate(&self) -> i32 {
- let frame_count = self.frame_stop - self.frame_start + 0x1;
-
- let mut render = Render::allocate(self.canvas_width, self.canvas_height);
- let mut image = Image::allocate( self.canvas_width, self.canvas_height);
-
- // zoom_start:
- let mut zoom = Float::with_val(PRECISION, 1.0 / 4.0);
-
- let zoom_stop = Float::with_val(PRECISION, &self.zoom);
-
- let zoom_factor = get_zoom_factor(&zoom, &zoom_stop, self.frame_stop);
-
- zoom = if self.frame_start > 0x0 {
- let mut zoom = zoom_factor.clone();
- zoom.pow_assign(frame_count);
-
- zoom
- } else {
- zoom
- };
-
- eprintln!("animating from #{} to #{} ({} frame(s)) at {}{:+}i to {:.3}x (fac. ~{:.3})", self.frame_start, self.frame_stop, frame_count + 0x1, self.centre.real.to_f32(), self.centre.imag.to_f32(), zoom_stop.to_f32(), zoom_factor.to_f32());
-
- for frame in 0x0..=frame_count {
+ pub(super) fn animate(&self) -> Result<(), String> {
+ // TO-DO: Proper animation for centre value when zooming.
+
+ let frame_count = self.stop.frame - self.start.frame + 0x1;
+ assert!(frame_count >= 0x2);
+
+ let mut render = Render::allocate(self.canvas_width, self.canvas_height)?;
+ let mut image = Image::allocate( self.canvas_width, self.canvas_height)?;
+
+ // Variables:
+ let mut centre = self.start.centre.clone();
+ let mut extra = self.start.extra.clone();
+ let mut zoom = self.start.zoom.clone();
+ let mut max_iter_count = self.start.max_iter_count as f32;
+ let mut colour_range = self.start.colour_range;
+
+ // Steps & factors:
+ let centre_real_step = get_step_bigfloat(self.stop.frame, &self.start.centre.real, &self.stop.centre.real);
+ let centre_imag_step = get_step_bigfloat(self.stop.frame, &self.start.centre.imag, &self.stop.centre.imag);
+ let extra_real_step = get_step_bigfloat(self.stop.frame, &self.start.extra.real, &self.stop.extra.real);
+ let extra_imag_step = get_step_bigfloat(self.stop.frame, &self.start.extra.imag, &self.stop.extra.imag);
+ let zoom_factor = get_factor_bigfloat(self.stop.frame, &self.start.zoom, &self.stop.zoom);
+ let max_iter_count_step = get_step(self.stop.frame, self.start.max_iter_count as f32, self.stop.max_iter_count as f32);
+ let colour_range_step = get_step(self.stop.frame, self.start.colour_range, self.stop.colour_range);
+
+ eprintln!("");
+ eprintln!("animating {frame_count} frames: the {}", self.fractal.name());
+ eprintln!(" re(c): {} \u{2192} {} (step: {centre_real_step})", self.start.centre.real, self.stop.centre.real);
+ eprintln!(" im(c): {} \u{2192} {} (step: {centre_imag_step})", self.start.centre.imag, self.stop.centre.imag);
+ eprintln!(" re(w): {} \u{2192} {} (step: {extra_real_step})", self.start.extra.real, self.stop.extra.real);
+ eprintln!(" im(w): {} \u{2192} {} (step: {extra_imag_step})", self.start.extra.imag, self.stop.extra.imag);
+ eprintln!(" zoom: {} \u{2192} {} (fac.: {zoom_factor})", self.start.zoom, self.stop.zoom);
+ eprintln!(" max. iter count: {} \u{2192} {} (step: {max_iter_count_step})", self.start.max_iter_count, self.stop.max_iter_count);
+ eprintln!(" col. range: {} \u{2192} {} (step: {colour_range_step})", self.start.colour_range, self.stop.colour_range);
+ eprintln!("");
+
+ for frame in 0x0..frame_count {
let frame_name = format!("frame{frame:010}");
Script::dump_frame(
@@ -67,28 +79,71 @@ impl Script {
&mut render,
self.fractal,
self.palette,
- &self.centre,
- &self.extra,
+ &centre,
+ &extra,
&zoom,
- self.max_iter_count,
- self.colour_range,
+ max_iter_count.round() as u32,
+ colour_range,
self.image_format,
- );
-
- zoom *= &zoom_factor;
+ )?;
+
+ centre.real += &centre_real_step;
+ centre.imag += &centre_imag_step;
+ extra.real += &extra_real_step;
+ extra.imag += &extra_imag_step;
+ zoom *= &zoom_factor;
+ max_iter_count += max_iter_count_step;
+ colour_range += colour_range_step;
}
- return 0x0;
+ return Ok(());
}
}
-fn get_zoom_factor(zoom_start: &Float, zoom_stop: &Float, frame_count: u32) -> Float {
- assert!(frame_count > 0x0);
+#[must_use]
+fn get_step(stop_x: u32, start_y: f32, stop_y: f32) -> f32 {
+ assert!(stop_x - START_X != 0x0);
+
+ const START_X: u32 = 0x1;
+
+ // a = (y1-y0)/(x1-x0)
+ return (stop_y - start_y) / (stop_x as f32 - START_X as f32);
+}
+
+#[allow(dead_code)]
+#[must_use]
+fn get_factor(stop_x: u32, start_y: f32, stop_y: f32) -> f32 {
+ assert!(stop_x - START_X != 0x0);
+
+ const START_X: u32 = 0x1;
+
+ // a = (y1/y0)^(1/(x1-x0))
+ return (stop_y / start_y).powf(1.0 / (stop_x as f32 - START_X as f32));
+}
+
+#[must_use]
+fn get_step_bigfloat(stop_x: u32, start_y: &Float, stop_y: &Float) -> Float {
+ assert!(stop_x - START_X > 0x0);
+
+ const START_X: u32 = 0x1;
+
+ let numerator = Float::with_val(PRECISION, stop_y - start_y);
+ let denominator = stop_x - START_X;
+
+ return numerator / denominator;
+}
+
+#[must_use]
+fn get_factor_bigfloat(stop_x: u32, start_y: &Float, stop_y: &Float) -> Float {
+ assert!(stop_x - START_X > 0x0);
+ if stop_y == start_y { return Float::with_val(PRECISION, 0x1) };
+
+ const START_X: u32 = 0x1;
// To get the zoom factor, we first want the 'a'
// value of the growth function from (0) to
// (frame_count) on the x-dimension and from
- // (zoom_start) to (zoom_stop) on the y-dimension:
+ // (zoom_start) to (stop_zoom) on the y-dimension:
//
// a = nroot(x1-x0,y1/y0),
//
@@ -99,14 +154,11 @@ fn get_zoom_factor(zoom_start: &Float, zoom_stop: &Float, frame_count: u32) -> F
//
// making the final equation
//
- // (y1/y0)^(1/(x1-x0)) = (zoom_stop/zoom_start)^(1/(frame_count-1)).
-
- let frame_start: f32 = 0.0;
- let frame_stop: f32 = frame_count as f32;
+ // (y1/y0)^(1/(x1-x0)) = (stop_zoom/zoom_start)^(1/(frame_count-1)).
- let exponent = Float::with_val(PRECISION, 1.0 / (frame_stop - frame_start));
+ let exponent = 1.0 / (stop_x as f64 - START_X as f64);
- let mut factor = Float::with_val(PRECISION, zoom_stop / zoom_start);
+ let mut factor = Float::with_val(PRECISION, stop_y / start_y);
factor.pow_assign(exponent);
return factor;
diff --git a/source/benoit/benoit/script/configure.rs b/source/benoit/benoit/script/configure.rs
index 9966199..be2e1cd 100644
--- a/source/benoit/benoit/script/configure.rs
+++ b/source/benoit/benoit/script/configure.rs
@@ -23,7 +23,7 @@
use crate::benoit::complex::Complex;
use crate::benoit::configuration::Configuration;
-use crate::benoit::script::Script;
+use crate::benoit::script::{Keyframe, Script};
impl Script {
#[must_use]
@@ -33,21 +33,28 @@ impl Script {
canvas_width: configuration.canvas_width,
canvas_height: configuration.canvas_height,
- frame_start: configuration.frame_start,
- frame_stop: configuration.frame_stop,
-
- centre: Complex::new(configuration.centre_real, configuration.centre_imag),
- zoom: configuration.zoom,
-
- extra: Complex::new(configuration.extra_real, configuration.extra_imag),
-
- max_iter_count: configuration.max_iter_count,
-
- palette: configuration.palette,
- colour_range: configuration.colour_range,
+ palette: configuration.palette,
dump_path: configuration.dump_path,
image_format: configuration.image_format,
+
+ start: Keyframe {
+ frame: configuration.start_frame,
+ centre: Complex::new(configuration.start_centre_real, configuration.start_centre_imag),
+ extra: Complex::new(configuration.start_extra_real, configuration.start_extra_imag),
+ zoom: configuration.start_zoom,
+ max_iter_count: configuration.start_max_iter_count,
+ colour_range: configuration.start_colour_range,
+ },
+
+ stop: Keyframe {
+ frame: configuration.stop_frame,
+ centre: Complex::new(configuration.stop_centre_real, configuration.stop_centre_imag),
+ extra: Complex::new(configuration.stop_extra_real, configuration.stop_extra_imag),
+ zoom: configuration.stop_zoom,
+ max_iter_count: configuration.stop_max_iter_count,
+ colour_range: configuration.stop_colour_range,
+ },
};
}
}
diff --git a/source/benoit/benoit/script/dump_frame.rs b/source/benoit/benoit/script/dump_frame.rs
index c3f1f46..86b62c6 100644
--- a/source/benoit/benoit/script/dump_frame.rs
+++ b/source/benoit/benoit/script/dump_frame.rs
@@ -48,8 +48,8 @@ impl Script {
max_iter_count: u32,
colour_range: f32,
image_format: ImageFormat,
- ) {
- eprint!("\"{name}\" (2^{:.9}x): rendering...", zoom.to_f64().log2());
+ ) -> Result<(), String> {
+ eprint!("\u{1B}[1m\u{1B}[94mX\u{1B}[0m \"{name}\" (2^{:.9}x): rendering...", zoom.to_f64().log2());
Launcher::set_title("Rendering...");
let time_start = Instant::now();
@@ -60,23 +60,25 @@ impl Script {
zoom,
extra,
max_iter_count,
- );
+ )?;
let render_time = time_start.elapsed();
eprint!(" {:.3}ms, colouring...", render_time.as_micros() as f32 / 1000.0);
Launcher::set_title("Colouring...");
- image.colour(&render, palette, max_iter_count, colour_range);
+ image.colour(&render, palette, max_iter_count, colour_range)?;
let colour_time = time_start.elapsed() - render_time;
eprint!(" {:.3}ms, dumping...", colour_time.as_micros() as f32 / 1000.0);
Launcher::set_title("Dumping...");
let path = format!("{dump_path}/{name}");
- image.dump(path.as_str(), image_format);
+ image.dump(path.as_str(), image_format)?;
let dump_time = time_start.elapsed() - colour_time - render_time;
- eprintln!(" {:.3}ms - \u{1B}[1m\u{1B}[92mdone\u{1B}[0m", dump_time.as_micros() as f32 / 1000.0);
- Launcher::set_title("Done");
+ eprintln!(" {:.3}ms\r\u{1B}[1m\u{1B}[92mO\u{1B}[0m", dump_time.as_micros() as f32 / 1000.0);
+ Launcher::set_title("Done, waiting...");
+
+ return Ok(());
}
}
diff --git a/source/benoit/benoit/script/run.rs b/source/benoit/benoit/script/run.rs
index 24a34b0..5a0f4c7 100644
--- a/source/benoit/benoit/script/run.rs
+++ b/source/benoit/benoit/script/run.rs
@@ -25,12 +25,10 @@ use crate::benoit::script::Script;
impl Script {
#[must_use]
- pub fn run(self) -> i32 {
- let code = match self.frame_stop == 0x0 && self.frame_start == 0x0 {
+ pub fn run(self) -> Result<(), String> {
+ return match self.stop.frame == 0x0 && self.start.frame == 0x0 {
false => self.animate(),
true => self.still(),
};
-
- return code;
}
}
diff --git a/source/benoit/benoit/script/still.rs b/source/benoit/benoit/script/still.rs
index 8da69ca..ee93b65 100644
--- a/source/benoit/benoit/script/still.rs
+++ b/source/benoit/benoit/script/still.rs
@@ -27,12 +27,23 @@ use crate::benoit::script::Script;
impl Script {
#[must_use]
- pub(super) fn still(&self) -> i32 {
- let mut render = Render::allocate(self.canvas_width, self.canvas_height);
- let mut image = Image::allocate( self.canvas_width, self.canvas_height);
+ pub(super) fn still(&self) -> Result<(), String> {
+ let mut render = Render::allocate(self.canvas_width, self.canvas_height)?;
+ let mut image = Image::allocate( self.canvas_width, self.canvas_height)?;
const FRAME_NAME: &str = "render";
+ eprintln!("");
+ eprintln!("rendering still: the {}", self.fractal.name());
+ eprintln!(" re(c): {}", self.stop.centre.real);
+ eprintln!(" im(c): {}", self.stop.centre.imag);
+ eprintln!(" re(w): {}", self.stop.extra.real);
+ eprintln!(" im(w): {}", self.stop.extra.imag);
+ eprintln!(" zoom: {}", self.stop.zoom);
+ eprintln!(" max. iter count: {}", self.stop.max_iter_count);
+ eprintln!(" col. range: {}", self.stop.colour_range);
+ eprintln!("");
+
Script::dump_frame(
self.dump_path.as_str(),
FRAME_NAME,
@@ -40,14 +51,14 @@ impl Script {
&mut render,
self.fractal,
self.palette,
- &self.centre,
- &self.extra,
- &self.zoom,
- self.max_iter_count,
- self.colour_range,
+ &self.stop.centre,
+ &self.stop.extra,
+ &self.stop.zoom,
+ self.stop.max_iter_count,
+ self.stop.colour_range,
self.image_format,
- );
+ )?;
- return 0x0;
+ return Ok(());
}
}
diff --git a/source/benoit/benoit/video/initialise.rs b/source/benoit/benoit/video/initialise.rs
index 64a0608..f78ee7a 100644
--- a/source/benoit/benoit/video/initialise.rs
+++ b/source/benoit/benoit/video/initialise.rs
@@ -31,18 +31,31 @@ use sdl2::render::BlendMode;
impl Video {
#[must_use]
- pub fn initialise(canvas_width: u32, canvas_height: u32, scale: u32) -> Video {
- let sdl = sdl2::init().expect("unable to initialise sdl2");
- let sdl_video = sdl.video().expect("unable to initialise video");
+ pub fn initialise(canvas_width: u32, canvas_height: u32, scale: u32) -> Result<Video, String> {
+ let sdl = match sdl2::init() {
+ Ok( sdl) => sdl,
+ Err(_) => return Err("unable to initialise sdl2".to_string()),
+ };
+
+ let sdl_video = match sdl.video() {
+ Ok( video) => video,
+ Err(_) => return Err("unable to initialise video".to_string()),
+ };
let window_title = format!("BENO\u{CE}T {:X}.{:X}.{:X}", VERSION.0, VERSION.1, VERSION.2);
let mut window_builder = sdl_video.window(window_title.as_str(), canvas_width * scale, canvas_height * scale);
window_builder.position_centered();
- let window = window_builder.build().expect("unable to open window");
+ let window = match window_builder.build() {
+ Ok( window) => window,
+ Err(_) => return Err("unable to open window".to_string()),
+ };
- let mut canvas = window.into_canvas().build().expect("unable to create canvas");
+ let mut canvas = match window.into_canvas().build() {
+ Ok( canvas) => canvas,
+ Err(_) => return Err("unable to build canvas".to_string()),
+ };
canvas.set_blend_mode(BlendMode::Blend);
@@ -54,10 +67,10 @@ impl Video {
canvas.clear();
canvas.present();
- return Video {
+ return Ok(Video {
sdl: sdl,
sdl_video: sdl_video,
canvas: canvas,
- };
+ });
}
}
diff --git a/source/benoit/benoit/video/sync.rs b/source/benoit/benoit/video/sync.rs
index c8c016a..46112eb 100644
--- a/source/benoit/benoit/video/sync.rs
+++ b/source/benoit/benoit/video/sync.rs
@@ -27,20 +27,28 @@ use std::thread::sleep;
use std::time::{Duration, Instant};
impl Video {
- pub fn sync(&self, frame_start: &Instant) {
+ pub fn sync(&self, start_frame: &Instant) -> Result<(), String> {
let frame_duration = {
- let index = self.canvas.window().display_index().expect("unable to get display index");
+ let index = match self.canvas.window().display_index() {
+ Ok( index) => index,
+ Err(_) => return Err("unable to get display index".to_string()),
+ };
- let mode = self.sdl_video.current_display_mode(index).expect("unable to get display mode");
+ let mode = match self.sdl_video.current_display_mode(index) {
+ Ok( mode) => mode,
+ Err(_) => return Err("unable to get display mode".to_string()),
+ };
Duration::from_secs(0x1) / mode.refresh_rate as u32
};
- let remaining = match frame_duration.checked_sub(frame_start.elapsed()) {
+ let remaining = match frame_duration.checked_sub(start_frame.elapsed()) {
Some(value) => value,
None => Duration::from_secs(0x0),
};
sleep(remaining);
+
+ return Ok(());
}
} \ No newline at end of file
diff --git a/source/benoit/main.rs b/source/benoit/main.rs
index 42822d3..1264873 100644
--- a/source/benoit/main.rs
+++ b/source/benoit/main.rs
@@ -29,7 +29,14 @@ use std::process::exit;
fn main() {
let launcher = Launcher::new();
- let code = launcher.run();
+ let result = launcher.run();
+
+ if let Err(ref error) = result { eprintln!("error: {error}") };
+
+ let code: i32 = match result {
+ Ok( _) => 0x0,
+ Err(_) => 0x1,
+ };
exit(code);
}