Skip to main content

brep_kernel/brep/
topology.rs

1use crate::{
2    make_cylinder_surface, make_line, make_plane, KernelTolerances, NurbsCurve, NurbsSurface, Vec2,
3    Vec3, Vec4,
4};
5use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
6use serde::{Deserialize, Serialize};
7
8#[derive(Clone, Debug, Deserialize, Serialize)]
9pub struct VertexRecord {
10    pub id: u64,
11    pub point: Vec3,
12}
13
14#[derive(Clone, Debug, Deserialize, Serialize)]
15pub struct EdgeRecord {
16    pub id: u64,
17    pub curve: NurbsCurve,
18    pub t0: f64,
19    pub t1: f64,
20    pub start_vertex_id: u64,
21    pub end_vertex_id: u64,
22    #[serde(default)]
23    pub degenerate: bool,
24    /// Persistent name carried through splits, welds, and booleans so the
25    /// application never has to re-derive edge identity geometrically.
26    #[serde(default, skip_serializing_if = "Option::is_none")]
27    pub name: Option<String>,
28}
29
30#[derive(Clone, Debug, Deserialize, Serialize)]
31pub struct CoedgeRecord {
32    pub id: u64,
33    pub edge_id: u64,
34    pub forward: bool,
35    pub pcurve: NurbsCurve,
36}
37
38#[derive(Clone, Debug, Deserialize, Serialize)]
39pub struct LoopRecord {
40    pub id: u64,
41    pub coedges: Vec<CoedgeRecord>,
42}
43
44#[derive(Clone, Debug, Deserialize, Serialize)]
45pub struct FaceRecord {
46    pub id: u64,
47    pub surface: NurbsSurface,
48    pub same_sense: bool,
49    pub loops: Vec<LoopRecord>,
50    /// Persistent name propagated through booleans (split fragments get
51    /// deterministic `_1`, `_2` suffixes; merges keep the first name).
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    pub name: Option<String>,
54}
55
56#[derive(Clone, Debug, Deserialize, Serialize)]
57pub struct ShellRecord {
58    pub id: u64,
59    pub faces: Vec<FaceRecord>,
60}
61
62#[derive(Clone, Debug, Deserialize, Serialize)]
63pub struct BrepSolid {
64    pub id: u64,
65    pub vertices: Vec<VertexRecord>,
66    pub edges: Vec<EdgeRecord>,
67    pub shells: Vec<ShellRecord>,
68    #[serde(default)]
69    pub genus: i64,
70}
71
72/// The closed kind of a [`ValidationIssue`]. Only the kinds a dispatcher
73/// needs are minted (the boolean's endpoint-gap repair lane keys on
74/// `CurveVertexGap`); everything else is `Other` and carries its message as
75/// the payload. A dispatcher matches the kind, never the message text.
76///
77/// On the wire it is a nested object, `"kind":{"name":"curve_vertex_gap",
78/// "at_start":true}`, NOT flattened into the issue: serde's `default` does not
79/// apply to a flattened internally-tagged enum, and a report written before
80/// kinds existed must still read back (as `Other`).
81#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
82#[serde(tag = "name", rename_all = "snake_case")]
83pub enum IssueKind {
84    /// An edge curve's start (`at_start`) or end does not land on its vertex.
85    CurveVertexGap { at_start: bool },
86    /// An edge used fewer than twice (a boundary the shell leaves open).
87    OpenEdge,
88    /// An edge used more than twice.
89    OverUsedEdge,
90    /// The Euler formula does not close.
91    GenusMismatch,
92    #[default]
93    Other,
94}
95
96#[derive(Clone, Debug, Deserialize, Serialize)]
97pub struct ValidationIssue {
98    pub severity: &'static str,
99    #[serde(default)]
100    pub kind: IssueKind,
101    pub message: String,
102}
103
104impl ValidationIssue {
105    fn error(message: impl Into<String>) -> Self {
106        Self::error_kind(IssueKind::Other, message)
107    }
108
109    fn error_kind(kind: IssueKind, message: impl Into<String>) -> Self {
110        Self {
111            severity: "error",
112            kind,
113            message: message.into(),
114        }
115    }
116
117    fn warning(message: impl Into<String>) -> Self {
118        Self {
119            severity: "warning",
120            kind: IssueKind::Other,
121            message: message.into(),
122        }
123    }
124}
125
126#[derive(Clone, Debug, Serialize)]
127pub struct ValidationReport {
128    pub issues: Vec<ValidationIssue>,
129    pub wire_warnings: Vec<ValidationIssue>,
130    pub max_pcurve_error: f64,
131}
132
133#[path = "topology/queries.rs"]
134mod queries;
135pub(crate) use queries::{nearest_edge, edge_endpoint_alignment, coedge_neighbor_endpoints, edge_use_counts};
136
137#[path = "topology/seam.rs"]
138mod seam;
139#[path = "topology/validate.rs"]
140mod validate;
141#[path = "topology/primitives.rs"]
142mod primitives;
143// BREP private tests: 97ac8ba87759ec8b
144
145pub use seam::{
146    analyze_doubly_periodic_seam_band, loop_seam_offsets, seam_band_uv_polygon, seam_band_uv_polygon_with,
147};
148pub(crate) use seam::doubly_periodic_has_only_collapsed_loops;
149pub(crate) use validate::adaptive_coedge_error;
150pub use primitives::{make_box_brep, make_cylinder_brep, make_pyramid_brep};
151
152// BREP private tests: 89aecfd8c93b10e3