geop_core_topology/coedge.rs
1use crate::{CoedgeId, VertexId};
2use geop_core_math::geop_error::{GeopError, GeopResult};
3
4use super::ids::{EdgeId, FaceId};
5use super::model::{Curve2, Curve3};
6
7/// Whether a coedge traverses its underlying edge in the same direction
8/// (forward: start→end) or the opposite direction (reversed: end→start).
9#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
10pub enum Sense {
11 Forward,
12 Reversed,
13}
14
15impl Sense {
16 /// The sense of a coedge running its edge forward if `forward`.
17 pub fn from_forward(forward: bool) -> Sense {
18 if forward {
19 Sense::Forward
20 } else {
21 Sense::Reversed
22 }
23 }
24
25 pub fn opposite(self) -> Sense {
26 match self {
27 Sense::Forward => Sense::Reversed,
28 Sense::Reversed => Sense::Forward,
29 }
30 }
31
32 /// `curve`, run the way a coedge of this sense runs it.
33 pub fn orient(self, curve: &Curve3) -> Curve3 {
34 match self {
35 Sense::Forward => curve.clone(),
36 Sense::Reversed => curve.reverse(),
37 }
38 }
39}
40
41/// What a coedge's own `(u, v)` boundary segment is actually backed by.
42#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
43pub enum CoedgeGeometry {
44 /// A real, shared edge — this coedge is one of exactly the two
45 /// (opposite `Sense`) that trace it, one per adjoining face. The usual
46 /// case.
47 Edge(EdgeId),
48 /// A degenerate loop segment that sits at a single, already-existing
49 /// vertex the whole way — no edge, and (unlike `Edge`) no second
50 /// coedge sharing it, since there's nothing to share between two
51 /// faces: it belongs to exactly one face's own loop. This is how a
52 /// loop closes over a surface row that's collapsed to a single point
53 /// (e.g. a pole) — the loop still needs *some* pcurve tracing that
54 /// row's full parameter range even though there's no 3-D geometry
55 /// there to back a real edge. Adds neither a new vertex nor a new
56 /// edge, so it leaves the Euler–Poincaré count as it is.
57 Vertex(VertexId),
58}
59
60#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
61pub struct Coedge {
62 pub geometry: CoedgeGeometry,
63 pub sense: Sense,
64 #[serde(with = "geop_core_math::exact")]
65 pub pcurve: Curve2,
66 pub next: CoedgeId,
67 pub prev: CoedgeId,
68 // face
69 pub face: FaceId,
70}
71
72impl Coedge {
73 /// This coedge's own edge — an error for a `Vertex`-backed coedge
74 /// (there's no edge to compare or collect there). Convenience for
75 /// code that only ever applies to edge-backed coedges.
76 pub fn edge(&self) -> GeopResult<EdgeId> {
77 match self.geometry {
78 CoedgeGeometry::Edge(id) => Ok(id),
79 CoedgeGeometry::Vertex(v) => Err(GeopError::new(format!(
80 "coedge is vertex-backed (vertex {}), not edge-backed",
81 v.0
82 ))),
83 }
84 }
85}