use super::*;
const SKETCH_SHEET_COLOR: [f32; 3] = [
0x67 as f32 / 255.0,
0xc7 as f32 / 255.0,
0xd4 as f32 / 255.0,
];
const PORT_TERMINATION_COLOR: [f32; 3] = [
0xe5 as f32 / 255.0,
0x32 as f32 / 255.0,
0x2d as f32 / 255.0,
];
const PORT_WAYPOINT_COLOR: [f32; 3] = [
0xff as f32 / 255.0,
0x8a as f32 / 255.0,
0x80 as f32 / 255.0,
];
fn curve_feature_segment_prefix(feature_type: &str, id: &str) -> Option<String> {
match feature_type {
"S" => Some(format!("{id}:G")),
"HX" => Some(format!("{id}:HelixEdge")),
"SP" => Some(format!("{id}:SplineEdge")),
"PORT" => Some(format!("{id}:PortLine")),
_ => None,
}
}
fn curve_feature_point_prefix(feature_type: &str, id: &str) -> Option<String> {
match feature_type {
"S" | "SP" => Some(format!("{id}:P")),
"PORT" => Some(format!("{id}:Base")),
_ => None,
}
}
impl EngineState {
fn committed_sketch_ids(&self) -> Vec<String> {
self.committed_curve_features()
.into_iter()
.map(|(id, _, _)| id)
.collect()
}
fn committed_curve_features(&self) -> Vec<(String, String, Option<String>)> {
self.committed_curve_features_typed()
.into_iter()
.map(|(id, _, prefix, point_prefix)| (id, prefix, point_prefix))
.collect()
}
fn committed_curve_features_typed(&self) -> Vec<(String, String, String, Option<String>)> {
let editing = self.sketch_edit.as_ref().map(|edit| edit.feature_id.as_str());
let rollback = self.history.rollback();
let mut ids = Vec::new();
for index in 0..=rollback {
let Some(feature_type) = self.history.feature_type(index) else {
continue;
};
let Some(id) = self.history.feature_id(index) else {
continue;
};
let Some(prefix) = curve_feature_segment_prefix(&feature_type, &id) else {
continue;
};
if Some(id.as_str()) == editing {
continue;
}
let point_prefix = curve_feature_point_prefix(&feature_type, &id);
ids.push((id, feature_type, prefix, point_prefix));
}
if !ids.is_empty() {
let consumed = self.consumed_feature_names();
ids.retain(|(id, _, _, _)| !consumed.contains(id));
}
if self.component_ports_visible {
if let Some(report) = &self.wire_harness_report {
for endpoint in report.endpoints.iter().filter(|e| e.component.is_some()) {
if ids.iter().any(|(id, _, _, _)| *id == endpoint.id) {
continue;
}
let Some(prefix) = curve_feature_segment_prefix("PORT", &endpoint.id) else {
continue;
};
let point_prefix = curve_feature_point_prefix("PORT", &endpoint.id);
ids.push((endpoint.id.clone(), "PORT".to_string(), prefix, point_prefix));
}
}
}
ids
}
pub fn component_ports_visible(&self) -> bool {
self.component_ports_visible
}
pub fn set_component_ports_visible(&mut self, visible: bool) {
if self.component_ports_visible == visible {
return;
}
self.component_ports_visible = visible;
self.refresh_committed_sketches();
}
fn curve_feature_color(&self, id: &str, feature_type: &str) -> [f32; 3] {
if feature_type != "PORT" {
return SKETCH_SHEET_COLOR;
}
let waypoint = self
.wire_harness_report
.as_ref()
.and_then(|report| report.endpoints.iter().find(|endpoint| endpoint.id == id))
.is_some_and(|endpoint| endpoint.kind == brep_kernel::PortKind::Waypoint);
if waypoint {
PORT_WAYPOINT_COLOR
} else {
PORT_TERMINATION_COLOR
}
}
fn consumed_feature_names(&self) -> std::collections::HashSet<String> {
let request: HistoryRequest = match serde_json::from_value(self.history.prefix_request()) {
Ok(request) => request,
Err(_) => return std::collections::HashSet::new(),
};
brep_kernel::execute_history(&request)
.results
.iter()
.flat_map(|feature| feature.removed.iter().cloned())
.collect()
}
pub fn refresh_committed_sketches(&mut self) {
let visible: Vec<(String, String, String, Option<String>)> = self
.committed_curve_features_typed()
.into_iter()
.filter(|(id, _, _, _)| !self.hidden_sketches.contains(id))
.collect();
let payloads: Vec<(String, [f32; 3], brep_kernel::DisplaySolidPayload)> = visible
.iter()
.filter_map(|(id, feature_type, prefix, point_prefix)| {
let profile = self
.sketch_profiles
.iter()
.find(|(name, _)| name == id)
.map(|(_, profile)| profile);
let segments: Vec<(String, Vec<brep_kernel::NurbsCurve>)> = self
.sketch_paths
.iter()
.filter(|(name, _)| name.starts_with(prefix.as_str()))
.cloned()
.collect();
let points: Vec<brep_kernel::Vec3> = match point_prefix {
Some(point_prefix) => self
.sketch_points
.iter()
.filter(|(name, point)| {
name.starts_with(point_prefix.as_str()) && !point.construction
})
.map(|(_, point)| point.position)
.collect(),
None => Vec::new(),
};
let payload = brep_kernel::sketch_display_payload(profile, &segments, &points);
if payload.mesh.indices.is_empty()
&& payload.edges.is_empty()
&& payload.vertices.is_empty()
{
return None;
}
Some((id.clone(), self.curve_feature_color(id, feature_type), payload))
})
.collect();
let mut fed: Vec<String> = Vec::with_capacity(payloads.len());
for (id, color, payload) in payloads {
let mut solid = crate::scene::solid_display_from_payload(&id, payload);
solid.is_sketch = true;
solid.color_override = Some(color);
self.scene.insert_solid(solid);
fed.push(id);
}
let fed_set: std::collections::HashSet<&str> = fed.iter().map(String::as_str).collect();
let stale: Vec<String> = self
.shown_sketch_ids
.iter()
.filter(|id| !fed_set.contains(id.as_str()))
.cloned()
.collect();
for id in stale {
self.scene.remove_solid(&id);
}
self.shown_sketch_ids = fed;
self.dirty = true;
}
pub fn sketch_visible(&self, id: &str) -> bool {
!self.hidden_sketches.contains(id)
}
pub fn set_sketch_visible(&mut self, id: &str, visible: bool) {
if visible {
self.hidden_sketches.remove(id);
} else {
self.hidden_sketches.insert(id.to_string());
}
self.refresh_committed_sketches();
}
pub fn committed_sketches(&self) -> Vec<(String, bool)> {
crate::visibility::named_visibility(self.committed_sketch_ids(), &self.hidden_sketches)
}
pub fn sketch_entities_json(&self) -> String {
crate::visibility::named_visibility_json(self.committed_sketches())
}
}