Skip to main content

geop_core_topology/euler/
ker.rs

1use crate::{
2    CoedgeId, Model,
3    boundary::{BoundaryIndex, BoundaryType},
4};
5use geop_core_math::{
6    geop_error::{GeopError, GeopResult, WithContext},
7    scalars::Scalar,
8};
9
10impl<S: Scalar> Model<S> {
11    // Kill the edge added by `mer`, undoing it: splices the ring `mer` moved
12    // onto its `existing_face_id` back into the ring it split off from,
13    // removing the boundary entry `mer` added there (unlike `kef`, the face
14    // itself is not deleted — `mer` never created it, it may have other
15    // boundaries of its own). `coedge_backward` and `coedge_forward` must be
16    // exactly the pair `mer` itself returned (the closing edge's two
17    // coedges, on the original face and on `existing_face_id` respectively).
18    pub fn ker(
19        self: &mut Model<S>,
20        coedge_backward: CoedgeId,
21        coedge_forward: CoedgeId,
22    ) -> GeopResult<()> {
23        let ctx = |e: GeopError| {
24            e.with_context(format!(
25                "Model::ker(coedge_backward={coedge_backward}, coedge_forward={coedge_forward})"
26            ))
27        };
28
29        let cb = self.get_coedge(coedge_backward)?.clone();
30        let ca = self.get_coedge(coedge_forward)?.clone();
31        if ca.edge().with_context(&ctx)? != cb.edge().with_context(&ctx)? {
32            return Err(ctx(GeopError::new(
33                "coedge_backward and coedge_forward must belong to the same edge",
34            )));
35        }
36        if ca.face == cb.face {
37            return Err(ctx(GeopError::new(
38                "coedge_backward and coedge_forward must belong to different faces (use kemr to merge two rings of the same face)",
39            )));
40        }
41
42        let moved_face_id = ca.face;
43        let survivor_face_id = cb.face;
44        let remove_index = self
45            .find_boundary_containing(moved_face_id, coedge_forward)
46            .with_context(&ctx)?;
47        let keep_index = self
48            .find_boundary_containing(survivor_face_id, coedge_backward)
49            .with_context(&ctx)?;
50
51        // Captured before the splice removes `coedge_forward`: if the ring
52        // being merged away is the *only* loop on `moved_face_id`, that face
53        // is left with no loop at all, and a face must still have exactly one
54        // outer boundary. It becomes a bare-vertex boundary at the vertex the
55        // ring detached from — precisely inverting `mer`, which promotes a
56        // bare-vertex boundary to the ring that arrives on it.
57        let detach_vertex = self
58            .coedge_start_vertex_id(coedge_forward)
59            .with_context(&ctx)?;
60
61        let survivor = ca.prev;
62
63        // ca and cb each sit between one ring's root and the other ring's
64        // prev, so removing them reconnects across: ca's prev links up with
65        // cb's next, and cb's prev links up with ca's next — merging both
66        // rings into one.
67        self.coedges.get_mut(&ca.prev).unwrap().next = cb.next;
68        self.coedges.get_mut(&cb.next).unwrap().prev = ca.prev;
69        self.coedges.get_mut(&cb.prev).unwrap().next = ca.next;
70        self.coedges.get_mut(&ca.next).unwrap().prev = cb.prev;
71        self.coedges.remove(&coedge_forward);
72        self.coedges.remove(&coedge_backward);
73        self.edges.remove(&ca.edge().with_context(&ctx)?);
74
75        // Every coedge that was on moved_face_id now belongs to the merged
76        // ring on survivor_face_id.
77        for c in self.iterate_loop_coedges(survivor).collect::<Vec<_>>() {
78            self.coedges.get_mut(&c).unwrap().face = survivor_face_id;
79        }
80
81        // The surviving boundary keeps whichever role it already had, and the
82        // ring that merged into it is gone. `remove_boundary` rejects an
83        // attempt to consume an outer loop, which is the honest failure for a
84        // caller that has asked to merge away the very loop bounding a face.
85        self.faces
86            .get_mut(&survivor_face_id)
87            .unwrap()
88            .set_boundary(keep_index, BoundaryType::Loop(survivor));
89        match remove_index {
90            BoundaryIndex::Outer => {
91                self.get_face_mut(moved_face_id).with_context(&ctx)?.outer =
92                    BoundaryType::Vertex(detach_vertex);
93            }
94            BoundaryIndex::Hole(_) => {
95                self.remove_boundary(moved_face_id, remove_index)
96                    .with_context(&ctx)?;
97            }
98        }
99
100        Ok(())
101    }
102}