use super::RunProvenance;
use crate::{
EditorModel, MainWindow,
bridge::render_thread::RenderContext,
gui::{
batch::tilt,
editor::{
auto_solve,
material_lookup::{EditorMaterialLookup, resolved_gem_material},
state::EditorState,
},
show_toast,
},
};
use indicatrix::optics::materials::GemMaterial;
use indicatrix_cut_core::Design;
use indicatrix_vault::{db::sqlite::Database, model::tilt_curves::TiltPerformanceCurves};
use slint::ComponentHandle;
use std::{
cell::RefCell,
rc::Rc,
sync::{
Arc, Mutex,
atomic::{AtomicBool, AtomicU64, Ordering as AtomicOrdering},
},
thread,
};
pub(in crate::gui::editor) fn setup_batch_tilt_for_open_design_callback(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
render_ctx: &Arc<Mutex<RenderContext>>,
db: &Arc<Mutex<Database>>,
) {
let state = Rc::clone(state);
let render_ctx = Arc::clone(render_ctx);
let db = Arc::clone(db);
let ui_weak = ui.as_weak();
let run_epoch = Arc::new(AtomicU64::new(0));
ui.global::<EditorModel>()
.on_compute_tilt_curves_for_open_design(move || {
let Some(ui) = ui_weak.upgrade() else {
return;
};
begin_batch_tilt_for_open_design(&ui, &state, &render_ctx, &db, &run_epoch);
});
}
struct BatchTiltRun {
design: Design,
custom_materials: Vec<GemMaterial>,
source_entry_id: Option<i64>,
provenance: RunProvenance,
run_epoch_done: Arc<AtomicU64>,
this_run: u64,
db: Arc<Mutex<Database>>,
ui_weak: slint::Weak<MainWindow>,
}
impl BatchTiltRun {
fn is_current(&self) -> bool {
self.run_epoch_done.load(AtomicOrdering::Relaxed) == self.this_run
}
}
fn begin_batch_tilt_for_open_design(
ui: &MainWindow,
state: &Rc<RefCell<EditorState>>,
render_ctx: &Arc<Mutex<RenderContext>>,
db: &Arc<Mutex<Database>>,
run_epoch: &Arc<AtomicU64>,
) {
let st = state.borrow();
let design = st.design.clone();
let source_entry_id = st.source_entry_id;
let provenance = RunProvenance::capture(&st);
drop(st);
let custom_materials = render_ctx
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.custom_materials
.as_ref()
.clone();
let this_run = run_epoch.fetch_add(1, AtomicOrdering::Relaxed) + 1;
let run = BatchTiltRun {
design,
custom_materials,
source_entry_id,
provenance,
run_epoch_done: Arc::clone(run_epoch),
this_run,
db: Arc::clone(db),
ui_weak: ui.as_weak(),
};
show_toast(
ui,
"Computing tilt-performance curves for this design...",
"info",
);
thread::spawn(move || run_batch_tilt_for_open_design(run));
}
fn run_batch_tilt_for_open_design(run: BatchTiltRun) {
let solved = match run.design.solve() {
Ok(solved) => solved,
Err(e) => {
let ui_weak = run.ui_weak.clone();
let run_epoch_done = Arc::clone(&run.run_epoch_done);
let this_run = run.this_run;
let _ = ui_weak.upgrade_in_event_loop(move |ui| {
if run_epoch_done.load(AtomicOrdering::Relaxed) == this_run {
show_toast(&ui, &format!("Cannot compute tilt curves: {e}"), "error");
}
});
return;
}
};
let planes = auto_solve::design_to_gpu_planes_from_solved(&run.design, &solved);
let lookup = EditorMaterialLookup::new(&run.custom_materials);
let material = resolved_gem_material(&run.design.material, &lookup);
let cancel = AtomicBool::new(false);
let curves = tilt::tilt_curves_for_planes(&planes, &material, None, &cancel);
let ui_weak = run.ui_weak.clone();
let _ = ui_weak.upgrade_in_event_loop(move |ui| {
apply_batch_tilt_result(&ui, curves, &run);
});
}
fn apply_batch_tilt_result(
ui: &MainWindow,
curves: Option<TiltPerformanceCurves>,
run: &BatchTiltRun,
) {
if !run.is_current() {
return;
}
let Some(curves) = curves else {
show_toast(
ui,
"Tilt-curve computation failed for this design.",
"error",
);
return;
};
if run.provenance.is_stale() {
show_toast(
ui,
"Result discarded: the design changed while computing tilt curves. Re-run to \
save an up-to-date result.",
"warning",
);
return;
}
match run.source_entry_id {
Some(entry_id) => {
if tilt::save_tilt_curves_for_entry(&run.db, entry_id, &curves) {
show_toast(
ui,
"Saved tilt-performance curves for this design.",
"success",
);
} else {
show_toast(
ui,
"Could not save tilt-performance curves for this design.",
"error",
);
}
}
None => show_toast(
ui,
"Computed tilt-performance curves -- save this design to the \
catalogue to keep them.",
"info",
),
}
}