use super::*;
use brep_kernel::{pmi_present, pmi_type, PmiLayoutStyle, PmiStatus};
const PMI_OVERLAY_GROUP: &str = "pmi-overlay";
const ARROW_PX: f64 = 12.0;
const TEXT_PX_AT_12PT: f64 = 11.0;
const PMI_RGB: [f32; 3] = [0.98, 0.78, 0.22];
const ERROR_RGB: [f32; 3] = [0.97, 0.32, 0.29];
const DISABLED_RGB: [f32; 3] = [0.55, 0.55, 0.55];
impl EngineState {
pub fn refresh_pmi_overlay(&mut self) {
let Some(active) = self.pmi_active_view.clone() else {
self.clear_pmi_overlay();
return;
};
if self.sketch_mode() {
self.clear_pmi_overlay();
return;
}
let Some(view) = self.pmi_report.as_ref().and_then(|report| report.view(&active)).cloned() else {
self.clear_pmi_overlay();
return;
};
let text_size = self
.pmi_state()
.find_view(&active)
.map(|view| view.display.text_size_pt)
.unwrap_or(12.0);
let wpp = self.camera.world_per_pixel();
let (_, up, view_dir) = self.camera.basis();
let style = PmiLayoutStyle {
arrow: ARROW_PX * wpp,
text_height: TEXT_PX_AT_12PT * wpp * (text_size / 12.0),
view_dir,
view_up: up,
plane: None,
};
let mut lines: Vec<f32> = Vec::new();
let mut line_colors: Vec<f32> = Vec::new();
let mut tris: Vec<f32> = Vec::new();
let mut tri_colors: Vec<f32> = Vec::new();
for row in &view.annotations {
if !row.enabled || row.status != PmiStatus::Ok {
continue;
}
let row_style = PmiLayoutStyle {
plane: row.plane,
..style
};
let drawn = pmi_present(&row.geometry, row.label_world, &row.text, &row_style);
for polyline in &drawn.polylines {
for pair in polyline.windows(2) {
for point in pair {
lines.extend(point.iter().map(|c| *c as f32));
line_colors.extend_from_slice(&PMI_RGB);
}
}
}
for arrow in &drawn.arrows {
for point in arrow {
tris.extend(point.iter().map(|c| *c as f32));
tri_colors.extend_from_slice(&PMI_RGB);
}
for point in [arrow[0], arrow[2], arrow[1]] {
tris.extend(point.iter().map(|c| *c as f32));
tri_colors.extend_from_slice(&PMI_RGB);
}
}
}
let group = if lines.is_empty() && tris.is_empty() {
serde_json::json!({ "name": PMI_OVERLAY_GROUP })
} else {
serde_json::json!({
"name": PMI_OVERLAY_GROUP,
"renderOrder": 10001,
"lines": { "positions": lines, "colors": line_colors },
"tris": { "positions": tris, "colors": tri_colors },
})
};
let _ = self.set_overlay_json(&serde_json::json!({ "groups": [group] }).to_string());
self.pmi_overlay_key = Some((if wpp > 0.0 { wpp } else { f64::MIN_POSITIVE }, view_dir));
self.dirty = true;
}
fn clear_pmi_overlay(&mut self) {
if self.pmi_overlay_key.take().is_some() {
let _ = self.set_overlay_json(
&serde_json::json!({ "groups": [ { "name": PMI_OVERLAY_GROUP } ] }).to_string(),
);
self.dirty = true;
}
}
pub fn ensure_pmi_overlay_current(&mut self) {
let Some((baked_wpp, baked_dir)) = self.pmi_overlay_key else {
if self.pmi_active_view.is_some() {
self.refresh_pmi_overlay();
}
return;
};
if self.sketch_mode() {
self.clear_pmi_overlay();
return;
}
let wpp = self.camera.world_per_pixel();
let (_, _, view_dir) = self.camera.basis();
let dot = baked_dir[0] * view_dir[0] + baked_dir[1] * view_dir[1] + baked_dir[2] * view_dir[2];
if super::overlay_wpp_stale(baked_wpp, wpp) || dot < (0.5f64).to_radians().cos() {
self.refresh_pmi_overlay();
}
}
pub fn pmi_labels_json(&self) -> String {
let Some(active) = self.pmi_active_view.as_deref() else {
return "[]".into();
};
let Some(view) = self.pmi_report.as_ref().and_then(|report| report.view(active)) else {
return "[]".into();
};
let text_size = self
.pmi_state()
.find_view(active)
.map(|view| view.display.text_size_pt)
.unwrap_or(12.0);
let rows: Vec<serde_json::Value> = view
.annotations
.iter()
.map(|row| {
let color = if row.status == PmiStatus::Error {
ERROR_RGB
} else if !row.enabled {
DISABLED_RGB
} else {
PMI_RGB
};
let icon = pmi_type(&row.kind).map(|def| def.icon).unwrap_or("");
let text = if row.status == PmiStatus::Error {
format!("{} {}", icon, row.id)
} else if row.text.is_empty() {
format!("{} {}", icon, row.id)
} else {
row.text.clone()
};
serde_json::json!({
"id": row.id,
"type": row.kind,
"icon": icon,
"text": text,
"status": match row.status { PmiStatus::Ok => "ok", PmiStatus::Error => "error" },
"message": row.message,
"color": [color[0], color[1], color[2]],
"world": row.label_world,
"enabled": row.enabled,
"open": self.pmi_open_annotation.as_deref() == Some(row.id.as_str()),
"textSizePt": text_size,
})
})
.collect();
serde_json::Value::Array(rows).to_string()
}
}