use super::{
RETARGET_ASYNC,
apply::revert_ghost_to_live_design,
build_retarget_candidate,
check_run::{candidate_anchors, is_invalid},
material::{target_display_name, target_material_selection},
};
use crate::{
MainWindow, RetargetModel,
bridge::render_thread::RenderContext,
gui::{
editor::{
retarget::{AnchorChange, RetargetProposal},
state::EditorState,
view,
},
solid_preview::preview_state::SolidPreviewState,
},
};
use indicatrix_cut_core::{Design, MaterialSelection};
use slint::ComponentHandle;
use std::sync::{Arc, Mutex, PoisonError, atomic::Ordering as AtomicOrdering};
#[derive(Debug, Clone, PartialEq)]
pub(in crate::gui::editor) struct RetargetKeepGuard {
proposal: RetargetProposal,
generation: u64,
anchors: Vec<AnchorChange>,
material: MaterialSelection,
}
pub(in crate::gui::editor) struct RetargetCompareInputs {
pub(in crate::gui::editor) current: Design,
pub(in crate::gui::editor) candidate: Design,
pub(in crate::gui::editor) after_label: String,
pub(in crate::gui::editor) guard: RetargetKeepGuard,
}
fn pending_proposal(st: &EditorState) -> Option<(RetargetProposal, u64)> {
RETARGET_ASYNC
.with(|cell| cell.borrow().pending.clone())
.or_else(|| st.pending_retarget.clone())
}
fn target_selection(
ui: &MainWindow,
render_ctx: &Arc<Mutex<RenderContext>>,
design: &Design,
) -> MaterialSelection {
let custom = render_ctx
.lock()
.unwrap_or_else(PoisonError::into_inner)
.custom_materials
.as_ref()
.clone();
let model = ui.global::<RetargetModel>();
target_material_selection(
design,
&custom,
model.get_target_material_index(),
&model.get_target_ri_override_text(),
)
}
#[must_use]
pub(in crate::gui::editor) fn current_retarget_guard(
ui: &MainWindow,
st: &EditorState,
render_ctx: &Arc<Mutex<RenderContext>>,
) -> Option<RetargetKeepGuard> {
let (proposal, generation) = pending_proposal(st)?;
let material = target_selection(ui, render_ctx, &st.design);
let anchors = candidate_anchors(generation).unwrap_or_default();
Some(RetargetKeepGuard {
proposal,
generation,
anchors,
material,
})
}
pub(in crate::gui::editor) fn retarget_compare_inputs(
ui: &MainWindow,
st: &EditorState,
render_ctx: &Arc<Mutex<RenderContext>>,
) -> Result<RetargetCompareInputs, String> {
let guard = current_retarget_guard(ui, st, render_ctx)
.ok_or_else(|| "No retarget proposal to compare yet.".to_string())?;
if guard.generation != st.generation.load(AtomicOrdering::Relaxed) {
return Err(
"The design changed since this proposal was built -- click Recompute first."
.to_string(),
);
}
let anchors = candidate_anchors(guard.generation)?;
let mut candidate = build_retarget_candidate(&st.design, &guard.proposal, &anchors)
.ok_or_else(|| {
"This proposal no longer applies to the current design -- click Recompute first."
.to_string()
})?;
candidate.material = guard.material.clone();
let mode = if ui.global::<RetargetModel>().get_mode_index() == 1 {
"Optimize"
} else {
"Shift"
};
let verdict = if is_invalid(guard.generation) {
", not valid"
} else {
""
};
let after_label = format!(
"Retarget to {} ({mode}{verdict})",
target_display_name(&guard.material)
);
Ok(RetargetCompareInputs {
current: st.design.clone(),
candidate,
after_label,
guard,
})
}
pub(in crate::gui::editor) fn discard_retarget_preview(
ui: &MainWindow,
render_ctx: &Arc<Mutex<RenderContext>>,
preview_state: &Arc<SolidPreviewState>,
solid_last_solved: &view::SolidLastSolved,
st: &EditorState,
) {
let model = ui.global::<RetargetModel>();
if !model.get_preview_enabled() {
return;
}
model.set_preview_enabled(false);
revert_ghost_to_live_design(ui, render_ctx, preview_state, solid_last_solved, st);
}