Skip to main content

geop_core_topology/euler/
mer.rs

1use crate::{
2    Coedge, CoedgeGeometry, CoedgeId, Edge, EdgeId, 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::nurb_curve::{NurbCurve2D, NurbCurve3D};
9use geop_core_math::{
10    geop_error::{GeopError, GeopResult, WithContext},
11    scalars::Scalar,
12    with_context,
13};
14
15impl<S: Scalar> Model<S> {
16    // Like `mef`, but instead of creating a brand new face for the ring
17    // being split off, moves it onto `existing_face_id` — an already-existing
18    // face, as an additional boundary of its own — leaving the rest of the
19    // ring (the "old ring") right where it was, on whichever face
20    // coedge1/coedge2 currently belong to. Useful for e.g. growing a hole's
21    // boundary directly on a real, already-built face while its own mirror
22    // ring is grown as scratch topology on a placeholder, then moved onto
23    // that placeholder for further work (see `geop_ops_extrude_revolve::extrude`).
24    // Curve and pcurve (validated against `existing_face_id`'s current
25    // surface) must go from coedge1's end vertex to coedge2's start vertex.
26    // PCurve reversed (on the original face) must go from coedge2's start
27    // vertex to coedge1's end vertex.
28    // Coedge1 and coedge2 must belong to the same loop of the same face.
29    // returns (new edge, new coedge on the original face, new coedge on
30    // existing_face_id)
31    pub fn mer(
32        self: &mut Model<S>,
33        coedge1: CoedgeId,
34        coedge2: CoedgeId,
35        curve: NurbCurve3D<S>,
36        pcurve: NurbCurve2D<S>,
37        pcurve_reversed: NurbCurve2D<S>,
38        existing_face_id: FaceId,
39    ) -> GeopResult<(EdgeId, CoedgeId, CoedgeId)> {
40        let ctx = |e: GeopError| {
41            e.with_context(format!(
42                "Model::mer(
43                coedge1={coedge1}
44                coedge2={coedge2}
45                curve={curve}
46                pcurve={pcurve}
47                pcurve_reversed={pcurve_reversed}
48                existing_face_id={existing_face_id}"
49            ))
50        };
51
52        let ce1 = self.get_coedge(coedge1)?.clone();
53        let ce2 = self.get_coedge(coedge2)?.clone();
54        validate_same_loop(self, coedge1, coedge2)?;
55        let old_face_id = ce1.face;
56        let old_face = self.get_face(old_face_id)?.clone();
57        let existing_face = self.get_face(existing_face_id)?.clone();
58
59        let start = self
60            .get_vertex(self.coedge_end_vertex_id(coedge1)?)?
61            .clone()
62            .point;
63        let end = self
64            .get_vertex(self.coedge_start_vertex_id(coedge2)?)?
65            .clone()
66            .point;
67
68        validate_pcurve_start_and_end(&existing_face.surface, &pcurve, &start, &end)
69            .with_context("initial arg validation")
70            .with_context(&ctx)?;
71        validate_pcurve_start_and_end(&old_face.surface, &pcurve_reversed, &end, &start)
72            .with_context("initial arg validation")
73            .with_context(&ctx)?;
74        validate_curve_start_and_end(&curve, &start, &end)
75            .with_context("initial arg validation")
76            .with_context(&ctx)?;
77
78        // Find which of `old_face`'s boundaries is the one being split, by
79        // index, before any pointers are mutated — see `mef` for why this
80        // has to happen now rather than by walking post-mutation pointers.
81        let old_boundary_idx = self
82            .find_boundary_containing(old_face_id, coedge1)
83            .with_context(&ctx)?;
84
85        // All coedges between coedge1 and coedge2 (inclusive) will end up on
86        // existing_face_id, so validate their pcurves against its surface.
87        let mut ring_members = vec![coedge2];
88        while *ring_members.last().unwrap() != coedge1 {
89            ring_members.push(self.get_coedge(*ring_members.last().unwrap())?.next);
90        }
91        for &member in &ring_members {
92            let c = self.get_coedge(member)?.clone();
93            let sp = self.coedge_start_vertex(member)?.point;
94            let ep = self.coedge_end_vertex(member)?.point;
95            validate_pcurve_start_and_end(&existing_face.surface, &c.pcurve, &sp, &ep)
96                .with_context(with_context!(
97                    "reassigning coedge {member} to existing_face_id"
98                ))
99                .with_context(&ctx)?;
100        }
101
102        // Create the new edge and coedges
103        let edge_id = self.insert_edge(Edge {
104            curve,
105            start_vertex: self.coedge_end_vertex_id(coedge1)?,
106            end_vertex: self.coedge_start_vertex_id(coedge2)?,
107        });
108        let next1 = ce1.next;
109        let prev2 = ce2.prev;
110
111        // coedge_forward: coedge1.end -> coedge2.start
112        let coedge_forward = self.insert_coedge(Coedge {
113            geometry: CoedgeGeometry::Edge(edge_id),
114            sense: Sense::Forward,
115            pcurve: pcurve,
116            next: coedge2,
117            prev: coedge1,
118            face: ce1.face, // fixed up below, once moved onto existing_face_id
119        });
120
121        // coedge_backward: coedge2.start -> coedge1.end
122        let coedge_backward = self.insert_coedge(Coedge {
123            geometry: CoedgeGeometry::Edge(edge_id),
124            sense: Sense::Reversed,
125            pcurve: pcurve_reversed,
126            next: next1,
127            prev: prev2,
128            face: ce1.face,
129        });
130
131        self.coedges.get_mut(&coedge1).unwrap().next = coedge_forward;
132        self.coedges.get_mut(&coedge2).unwrap().prev = coedge_forward;
133        self.coedges.get_mut(&prev2).unwrap().next = coedge_backward;
134        self.coedges.get_mut(&next1).unwrap().prev = coedge_backward;
135
136        // Add the split-off ring as a new boundary of existing_face_id.
137        // Where the ring lands depends on what already bounds the face it
138        // moves to. A ring can only be a *hole* of a face that already has a
139        // loop around it; a face whose boundary is still a bare vertex has no
140        // such loop, so the arriving ring becomes the loop that bounds it —
141        // the same promotion `mve` performs when an edge first grows off a
142        // lone vertex. Splitting a ring off within a face that is already
143        // bounded leaves its outer extent unchanged, so there it is a hole.
144        let existing = self.faces.get_mut(&existing_face_id).unwrap();
145        match existing.outer {
146            BoundaryType::Vertex(_) => existing.outer = BoundaryType::Loop(coedge2),
147            BoundaryType::Loop(_) => existing.holes.push(BoundaryType::Loop(coedge2)),
148        }
149
150        // reassign the coedge faces
151        self.coedges.get_mut(&coedge_forward).unwrap().face = existing_face_id;
152        for &member in &ring_members {
153            self.coedges.get_mut(&member).unwrap().face = existing_face_id;
154        }
155
156        // The boundary we found above (by index, pre-mutation) is the one
157        // that just split into the ring now owned by `existing_face_id` and
158        // this old ring (whose coedges' `.face` was never touched) — repoint
159        // it at the old ring via `next1`.
160        self.faces
161            .get_mut(&old_face_id)
162            .unwrap()
163            .set_boundary(old_boundary_idx, BoundaryType::Loop(next1));
164
165        Ok((edge_id, coedge_backward, coedge_forward))
166    }
167}