Skip to main content

geop_core_topology/model/
mod.rs

1use std::collections::HashMap;
2
3use geop_core_geometry::nurb_curve::NurbCurve;
4use geop_core_math::scalars::Scalar;
5
6use super::{
7    Coedge, CoedgeId, Edge, EdgeId, Face, FaceId, Shell, ShellId, Solid, SolidId, Vertex, VertexId,
8};
9
10mod create;
11mod display;
12mod get;
13mod iterate;
14
15/// A 2-D parameter-space NURBS curve (pcurve).
16///
17/// Control points are in homogeneous parameter space: `(wu, wv, w)`.
18pub type Curve2<S> = NurbCurve<S, 3>;
19
20/// A 3-D NURBS curve used as edge geometry.
21///
22/// Control points are in homogeneous 3-D space: `(wx, wy, wz, w)`.
23pub type Curve3<S> = NurbCurve<S, 4>;
24
25/// The top-level boundary-representation model.
26///
27/// `Vertex`, `Edge`, `Coedge`, `Face`, `Shell` and `Solid` are the entities
28/// genuinely shared by reference (an edge by its two coedges via `opposite`,
29/// a vertex by every incident edge, and so on) — these live here in arenas
30/// and are referenced by stable typed IDs.  Their geometry (a `Curve3` per
31/// `Edge`, a `Curve2` pcurve per `Coedge`, a `NurbSurface` per `Face`) is
32/// owned 1:1 by that entity directly rather than through another arena, since
33/// nothing ever needs to reference it independently.  A loop has no entity of
34/// its own: each entry of `Face::boundaries` just anchors a `CoedgeId` whose
35/// `next`/`prev` cycle traces the whole loop.
36///
37/// Hierarchy (each arrow means "references one or more"):
38/// ```text
39/// Solid ──▶ Shell(s) ──▶ Face(s) [+Surface] ──▶ Coedge(s) [+Curve2] ──▶ Edge [+Curve3] ──▶ Vertex
40/// ```
41/// V - E + F - L = 2 * (S - G), E = 2C for a single-shell solid with genus G,
42/// where V = #vertices, E = #edges, F = #faces, L = #hole loops (i.e.
43/// `Σ (face.boundaries.len() - 1)`, not counting each face's mandatory outer loop),
44/// S = #shells, and G = genus.  Euler's formula generalizes to multiple
45/// shells and/or genus > 0.
46///
47/// Creation (`insert_*`), lookup (`get_*`), and iteration (`iterate_*`)
48/// methods each live in their own private submodule (`model::create`,
49/// `model::get`, `model::iterate`) — all still just plain inherent `Model`
50/// methods from the outside.
51#[derive(Clone)]
52pub struct Model<S: Scalar> {
53    pub vertices: HashMap<VertexId, Vertex<S>>, // V
54    pub edges: HashMap<EdgeId, Edge<S>>,        // E
55    pub coedges: HashMap<CoedgeId, Coedge<S>>,  // C
56    pub faces: HashMap<FaceId, Face<S>>,        // F
57    pub shells: HashMap<ShellId, Shell>,        // S
58    pub solids: HashMap<SolidId, Solid>,
59
60    next_id: u64,
61}
62
63impl<S: Scalar> Model<S> {
64    pub fn new() -> Self {
65        Self {
66            vertices: HashMap::new(),
67            edges: HashMap::new(),
68            coedges: HashMap::new(),
69            faces: HashMap::new(),
70            shells: HashMap::new(),
71            solids: HashMap::new(),
72            next_id: 1,
73        }
74    }
75
76    fn fresh_id(&mut self) -> u64 {
77        let id = self.next_id;
78        self.next_id += 1;
79        id
80    }
81}