use crate::{
gui::editor::{deep_solve, optimize_solve, retarget},
settings,
};
use indicatrix_cut_core::{Design, FreshDesignSpec, History, OptimizeOutcome, RemapRounding};
use std::{
cell::RefCell,
collections::BTreeSet,
sync::{
Arc, Mutex,
atomic::{AtomicU64, Ordering as AtomicOrdering},
},
};
pub(super) const GEAR_PRESETS: [i32; 6] = [96, 80, 77, 72, 64, 120];
const ANCHOR_EXPLAINER_SUPPRESS_KEY: &str = "anchor_explainer";
thread_local! {
static ANCHOR_EXPLAINER_DECIDED_THIS_SESSION: std::cell::Cell<bool> =
const { std::cell::Cell::new(false) };
}
fn anchor_explainer_is_suppressed() -> bool {
let settings_path = settings::store::default_settings_path();
settings::store::load_or_default(&settings_path)
.settings
.is_confirm_suppressed(ANCHOR_EXPLAINER_SUPPRESS_KEY)
}
pub(in crate::gui::editor) fn anchor_explainer_suppress_permanently() {
let settings_path = settings::store::default_settings_path();
let mut file = settings::store::load_or_default(&settings_path);
file.settings
.suppress_confirm(ANCHOR_EXPLAINER_SUPPRESS_KEY);
let _ = settings::store::save(&settings_path, &file);
}
#[must_use]
pub(in crate::gui::editor) fn should_open_anchor_explainer(
design_has_missing_anchor: bool,
) -> bool {
if !design_has_missing_anchor {
return false;
}
if ANCHOR_EXPLAINER_DECIDED_THIS_SESSION.with(std::cell::Cell::get) {
return false;
}
ANCHOR_EXPLAINER_DECIDED_THIS_SESSION.with(|decided| decided.set(true));
!anchor_explainer_is_suppressed()
}
pub(in crate::gui::editor) const ANGLE_NUDGE_COALESCE_WINDOW: std::time::Duration =
std::time::Duration::from_millis(1500);
pub(in crate::gui::editor) struct PendingGearRemap {
pub(in crate::gui::editor) from_gear: i32,
pub(in crate::gui::editor) to_gear: i32,
pub(in crate::gui::editor) symmetry_order: u32,
pub(in crate::gui::editor) mirror: bool,
pub(in crate::gui::editor) rounding: RemapRounding,
}
pub(in crate::gui::editor) enum PendingUnsavedAction {
New {
spec: FreshDesignSpec,
template_index: i32,
},
LoadSelected,
OpenNative,
}
pub(in crate::gui::editor) struct EditorState {
pub(in crate::gui::editor) design: Design,
pub(in crate::gui::editor) history: History,
pub(in crate::gui::editor) printed_proportions:
Option<indicatrix::geometry::stone_metrics::ExternalProportions>,
pub(in crate::gui::editor) generation: Arc<AtomicU64>,
pub(in crate::gui::editor) design_epoch: Arc<AtomicU64>,
pub(in crate::gui::editor) saved_generation: u64,
pub(in crate::gui::editor) pending_unsaved_action: Option<PendingUnsavedAction>,
pub(in crate::gui::editor) deep_solve: Option<deep_solve::DeepSolveHandle>,
pub(in crate::gui::editor) optimize: Option<optimize_solve::OptimizeSolveHandle>,
pub(in crate::gui::editor) pending_optimize: Arc<Mutex<Option<(OptimizeOutcome, u64)>>>,
pub(in crate::gui::editor) deep_solve_result_generation: Option<u64>,
pub(in crate::gui::editor) asc_filename: Option<String>,
pub(in crate::gui::editor) original_asc_text: Option<String>,
pub(in crate::gui::editor) source_entry_id: Option<i64>,
pub(in crate::gui::editor) used_placeholder: bool,
pub(in crate::gui::editor) pending_gear_remap: Option<PendingGearRemap>,
pub(in crate::gui::editor) pending_retarget: Option<(retarget::RetargetProposal, u64)>,
pub(in crate::gui::editor) multi_selected: BTreeSet<usize>,
pub(in crate::gui::editor) last_pushed_scratch: RefCell<super::history::PushedScratch>,
pub(in crate::gui::editor) material_combo_cache: RefCell<super::material::MaterialComboCache>,
pub(in crate::gui::editor) has_design: bool,
}
impl EditorState {
pub(in crate::gui::editor) fn fresh() -> Self {
let preform = indicatrix_cut_core::PreformSpec::cylinder(96, 1.5, 1.0, 1.5);
Self {
design: Design::fresh(preform, 96, 8, 1.54),
history: History::with_coalesce_window(ANGLE_NUDGE_COALESCE_WINDOW),
printed_proportions: None,
generation: Arc::new(AtomicU64::new(0)),
design_epoch: Arc::new(AtomicU64::new(0)),
saved_generation: 0,
pending_unsaved_action: None,
deep_solve: None,
optimize: None,
pending_optimize: Arc::new(Mutex::new(None)),
deep_solve_result_generation: None,
asc_filename: None,
original_asc_text: None,
pending_gear_remap: None,
pending_retarget: None,
multi_selected: BTreeSet::new(),
source_entry_id: None,
used_placeholder: false,
last_pushed_scratch: RefCell::new(super::history::PushedScratch::default()),
material_combo_cache: RefCell::new(super::material::MaterialComboCache::default()),
has_design: false,
}
}
pub(in crate::gui::editor) fn fresh_from_spec(spec: FreshDesignSpec) -> Self {
Self {
design: Design::fresh_from_spec(spec),
history: History::with_coalesce_window(ANGLE_NUDGE_COALESCE_WINDOW),
printed_proportions: None,
generation: Arc::new(AtomicU64::new(0)),
design_epoch: Arc::new(AtomicU64::new(0)),
saved_generation: 0,
pending_unsaved_action: None,
deep_solve: None,
optimize: None,
pending_optimize: Arc::new(Mutex::new(None)),
deep_solve_result_generation: None,
asc_filename: None,
original_asc_text: None,
pending_gear_remap: None,
pending_retarget: None,
multi_selected: BTreeSet::new(),
source_entry_id: None,
used_placeholder: false,
last_pushed_scratch: RefCell::new(super::history::PushedScratch::default()),
material_combo_cache: RefCell::new(super::material::MaterialComboCache::default()),
has_design: true,
}
}
pub(in crate::gui::editor) fn replace_wholesale(&mut self, mut replacement: Self) {
replacement.generation = Arc::clone(&self.generation);
let now = replacement.generation.fetch_add(1, AtomicOrdering::Relaxed) + 1;
replacement.saved_generation = now;
replacement.design_epoch = Arc::clone(&self.design_epoch);
replacement
.design_epoch
.fetch_add(1, AtomicOrdering::Relaxed);
*self = replacement;
}
}