Skip to main content

geop_core_topology/edit/
merge_vertex.rs

1use crate::{CoedgeGeometry, Model, VertexId, boundary::BoundaryType};
2use geop_core_math::geop_error::{GeopError, GeopResult};
3
4impl Model {
5    /// Merges `vertex_deleted_id` into `vertex_into_id`: unions their points
6    /// into `vertex_into_id`, repoints every edge/coedge/boundary reference
7    /// from `vertex_deleted_id` to `vertex_into_id`, then drops the now
8    ///-unreferenced vertex.
9    pub fn merge_vertex(
10        &mut self,
11        vertex_into_id: VertexId,
12        vertex_deleted_id: VertexId,
13    ) -> GeopResult<()> {
14        // A self-merge would delete a vertex still referenced everywhere it
15        // stood in for `vertex_into_id` a moment ago — always a caller bug
16        // (typically: merging against a stale vertex snapshot taken before
17        // an earlier merge already welded the two together), so this is
18        // rejected rather than silently absorbed.
19        if vertex_into_id == vertex_deleted_id {
20            return Err(GeopError::new(format!(
21                "Model::merge_vertex: vertex_into_id and vertex_deleted_id are both {vertex_into_id} — refusing to merge a vertex into itself"
22            )));
23        }
24
25        // union the deleted vertex into point
26        let deleted_point = self.get_vertex(vertex_deleted_id)?.point;
27        let vertex_into = self.get_vertex_mut(vertex_into_id)?;
28        vertex_into.point = vertex_into.point.union(&deleted_point);
29
30        // Find all occurences of vertex_deleted_id and replace with
31        // vertex_into_id. Only what refers to it is touched, so that a model
32        // sharing its arenas with a copy copies only those entities.
33        let edges: Vec<_> = (self.edges.iter())
34            .filter(|(_, e)| [e.start_vertex, e.end_vertex].contains(&vertex_deleted_id))
35            .map(|(&id, _)| id)
36            .collect();
37        for id in edges {
38            let edge = self.get_edge_mut(id)?;
39            if edge.start_vertex == vertex_deleted_id {
40                edge.start_vertex = vertex_into_id;
41            }
42            if edge.end_vertex == vertex_deleted_id {
43                edge.end_vertex = vertex_into_id;
44            }
45        }
46
47        let coedges: Vec<_> = (self.coedges.iter())
48            .filter(|(_, c)| c.geometry == CoedgeGeometry::Vertex(vertex_deleted_id))
49            .map(|(&id, _)| id)
50            .collect();
51        for id in coedges {
52            self.get_coedge_mut(id)?.geometry = CoedgeGeometry::Vertex(vertex_into_id);
53        }
54
55        let faces: Vec<_> = (self.faces.iter())
56            .filter(|(_, f)| {
57                f.boundaries()
58                    .any(|b| b == BoundaryType::Vertex(vertex_deleted_id))
59            })
60            .map(|(&id, _)| id)
61            .collect();
62        for id in faces {
63            for boundary in self.get_face_mut(id)?.boundaries_mut() {
64                if *boundary == BoundaryType::Vertex(vertex_deleted_id) {
65                    *boundary = BoundaryType::Vertex(vertex_into_id);
66                }
67            }
68        }
69
70        self.vertices.remove(&vertex_deleted_id);
71
72        Ok(())
73    }
74}