Skip to main content

brep_render/
visibility.rs

1//! Visibility masks for solid faces, edges, vertices, and named overlays.
2//!
3//! [`EntityVisibility`] stores hidden indices; [`GroupState`] gives Scene-tree
4//! checkboxes a tristate readout. Reused solids retain their masks, while newly
5//! tessellated solids start visible. The renderer skips hidden geometry and
6//! coalesces contiguous visible face ranges without rebuilding meshes.
7
8use crate::engine_state::EngineState;
9use crate::scene::{RenderScene, SolidDisplay};
10use std::collections::HashSet;
11
12/// Preserve scene-list order, including hidden entries for their checkboxes.
13pub(crate) fn named_visibility(
14    names: impl IntoIterator<Item = String>,
15    hidden: &HashSet<String>,
16) -> Vec<(String, bool)> {
17    names.into_iter().map(|name| {
18        let visible = !hidden.contains(&name);
19        (name, visible)
20    }).collect()
21}
22
23pub(crate) fn named_visibility_json(entries: Vec<(String, bool)>) -> String {
24    let list: Vec<serde_json::Value> = entries.into_iter()
25        .map(|(name, visible)| serde_json::json!({ "name": name, "visible": visible }))
26        .collect();
27    serde_json::Value::Array(list).to_string()
28}
29
30/// Which sub-entity list of a solid a visibility toggle addresses.
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum EntityKind {
33    Face,
34    Edge,
35    Vertex,
36}
37
38impl EntityKind {
39    /// Parse the Scene-tree / verifier kind token (`"face"` / `"edge"` /
40    /// `"vertex"`, case-insensitive). `None` for anything else.
41    pub fn parse(kind: &str) -> Option<Self> {
42        match kind.to_ascii_lowercase().as_str() {
43            "face" => Some(Self::Face),
44            "edge" => Some(Self::Edge),
45            "vertex" => Some(Self::Vertex),
46            _ => None,
47        }
48    }
49
50    /// The lowercase token (round-trips with [`parse`](Self::parse)).
51    pub fn as_str(self) -> &'static str {
52        match self {
53            Self::Face => "face",
54            Self::Edge => "edge",
55            Self::Vertex => "vertex",
56        }
57    }
58}
59
60/// A group checkbox's tristate: every entity of the kind shown, none shown, or a
61/// mix. An empty group reads [`All`](GroupState::All) — nothing to hide.
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum GroupState {
64    All,
65    Partial,
66    None,
67}
68
69impl GroupState {
70    /// The lowercase token the verifier asserts against (`"all"` / `"partial"` /
71    /// `"none"`).
72    pub fn as_str(self) -> &'static str {
73        match self {
74            Self::All => "all",
75            Self::Partial => "partial",
76            Self::None => "none",
77        }
78    }
79}
80
81/// The hidden-entity sets of ONE solid (empty = all visible). Indices are into
82/// the owning [`SolidDisplay`]'s `faces` / `edges` / `vertices`.
83#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
84pub struct EntityVisibility {
85    hidden_faces: HashSet<usize>,
86    hidden_edges: HashSet<usize>,
87    hidden_vertices: HashSet<usize>,
88}
89
90impl EntityVisibility {
91    fn set_of(&self, kind: EntityKind) -> &HashSet<usize> {
92        match kind {
93            EntityKind::Face => &self.hidden_faces,
94            EntityKind::Edge => &self.hidden_edges,
95            EntityKind::Vertex => &self.hidden_vertices,
96        }
97    }
98
99    fn set_mut(&mut self, kind: EntityKind) -> &mut HashSet<usize> {
100        match kind {
101            EntityKind::Face => &mut self.hidden_faces,
102            EntityKind::Edge => &mut self.hidden_edges,
103            EntityKind::Vertex => &mut self.hidden_vertices,
104        }
105    }
106
107    /// Whether entity `index` of `kind` is currently shown.
108    pub fn is_visible(&self, kind: EntityKind, index: usize) -> bool {
109        !self.set_of(kind).contains(&index)
110    }
111
112    /// Whether ANY entity of `kind` is hidden — the render pass's fast-path
113    /// guard (nothing hidden ⇒ keep the single whole-buffer draw).
114    pub fn any_hidden(&self, kind: EntityKind) -> bool {
115        !self.set_of(kind).is_empty()
116    }
117
118    /// Show/hide a single entity of `kind`.
119    pub fn set_visible(&mut self, kind: EntityKind, index: usize, visible: bool) {
120        if visible {
121            self.set_mut(kind).remove(&index);
122        } else {
123            self.set_mut(kind).insert(index);
124        }
125    }
126
127    /// Show/hide EVERY entity of `kind`. `count` is the number of entities of
128    /// that kind, so "hide all" enumerates them (and "show all" clears).
129    pub fn set_group_visible(&mut self, kind: EntityKind, count: usize, visible: bool) {
130        let set = self.set_mut(kind);
131        set.clear();
132        if !visible {
133            set.extend(0..count);
134        }
135    }
136
137    /// The group tristate for `count` entities of `kind`. O(1): the hidden set
138    /// satisfies `hidden ⊆ 0..count` (see [`all_hidden`](Self::all_hidden)), so its
139    /// `len()` IS the hidden count — no per-entity scan. Called per solid per egui
140    /// frame by the Scene panel, so the O(1) matters on point/edge-heavy models.
141    pub fn group_state(&self, kind: EntityKind, count: usize) -> GroupState {
142        if count == 0 {
143            return GroupState::All;
144        }
145        let hidden = self.set_of(kind).len();
146        if hidden == 0 {
147            GroupState::All
148        } else if hidden >= count {
149            GroupState::None
150        } else {
151            GroupState::Partial
152        }
153    }
154
155    // Render-pass conveniences (kept terse — called per solid per frame).
156    pub fn is_face_visible(&self, index: usize) -> bool {
157        self.is_visible(EntityKind::Face, index)
158    }
159    pub fn is_edge_visible(&self, index: usize) -> bool {
160        self.is_visible(EntityKind::Edge, index)
161    }
162    pub fn is_vertex_visible(&self, index: usize) -> bool {
163        self.is_visible(EntityKind::Vertex, index)
164    }
165    pub fn any_face_hidden(&self) -> bool {
166        self.any_hidden(EntityKind::Face)
167    }
168    pub fn any_edge_hidden(&self) -> bool {
169        self.any_hidden(EntityKind::Edge)
170    }
171    pub fn any_vertex_hidden(&self) -> bool {
172        self.any_hidden(EntityKind::Vertex)
173    }
174
175    /// Whether EVERY entity of `kind` is hidden — O(1). A solid's hidden set is
176    /// always paired with the entity counts it was built against: geometry changes
177    /// replace the whole `SolidDisplay` (fresh, empty visibility), and a reused
178    /// display keeps its counts AND its hidden set together — so `hidden ⊆ 0..count`
179    /// holds universally and `len() == count` ⇔ the whole group is off (individual
180    /// hides leave `len() < count`). `>=` rather than `==` is defensive only. The
181    /// render pass uses this to skip a fully-hidden group's per-vertex draw loop.
182    pub fn all_hidden(&self, kind: EntityKind, count: usize) -> bool {
183        count > 0 && self.set_of(kind).len() >= count
184    }
185    pub fn all_vertices_hidden(&self, count: usize) -> bool {
186        self.all_hidden(EntityKind::Vertex, count)
187    }
188}
189
190/// The count of entities of `kind` on a solid (group toggles / tristate).
191fn entity_count(solid: &SolidDisplay, kind: EntityKind) -> usize {
192    match kind {
193        EntityKind::Face => solid.faces.len(),
194        EntityKind::Edge => solid.edges.len(),
195        EntityKind::Vertex => solid.vertices.len(),
196    }
197}
198
199// --- RenderScene: per-entity / group visibility (kept here, not in scene.rs, so
200//     the visibility surface stays in one module) ------------------------------
201impl RenderScene {
202    /// Show/hide ONE face/edge/vertex of a solid. False if the solid is unknown.
203    pub fn set_entity_visible(
204        &mut self,
205        solid: &str,
206        kind: EntityKind,
207        index: usize,
208        visible: bool,
209    ) -> bool {
210        match self.solid_mut(solid) {
211            Some(s) => {
212                s.visibility.set_visible(kind, index, visible);
213                true
214            }
215            None => false,
216        }
217    }
218
219    /// Show/hide a whole group (all faces / all edges / all vertices) of a solid.
220    /// False if the solid is unknown.
221    pub fn set_group_visible(&mut self, solid: &str, kind: EntityKind, visible: bool) -> bool {
222        let Some(count) = self.solid(solid).map(|s| entity_count(s, kind)) else {
223            return false;
224        };
225        // The solid exists (count resolved above), so `solid_mut` is Some.
226        if let Some(s) = self.solid_mut(solid) {
227            s.visibility.set_group_visible(kind, count, visible);
228        }
229        true
230    }
231
232    /// Whether entity `index` of `kind` on `solid` is shown (`None` if unknown).
233    pub fn entity_visible(&self, solid: &str, kind: EntityKind, index: usize) -> Option<bool> {
234        self.solid(solid).map(|s| s.visibility.is_visible(kind, index))
235    }
236
237    /// The group tristate for `solid`'s `kind` (`None` if the solid is unknown).
238    pub fn group_visibility(&self, solid: &str, kind: EntityKind) -> Option<GroupState> {
239        self.solid(solid)
240            .map(|s| s.visibility.group_state(kind, entity_count(s, kind)))
241    }
242}
243
244// --- EngineState: the UI-facing visibility API (marks dirty; the Scene panel
245//     drives these and reads the queries back) ---------------------------------
246impl EngineState {
247    /// Show/hide ONE face/edge/vertex of a solid, by its index in the solid's
248    /// face/edge/vertex list — the same index the Scene tree enumerates. Marks
249    /// dirty. Returns false if the solid is unknown.
250    pub fn set_entity_visible(
251        &mut self,
252        solid: &str,
253        kind: EntityKind,
254        index: usize,
255        visible: bool,
256    ) -> bool {
257        let ok = self.scene.set_entity_visible(solid, kind, index, visible);
258        if ok {
259            self.dirty = true;
260        }
261        ok
262    }
263
264    /// Show/hide a WHOLE group (all faces / all edges / all vertices of a solid).
265    /// Marks dirty. Returns false if the solid is unknown.
266    pub fn set_group_visible(&mut self, solid: &str, kind: EntityKind, visible: bool) -> bool {
267        let ok = self.scene.set_group_visible(solid, kind, visible);
268        if ok {
269            self.dirty = true;
270        }
271        ok
272    }
273
274    /// Whether entity `index` of `kind` on `solid` is shown (`None` if unknown).
275    pub fn entity_visible(&self, solid: &str, kind: EntityKind, index: usize) -> Option<bool> {
276        self.scene.entity_visible(solid, kind, index)
277    }
278
279    /// The group tristate for `solid`'s `kind` (`None` if the solid is unknown).
280    pub fn group_visibility(&self, solid: &str, kind: EntityKind) -> Option<GroupState> {
281        self.scene.group_visibility(solid, kind)
282    }
283
284    /// Per-entity + group visibility as JSON — the readout the headed verifier
285    /// asserts against (a companion to
286    /// [`scene_entities_json`](EngineState::scene_entities_json), which lists the
287    /// entities but not their per-entity visibility):
288    /// `[{name, visible, faces:{group, states:[bool;n]}, edges:{…}, vertices:{…}}]`
289    /// where `states[i]` is entity `i`'s visibility and `group` is
290    /// `"all"|"partial"|"none"`.
291    pub fn scene_visibility_json(&self) -> String {
292        let kinds = [
293            ("faces", EntityKind::Face),
294            ("edges", EntityKind::Edge),
295            ("vertices", EntityKind::Vertex),
296        ];
297        let solids: Vec<serde_json::Value> = self
298            .scene
299            .solids()
300            .iter()
301            .map(|solid| {
302                let mut obj = serde_json::Map::new();
303                obj.insert("name".into(), serde_json::json!(solid.name));
304                obj.insert("visible".into(), serde_json::json!(solid.visible));
305                for (key, kind) in kinds {
306                    let count = entity_count(solid, kind);
307                    let states: Vec<bool> =
308                        (0..count).map(|i| solid.visibility.is_visible(kind, i)).collect();
309                    obj.insert(
310                        key.into(),
311                        serde_json::json!({
312                            "group": solid.visibility.group_state(kind, count).as_str(),
313                            "states": states,
314                        }),
315                    );
316                }
317                serde_json::Value::Object(obj)
318            })
319            .collect();
320        serde_json::Value::Array(solids).to_string()
321    }
322}
323
324// BREP private tests: 91ff1544af62716f