use super::{
MeshCache, SolidRasterizer, controller::SolidPreviewState, request::RedrawRequest,
sink::PreviewFrame, state::WorkerMemory, to_diagram_pixel_buffer, to_pixel_buffer,
types::PickBuffer,
};
use std::sync::{
Arc, PoisonError,
mpsc::{self, Sender},
};
pub fn render_request(
mesh_cache: &mut MeshCache,
rasterizer: &mut SolidRasterizer,
edges_rasterizer: &mut SolidRasterizer,
memory: &mut WorkerMemory,
request: RedrawRequest,
) -> Option<PreviewFrame> {
let planned = matches!(request, RedrawRequest::Planned(_));
let frame = indicatrix_solid::preview::render_request(
mesh_cache,
rasterizer,
edges_rasterizer,
memory,
request,
)?;
let image = to_pixel_buffer(rasterizer);
let edges_image = frame.has_edges.then(|| to_pixel_buffer(edges_rasterizer));
let has_diagram = frame.diagram.is_some();
let (diagram_image, diagram_pick, diagram_tooth_pick, diagram_panel_pick) =
frame.diagram.map_or((None, None, None, None), |diagram| {
let image = to_diagram_pixel_buffer(&diagram);
let panel_pick = PickBuffer {
width: diagram.width,
height: diagram.height,
pick: diagram.panel_tags(),
};
let tooth_pick = PickBuffer {
width: diagram.width,
height: diagram.height,
pick: diagram.tooth,
};
let pick = PickBuffer {
width: diagram.width,
height: diagram.height,
pick: diagram.pick,
};
(Some(image), Some(pick), Some(tooth_pick), Some(panel_pick))
});
Some(PreviewFrame {
image,
has_solid: frame.has_solid,
status: frame.status,
solved: frame.solved,
stale: frame.stale,
pick: frame.pick,
edges_image,
diagram_image,
has_diagram,
diagram_pick,
diagram_tooth_pick,
diagram_panel_pick,
diagram_hover_text: frame.diagram_hover_text,
diagram_facet_tier: frame.diagram_facet_tier,
planes: frame.planes,
hover_text: frame.hover_text,
facet_tier: frame.facet_tier,
generation: frame.generation,
planned,
mesh_bounding_radius: frame.mesh_bounding_radius,
geometry: frame.geometry,
})
}
impl SolidPreviewState {
pub(super) fn submit(&self, request: RedrawRequest) {
let tx = {
let mut guard = self.wake.lock().unwrap_or_else(PoisonError::into_inner);
if guard.is_none() {
*guard = Some(self.spawn_worker());
}
guard
.clone()
.expect("just initialized above if it was empty")
};
if self.gate.submit(request).is_some() {
let _ = tx.send(());
}
}
fn spawn_worker(&self) -> Sender<()> {
let (tx, rx) = mpsc::channel::<()>();
let sink = Arc::clone(&self.sink);
let gate = Arc::clone(&self.gate);
let outlines = Arc::clone(&self.outlines);
std::thread::spawn(move || {
let mut mesh_cache = MeshCache::default();
let mut rasterizer = SolidRasterizer::new(1, 1);
let mut edges_rasterizer = SolidRasterizer::new(1, 1);
let mut memory = WorkerMemory {
outlines: Some(outlines),
..WorkerMemory::default()
};
for () in rx {
let Some(request) = gate.take() else {
continue;
};
let Some(frame) = render_request(
&mut mesh_cache,
&mut rasterizer,
&mut edges_rasterizer,
&mut memory,
request,
) else {
continue;
};
sink.apply(frame);
}
});
tx
}
}