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}