1use crate::engine_state::EngineState;
33use crate::scene::{RenderScene, SolidDisplay};
34use std::collections::HashSet;
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum EntityKind {
39 Face,
40 Edge,
41 Vertex,
42}
43
44impl EntityKind {
45 pub fn parse(kind: &str) -> Option<Self> {
48 match kind.to_ascii_lowercase().as_str() {
49 "face" => Some(Self::Face),
50 "edge" => Some(Self::Edge),
51 "vertex" => Some(Self::Vertex),
52 _ => None,
53 }
54 }
55
56 pub fn as_str(self) -> &'static str {
58 match self {
59 Self::Face => "face",
60 Self::Edge => "edge",
61 Self::Vertex => "vertex",
62 }
63 }
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub enum GroupState {
70 All,
71 Partial,
72 None,
73}
74
75impl GroupState {
76 pub fn as_str(self) -> &'static str {
79 match self {
80 Self::All => "all",
81 Self::Partial => "partial",
82 Self::None => "none",
83 }
84 }
85}
86
87#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
90pub struct EntityVisibility {
91 hidden_faces: HashSet<usize>,
92 hidden_edges: HashSet<usize>,
93 hidden_vertices: HashSet<usize>,
94}
95
96impl EntityVisibility {
97 fn set_of(&self, kind: EntityKind) -> &HashSet<usize> {
98 match kind {
99 EntityKind::Face => &self.hidden_faces,
100 EntityKind::Edge => &self.hidden_edges,
101 EntityKind::Vertex => &self.hidden_vertices,
102 }
103 }
104
105 fn set_mut(&mut self, kind: EntityKind) -> &mut HashSet<usize> {
106 match kind {
107 EntityKind::Face => &mut self.hidden_faces,
108 EntityKind::Edge => &mut self.hidden_edges,
109 EntityKind::Vertex => &mut self.hidden_vertices,
110 }
111 }
112
113 pub fn is_visible(&self, kind: EntityKind, index: usize) -> bool {
115 !self.set_of(kind).contains(&index)
116 }
117
118 pub fn any_hidden(&self, kind: EntityKind) -> bool {
121 !self.set_of(kind).is_empty()
122 }
123
124 pub fn set_visible(&mut self, kind: EntityKind, index: usize, visible: bool) {
126 if visible {
127 self.set_mut(kind).remove(&index);
128 } else {
129 self.set_mut(kind).insert(index);
130 }
131 }
132
133 pub fn set_group_visible(&mut self, kind: EntityKind, count: usize, visible: bool) {
136 let set = self.set_mut(kind);
137 set.clear();
138 if !visible {
139 set.extend(0..count);
140 }
141 }
142
143 pub fn group_state(&self, kind: EntityKind, count: usize) -> GroupState {
148 if count == 0 {
149 return GroupState::All;
150 }
151 let hidden = self.set_of(kind).len();
152 if hidden == 0 {
153 GroupState::All
154 } else if hidden >= count {
155 GroupState::None
156 } else {
157 GroupState::Partial
158 }
159 }
160
161 pub fn is_face_visible(&self, index: usize) -> bool {
163 self.is_visible(EntityKind::Face, index)
164 }
165 pub fn is_edge_visible(&self, index: usize) -> bool {
166 self.is_visible(EntityKind::Edge, index)
167 }
168 pub fn is_vertex_visible(&self, index: usize) -> bool {
169 self.is_visible(EntityKind::Vertex, index)
170 }
171 pub fn any_face_hidden(&self) -> bool {
172 self.any_hidden(EntityKind::Face)
173 }
174 pub fn any_edge_hidden(&self) -> bool {
175 self.any_hidden(EntityKind::Edge)
176 }
177 pub fn any_vertex_hidden(&self) -> bool {
178 self.any_hidden(EntityKind::Vertex)
179 }
180
181 pub fn all_hidden(&self, kind: EntityKind, count: usize) -> bool {
189 count > 0 && self.set_of(kind).len() >= count
190 }
191 pub fn all_vertices_hidden(&self, count: usize) -> bool {
192 self.all_hidden(EntityKind::Vertex, count)
193 }
194}
195
196fn entity_count(solid: &SolidDisplay, kind: EntityKind) -> usize {
198 match kind {
199 EntityKind::Face => solid.faces.len(),
200 EntityKind::Edge => solid.edges.len(),
201 EntityKind::Vertex => solid.vertices.len(),
202 }
203}
204
205impl RenderScene {
208 pub fn set_entity_visible(
210 &mut self,
211 solid: &str,
212 kind: EntityKind,
213 index: usize,
214 visible: bool,
215 ) -> bool {
216 match self.solid_mut(solid) {
217 Some(s) => {
218 s.visibility.set_visible(kind, index, visible);
219 true
220 }
221 None => false,
222 }
223 }
224
225 pub fn set_group_visible(&mut self, solid: &str, kind: EntityKind, visible: bool) -> bool {
228 let Some(count) = self.solid(solid).map(|s| entity_count(s, kind)) else {
229 return false;
230 };
231 if let Some(s) = self.solid_mut(solid) {
233 s.visibility.set_group_visible(kind, count, visible);
234 }
235 true
236 }
237
238 pub fn entity_visible(&self, solid: &str, kind: EntityKind, index: usize) -> Option<bool> {
240 self.solid(solid).map(|s| s.visibility.is_visible(kind, index))
241 }
242
243 pub fn group_visibility(&self, solid: &str, kind: EntityKind) -> Option<GroupState> {
245 self.solid(solid)
246 .map(|s| s.visibility.group_state(kind, entity_count(s, kind)))
247 }
248}
249
250impl EngineState {
253 pub fn set_entity_visible(
257 &mut self,
258 solid: &str,
259 kind: EntityKind,
260 index: usize,
261 visible: bool,
262 ) -> bool {
263 let ok = self.scene.set_entity_visible(solid, kind, index, visible);
264 if ok {
265 self.dirty = true;
266 }
267 ok
268 }
269
270 pub fn set_group_visible(&mut self, solid: &str, kind: EntityKind, visible: bool) -> bool {
273 let ok = self.scene.set_group_visible(solid, kind, visible);
274 if ok {
275 self.dirty = true;
276 }
277 ok
278 }
279
280 pub fn entity_visible(&self, solid: &str, kind: EntityKind, index: usize) -> Option<bool> {
282 self.scene.entity_visible(solid, kind, index)
283 }
284
285 pub fn group_visibility(&self, solid: &str, kind: EntityKind) -> Option<GroupState> {
287 self.scene.group_visibility(solid, kind)
288 }
289
290 pub fn scene_visibility_json(&self) -> String {
298 let kinds = [
299 ("faces", EntityKind::Face),
300 ("edges", EntityKind::Edge),
301 ("vertices", EntityKind::Vertex),
302 ];
303 let solids: Vec<serde_json::Value> = self
304 .scene
305 .solids()
306 .iter()
307 .map(|solid| {
308 let mut obj = serde_json::Map::new();
309 obj.insert("name".into(), serde_json::json!(solid.name));
310 obj.insert("visible".into(), serde_json::json!(solid.visible));
311 for (key, kind) in kinds {
312 let count = entity_count(solid, kind);
313 let states: Vec<bool> =
314 (0..count).map(|i| solid.visibility.is_visible(kind, i)).collect();
315 obj.insert(
316 key.into(),
317 serde_json::json!({
318 "group": solid.visibility.group_state(kind, count).as_str(),
319 "states": states,
320 }),
321 );
322 }
323 serde_json::Value::Object(obj)
324 })
325 .collect();
326 serde_json::Value::Array(solids).to_string()
327 }
328}
329
330#[cfg(test)]
331mod tests {
332 use super::*;
333
334 fn cube_history(name: &str) -> String {
335 serde_json::json!({
336 "expressions": "",
337 "configurator": {},
338 "features": [{
339 "type": "P.CU",
340 "inputParams": {
341 "id": name,
342 "sizeX": 10.0, "sizeY": 10.0, "sizeZ": 10.0,
343 "transform": {
344 "position": [0.0, 0.0, 0.0],
345 "rotationEuler": [0.0, 0.0, 0.0],
346 "scale": [1.0, 1.0, 1.0]
347 },
348 "boolean": { "targets": [], "operation": "NONE" }
349 },
350 "persistentData": {}
351 }]
352 })
353 .to_string()
354 }
355
356 #[test]
357 fn entity_visibility_hide_show_and_group_tristate() {
358 let mut vis = EntityVisibility::default();
359 assert!(vis.is_visible(EntityKind::Face, 0));
361 assert!(!vis.any_hidden(EntityKind::Face));
362 assert_eq!(vis.group_state(EntityKind::Face, 6), GroupState::All);
363
364 vis.set_visible(EntityKind::Face, 2, false);
366 assert!(!vis.is_visible(EntityKind::Face, 2));
367 assert!(vis.is_visible(EntityKind::Face, 3));
368 assert!(vis.any_hidden(EntityKind::Face));
369 assert_eq!(vis.group_state(EntityKind::Face, 6), GroupState::Partial);
370
371 vis.set_visible(EntityKind::Face, 2, true);
373 assert_eq!(vis.group_state(EntityKind::Face, 6), GroupState::All);
374
375 vis.set_group_visible(EntityKind::Face, 6, false);
377 assert_eq!(vis.group_state(EntityKind::Face, 6), GroupState::None);
378 assert!(!vis.is_visible(EntityKind::Face, 5));
379 vis.set_visible(EntityKind::Face, 5, true);
381 assert_eq!(vis.group_state(EntityKind::Face, 6), GroupState::Partial);
382 assert_eq!(vis.group_state(EntityKind::Edge, 12), GroupState::All);
384 }
385
386 #[test]
387 fn engine_per_entity_and_group_visibility_through_scene() {
388 let mut engine = EngineState::new();
389 engine.set_history_json(&cube_history("Box")).unwrap();
390
391 assert!(engine.set_entity_visible("Box", EntityKind::Face, 0, false));
393 assert_eq!(engine.entity_visible("Box", EntityKind::Face, 0), Some(false));
394 assert_eq!(engine.entity_visible("Box", EntityKind::Face, 1), Some(true));
395 assert_eq!(
396 engine.group_visibility("Box", EntityKind::Face),
397 Some(GroupState::Partial)
398 );
399
400 assert!(engine.set_group_visible("Box", EntityKind::Face, false));
402 assert_eq!(
403 engine.group_visibility("Box", EntityKind::Face),
404 Some(GroupState::None)
405 );
406 assert_eq!(
407 engine.group_visibility("Box", EntityKind::Edge),
408 Some(GroupState::All)
409 );
410
411 assert!(engine.set_group_visible("Box", EntityKind::Face, true));
413 assert_eq!(
414 engine.group_visibility("Box", EntityKind::Face),
415 Some(GroupState::All)
416 );
417
418 assert!(!engine.set_entity_visible("Nope", EntityKind::Edge, 0, false));
420 assert_eq!(engine.entity_visible("Nope", EntityKind::Edge, 0), None);
421 assert_eq!(engine.group_visibility("Nope", EntityKind::Edge), None);
422 }
423
424 #[test]
425 fn scene_visibility_json_reports_states_and_group() {
426 let mut engine = EngineState::new();
427 engine.set_history_json(&cube_history("Box")).unwrap();
428 engine.set_entity_visible("Box", EntityKind::Edge, 3, false);
429
430 let json: serde_json::Value =
431 serde_json::from_str(&engine.scene_visibility_json()).unwrap();
432 let solid = &json.as_array().unwrap()[0];
433 assert_eq!(solid["name"], "Box");
434 assert_eq!(solid["visible"], true);
435 assert_eq!(solid["faces"]["group"], "all");
437 assert_eq!(solid["faces"]["states"].as_array().unwrap().len(), 6);
438 assert_eq!(solid["edges"]["group"], "partial");
440 assert_eq!(solid["edges"]["states"][3], false);
441 assert_eq!(solid["edges"]["states"][2], true);
442 }
443
444 #[test]
445 fn entity_kind_token_roundtrips() {
446 for k in [EntityKind::Face, EntityKind::Edge, EntityKind::Vertex] {
447 assert_eq!(EntityKind::parse(k.as_str()), Some(k));
448 }
449 assert_eq!(EntityKind::parse("FACE"), Some(EntityKind::Face));
450 assert_eq!(EntityKind::parse("solid"), None);
451 }
452}