use super::{controller::unix_now, wiring::Deps};
use crate::{
MainWindow, TiltVideoExportModel,
gui::{render::render_export::wiring::design_naming, tilt::video_export},
};
use indicatrix::color::metrics::PROFILE_AZIMUTHS_DEG;
use indicatrix_render_jobs::{
codec,
job::{DesignInfo, JOB_FORMAT_VERSION},
paths::reserve_unique_folder,
};
use indicatrix_vault::model::render_job::NewRenderJob;
use slint::ComponentHandle;
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct VideoFacts {
pub start_deg: f64,
pub end_deg: f64,
pub step_deg: f64,
pub frames: usize,
pub fps: u32,
pub width: u32,
pub height: u32,
pub samples: u32,
}
pub fn video_label(design: &str, axis_deg: f32) -> String {
format!("{design} \u{b7} tilt video \u{b7} axis {axis_deg:.0}\u{b0}")
}
fn degrees(value: f64) -> String {
let text = format!("{value:.2}");
text.trim_end_matches('0').trim_end_matches('.').to_string()
}
pub fn video_summary(facts: &VideoFacts) -> String {
format!(
"{}\u{b0} to {}\u{b0} in {}\u{b0} steps \u{b7} {} frames \u{b7} {} fps \u{b7} {} \u{d7} {} \u{b7} {} samples",
degrees(facts.start_deg),
degrees(facts.end_deg),
degrees(facts.step_deg),
facts.frames,
facts.fps,
facts.width,
facts.height,
facts.samples,
)
}
fn refuse(ui: &MainWindow, message: &str) {
let model = ui.global::<TiltVideoExportModel>();
model.set_has_error(true);
model.set_status_message(message.into());
}
pub(super) fn add_tilt_video_job(ui: &MainWindow, deps: &Deps, axis_index: i32) {
let render_ctx = deps.render_ctx.clone();
let settings_store = deps.settings_store.clone();
let deps = deps.clone();
video_export::prepare_queued_video(
ui,
&render_ctx,
&settings_store,
axis_index,
move |ui, prepared| match prepared {
Ok(None) => {
let model = ui.global::<TiltVideoExportModel>();
model.set_has_error(false);
model.set_status_message("Nothing was added.".into());
}
Ok(Some(request)) => queue_request(ui, &deps, request, axis_index),
Err(message) => refuse(
ui,
&message.replacen("Nothing was exported", "Nothing was added", 1),
),
},
);
}
fn queue_request(
ui: &MainWindow,
deps: &Deps,
mut request: video_export::run::VideoExportRequest,
axis_index: i32,
) {
let taken = {
let db = deps
.db
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
db.render_job_output_paths()
};
let taken: Vec<PathBuf> = match taken {
Ok(paths) => paths.into_iter().map(PathBuf::from).collect(),
Err(error) => {
refuse(
ui,
&format!("The render job list could not be read: {error}"),
);
return;
}
};
request.out_dir = reserve_unique_folder(&request.out_dir, &taken, &|path| path.exists());
let (design, designer, shape, ri) = design_naming(ui, &deps.render_ctx);
let axis_deg = usize::try_from(axis_index)
.ok()
.and_then(|index| PROFILE_AZIMUTHS_DEG.get(index).copied())
.unwrap_or(0.0);
let label = video_label(&design, axis_deg);
let summary = video_summary(&VideoFacts {
start_deg: request.start_deg,
end_deg: request.end_deg,
step_deg: request.step_deg,
frames: request.total_frames,
fps: request.fps,
width: request.width,
height: request.height,
samples: request.samples_per_pixel,
});
let now = unix_now();
let design_info = DesignInfo {
title: design,
designer,
shape,
ri,
material: request.scene.material.name.clone(),
};
let video_name = request.out_name.clone();
let text =
super::convert::build_video_job(&request, label.clone(), design_info, video_name, now)
.and_then(|job| codec::to_text(&job).map_err(|e| e.to_string()));
let text = match text {
Ok(text) => text,
Err(message) => {
refuse(ui, &message);
return;
}
};
let output = request.out_dir.to_string_lossy().into_owned();
let stored = {
let db = deps
.db
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
db.add_render_job(
&NewRenderJob {
kind: "tilt_video",
label: &label,
summary: &summary,
frames_total: u32::try_from(request.total_frames).unwrap_or(u32::MAX),
output_path: &output,
snapshot_version: JOB_FORMAT_VERSION,
snapshot: &text,
},
now,
)
};
match stored {
Ok(_) => {
super::controller::with_controller(super::controller::JobController::refresh);
let message = "Added 1 render job to the queue. Start it from File > Render Jobs...";
let model = ui.global::<TiltVideoExportModel>();
model.set_has_error(false);
model.set_status_message(message.into());
crate::gui::show_toast(ui, message, "info");
}
Err(error) => refuse(ui, &format!("The job could not be saved: {error}")),
}
}