Skip to main content

polyscope_core/
group.rs

1//! Group structure for organizing structures hierarchically.
2//!
3//! Groups allow organizing structures into a tree hierarchy. When a group is
4//! disabled, all of its child structures and groups are also hidden.
5
6use std::collections::HashSet;
7
8/// A group that can contain structures and other groups.
9///
10/// Groups provide organizational hierarchy for structures in the viewer.
11/// Enabling or disabling a group affects all of its descendants.
12#[derive(Debug, Clone)]
13pub struct Group {
14    /// The unique name of this group.
15    name: String,
16    /// Whether this group is enabled (visible).
17    enabled: bool,
18    /// Whether to show child details in the UI.
19    show_child_details: bool,
20    /// Names of child structures (`type_name:name` format).
21    child_structures: HashSet<String>,
22    /// Names of child groups.
23    child_groups: HashSet<String>,
24    /// Name of parent group (if any).
25    parent_group: Option<String>,
26}
27
28impl Group {
29    /// Creates a new group with the given name.
30    pub fn new(name: impl Into<String>) -> Self {
31        Self {
32            name: name.into(),
33            enabled: true,
34            show_child_details: true,
35            child_structures: HashSet::new(),
36            child_groups: HashSet::new(),
37            parent_group: None,
38        }
39    }
40
41    /// Returns the name of this group.
42    #[must_use]
43    pub fn name(&self) -> &str {
44        &self.name
45    }
46
47    /// Returns whether this group is enabled.
48    #[must_use]
49    pub fn is_enabled(&self) -> bool {
50        self.enabled
51    }
52
53    /// Sets whether this group is enabled.
54    pub fn set_enabled(&mut self, enabled: bool) {
55        self.enabled = enabled;
56    }
57
58    /// Returns whether child details should be shown in UI.
59    #[must_use]
60    pub fn show_child_details(&self) -> bool {
61        self.show_child_details
62    }
63
64    /// Sets whether child details should be shown in UI.
65    pub fn set_show_child_details(&mut self, show: bool) {
66        self.show_child_details = show;
67    }
68
69    /// Returns the parent group name, if any.
70    #[must_use]
71    pub fn parent_group(&self) -> Option<&str> {
72        self.parent_group.as_deref()
73    }
74
75    /// Sets the parent group.
76    pub fn set_parent_group(&mut self, parent: Option<String>) {
77        self.parent_group = parent;
78    }
79
80    /// Adds a structure to this group.
81    ///
82    /// The structure is identified by "`type_name:name`" format.
83    pub fn add_structure(&mut self, type_name: &str, name: &str) {
84        self.child_structures.insert(format!("{type_name}:{name}"));
85    }
86
87    /// Removes a structure from this group.
88    pub fn remove_structure(&mut self, type_name: &str, name: &str) {
89        self.child_structures.remove(&format!("{type_name}:{name}"));
90    }
91
92    /// Returns whether this group contains a structure.
93    #[must_use]
94    pub fn contains_structure(&self, type_name: &str, name: &str) -> bool {
95        self.child_structures
96            .contains(&format!("{type_name}:{name}"))
97    }
98
99    /// Adds a child group.
100    pub fn add_child_group(&mut self, group_name: &str) {
101        self.child_groups.insert(group_name.to_string());
102    }
103
104    /// Removes a child group.
105    pub fn remove_child_group(&mut self, group_name: &str) {
106        self.child_groups.remove(group_name);
107    }
108
109    /// Returns whether this group contains a child group.
110    #[must_use]
111    pub fn contains_child_group(&self, group_name: &str) -> bool {
112        self.child_groups.contains(group_name)
113    }
114
115    /// Returns the child structure identifiers.
116    pub fn child_structures(&self) -> impl Iterator<Item = (&str, &str)> {
117        self.child_structures.iter().filter_map(|s| {
118            let parts: Vec<&str> = s.splitn(2, ':').collect();
119            if parts.len() == 2 {
120                Some((parts[0], parts[1]))
121            } else {
122                None
123            }
124        })
125    }
126
127    /// Returns the child group names.
128    pub fn child_groups(&self) -> impl Iterator<Item = &str> {
129        self.child_groups.iter().map(std::string::String::as_str)
130    }
131
132    /// Returns the number of child structures.
133    #[must_use]
134    pub fn num_child_structures(&self) -> usize {
135        self.child_structures.len()
136    }
137
138    /// Returns the number of child groups.
139    #[must_use]
140    pub fn num_child_groups(&self) -> usize {
141        self.child_groups.len()
142    }
143
144    /// Returns true if this group has no children.
145    #[must_use]
146    pub fn is_empty(&self) -> bool {
147        self.child_structures.is_empty() && self.child_groups.is_empty()
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn test_group_creation() {
157        let group = Group::new("test_group");
158        assert_eq!(group.name(), "test_group");
159        assert!(group.is_enabled());
160        assert!(group.is_empty());
161    }
162
163    #[test]
164    fn test_add_structure() {
165        let mut group = Group::new("test");
166        group.add_structure("PointCloud", "my_points");
167        assert!(group.contains_structure("PointCloud", "my_points"));
168        assert!(!group.is_empty());
169        assert_eq!(group.num_child_structures(), 1);
170    }
171
172    #[test]
173    fn test_remove_structure() {
174        let mut group = Group::new("test");
175        group.add_structure("PointCloud", "my_points");
176        group.remove_structure("PointCloud", "my_points");
177        assert!(!group.contains_structure("PointCloud", "my_points"));
178        assert!(group.is_empty());
179    }
180
181    #[test]
182    fn test_group_hierarchy() {
183        let mut parent = Group::new("parent");
184        parent.add_child_group("child");
185        assert!(parent.contains_child_group("child"));
186        assert_eq!(parent.num_child_groups(), 1);
187
188        let mut child = Group::new("child");
189        assert!(child.parent_group().is_none());
190        child.set_parent_group(Some("parent".to_string()));
191        assert_eq!(child.parent_group(), Some("parent"));
192    }
193}