use super::{
FacetMap, SolidPreviewState, SolidStyle, live_update,
request::{PlanJob, PlannedFrame, RedrawRequest},
};
use std::sync::{
Arc, PoisonError,
mpsc::{self, Sender},
};
pub fn build_planned_frame(job: PlanJob, budget: std::time::Duration) -> PlannedFrame {
let PlanJob {
design,
dirty,
last_solved,
camera,
size,
selected_tier,
n_d,
view_mode,
generation,
show_preform,
enlarged_panel,
tier_cutoff,
} = job;
let plan = live_update::plan_preview(
&design,
last_solved.as_deref(),
&dirty,
budget,
&live_update::RealSolver,
tier_cutoff,
);
let empty_pending = std::collections::BTreeSet::new();
let pending_tiers = match &plan.freshness {
live_update::Freshness::Stale { pending } => pending,
_ => &empty_pending,
};
let facet_map = FacetMap::from_design(&design, plan.solved.as_deref().unwrap_or(&[]));
let overlay = facet_map.overlay_flags(&design, n_d, selected_tier, pending_tiers);
let unsolvable_status = match &plan.freshness {
live_update::Freshness::Unsolvable(err) => {
Some(format!("Preview cannot be solved: {err}."))
}
_ => None,
};
let is_unsolvable = unsolvable_status.is_some();
let preform_plane_count = facet_map.preform_plane_count();
let style = SolidStyle {
flagged: overlay.flagged,
pending: overlay.pending,
selected: overlay.selected,
preform_plane_count,
show_preform,
..SolidStyle::default()
};
let stale = matches!(plan.freshness, live_update::Freshness::Stale { .. });
let solved = if is_unsolvable {
last_solved
} else {
plan.solved
};
PlannedFrame {
design,
planes: plan.planes,
style,
solved,
stale,
unsolvable_status,
camera,
size,
view_mode,
generation,
n_d,
enlarged_panel,
}
}
impl SolidPreviewState {
pub(super) fn submit_plan(&self, job: PlanJob) {
let tx = {
let mut guard = self
.plan_wake
.lock()
.unwrap_or_else(PoisonError::into_inner);
if guard.is_none() {
*guard = Some(self.spawn_plan_worker());
}
guard
.clone()
.expect("just initialized above if it was empty")
};
if self.plan_gate.submit(job).is_some() {
let _ = tx.send(());
}
}
fn spawn_plan_worker(&self) -> Sender<()> {
let (tx, rx) = mpsc::channel::<()>();
let plan_gate = Arc::clone(&self.plan_gate);
let self_weak = self
.self_weak
.lock()
.unwrap_or_else(PoisonError::into_inner)
.clone();
std::thread::spawn(move || {
for () in rx {
let Some(job) = plan_gate.take() else {
continue;
};
let frame = build_planned_frame(job, live_update::DEFAULT_PREVIEW_BUDGET);
if let Some(state) = self_weak.upgrade() {
state.submit(RedrawRequest::Planned(Box::new(frame)));
}
}
});
tx
}
}