/*
Copyright 2021, 2023-2024 Gabriel Bjørnager Jen-
sen.
This file is part of benoit-cli.
benoit-cli is free software: you can redistrib-
ute it and/or modify it under the terms of the
GNU General Public License as published by the
Free Software Foundation, either version 3 of
the License, or (at your option) any later ver-
sion.
benoit-cli is distributed in the hope that it
will be useful, but WITHOUT ANY WARRANTY; with-
out even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Gene-
ral Public License along with benoit-cli. If
not, see .
*/
use crate::set_title;
use crate::error::Error;
use crate::instance::Instance;
use crate::config::Config;
use crate::keyframe::Keyframe;
use benoit::log;
use benoit::render::{Render, RenderConfig};
use benoit::stopwatch::Stopwatch;
use std::path::Path;
#[cfg(feature = "notify")]
use notify_rust::{Notification, Timeout};
impl Instance {
/// Consumes the programme instance and starts execution.
///
/// # Errors
///
/// Returns any error that occurs during executiom, if any at all.
///
/// This does not include panics, which are *not* handled by us (at the moment).
#[allow(clippy::unused_self)]
pub fn run(self, config: &Config) -> Result<(), Error> {
let mut render = Render::new(config.render_size.0, config.render_size.1)?;
let mut dump_frame = |keyframe: &Keyframe, path: &Path| -> Result<(), Error> {
let render_config = RenderConfig {
fractal: config.fractal.clone(),
inverse: config.inverse,
julia: config.julia,
max_iter_count: keyframe.max_iter_count,
centre: keyframe.centre.clone(),
seed: keyframe.seed.clone(),
zoom: keyframe.zoom.clone(),
};
run_job("render", || render.generate(&render_config));
run_job("paint", || render.plot(config.palette, keyframe.colour_range))?;
run_job("dump", || render.dump_image(path))?;
Ok(())
};
let mut global_timer = Stopwatch::from_now();
for keyframe in config.start.interpolate_between(&config.stop, config.frame_count) {
log!(value, keyframe);
let path = {
let mut path = config.output_directory.clone();
path.push(format!("frame{:010}.png", keyframe.frame));
path
};
eprint!("\u{001B}[1m");
eprintln!("frame no. {:.>10} \u{2510}", keyframe.frame);
eprintln!(" of {:.>10} total \u{2534}> \"{}\"", config.stop.frame, path.display());
eprint!("\u{001B}[22m");
let frame_timer = Stopwatch::test_result(|| dump_frame(&keyframe, &path))?;
global_timer.note();
eprintln!(" = \u{001B}[4m{frame_timer}\u{001B}[24m\u{001B}[2m ({global_timer} total)\u{001B}[22m");
eprintln!();
}
global_timer.note();
eprintln!("\u{001B}[1manimation completed\u{001B}[22m - took {global_timer} in total");
set_title!("done");
#[cfg(feature = "notify")]
{
// We don't really care if the notification fails.
let _ = Notification::new()
.appname("Benoit")
.summary("\u{2728} Animation completed! \u{2728}")
.body(&format!("Took {global_timer}."))
.icon("benoit")
.timeout(Timeout::Never)
.show();
}
Ok(())
}
}
pub fn run_job Output, Output>(name: &str, func: F) -> Output {
eprint!("\u{2610} running job `{name}` ...");
set_title!("{name}");
let (timer, result) = Stopwatch::test(func);
// Indent to the 40th column; two have already been
// used for the bullet:
eprintln!("\r\u{2612} \u{001B}[2m{: <38}\u{001B}[22m\u{001B}[1m+ {timer}\u{001B}[22m", format!("completed job `{name}`"));
result
}
//
// ## ##
// # # #
// # #
// # #
// # #
// # #
// # #
// #
//