Skip to main content

geop_core_topology/model/
mod.rs

1use imbl::OrdMap;
2
3use geop_core_geometry::nurb_curve::NurbCurve;
4
5use super::{
6    Coedge, CoedgeId, Edge, EdgeId, Face, FaceId, Shell, ShellId, Solid, SolidId, Vertex, VertexId,
7    Wire, WireId,
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 = NurbCurve<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 = NurbCurve<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/// Wire ──▶ Edge(s) [+Curve3], Vertex(es)
41/// ```
42/// A [`Wire`] holds edges and vertices that bound no face: curves in space.
43/// V - E + F - L = 2 * (Scalar - G), E = 2C for a single-shell solid with genus G,
44/// where V = #vertices, E = #edges, F = #faces, L = #hole loops (i.e.
45/// `Σ (face.boundaries.len() - 1)`, not counting each face's mandatory outer loop),
46/// Scalar = #shells, and G = genus.  Euler's formula generalizes to multiple
47/// shells and/or genus > 0.
48///
49/// Creation (`insert_*`), lookup (`get_*`), and iteration (`iterate_*`)
50/// methods each live in their own private submodule (`model::create`,
51/// `model::get`, `model::iterate`) — all still just plain inherent `Model`
52/// methods from the outside.
53///
54/// The arenas are ordered by id, which is the order of creation: every walk
55/// over a model's entities — validation, a boolean's classification, the
56/// remesh's pairing of faces — sees them in the same order on every run.
57/// A `HashMap` iterates in an order seeded afresh in each process, and a
58/// validation failure that depended on which face pair was checked first
59/// appeared and disappeared between runs.
60#[derive(Clone, Debug, PartialEq)]
61pub struct Model {
62    pub vertices: OrdMap<VertexId, Vertex>, // V
63    pub edges: OrdMap<EdgeId, Edge>,        // E
64    pub coedges: OrdMap<CoedgeId, Coedge>,  // C
65    pub faces: OrdMap<FaceId, Face>,        // F
66    pub shells: OrdMap<ShellId, Shell>,     // Scalar
67    pub solids: OrdMap<SolidId, Solid>,
68    /// Edges and vertices bounding no face (see [`Wire`]).
69    pub wires: OrdMap<WireId, Wire>,
70
71    next_id: u64,
72}
73
74impl Default for Model {
75    fn default() -> Self {
76        Self::new()
77    }
78}
79
80impl Model {
81    pub fn new() -> Self {
82        Self {
83            vertices: OrdMap::new(),
84            edges: OrdMap::new(),
85            coedges: OrdMap::new(),
86            faces: OrdMap::new(),
87            shells: OrdMap::new(),
88            solids: OrdMap::new(),
89            wires: OrdMap::new(),
90            next_id: 1,
91        }
92    }
93
94    /// The id the next entity it creates gets: what a model written to be
95    /// read back as it was keeps with its entities ([`Model::with_next_id`]).
96    pub fn next_id(&self) -> u64 {
97        self.next_id
98    }
99
100    /// It, creating entities from the id `next_id` on: a model read back as
101    /// it was written, its entities set as they were. `next_id` is past
102    /// every id it holds, as [`Model::next_id`] gave it.
103    pub fn with_next_id(mut self, next_id: u64) -> Self {
104        self.next_id = next_id;
105        self
106    }
107
108    fn fresh_id(&mut self) -> u64 {
109        let id = self.next_id;
110        self.next_id += 1;
111        id
112    }
113}