use crate::automation::hit_keys::HitKeyDoc;
use brep_render::assembly_status;
use brep_render::engine_state::{EngineState, InterferencePair, InterferenceReport};
use eframe::egui;
use serde_json::Value;
use std::collections::HashMap;
fn pair_label(pair: &InterferencePair) -> String {
let mut label = format!(
"{} \u{00d7} {} \u{2014} {} mm\u{00b3}",
pair.a,
pair.b,
super::info_windows::num(pair.volume)
);
let hidden: Vec<&str> = [
pair.a_hidden.then_some(pair.a.as_str()),
pair.b_hidden.then_some(pair.b.as_str()),
]
.into_iter()
.flatten()
.collect();
if !hidden.is_empty() {
label.push_str(&format!(" (hidden: {})", hidden.join(", ")));
}
label
}
fn status_color(status: &str) -> egui::Color32 {
let [r, g, b] = assembly_status::status_color_rgb(status);
egui::Color32::from_rgb(r, g, b)
}
#[derive(Default)]
pub struct InterferenceWindow {
open: bool,
report: Option<InterferenceReport>,
hits: HashMap<String, egui::Rect>,
}
impl InterferenceWindow {
pub fn new() -> Self {
Self::default()
}
pub fn open_and_run(&mut self, state: &mut EngineState) {
self.report = Some(state.interference_check());
self.open = true;
}
pub fn show(&mut self, ctx: &egui::Context, state: &mut EngineState) {
self.hits.clear();
if !self.open {
return;
}
let mut open = true;
egui::Window::new("Interference")
.id(egui::Id::new("brep-interference-window"))
.open(&mut open)
.movable(true)
.resizable(true)
.default_size([360.0, 320.0])
.default_pos([860.0, 80.0])
.show(ctx, |ui| {
egui::ScrollArea::vertical()
.auto_shrink([false, false])
.show(ui, |ui| self.body(ui, state));
});
self.open = open;
}
fn body(&mut self, ui: &mut egui::Ui, state: &mut EngineState) {
ui.horizontal(|ui| {
let rerun = ui.button("Re-run");
self.hits.insert("interference:rerun".into(), rerun.rect);
if rerun.clicked() {
self.report = Some(state.interference_check());
}
if let Some(report) = &self.report {
ui.weak(format!(
"{} components \u{00b7} {} pairs \u{00b7} {} boolean{}",
report.component_count,
report.pair_total,
report.booleans_run,
if report.booleans_run == 1 { "" } else { "s" }
));
}
});
ui.separator();
let Some(report) = self.report.clone() else {
ui.weak("Run the check from the toolbar.");
return;
};
if report.component_count < 2 {
ui.weak("Needs at least two components.");
return;
}
if report.pairs.is_empty() {
ui.colored_label(
status_color("satisfied"),
format!(
"\u{2713} No interference \u{2014} {} pair{} checked",
report.pair_total,
if report.pair_total == 1 { "" } else { "s" }
),
);
} else {
for pair in &report.pairs {
let row = ui.selectable_label(
false,
egui::RichText::new(pair_label(pair)).color(status_color("error")),
);
self.hits
.insert(format!("interference:pair:{}x{}", pair.a, pair.b), row.rect);
if row.clicked() {
state.select_components(&[pair.a.clone(), pair.b.clone()]);
}
}
}
if !report.unverified.is_empty() {
ui.add_space(4.0);
ui.label("Not verified (boolean refused):");
for line in &report.unverified {
ui.colored_label(status_color("unsupported-selection"), line);
}
}
if !report.skipped.is_empty() {
ui.add_space(4.0);
ui.label("Not checked:");
for line in &report.skipped {
ui.weak(line);
}
}
}
#[cfg_attr(not(target_arch = "wasm32"), allow(dead_code))]
pub fn state_json(&self) -> String {
let report = self.report.as_ref().map(|report| {
serde_json::json!({
"componentCount": report.component_count,
"pairTotal": report.pair_total,
"booleansRun": report.booleans_run,
"pairs": report
.pairs
.iter()
.map(|pair| {
serde_json::json!({
"a": pair.a,
"b": pair.b,
"volume": pair.volume,
"aHidden": pair.a_hidden,
"bHidden": pair.b_hidden,
"label": pair_label(pair),
})
})
.collect::<Vec<_>>(),
"skipped": report.skipped,
"unverified": report.unverified,
})
});
serde_json::json!({
"open": self.open,
"report": report.unwrap_or(Value::Null),
})
.to_string()
}
#[cfg_attr(not(target_arch = "wasm32"), allow(dead_code))]
pub fn hits_json(&self) -> String {
crate::automation::hit_rects::hits_json(&self.hits)
}
}
pub static HIT_KEYS: &[HitKeyDoc] = &[
HitKeyDoc { panel: "interference", prefix: "interference:rerun", meaning: "rerun the interference check", command: Some("interference_check") },
HitKeyDoc { panel: "interference", prefix: "interference:", meaning: "an interference result row", command: None },
];