use super::environment;
use indicatrix::{
color::metrics::{SweepProgress, total_evaluations},
optics::{LightingPreset, materials::GemMaterial},
};
use indicatrix_editor::{metric_deltas::measure_tilt_average_geom, sweep::TiltAverages};
use indicatrix_solid::preview::StoneGeometryBuf;
use std::{
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
thread,
};
const PROGRESS_EVERY: usize = 16;
pub(in crate::gui::editor::compare) struct TiltRequest {
pub(in crate::gui::editor::compare) run: u64,
pub(super) cancel: Arc<AtomicBool>,
pub(super) sides: [(StoneGeometryBuf, GemMaterial); 2],
pub(super) lighting: LightingPreset,
}
pub(in crate::gui::editor::compare) enum TiltOutcome {
Averages {
before: TiltAverages,
after: TiltAverages,
},
Stopped,
}
pub(in crate::gui::editor::compare) enum TiltEvent {
Progress {
done: usize,
},
Finished(TiltOutcome),
}
pub(in crate::gui::editor::compare) struct TiltReport {
pub(in crate::gui::editor::compare) session_id: u64,
pub(in crate::gui::editor::compare) run: u64,
pub(in crate::gui::editor::compare) event: TiltEvent,
}
struct EndGuard {
session_id: u64,
run: u64,
post: fn(TiltReport),
sent: bool,
}
impl EndGuard {
fn finish(&mut self, outcome: TiltOutcome) {
self.sent = true;
(self.post)(TiltReport {
session_id: self.session_id,
run: self.run,
event: TiltEvent::Finished(outcome),
});
}
}
impl Drop for EndGuard {
fn drop(&mut self) {
if !self.sent {
(self.post)(TiltReport {
session_id: self.session_id,
run: self.run,
event: TiltEvent::Finished(TiltOutcome::Stopped),
});
}
}
}
pub(in crate::gui::editor::compare) fn spawn_tilt(
session_id: u64,
request: TiltRequest,
post: fn(TiltReport),
) -> Result<(), String> {
thread::Builder::new()
.name("compare-tilt".to_string())
.spawn(move || run_tilt(session_id, &request, post))
.map(|_| ())
.map_err(|error| format!("The tilt average could not start: {error}."))
}
fn tilt_side(
session_id: u64,
request: &TiltRequest,
side: usize,
post: fn(TiltReport),
) -> Option<TiltAverages> {
let (stone, material) = &request.sides[side];
let offset = side * total_evaluations();
let mut step = |progress: SweepProgress| {
if request.cancel.load(Ordering::Relaxed) {
return false;
}
if progress.done.is_multiple_of(PROGRESS_EVERY) {
post(TiltReport {
session_id,
run: request.run,
event: TiltEvent::Progress {
done: offset + progress.done,
},
});
}
true
};
measure_tilt_average_geom(
stone.as_geometry(),
material,
environment(request.lighting),
&mut step,
)
}
fn run_tilt(session_id: u64, request: &TiltRequest, post: fn(TiltReport)) {
let mut guard = EndGuard {
session_id,
run: request.run,
post,
sent: false,
};
let Some(before) = tilt_side(session_id, request, 0, post) else {
return;
};
let Some(after) = tilt_side(session_id, request, 1, post) else {
return;
};
guard.finish(TiltOutcome::Averages { before, after });
}