geop_core_topology/euler/add_vertex_coedge.rs
1use crate::{
2 Coedge, CoedgeGeometry, CoedgeId, Model, Sense, VertexId,
3 argument_validation::validate_pcurve_start_and_end,
4};
5use geop_core_geometry::nurb_curve::NurbCurve2D;
6use geop_core_math::{
7 geop_error::{GeopError, GeopResult, WithContext},
8 scalars::Scalar,
9};
10
11impl<S: Scalar> Model<S> {
12 /// Splice a single degenerate coedge — backed directly by `vertex` (see
13 /// [`CoedgeGeometry::Vertex`]), not a real edge — into `after`'s own
14 /// loop, right after it. `pcurve` must both start and end at `vertex`'s
15 /// own position, as mapped through `after`'s face's surface (it's
16 /// meant to sweep some parameter-space range while sitting at that one
17 /// 3-D point the whole way, e.g. tracing a pole row's full angular
18 /// span).
19 ///
20 /// Unlike every other euler operator here, this isn't one: it doesn't
21 /// touch `V`, `E`, or `F` (`vertex` already exists, and no edge is
22 /// created), so there's no Euler–Poincaré invariant for it to need to
23 /// preserve — it's pure loop bookkeeping, letting a face's own
24 /// boundary legitimately pass through an already-shared vertex without
25 /// requiring a real (and, for a single-face-only detour, otherwise
26 /// unpaired) edge to carry a pcurve.
27 pub fn add_vertex_coedge(
28 self: &mut Model<S>,
29 after: CoedgeId,
30 vertex: VertexId,
31 pcurve: NurbCurve2D<S>,
32 ) -> GeopResult<CoedgeId> {
33 let ctx = |e: GeopError| {
34 e.with_context(format!(
35 "Model::add_vertex_coedge(after={after}, vertex={vertex}, pcurve={pcurve})"
36 ))
37 };
38
39 let ce = self.get_coedge(after)?.clone();
40 let face = self.get_face(ce.face)?.clone();
41 let p = self.get_vertex(vertex)?.point;
42
43 validate_pcurve_start_and_end(&face.surface, &pcurve, &p, &p).with_context(&ctx)?;
44
45 let new_coedge = self.insert_coedge(Coedge {
46 geometry: CoedgeGeometry::Vertex(vertex),
47 sense: Sense::Forward,
48 pcurve,
49 next: ce.next,
50 prev: after,
51 face: ce.face,
52 });
53
54 self.coedges.get_mut(&after).unwrap().next = new_coedge;
55 self.coedges.get_mut(&ce.next).unwrap().prev = new_coedge;
56
57 Ok(new_coedge)
58 }
59}