Skip to main content

geop_core_topology/euler/
kef.rs

1use crate::{EdgeId, FaceId, Model, boundary::BoundaryType};
2use geop_core_math::{
3    geop_error::{GeopError, GeopResult},
4    scalars::Scalar,
5};
6
7impl<S: Scalar> Model<S> {
8    /// Kill the edge added by `mef`, merging `killed_face` back into the
9    /// face on the other side of `edge`: the absorbed face's ring is
10    /// spliced back into the surviving face's ring (undoing the split),
11    /// and `killed_face` is removed from the model and from its shell's
12    /// face list. `edge`'s two coedges must currently lie on two
13    /// *different* faces, one of which is `killed_face`.
14    pub fn kef(self: &mut Model<S>, edge: EdgeId, killed_face: FaceId) -> GeopResult<()> {
15        let ctx = |e: GeopError| {
16            e.with_context(format!(
17                "Model::kef(edge={edge}, killed_face={killed_face})"
18            ))
19        };
20
21        let refs = self.coedges_of_edge(edge);
22        if refs.len() != 2 {
23            return Err(ctx(GeopError::new("edge must have exactly two coedges")));
24        }
25        let (c0_id, c1_id) = (refs[0], refs[1]);
26        let c0 = self.get_coedge(c0_id)?.clone();
27        let c1 = self.get_coedge(c1_id)?.clone();
28        if c0.face == c1.face {
29            return Err(ctx(GeopError::new(
30                "edge's coedges must belong to different faces (use ker to merge two rings of the same face)",
31            )));
32        }
33
34        let absorbed_face_id = killed_face;
35        let absorbed_face = self.get_face(absorbed_face_id)?.clone();
36
37        let ((ca_id, ca), (cb_id, cb)) = if c0.face == killed_face {
38            ((c1_id, c1), (c0_id, c0))
39        } else {
40            ((c0_id, c0), (c1_id, c1))
41        };
42
43        let surviving_face_id = ca.face;
44        let keep_index = self
45            .find_boundary_containing(surviving_face_id, ca_id)
46            .map_err(|_| {
47                ctx(GeopError::new(
48                    "could not find the ring containing the surviving coedge",
49                ))
50            })?;
51
52        let survivor = ca.prev;
53
54        // ca and cb each sit between one ring's root and the other ring's
55        // prev, so removing them reconnects across: ca's prev links up with
56        // cb's next, and cb's prev links up with ca's next — merging both
57        // rings into one.
58        self.coedges.get_mut(&ca.prev).unwrap().next = cb.next;
59        self.coedges.get_mut(&cb.next).unwrap().prev = ca.prev;
60        self.coedges.get_mut(&cb.prev).unwrap().next = ca.next;
61        self.coedges.get_mut(&ca.next).unwrap().prev = cb.prev;
62        self.coedges.remove(&ca_id);
63        self.coedges.remove(&cb_id);
64        self.edges.remove(&edge);
65
66        // Every coedge that was on the absorbed face now belongs to the
67        // merged ring on the surviving face.
68        for c in self.iterate_loop_coedges(survivor).collect::<Vec<_>>() {
69            self.coedges.get_mut(&c).unwrap().face = surviving_face_id;
70        }
71
72        // The absorbed face's holes are still holes of the merged material —
73        // they describe regions removed from a patch that now belongs to the
74        // surviving face, so they have to come across. Dropping them (as
75        // deleting the face outright would) silently fills them in.
76        let absorbed_holes = absorbed_face.holes.clone();
77        let surviving = self.faces.get_mut(&surviving_face_id).unwrap();
78        surviving.set_boundary(keep_index, BoundaryType::Loop(survivor));
79        surviving.holes.extend(absorbed_holes);
80        self.faces.remove(&absorbed_face_id);
81        if let Some(shell) = self.shells.get_mut(&absorbed_face.shell) {
82            shell.faces.retain(|&f| f != absorbed_face_id);
83        }
84
85        Ok(())
86    }
87}