use super::{
CachedMesh, DiagramConfig, MeshCache, SolidMesh, SolidRasterizer,
controller::SolidPreviewState,
diagram2d, render_edges_layer,
request::RedrawRequest,
sink::PreviewFrame,
state::{DiagramMemory, WorkerMemory, resolve_request_state},
to_diagram_pixel_buffer, to_pixel_buffer,
types::PickBuffer,
};
use indicatrix::optics::raytracer::Camera;
use std::sync::{
Arc, PoisonError,
mpsc::{self, Sender},
};
type DiagramOutputs = (
Option<slint::SharedPixelBuffer<slint::Rgba8Pixel>>,
bool,
Option<PickBuffer>,
Option<PickBuffer>,
Option<Vec<String>>,
Option<Vec<Option<usize>>>,
);
fn build_diagram_outputs(
mesh_cache: &mut MeshCache,
planes: &[(glam::Vec3, f32)],
size: (u32, u32),
view_mode: u8,
last_diagram: &DiagramMemory,
) -> DiagramOutputs {
if view_mode != 3 {
return (None, false, None, None, None, None);
}
let (image, has_diagram, pick, tooth_pick) =
mesh_cache
.get_or_build(planes)
.map_or((None, false, None, None), |cached| {
let config = DiagramConfig {
width: size.0,
height: size.1,
gear_teeth: last_diagram.gear_teeth,
gear_reference_angle: last_diagram.gear_reference_angle,
symmetry_order: last_diagram.symmetry_order,
mirror: last_diagram.mirror,
};
let diagram_frame = last_diagram.enlarged_panel.map_or_else(
|| diagram2d::render_diagram(&cached.mesh, &config, &last_diagram.style),
|panel| {
diagram2d::render_diagram_single_panel(
&cached.mesh,
&config,
&last_diagram.style,
panel,
)
},
);
let image = to_diagram_pixel_buffer(&diagram_frame);
let tooth_pick = PickBuffer {
width: diagram_frame.width,
height: diagram_frame.height,
pick: diagram_frame.tooth,
};
let pick = PickBuffer {
width: diagram_frame.width,
height: diagram_frame.height,
pick: diagram_frame.pick,
};
(Some(image), true, Some(pick), Some(tooth_pick))
});
(
image,
has_diagram,
pick,
tooth_pick,
Some(last_diagram.hover_text.clone()),
Some(last_diagram.facet_tier.clone()),
)
}
pub fn render_request(
mesh_cache: &mut MeshCache,
rasterizer: &mut SolidRasterizer,
edges_rasterizer: &mut SolidRasterizer,
memory: &mut WorkerMemory,
request: RedrawRequest,
) -> Option<PreviewFrame> {
let (planes, camera_pose, size, view_mode, style, solved, stale, unsolvable_status, generation) =
resolve_request_state(memory, mesh_cache, request)?;
rasterizer.resize(size.0, size.1);
let camera = Camera::new(
camera_pose.yaw,
camera_pose.pitch,
camera_pose.distance,
42.0,
);
let (image, has_solid, status) = if let Some(cached) = mesh_cache.get_or_build(&planes) {
rasterizer.render_prepared(cached, &camera, &style);
(to_pixel_buffer(rasterizer), true, String::new())
} else if let Some(cached) = mesh_cache.last_closed() {
let dimmed = super::state::dim_style(style.clone());
rasterizer.render_prepared(cached, &camera, &dimmed);
(
to_pixel_buffer(rasterizer),
true,
mesh_cache.status_message(),
)
} else {
rasterizer.render(&SolidMesh::default(), &camera, &style);
(
to_pixel_buffer(rasterizer),
false,
mesh_cache.status_message(),
)
};
let status = unsolvable_status.unwrap_or(status);
let pick = PickBuffer {
width: rasterizer.width,
height: rasterizer.height,
pick: rasterizer.pick.clone(),
};
let edges_image = if view_mode == 2 {
mesh_cache.get_or_build(&planes).map(|cached| {
edges_rasterizer.resize(size.0, size.1);
render_edges_layer(edges_rasterizer, cached, &camera, &style);
to_pixel_buffer(edges_rasterizer)
})
} else {
None
};
let (
diagram_image,
has_diagram,
diagram_pick,
diagram_tooth_pick,
diagram_hover_text,
diagram_facet_tier,
) = build_diagram_outputs(mesh_cache, &planes, size, view_mode, &memory.diagram);
let hover_text = memory.diagram.hover_text.clone();
let facet_tier = memory.diagram.facet_tier.clone();
let mesh_bounding_radius = mesh_cache
.get_or_build(&planes)
.map(CachedMesh::bounding_radius)
.or_else(|| mesh_cache.last_closed().map(CachedMesh::bounding_radius))
.unwrap_or(super::sink::DEFAULT_MESH_BOUNDING_RADIUS);
Some(PreviewFrame {
image,
has_solid,
status,
solved,
stale,
pick,
edges_image,
diagram_image,
has_diagram,
diagram_pick,
diagram_tooth_pick,
diagram_hover_text,
diagram_facet_tier,
planes,
hover_text,
facet_tier,
generation,
mesh_bounding_radius,
})
}
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);
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::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
}
}