geop_core_topology/edit/merge_edge.rs
1use crate::{CoedgeGeometry, EdgeId, Model};
2use geop_core_math::{
3 geop_error::{GeopError, GeopResult},
4 scalars::Scalar,
5};
6
7impl<S: Scalar> Model<S> {
8 /// Merges `edge_deleted_id` into `edge_into_id`: repoints every coedge
9 /// tracing the deleted edge to the surviving one, then drops the
10 /// now-unreferenced edge. `reversed` says whether the deleted edge ran
11 /// start<->end the *other* way round relative to the surviving one — if
12 /// so, every repointed coedge's `sense` is flipped so it keeps tracing
13 /// the same physical direction it always did.
14 ///
15 /// TODO: `edge_into_id`'s own curve currently stays exactly as it was —
16 /// it should instead be widened (unioned) to certainly contain the
17 /// deleted edge's curve too, so the kept edge's geometry honestly
18 /// reflects both original edges' combined tolerance instead of silently
19 /// favoring whichever one happened to survive.
20 pub fn merge_edge(
21 &mut self,
22 edge_into_id: EdgeId,
23 edge_deleted_id: EdgeId,
24 reversed: bool,
25 ) -> GeopResult<()> {
26 // Same self-merge hazard as `merge_vertex` — always a caller bug
27 // (typically: acting on a stale edge snapshot), rejected rather than
28 // silently absorbed.
29 if edge_into_id == edge_deleted_id {
30 return Err(GeopError::new(format!(
31 "Model::merge_edge: edge_into_id and edge_deleted_id are both {edge_into_id} — refusing to merge an edge into itself"
32 )));
33 }
34
35 for coedge in self.coedges.values_mut() {
36 if coedge.geometry == CoedgeGeometry::Edge(edge_deleted_id) {
37 coedge.geometry = CoedgeGeometry::Edge(edge_into_id);
38 if reversed {
39 coedge.sense = coedge.sense.opposite();
40 }
41 }
42 }
43
44 self.edges.remove(&edge_deleted_id);
45
46 Ok(())
47 }
48}