Skip to main content

runmat_meshing_surface/validate/
types.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
4pub struct SurfaceValidationOptions {
5    pub max_projection_error_m: f64,
6    pub min_orientation_alignment: f64,
7    pub require_source_edge_conformity: bool,
8}
9
10impl Default for SurfaceValidationOptions {
11    fn default() -> Self {
12        Self {
13            max_projection_error_m: 1.0e-8,
14            min_orientation_alignment: 1.0 - 1.0e-8,
15            require_source_edge_conformity: true,
16        }
17    }
18}
19
20#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
21pub struct SurfaceValidationReport {
22    pub source_face_count: usize,
23    pub surface_element_count: usize,
24    pub source_edge_loop_count: usize,
25    pub closed_source_edge_loop_count: usize,
26    pub conforming_source_edge_count: usize,
27    pub missing_source_edge_count: usize,
28    pub max_projection_error_m: f64,
29    pub min_orientation_alignment: f64,
30    pub face_coverage_ratio: f64,
31}
32
33#[derive(Debug, Clone, PartialEq)]
34pub enum SurfaceValidationError {
35    InvalidOptions,
36    EmptySurface,
37    MissingSurfaceNode {
38        element_id: u32,
39        node_id: u32,
40    },
41    MissingSourceFace {
42        source_face_id: u32,
43    },
44    MissingSourceEdge {
45        source_edge_id: u32,
46    },
47    MissingCadFaceId {
48        element_id: u32,
49    },
50    MissingCadFace {
51        cad_face_id: String,
52    },
53    InvalidCadLoopEdgeId {
54        cad_face_id: String,
55        loop_edge_id: String,
56    },
57    SourceEdgeOutsideCadFace {
58        cad_face_id: String,
59        source_edge_id: u32,
60    },
61    EdgeConformityFailed {
62        source_edge_id: u32,
63        source_edge_node_ids: [u32; 2],
64        recovered_segment_count: usize,
65    },
66    OpenSourceLoop {
67        source_edge_id: u32,
68        endpoint_id: u32,
69        incidence_count: usize,
70    },
71    DegenerateElement {
72        element_id: u32,
73    },
74    InvalidElementNormal {
75        element_id: u32,
76    },
77    InvalidElementArea {
78        element_id: u32,
79    },
80    InvalidParametricEvidence {
81        element_id: u32,
82    },
83    InvalidProjectionEvidence {
84        element_id: u32,
85    },
86    ProjectionError {
87        element_id: u32,
88        error_m: f64,
89        max_error_m: f64,
90    },
91    OrientationMismatch {
92        element_id: u32,
93        source_face_id: u32,
94        alignment: f64,
95        min_alignment: f64,
96    },
97    UncoveredSourceFace {
98        source_face_id: u32,
99    },
100    UncoveredCadFace {
101        cad_face_id: String,
102    },
103}
104
105impl std::fmt::Display for SurfaceValidationError {
106    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        match self {
108            Self::InvalidOptions => write!(
109                formatter,
110                "surface validation options must use finite projection and orientation thresholds"
111            ),
112            Self::EmptySurface => write!(formatter, "surface validation input has no elements"),
113            Self::MissingSurfaceNode {
114                element_id,
115                node_id,
116            } => write!(
117                formatter,
118                "surface element {element_id} references missing node {node_id}"
119            ),
120            Self::MissingSourceFace { source_face_id } => {
121                write!(formatter, "source face {source_face_id} is missing")
122            }
123            Self::MissingSourceEdge { source_edge_id } => {
124                write!(formatter, "source edge {source_edge_id} is missing")
125            }
126            Self::MissingCadFaceId { element_id } => write!(
127                formatter,
128                "surface element {element_id} does not carry CAD face provenance"
129            ),
130            Self::MissingCadFace { cad_face_id } => {
131                write!(formatter, "CAD face {cad_face_id} is missing")
132            }
133            Self::InvalidCadLoopEdgeId {
134                cad_face_id,
135                loop_edge_id,
136            } => write!(
137                formatter,
138                "CAD face {cad_face_id} has invalid loop edge id {loop_edge_id}"
139            ),
140            Self::SourceEdgeOutsideCadFace {
141                cad_face_id,
142                source_edge_id,
143            } => write!(
144                formatter,
145                "source edge {source_edge_id} is not a loop edge for CAD face {cad_face_id}"
146            ),
147            Self::EdgeConformityFailed {
148                source_edge_id,
149                source_edge_node_ids,
150                recovered_segment_count,
151            } => write!(
152                formatter,
153                "source edge {source_edge_id} with endpoints {:?} is not represented by a matching surface element edge; recovered surface segment count is {recovered_segment_count}",
154                source_edge_node_ids
155            ),
156            Self::OpenSourceLoop {
157                source_edge_id,
158                endpoint_id,
159                incidence_count,
160            } => write!(
161                formatter,
162                "source edge {source_edge_id} endpoint {endpoint_id} has loop incidence {incidence_count}, expected 2"
163            ),
164            Self::DegenerateElement { element_id } => {
165                write!(formatter, "surface element {element_id} is degenerate")
166            }
167            Self::InvalidElementNormal { element_id } => write!(
168                formatter,
169                "surface element {element_id} has invalid unit-normal evidence"
170            ),
171            Self::InvalidElementArea { element_id } => write!(
172                formatter,
173                "surface element {element_id} has invalid area evidence"
174            ),
175            Self::InvalidParametricEvidence { element_id } => write!(
176                formatter,
177                "surface element {element_id} has invalid parametric coordinate evidence"
178            ),
179            Self::InvalidProjectionEvidence { element_id } => write!(
180                formatter,
181                "surface element {element_id} has invalid projection evidence"
182            ),
183            Self::ProjectionError {
184                element_id,
185                error_m,
186                max_error_m,
187            } => write!(
188                formatter,
189                "surface element {element_id} projection error {error_m:.6e} m exceeds {max_error_m:.6e} m"
190            ),
191            Self::OrientationMismatch {
192                element_id,
193                source_face_id,
194                alignment,
195                min_alignment,
196            } => write!(
197                formatter,
198                "surface element {element_id} on source face {source_face_id} orientation alignment {alignment:.6e} is below {min_alignment:.6e}"
199            ),
200            Self::UncoveredSourceFace { source_face_id } => {
201                write!(formatter, "source face {source_face_id} is not covered")
202            }
203            Self::UncoveredCadFace { cad_face_id } => {
204                write!(formatter, "CAD face {cad_face_id} is not covered")
205            }
206        }
207    }
208}
209
210impl std::error::Error for SurfaceValidationError {}