1use hashbrown::HashSet;
2use itertools::Itertools;
3use tracing::{error, instrument};
4
5use crate::{FaceId, HalfedgeId, MeshGraph, VertexId};
6
7impl MeshGraph {
8 #[instrument(skip(self))]
15 pub fn remove_face(&mut self, face_id: FaceId) -> (Vec<VertexId>, Vec<HalfedgeId>) {
16 if !self.faces.contains_key(face_id) {
17 return (vec![], vec![]);
18 }
19
20 let halfedges = self
21 .halfedges
22 .iter()
23 .filter_map(|(he_id, he)| {
24 if he.face == Some(face_id) {
25 Some((he_id, *he))
26 } else {
27 None
28 }
29 })
30 .collect_vec();
31
32 #[cfg(feature = "instrumentation")]
33 crate::record_op_trace!(
34 "remove_face({face_id:?}) members={:?}",
35 halfedges.iter().map(|(id, _)| id).collect_vec()
36 );
37
38 let mut removed_halfedges = HashSet::with_capacity(4);
39
40 for (he_id, he) in halfedges {
41 let Some(twin_id) = he.twin else {
45 removed_halfedges.insert(he_id);
46 continue;
47 };
48 let Some(twin) = self.halfedges.get(twin_id) else {
49 removed_halfedges.insert(he_id);
50 continue;
51 };
52 if twin.is_boundary() {
53 removed_halfedges.insert(he_id);
54 removed_halfedges.insert(twin_id);
55 } else if twin.twin != Some(he_id) {
56 removed_halfedges.insert(he_id);
57 } else {
58 self.halfedges[he_id].face = None;
60 self.halfedges[he_id].next = None;
61 }
62 }
63
64 #[cfg(feature = "rerun")]
65 self.log_hes_rerun(
66 "deleted_by_face_deletion",
67 &removed_halfedges.iter().copied().collect::<Vec<_>>(),
68 );
69
70 let mut removed_vertices = Vec::with_capacity(3);
71 let mut touched_start_vertices = HashSet::with_capacity(3);
72
73 for &he_id in &removed_halfedges {
75 let Some(start_v_id) = self.halfedges[he_id].start_vertex(self) else {
82 continue;
83 };
84 touched_start_vertices.insert(start_v_id);
85 if let Some(out_he_ids) = self.outgoing_halfedges.get_mut(start_v_id) {
86 out_he_ids.retain(|&id| id != he_id);
87 } else {
88 error!("No outgoing halfedges found for vertex {start_v_id:?}");
89 }
90 }
91
92 for start_v_id in touched_start_vertices {
93 let surviving_out_he = self
96 .outgoing_halfedges
97 .get(start_v_id)
98 .and_then(|out_he_ids| out_he_ids.first().copied());
99
100 if let Some(out_he_id) = surviving_out_he {
101 if let Some(v) = self.vertices.get_mut(start_v_id) {
102 v.outgoing_halfedge = Some(out_he_id);
103 } else {
104 error!("Vertex {start_v_id:?} not found");
105 }
106 } else {
107 self.remove_only_vertex(start_v_id);
108 removed_vertices.push(start_v_id);
109 }
110 }
111
112 #[cfg(feature = "instrumentation")]
113 self.probe_live_face_removal(
114 &removed_halfedges.iter().copied().collect::<Vec<_>>(),
115 "remove_face_tail",
116 );
117
118 for he_id in &removed_halfedges {
119 self.halfedges.remove(*he_id);
120 }
121
122 self.bvh.remove(self.faces[face_id].index);
124 self.faces.remove(face_id);
125 #[cfg(feature = "instrumentation")]
126 crate::record_face_death(face_id);
127
128 (removed_vertices, Vec::from_iter(removed_halfedges))
129 }
130
131 pub fn remove_only_vertex(&mut self, vertex_id: VertexId) {
133 self.outgoing_halfedges.remove(vertex_id);
134 self.positions.remove(vertex_id);
135 if let Some(normals) = &mut self.vertex_normals {
136 normals.remove(vertex_id);
137 }
138 self.vertices.remove(vertex_id);
139 }
140
141 pub fn remove_only_halfedge(&mut self, he_id: HalfedgeId) {
146 if let Some(he) = self.halfedges.get(he_id) {
147 if let Some(start_v_id) = he.start_vertex(self)
148 && let Some(hes) = self.outgoing_halfedges.get_mut(start_v_id)
149 {
150 hes.retain(|out_he_id| *out_he_id != he_id);
151 }
152
153 #[cfg(feature = "instrumentation")]
154 self.probe_live_face_removal(&[he_id], "remove_only_halfedge");
155 self.halfedges.remove(he_id);
156 }
157 }
158
159 pub fn remove_only_halfedge_and_twin(&mut self, he_id: HalfedgeId) {
160 if let Some(he) = self.halfedges.get(he_id) {
161 if let Some(start_v_id) = he.start_vertex(self)
162 && let Some(hes) = self.outgoing_halfedges.get_mut(start_v_id)
163 {
164 hes.retain(|out_he_id| *out_he_id != he_id);
165 }
166
167 if let Some(twin_he_id) = he.twin {
168 self.remove_only_halfedge(twin_he_id);
169 }
170
171 #[cfg(feature = "instrumentation")]
174 self.probe_live_face_removal(&[he_id], "remove_only_halfedge_and_twin");
175 self.halfedges.remove(he_id);
176 }
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use glam::{Mat4, vec3};
183
184 use crate::{
185 utils::{extend_with, get_tracing_subscriber},
186 *,
187 };
188
189 #[test]
190 fn test_remove_face() {
191 get_tracing_subscriber();
192 let mut meshgraph = MeshGraph::new();
193 let p_c = vec3(0.0, 0.0, 0.0);
194 let p_1 = vec3(0.0, 1.0, 0.0);
195 let p_2 = vec3(-1.0, 0.5, 0.0);
196 let p_3 = vec3(-1.0, -0.5, 0.0);
197 let p_4 = vec3(0.0, -1.0, 0.0);
198 let p_5 = vec3(1.0, -0.5, 0.0);
199 let p_6 = vec3(1.0, 0.5, 0.0);
200
201 let points = vec![p_c, p_1, p_2, p_3, p_4, p_5, p_6];
202 let vertex_ids = extend_with(&mut meshgraph, &points.clone(), Mat4::default(), 5.0, 0);
203
204 #[cfg(feature = "rerun")]
205 {
206 meshgraph.log_rerun();
207 RR.flush_blocking().unwrap();
208 }
209
210 let face_count = meshgraph.faces.len();
211 let face_id = meshgraph.vertices[vertex_ids[6]]
212 .faces(&meshgraph)
213 .next()
214 .unwrap();
215 meshgraph.remove_face(face_id);
216 #[cfg(feature = "rerun")]
217 {
218 meshgraph.log_rerun();
219 RR.flush_blocking().unwrap();
220 }
221
222 assert_eq!(meshgraph.faces.len(), face_count - 1);
223 }
224}