summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md10
-rw-r--r--Cargo.toml2
-rw-r--r--README.md2
-rw-r--r--source/benoit/benoit.rs3
-rw-r--r--source/benoit/benoit/configuration/load.rs52
-rw-r--r--source/benoit/benoit/image/dump.rs6
-rw-r--r--source/benoit/benoit/launcher.rs38
-rw-r--r--source/benoit/benoit/launcher/parse_arguments.rs48
-rw-r--r--source/benoit/benoit/launcher/print_help.rs65
-rw-r--r--source/benoit/benoit/launcher/print_message.rs36
-rw-r--r--source/benoit/benoit/launcher/run.rs60
-rw-r--r--source/benoit/benoit/launcher/set_title.rs (renamed from source/benoit/benoit/script/set_title.rs)6
-rw-r--r--source/benoit/benoit/launcher/setup.rs41
-rw-r--r--source/benoit/benoit/script.rs1
-rw-r--r--source/benoit/benoit/script/dump_frame.rs9
-rw-r--r--source/benoit/main.rs52
16 files changed, 338 insertions, 93 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cece66f..daeaf95 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+# 2.6.3
+
+* Add help
+* Update readme
+* Fix configuration validation
+* Fix 'julia' not being respected in configurations
+* Improve error handling
+* Add launcher structure
+* Always set terminal title
+
# 2.6.2
* Reorder fractal kinds
diff --git a/Cargo.toml b/Cargo.toml
index a5b964b..8294d21 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "benoit"
-version = "2.6.2"
+version = "2.6.3"
authors = ["Gabriel Bjørnager Jensen"]
edition = "2021"
description = "Multithreaded Mandelbrot renderer with support for PNG and WebP encoding."
diff --git a/README.md b/README.md
index dd39ccd..124fe69 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[*Benoit*](https://mandelbrot.dk/benoit) is a free and open‐source Mandelbrot renderer written in Rust. Its goal is to render arbitrary positions as performant and accurate as possiple. Usage:
```
-benoit [path]
+benoit [--help] [path]
```
… where *path* denotes the configuration file to read (optional). If no path is provided, the program is run in *interactive* mode, wherein the fractal is rendered in real‐time. Otherwise, *script* mode is run using the provided configuration.
diff --git a/source/benoit/benoit.rs b/source/benoit/benoit.rs
index 8292e0c..b7878b2 100644
--- a/source/benoit/benoit.rs
+++ b/source/benoit/benoit.rs
@@ -27,6 +27,7 @@ pub mod complex;
pub mod configuration;
pub mod image;
pub mod fractal;
+pub mod launcher;
pub mod palette;
pub mod render;
pub mod render_data;
@@ -36,7 +37,7 @@ pub mod video;
pub const VERSION: (u32, u32, u32) = (
0x2, // Major
0x6, // Minor
- 0x2, // Patch
+ 0x3, // Patch
);
pub const PRECISION: u32 = 0x80;
diff --git a/source/benoit/benoit/configuration/load.rs b/source/benoit/benoit/configuration/load.rs
index a993a4d..8c02be9 100644
--- a/source/benoit/benoit/configuration/load.rs
+++ b/source/benoit/benoit/configuration/load.rs
@@ -37,7 +37,7 @@ use toml::{Table, Value};
impl Configuration {
#[must_use]
- pub fn load(path: &str) -> Configuration {
+ pub fn load(path: &str) -> Result<Configuration, String> {
eprintln!("loading configuration at \"{path}\"");
let mut configuration = Configuration::default();
@@ -51,18 +51,8 @@ impl Configuration {
get_integer(&mut configuration.thread_count, &configuration_table, "thread_count");
- if let Some(name) = get_string(&configuration_table, "fractal") {
- configuration.fractal.kind = match FractalKind::from_str(name.as_str()) {
- Ok(kind) => kind,
- Err(message) => panic!("{message}"),
- };
- }
-
- {
- let mut inverse = false;
- get_boolean(&mut inverse, &configuration_table, "inverse");
- configuration.fractal.inverse = inverse;
- }
+ get_boolean(&mut configuration.fractal.inverse, &configuration_table, "inverse");
+ get_boolean(&mut configuration.fractal.julia, &configuration_table, "julia");
get_integer(&mut configuration.canvas_width, &configuration_table, "canvas_width");
get_integer(&mut configuration.canvas_height, &configuration_table, "canvas_height");
@@ -77,41 +67,37 @@ impl Configuration {
get_integer(&mut configuration.max_iter_count, &configuration_table, "maximum_iteration_count");
- if let Some(name) = get_string(&configuration_table, "palette") {
- configuration.palette = match Palette::from_str(name.as_str()) {
- Ok(palette) => palette,
- Err(message) => panic!("{message}"),
- };
+ get_float(&mut configuration.colour_range, &configuration_table, "colour_range");
+
+ // Enumerations:
+ if let Some(name) = get_string(&configuration_table, "fractal") {
+ configuration.fractal.kind = FractalKind::from_str(name.as_str())?;
}
- get_float(&mut configuration.colour_range, &configuration_table, "colour_range");
+ if let Some(name) = get_string(&configuration_table, "palette") {
+ configuration.palette = Palette::from_str(name.as_str())?;
+ }
if let Some(path) = get_string(&configuration_table, "dump_path") {
configuration.dump_path = path.clone();
}
if let Some(name) = get_string(&configuration_table, "image_format") {
- configuration.image_format = match ImageFormat::from_str(name.as_str()) {
- Ok(format) => format,
- Err(message) => panic!("{message}"),
- };
+ configuration.image_format = ImageFormat::from_str(name.as_str())?;
}
- match check_configuration(&configuration) {
- Err(message) => panic!("invalid configuration: {message}"),
- _ => {},
- }
+ validate_configuration(&configuration)?;
- return configuration;
+ return Ok(configuration);
}
}
-fn check_configuration(configuration: &Configuration) -> Result<(), &str> {
+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 {
- return Err("only non-zero values for canvas_width are allowed");
+ 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 {
@@ -126,6 +112,10 @@ fn check_configuration(configuration: &Configuration) -> Result<(), &str> {
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(());
}
diff --git a/source/benoit/benoit/image/dump.rs b/source/benoit/benoit/image/dump.rs
index 18c166c..1e029c4 100644
--- a/source/benoit/benoit/image/dump.rs
+++ b/source/benoit/benoit/image/dump.rs
@@ -31,9 +31,11 @@ use std::io::BufWriter;
impl Image {
pub fn dump(&self, path: &str, format: ImageFormat) {
+ use ImageFormat::*;
+
match format {
- ImageFormat::Png => self.dump_png( path),
- ImageFormat::Webp => self.dump_webp(path),
+ Png => self.dump_png( path),
+ Webp => self.dump_webp(path),
}
}
diff --git a/source/benoit/benoit/launcher.rs b/source/benoit/benoit/launcher.rs
new file mode 100644
index 0000000..3a5ea48
--- /dev/null
+++ b/source/benoit/benoit/launcher.rs
@@ -0,0 +1,38 @@
+/*
+ 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 parse_arguments;
+pub mod print_help;
+pub mod print_message;
+pub mod run;
+pub mod set_title;
+pub mod setup;
+
+pub struct Launcher {}
+
+impl Launcher {
+ #[must_use]
+ pub fn new() -> Launcher {
+ return Launcher {};
+ }
+}
diff --git a/source/benoit/benoit/launcher/parse_arguments.rs b/source/benoit/benoit/launcher/parse_arguments.rs
new file mode 100644
index 0000000..73d2d79
--- /dev/null
+++ b/source/benoit/benoit/launcher/parse_arguments.rs
@@ -0,0 +1,48 @@
+/*
+ Copyright 2021, 2023 Gabriel Bjørnager Jensen.
+
+ This file is part of Benoit.
+
+ Benoit is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Benoit is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without
+ even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Benoit.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::benoit::configuration::Configuration;
+use crate::benoit::launcher::Launcher;
+
+use std::env::args;
+
+impl Launcher {
+ #[must_use]
+ pub(super) fn parse_arguments(&self) -> (Configuration, bool) {
+ let mut arguments = args();
+
+ return match arguments.nth(0x1) {
+ Some(argument) => {
+ if argument == "--help" { Launcher::print_help() };
+
+ let configuration = match Configuration::load(argument.as_str()) {
+ Ok( configuration) => configuration,
+ Err(message) => panic!("error parsing configuration: {message}"),
+ };
+
+ (configuration, false)
+ },
+ _ => (Configuration::default(), true),
+ };
+ }
+}
diff --git a/source/benoit/benoit/launcher/print_help.rs b/source/benoit/benoit/launcher/print_help.rs
new file mode 100644
index 0000000..4d03053
--- /dev/null
+++ b/source/benoit/benoit/launcher/print_help.rs
@@ -0,0 +1,65 @@
+/*
+ Copyright 2021, 2023 Gabriel Bjørnager Jensen.
+
+ This file is part of Benoit.
+
+ Benoit is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Benoit is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without
+ even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Benoit.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::benoit::launcher::Launcher;
+
+use std::process::exit;
+
+impl Launcher {
+ pub(super) fn print_help() -> ! {
+ println!("Usage:");
+ println!(" benoit [--help] [path]");
+ println!();
+ println!("Configuration:");
+ println!(" thread_count 0..=4294967295");
+ println!();
+ println!(" fractal \"mandelbrot\"|\"multibrot3\"|\"multibrot4\"|\"burningship\"|\"tricorn\"");
+ 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!();
+ 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!("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!();
+
+ exit(0x0);
+ }
+}
diff --git a/source/benoit/benoit/launcher/print_message.rs b/source/benoit/benoit/launcher/print_message.rs
new file mode 100644
index 0000000..5f9314e
--- /dev/null
+++ b/source/benoit/benoit/launcher/print_message.rs
@@ -0,0 +1,36 @@
+/*
+ Copyright 2021, 2023 Gabriel Bjørnager Jensen.
+
+ This file is part of Benoit.
+
+ Benoit is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Benoit is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without
+ even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Benoit.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::benoit::VERSION;
+use crate::benoit::launcher::Launcher;
+
+impl Launcher {
+ pub(super) fn print_message() {
+ println!();
+ println!(" \u{1B}[1mBENO\u{CE}T\u{1B}[0m {:X}.{:X}.{:X}", VERSION.0, VERSION.1, VERSION.2);
+ println!(" Copyright 2021, 2023 \u{1B}[1mGabriel Bj\u{F8}rnager Jensen\u{1B}[0m.");
+ println!();
+ println!(" \u{1B}[3mLe p\u{E8}re cogita et c'est pourquoi il fut.\u{1B}[0m");
+ println!();
+ }
+}
diff --git a/source/benoit/benoit/launcher/run.rs b/source/benoit/benoit/launcher/run.rs
new file mode 100644
index 0000000..f949e6a
--- /dev/null
+++ b/source/benoit/benoit/launcher/run.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::app::App;
+use crate::benoit::launcher::Launcher;
+use crate::benoit::script::Script;
+
+use std::thread::available_parallelism;
+
+impl Launcher {
+ #[must_use]
+ pub fn run(self) -> i32 {
+ Launcher::print_message();
+
+ let (mut configuration, interative) = 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
+ };
+
+ self.setup(configuration.thread_count);
+
+ return if interative {
+ eprintln!("running in iteractive mode");
+
+ let app = App::configure(configuration);
+ app.run()
+ } else {
+ eprintln!("running in script mode");
+
+ let script = Script::configure(configuration);
+ script.run()
+ };
+ }
+}
diff --git a/source/benoit/benoit/script/set_title.rs b/source/benoit/benoit/launcher/set_title.rs
index b8bcb56..8066369 100644
--- a/source/benoit/benoit/script/set_title.rs
+++ b/source/benoit/benoit/launcher/set_title.rs
@@ -21,7 +21,7 @@
If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::benoit::script::Script;
+use crate::benoit::launcher::Launcher;
#[cfg(windows)]
extern crate windows;
@@ -29,8 +29,8 @@ extern crate windows;
#[cfg(windows)]
use windows::Win32::System::Console::SetConsoleTitleA;
-impl Script {
- pub(super) fn set_title(title: &str) {
+impl Launcher {
+ pub fn set_title(title: &str) {
#[cfg(unix)]
{ eprint!("\u{1B}]0;{title}\u{07}") };
diff --git a/source/benoit/benoit/launcher/setup.rs b/source/benoit/benoit/launcher/setup.rs
new file mode 100644
index 0000000..3e9be56
--- /dev/null
+++ b/source/benoit/benoit/launcher/setup.rs
@@ -0,0 +1,41 @@
+/*
+ Copyright 2021, 2023 Gabriel Bjørnager Jensen.
+
+ This file is part of Benoit.
+
+ Benoit is free software: you can redistribute it
+ and/or modify it under the terms of the GNU
+ Affero General Public License as published by
+ the Free Software Foundation, either version 3
+ of the License, or (at your option) any later
+ version.
+
+ Benoit is distributed in the hope that it will
+ be useful, but WITHOUT ANY WARRANTY; without
+ even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU
+ Affero General Public License along with Benoit.
+ If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use crate::benoit::VERSION;
+use crate::benoit::launcher::Launcher;
+use crate::benoit::palette::fill_palettes;
+
+extern crate rayon;
+
+use rayon::ThreadPoolBuilder;
+
+impl Launcher {
+ pub(super) fn setup(&self, thread_count: u32) {
+ Launcher::set_title(format!("BENO\u{CE}T {:X}.{:X}.{:X}", VERSION.0, VERSION.1, VERSION.2).as_str());
+
+ fill_palettes();
+
+ eprintln!("using {} threads", thread_count);
+ ThreadPoolBuilder::new().num_threads(thread_count as usize).build_global().unwrap();
+ }
+}
diff --git a/source/benoit/benoit/script.rs b/source/benoit/benoit/script.rs
index 8cff0ca..2b1a7d0 100644
--- a/source/benoit/benoit/script.rs
+++ b/source/benoit/benoit/script.rs
@@ -34,7 +34,6 @@ pub mod animate;
pub mod configure;
pub mod dump_frame;
pub mod run;
-pub mod set_title;
pub mod still;
pub struct Script {
diff --git a/source/benoit/benoit/script/dump_frame.rs b/source/benoit/benoit/script/dump_frame.rs
index 84125e1..c3f1f46 100644
--- a/source/benoit/benoit/script/dump_frame.rs
+++ b/source/benoit/benoit/script/dump_frame.rs
@@ -24,6 +24,7 @@
use crate::benoit::complex::Complex;
use crate::benoit::fractal::Fractal;
use crate::benoit::image::{Image, ImageFormat};
+use crate::benoit::launcher::Launcher;
use crate::benoit::palette::Palette;
use crate::benoit::render::Render;
use crate::benoit::script::Script;
@@ -49,7 +50,7 @@ impl Script {
image_format: ImageFormat,
) {
eprint!("\"{name}\" (2^{:.9}x): rendering...", zoom.to_f64().log2());
- Script::set_title("Rendering...");
+ Launcher::set_title("Rendering...");
let time_start = Instant::now();
@@ -63,19 +64,19 @@ impl Script {
let render_time = time_start.elapsed();
eprint!(" {:.3}ms, colouring...", render_time.as_micros() as f32 / 1000.0);
- Script::set_title("Colouring...");
+ Launcher::set_title("Colouring...");
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);
- Script::set_title("Dumping...");
+ Launcher::set_title("Dumping...");
let path = format!("{dump_path}/{name}");
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);
- Script::set_title("Done");
+ Launcher::set_title("Done");
}
}
diff --git a/source/benoit/main.rs b/source/benoit/main.rs
index eb857b8..42822d3 100644
--- a/source/benoit/main.rs
+++ b/source/benoit/main.rs
@@ -23,59 +23,13 @@
mod benoit;
-use crate::benoit::VERSION;
-use crate::benoit::app::App;
-use crate::benoit::configuration::Configuration;
-use crate::benoit::palette::fill_palettes;
-use crate::benoit::script::Script;
+use crate::benoit::launcher::Launcher;
-extern crate rayon;
-
-use rayon::ThreadPoolBuilder;
-use std::env::args;
use std::process::exit;
-use std::thread::available_parallelism;
fn main() {
- println!();
- println!(" \u{1B}[1mBENO\u{CE}T\u{1B}[0m {:X}.{:X}.{:X}", VERSION.0, VERSION.1, VERSION.2);
- println!(" Copyright 2021, 2023 Gabriel Bj\u{F8}rnager Jensen.");
- println!();
- println!(" \u{1B}[3mLe p\u{E8}re cogita et c'est pourquoi il fut.\u{1B}[0m");
- println!();
-
- fill_palettes();
-
- let mut arguments = args();
-
- let (mut configuration, interative) = match arguments.nth(0x1) {
- Some(path) => (Configuration::load(path.as_str()), false),
- _ => (Configuration::default(), true),
- };
-
- configuration.thread_count = if configuration.thread_count == 0x0 {
- match available_parallelism() {
- Ok(ammount) => ammount.get() as u32,
- _ => 0x2, // We assume at least two threads.
- }
- } else {
- configuration.thread_count
- };
-
- eprintln!("using {} threads", configuration.thread_count);
- ThreadPoolBuilder::new().num_threads(configuration.thread_count as usize).build_global().unwrap();
-
- let code = if interative {
- eprintln!("running in iteractive mode");
-
- let app = App::configure(configuration);
- app.run()
- } else {
- eprintln!("running in script mode");
-
- let script = Script::configure(configuration);
- script.run()
- };
+ let launcher = Launcher::new();
+ let code = launcher.run();
exit(code);
}