ifc_lite_geometry/space_dcel/
remove.rs1use super::walk::FaceWalk;
10use super::{EditError, Face, FaceId, FacePatch, HalfEdgeId, SpacePlate, VertexId, EPS_COLL};
11use super::geom2d::perp_distance;
12
13impl SpacePlate {
14 pub(super) fn vertex_degree(&self, v: VertexId) -> usize {
16 let vi = v.0 as usize;
17 if vi >= self.vertices.len() || !self.vertices[vi].alive {
18 return 0;
19 }
20 self.outgoing_half_edges(v).count()
21 }
22
23 pub(super) fn remove_spur_edge(&mut self, spur_he: HalfEdgeId) -> Result<(), EditError> {
29 let hi = spur_he.0 as usize;
30 if hi >= self.half_edges.len() || !self.half_edges[hi].alive {
31 return Err(EditError::StaleHandle);
32 }
33 let t = self.half_edges[hi].twin;
34 let (s, s_t) = if self.vertex_degree(self.half_edges[hi].origin) == 1 {
36 (spur_he, t)
37 } else if self.vertex_degree(self.half_edges[t.0 as usize].origin) == 1 {
38 (t, spur_he)
39 } else {
40 return Err(EditError::VertexNotDissolvable); };
42 let tip = self.half_edges[s.0 as usize].origin;
43 let j = self.half_edges[s_t.0 as usize].origin;
44 let f = self.half_edges[s.0 as usize].face;
45 if self.half_edges[s_t.0 as usize].face != f
48 || self.half_edges[s_t.0 as usize].next != s
49 || self.half_edges[s.0 as usize].prev != s_t
50 {
51 return Err(EditError::StaleHandle);
52 }
53 let a = self.half_edges[s_t.0 as usize].prev; let b = self.half_edges[s.0 as usize].next; if a == s {
57 self.half_edges[s.0 as usize].alive = false;
62 self.half_edges[s_t.0 as usize].alive = false;
63 self.vertices[tip.0 as usize].outgoing = None;
64 self.vertices[tip.0 as usize].alive = false;
65 self.vertices[j.0 as usize].outgoing = None;
66 self.vertices[j.0 as usize].alive = false;
67 self.reanchor_face_if_dead(f);
68 return Ok(());
69 }
70
71 self.half_edges[a.0 as usize].next = b;
73 self.half_edges[b.0 as usize].prev = a;
74 self.half_edges[s.0 as usize].alive = false;
75 self.half_edges[s_t.0 as usize].alive = false;
76 self.vertices[tip.0 as usize].outgoing = None;
77 self.vertices[tip.0 as usize].alive = false;
78 self.repair_vertex_outgoing(j, s_t);
79 if matches!(self.faces[f.0 as usize].half_edge, Some(h) if h == s || h == s_t) {
80 self.faces[f.0 as usize].half_edge = Some(a);
81 }
82 Ok(())
83 }
84
85 pub fn prune_orphans(&mut self) -> usize {
91 let mut removed = 0usize;
92 loop {
96 let mut progress = false;
97 let tips: Vec<VertexId> = (0..self.vertices.len())
98 .map(|i| VertexId(i as u32))
99 .filter(|&v| self.vertex_degree(v) == 1)
100 .collect();
101 for tip in tips {
102 if self.vertex_degree(tip) != 1 {
103 continue; }
105 let s = self.outgoing_half_edges(tip).next();
106 if let Some(s) = s {
107 if self.remove_spur_edge(s).is_ok() {
108 removed += 1;
109 progress = true;
110 }
111 }
112 }
113 if !progress {
114 break;
115 }
116 }
117 for i in 0..self.vertices.len() {
119 let v = VertexId(i as u32);
120 if self.vertices[i].alive && self.vertex_degree(v) == 0 {
121 self.vertices[i].alive = false;
122 self.vertices[i].outgoing = None;
123 removed += 1;
124 }
125 }
126 loop {
128 let mut progress = false;
129 let cands: Vec<VertexId> = (0..self.vertices.len())
130 .map(|i| VertexId(i as u32))
131 .filter(|&v| self.vertex_degree(v) == 2)
132 .collect();
133 for v in cands {
134 if self.vertex_degree(v) != 2 {
135 continue;
136 }
137 let outs: Vec<HalfEdgeId> = self.outgoing_half_edges(v).collect();
138 let p = self.vertices[v.0 as usize].pos;
139 let x = self.vertices[self.dest(outs[0]).0 as usize].pos;
140 let y = self.vertices[self.dest(outs[1]).0 as usize].pos;
141 if perp_distance(p, x, y) >= EPS_COLL {
142 continue; }
144 if self.dissolve_vertex(v).is_ok() {
145 removed += 1;
146 progress = true;
147 }
148 }
149 if !progress {
150 break;
151 }
152 }
153 removed
154 }
155
156 pub fn remove_edge(&mut self, edge: HalfEdgeId) -> Result<Vec<FacePatch>, EditError> {
164 let hi = edge.0 as usize;
165 if hi >= self.half_edges.len() || !self.half_edges[hi].alive {
166 return Err(EditError::StaleHandle);
167 }
168 let t = self.half_edges[hi].twin;
169 let f_keep = self.half_edges[hi].face;
170 let f_drop = self.half_edges[t.0 as usize].face;
171 let keep_outer = self.faces[f_keep.0 as usize].is_outer;
172 let drop_outer = self.faces[f_drop.0 as usize].is_outer;
173
174 if f_keep != f_drop && !keep_outer && !drop_outer {
175 return self.merge_faces(edge); }
177 if f_keep != f_drop && keep_outer != drop_outer {
178 return Err(EditError::BordersExterior); }
180
181 let hn = self.half_edges[hi].next;
183 let hp = self.half_edges[hi].prev;
184 let tn = self.half_edges[t.0 as usize].next;
185 let tp = self.half_edges[t.0 as usize].prev;
186 let oh = self.half_edges[hi].origin;
187 let ot = self.half_edges[t.0 as usize].origin;
188
189 self.half_edges[hp.0 as usize].next = tn;
190 self.half_edges[tn.0 as usize].prev = hp;
191 self.half_edges[tp.0 as usize].next = hn;
192 self.half_edges[hn.0 as usize].prev = tp;
193
194 if f_drop != f_keep {
195 self.faces[f_keep.0 as usize].half_edge = Some(hp);
197 let merged: Vec<HalfEdgeId> = self.face_half_edges(f_keep).collect();
198 for he in merged {
199 self.half_edges[he.0 as usize].face = f_keep;
200 }
201 self.faces[f_drop.0 as usize].alive = false;
202 self.faces[f_drop.0 as usize].half_edge = None;
203 }
204 self.half_edges[hi].alive = false;
205 self.half_edges[t.0 as usize].alive = false;
206 self.repair_vertex_outgoing(oh, edge);
207 self.repair_vertex_outgoing(ot, t);
208 self.reanchor_face_if_dead(f_keep);
211 if f_drop == f_keep {
212 self.split_bridge_cycles(f_keep, hn, tn);
213 }
214
215 self.prune_orphans();
216
217 let mut out = Vec::new();
218 if self.faces[f_keep.0 as usize].alive && !self.faces[f_keep.0 as usize].is_outer {
219 out.push(self.face_patch(f_keep));
220 }
221 Ok(out)
222 }
223
224 fn split_bridge_cycles(&mut self, f: FaceId, hn: HalfEdgeId, tn: HalfEdgeId) {
234 if !self.half_edges[hn.0 as usize].alive || !self.half_edges[tn.0 as usize].alive {
235 return;
236 }
237 let walk = |start: HalfEdgeId| FaceWalk { plate: self, start: Some(start), cur: None };
238 let cycle_h: Vec<HalfEdgeId> = walk(hn).collect();
239 if cycle_h.contains(&tn) {
240 return; }
242 let cycle_t: Vec<HalfEdgeId> = walk(tn).collect();
243 let (area_h, area_t) = (self.signed_area_of_cycle(&cycle_h), self.signed_area_of_cycle(&cycle_t));
244 let (keep, split, split_area) =
245 if area_h >= area_t { (cycle_h, cycle_t, area_t) } else { (cycle_t, cycle_h, area_h) };
246 let parent = self.faces[f.0 as usize].clone();
247 let is_outer = split_area <= 0.0;
248 let new_face = FaceId(self.faces.len() as u32);
249 self.faces.push(Face {
250 half_edge: split.first().copied(),
251 is_outer,
252 is_room: !is_outer && parent.is_room,
253 alive: true,
254 ..parent
255 });
256 for he in split {
257 self.half_edges[he.0 as usize].face = new_face;
258 }
259 self.faces[f.0 as usize].half_edge = keep.first().copied();
260 }
261}