Skip to main content

geop_core_topology/euler/
kemr.rs

1use crate::{CoedgeId, Model, argument_validation::validate_same_loop, boundary::BoundaryType};
2use geop_core_math::{
3    geop_error::{GeopError, GeopResult, WithContext},
4    scalars::Scalar,
5};
6
7impl<S: Scalar> Model<S> {
8    // Kill the edge added by mekr, splitting the single loop it merged back into two
9    // loops (rings) and adding a boundary entry for the newly separated ring. ca_id
10    // and cb_id are mekr's own two coedges (its returned coedge_a and coedge_b) and
11    // must currently lie on the same loop of the same face (the shape mekr leaves
12    // behind).
13    pub fn kemr(self: &mut Model<S>, ca_id: CoedgeId, cb_id: CoedgeId) -> GeopResult<()> {
14        let ctx =
15            |e: GeopError| e.with_context(format!("Model::kemr(ca_id={ca_id}, cb_id={cb_id})"));
16
17        let ca = self.get_coedge(ca_id)?.clone();
18        let cb = self.get_coedge(cb_id)?.clone();
19        if ca.edge().with_context(&ctx)? != cb.edge().with_context(&ctx)? {
20            return Err(ctx(GeopError::new(
21                "ca_id and cb_id must belong to the same edge",
22            )));
23        }
24        if ca.face != cb.face {
25            return Err(ctx(GeopError::new(
26                "ca_id and cb_id must belong to the same face",
27            )));
28        }
29        validate_same_loop(&self, ca_id, cb_id).with_context(&ctx)?;
30
31        let face_id = ca.face;
32        let ring_index = self
33            .find_boundary_containing(face_id, ca_id)
34            .with_context(&ctx)?;
35
36        let ring1 = cb.next;
37        let ring2 = ca.next;
38
39        // ca and cb each sit between one root coedge and the other loop's prev, so
40        // removing them reconnects across: ca's prev links up with cb's next, and
41        // cb's prev links up with ca's next.
42        self.coedges.get_mut(&ca.prev).unwrap().next = cb.next;
43        self.coedges.get_mut(&cb.next).unwrap().prev = ca.prev;
44        self.coedges.get_mut(&cb.prev).unwrap().next = ca.next;
45        self.coedges.get_mut(&ca.next).unwrap().prev = cb.prev;
46        self.coedges.remove(&ca_id);
47        self.coedges.remove(&cb_id);
48        self.edges.remove(&ca.edge().with_context(&ctx)?);
49
50        // The loop that was cut in two keeps its own role — outer stays
51        // outer, a hole stays that hole — and the second ring it split off
52        // becomes a new hole. Splitting a loop can only ever *add* an inner
53        // boundary: the material still ends at the same outermost ring.
54        let face = self.faces.get_mut(&face_id).unwrap();
55        face.set_boundary(ring_index, BoundaryType::Loop(ring1));
56        face.holes.push(BoundaryType::Loop(ring2));
57
58        Ok(())
59    }
60}