Skip to main content

geop_core_topology/euler/
mef.rs

1use crate::{
2    Coedge, CoedgeGeometry, CoedgeId, Edge, EdgeId, Face, FaceId, Model, Sense,
3    argument_validation::{
4        validate_curve_start_and_end, validate_pcurve_start_and_end, validate_same_loop,
5    },
6    boundary::BoundaryType,
7};
8use geop_core_geometry::{
9    nurb_curve::{NurbCurve2D, NurbCurve3D},
10    nurb_surface::NurbSurface3D,
11};
12use geop_core_math::{
13    geop_error::{GeopError, GeopResult, WithContext},
14    scalars::Scalar,
15    with_context,
16};
17
18impl<S: Scalar> Model<S> {
19    // Create a new face by inserting an edge between
20    // coedge1's end vertex and coedge2's start vertex.
21    // Curve and pcurve (on the new face) must go from coedge1's end vertex to coedge2's start vertex.
22    // PCurve reversed (on the existing face) must go from coedge2's start vertex to coedge1's end vertex.
23    // Coedge1 and coedge2 must belong to the same loop of the same face.
24    // The new face will have the edge and coedge 1 and 2 on the boundary.
25    // returns (new edge, new face, new coedge on the existing face)
26    pub fn mef(
27        self: &mut Model<S>,
28        coedge1: CoedgeId,
29        coedge2: CoedgeId,
30        curve: NurbCurve3D<S>,
31        pcurve: NurbCurve2D<S>,
32        pcurve_reversed: NurbCurve2D<S>,
33        new_surface: NurbSurface3D<S>,
34    ) -> GeopResult<(EdgeId, FaceId, CoedgeId, CoedgeId)> {
35        let ctx = |e: GeopError| {
36            e.with_context(format!(
37                "Model::mef(
38                coedge1={coedge1}
39                coedge2={coedge2}
40                curve={curve}
41                pcurve={pcurve}
42                pcurve_reversed={pcurve_reversed}
43                new_surface={new_surface}"
44            ))
45        };
46
47        let ce1 = self.get_coedge(coedge1)?.clone();
48        let ce2 = self.get_coedge(coedge2)?.clone();
49        validate_same_loop(self, coedge1, coedge2)?;
50        let old_face_id = ce1.face;
51        let old_face = self.get_face(old_face_id)?.clone();
52
53        let start = self
54            .get_vertex(self.coedge_end_vertex_id(coedge1)?)?
55            .clone()
56            .point;
57        let end = self
58            .get_vertex(self.coedge_start_vertex_id(coedge2)?)?
59            .clone()
60            .point;
61
62        validate_pcurve_start_and_end(&new_surface, &pcurve, &start, &end)
63            .with_context("initial arg validation")
64            .with_context(&ctx)?;
65        validate_pcurve_start_and_end(&old_face.surface, &pcurve_reversed, &end, &start)
66            .with_context("initial arg validation")
67            .with_context(&ctx)?;
68        validate_curve_start_and_end(&curve, &start, &end)
69            .with_context("initial arg validation")
70            .with_context(&ctx)?;
71
72        // Find which of `old_face`'s boundaries is the one being split (i.e.
73        // the loop coedge1/coedge2 belong to), by index — done now, before
74        // any pointers are mutated below, so the traversal walks the still
75        // fully-intact original ring and unambiguously reaches `coedge1`
76        // regardless of where that boundary's own anchor happens to sit in
77        // it. Works for any number of boundaries (e.g. holes): exactly one
78        // can structurally contain `coedge1`, since a face's boundaries are
79        // disjoint loops.
80        let old_boundary_idx = self
81            .find_boundary_containing(old_face_id, coedge1)
82            .with_context(&ctx)?;
83
84        // All coedges between coedge1 and coedge2 (inclusive) will end up on the new face, so validate their pcurves against the new surface.
85        let mut ring_members = vec![coedge2];
86        while *ring_members.last().unwrap() != coedge1 {
87            ring_members.push(self.get_coedge(*ring_members.last().unwrap())?.next);
88        }
89        for &member in &ring_members {
90            let c = self.get_coedge(member)?.clone();
91            let sp = self.coedge_start_vertex(member)?.point;
92            let ep = self.coedge_end_vertex(member)?.point;
93            validate_pcurve_start_and_end(&new_surface, &c.pcurve, &sp, &ep)
94                .with_context(with_context!("reassigning coedge {member} to new face"))
95                .with_context(&ctx)?;
96        }
97
98        // Create the new edge and coedges
99        let edge_id = self.insert_edge(Edge {
100            curve,
101            start_vertex: self.coedge_end_vertex_id(coedge1)?,
102            end_vertex: self.coedge_start_vertex_id(coedge2)?,
103        });
104        let next1 = ce1.next;
105        let prev2 = ce2.prev;
106
107        // coedge_forward: coedge1.end -> coedge2.start
108        let coedge_forward = self.insert_coedge(Coedge {
109            geometry: CoedgeGeometry::Edge(edge_id),
110            sense: Sense::Forward,
111            pcurve: pcurve,
112            next: coedge2,
113            prev: coedge1,
114            face: ce1.face, // fixed up below, once the new face exists
115        });
116
117        // coedge_backward: coedge2.start -> coedge1.end
118        let coedge_backward = self.insert_coedge(Coedge {
119            geometry: CoedgeGeometry::Edge(edge_id),
120            sense: Sense::Reversed,
121            pcurve: pcurve_reversed,
122            next: next1,
123            prev: prev2,
124            face: ce1.face,
125        });
126
127        self.coedges.get_mut(&coedge1).unwrap().next = coedge_forward;
128        self.coedges.get_mut(&coedge2).unwrap().prev = coedge_forward;
129        self.coedges.get_mut(&prev2).unwrap().next = coedge_backward;
130        self.coedges.get_mut(&next1).unwrap().prev = coedge_backward;
131
132        // Create the new face
133        let new_face_id = self.insert_face(Face {
134            surface: new_surface,
135            outer: BoundaryType::Loop(coedge2),
136            holes: Vec::new(),
137            shell: old_face.shell,
138        });
139        self.shells
140            .get_mut(&old_face.shell)
141            .unwrap()
142            .faces
143            .push(new_face_id);
144
145        // reassign the coedge faces
146        self.coedges.get_mut(&coedge_forward).unwrap().face = new_face_id;
147        for &member in &ring_members {
148            self.coedges.get_mut(&member).unwrap().face = new_face_id;
149        }
150
151        // The boundary we found above (by index, pre-mutation) is the one
152        // that just split into the new ring (now owned by `new_face_id`,
153        // already given its own boundary entry above) and this old ring
154        // (whose coedges' `.face` was never touched) — repoint it at the old
155        // ring via `next1`.
156        // Splitting a face's *outer* loop makes two faces, each bounded by
157        // one of the halves — so the new face's outer loop is the new ring
158        // and the old face keeps its own role. Splitting a *hole* instead
159        // carves a new face out of that hole's interior: the new face is
160        // bounded by the ring that came off, and the old face's hole
161        // continues to be a hole. Either way the boundary that split keeps
162        // its kind, which is what `set_boundary` expresses.
163        //
164        // Holes of the old face are left where they are; `mef` has no way to
165        // know which side of the new edge each falls on. Callers that split a
166        // face carrying holes must reclassify them afterwards (see
167        // `Model::reclassify_holes`).
168        self.faces
169            .get_mut(&old_face_id)
170            .unwrap()
171            .set_boundary(old_boundary_idx, BoundaryType::Loop(next1));
172
173        Ok((edge_id, new_face_id, coedge_forward, coedge_backward))
174    }
175}