1use hashbrown::HashSet;
2use tracing::{error, instrument};
3
4use super::{FaceId, HalfedgeId, MeshGraph, VertexId};
5
6#[derive(Debug, Clone, Default)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct Selection {
9 pub vertices: HashSet<VertexId>,
10 pub halfedges: HashSet<HalfedgeId>,
11 pub faces: HashSet<FaceId>,
12}
13
14impl Selection {
15 pub fn select_all(mesh_graph: &MeshGraph) -> Self {
16 Self {
17 faces: mesh_graph.faces.keys().collect(),
18 ..Default::default()
19 }
20 }
21
22 pub fn resolve_to_halfedges(&self, mesh_graph: &MeshGraph) -> HashSet<HalfedgeId> {
29 let mut halfedges = self.halfedges.clone();
30
31 for face in &self.faces {
32 let Some(face) = mesh_graph.faces.get(*face) else {
33 error!("Face not found");
34 continue;
35 };
36 halfedges.extend(face.halfedges(mesh_graph));
37 }
38
39 for vertex in &self.vertices {
40 let Some(vertex) = mesh_graph.vertices.get(*vertex) else {
41 error!("Vertex not found");
42 continue;
43 };
44 halfedges.extend(vertex.outgoing_halfedges(mesh_graph));
45 }
46
47 halfedges
48 }
49
50 pub fn resolve_to_vertices(&self, mesh_graph: &MeshGraph) -> HashSet<VertexId> {
51 let mut vertices = self.vertices.clone();
52
53 for halfedge in &self.halfedges {
54 let Some(he) = mesh_graph.halfedges.get(*halfedge) else {
55 error!("Halfedge not found");
56 continue;
57 };
58 if let Some(start_vertex) = he.start_vertex(mesh_graph) {
59 vertices.insert(start_vertex);
60 } else {
61 error!("Start vertex not found");
62 }
63 vertices.insert(he.end_vertex);
64 }
65
66 for face in &self.faces {
67 let Some(face) = mesh_graph.faces.get(*face) else {
68 error!("Face not found");
69 continue;
70 };
71 vertices.extend(face.vertices(mesh_graph));
72 }
73
74 vertices
75 }
76
77 #[instrument(skip(mesh_graph))]
80 pub fn grow(&mut self, mesh_graph: &MeshGraph) -> HashSet<VertexId> {
82 let existing_verts = self.resolve_to_vertices(mesh_graph);
83
84 let mut new_verts = HashSet::new();
85
86 for vert_id in &existing_verts {
87 if let Some(vert) = mesh_graph.vertices.get(*vert_id) {
88 for neighbor in vert.neighbours(mesh_graph) {
89 if !existing_verts.contains(&neighbor) {
90 new_verts.insert(neighbor);
91 self.insert(neighbor);
92 }
93 }
94 } else {
95 error!("Vertex not found");
96 }
97 }
98
99 new_verts
100 }
101}
102
103pub trait SelectionOps<T> {
104 fn insert(&mut self, item: T);
105 fn remove(&mut self, item: T);
106}
107
108impl SelectionOps<VertexId> for Selection {
109 fn insert(&mut self, item: VertexId) {
110 self.vertices.insert(item);
111 }
112
113 fn remove(&mut self, item: VertexId) {
114 self.vertices.remove(&item);
115 }
116}
117
118impl SelectionOps<HalfedgeId> for Selection {
119 fn insert(&mut self, item: HalfedgeId) {
120 self.halfedges.insert(item);
121 }
122
123 fn remove(&mut self, item: HalfedgeId) {
124 self.halfedges.remove(&item);
125 }
126}
127
128impl SelectionOps<FaceId> for Selection {
129 fn insert(&mut self, item: FaceId) {
130 self.faces.insert(item);
131 }
132
133 fn remove(&mut self, item: FaceId) {
134 self.faces.remove(&item);
135 }
136}
137
138impl From<VertexId> for Selection {
139 fn from(value: VertexId) -> Self {
140 Self::from_iter(vec![value])
141 }
142}
143
144impl From<HalfedgeId> for Selection {
145 fn from(value: HalfedgeId) -> Self {
146 Self::from_iter(vec![value])
147 }
148}
149
150impl From<FaceId> for Selection {
151 fn from(value: FaceId) -> Self {
152 Self::from_iter(vec![value])
153 }
154}
155
156impl FromIterator<VertexId> for Selection {
157 fn from_iter<T: IntoIterator<Item = VertexId>>(iter: T) -> Self {
158 Selection {
159 vertices: HashSet::from_iter(iter),
160 ..Default::default()
161 }
162 }
163}
164
165impl FromIterator<HalfedgeId> for Selection {
166 fn from_iter<T: IntoIterator<Item = HalfedgeId>>(iter: T) -> Self {
167 Selection {
168 halfedges: HashSet::from_iter(iter),
169 ..Default::default()
170 }
171 }
172}
173
174impl FromIterator<FaceId> for Selection {
175 fn from_iter<T: IntoIterator<Item = FaceId>>(iter: T) -> Self {
176 Selection {
177 faces: HashSet::from_iter(iter),
178 ..Default::default()
179 }
180 }
181}
182
183macro_rules! impl_from_for_selection {
184 ($type:ident) => {
185 impl From<$type<VertexId>> for Selection {
186 fn from(value: $type<VertexId>) -> Self {
187 Self::from_iter(value)
188 }
189 }
190 impl From<$type<HalfedgeId>> for Selection {
191 fn from(value: $type<HalfedgeId>) -> Self {
192 Self::from_iter(value)
193 }
194 }
195 impl From<$type<FaceId>> for Selection {
196 fn from(value: $type<FaceId>) -> Self {
197 Self::from_iter(value)
198 }
199 }
200 };
201}
202
203impl_from_for_selection!(Vec);
204impl_from_for_selection!(HashSet);
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use crate::utils::build_grid;
210
211 #[test]
217 fn test_resolve_tolerates_dead_faces_in_the_selection() {
218 let mut mg = build_grid(4);
219 let selection = Selection::select_all(&mg);
220
221 mg.collapse_until_edges_above_min_length(1.5, &mut HashSet::new());
222
223 assert!(
224 selection
225 .faces
226 .iter()
227 .any(|f_id| !mg.faces.contains_key(*f_id)),
228 "fixture failed to invalidate anything - test is vacuous"
229 );
230
231 for he_id in selection.resolve_to_halfedges(&mg) {
232 assert!(
233 mg.halfedges.contains_key(he_id),
234 "resolved halfedge {he_id:?} is dead"
235 );
236 }
237 for v_id in selection.resolve_to_vertices(&mg) {
238 assert!(
239 mg.vertices.contains_key(v_id),
240 "resolved vertex {v_id:?} is dead"
241 );
242 }
243 }
244
245 #[test]
253 fn test_resolve_keeps_live_ids_when_the_selection_is_stale() {
254 let mut mg = build_grid(4);
255 let selection = Selection {
256 vertices: mg.vertices.keys().collect(),
257 halfedges: mg.halfedges.keys().collect(),
258 faces: mg.faces.keys().collect(),
259 };
260
261 mg.collapse_until_edges_above_min_length(1.5, &mut HashSet::new());
262
263 let live_vertices_before: HashSet<VertexId> = selection
264 .vertices
265 .iter()
266 .copied()
267 .filter(|v_id| mg.vertices.contains_key(*v_id))
268 .collect();
269 let live_halfedges_before: HashSet<HalfedgeId> = selection
270 .halfedges
271 .iter()
272 .copied()
273 .filter(|he_id| mg.halfedges.contains_key(*he_id))
274 .collect();
275
276 assert!(
277 live_vertices_before.len() < selection.vertices.len(),
278 "fixture failed to invalidate anything - test is vacuous"
279 );
280
281 let resolved_halfedges = selection.resolve_to_halfedges(&mg);
282 let resolved_vertices = selection.resolve_to_vertices(&mg);
283
284 for v_id in &live_vertices_before {
285 assert!(
286 resolved_vertices.contains(v_id),
287 "live vertex {v_id:?} missing from resolve_to_vertices"
288 );
289 }
290 for he_id in &live_halfedges_before {
291 assert!(
292 resolved_halfedges.contains(he_id),
293 "live halfedge {he_id:?} missing from resolve_to_halfedges"
294 );
295 }
296 }
297}