1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
use axiolid_brep::{Curve2Id, ExactBRep};
use axiolid_core::{Interval, Scalar};
use axiolid_nurbs::{
CertifiedSurfaceSurfaceIntersection3, CertifiedSurfaceSurfaceIntersectionOptions,
TransverseSurfaceSurfaceTrace3,
};
use axiolid_topology::{EdgeId, FaceId};
/// Explicit resource and residual policy for certified topology integration.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CertifiedSurfacePairSplitOptions {
/// Policy for the underlying certified surface/surface query.
intersection: CertifiedSurfaceSurfaceIntersectionOptions,
/// Largest certified surface/carrier residual accepted by the B-rep handoff.
max_surface_residual: Scalar,
}
impl CertifiedSurfacePairSplitOptions {
/// Validate and construct a split policy.
pub fn new(
intersection: CertifiedSurfaceSurfaceIntersectionOptions,
max_surface_residual: Scalar,
) -> Result<Self, axiolid_contracts::GeomError> {
if !max_surface_residual.is_finite() || max_surface_residual <= 0.0 {
return Err(axiolid_contracts::GeomError::InvalidInput(
"surface-pair split residual must be finite and positive".into(),
));
}
Ok(Self {
intersection,
max_surface_residual,
})
}
pub(super) fn intersection_options(self) -> CertifiedSurfaceSurfaceIntersectionOptions {
self.intersection
}
pub(super) fn max_surface_residual(self) -> Scalar {
self.max_surface_residual
}
}
impl Default for CertifiedSurfacePairSplitOptions {
fn default() -> Self {
Self {
intersection: CertifiedSurfaceSurfaceIntersectionOptions::default(),
max_surface_residual: 1.0e-7,
}
}
}
/// Which input surface is partitioned by the certified chord.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SurfacePairMember {
/// The first function argument.
First,
/// The second function argument.
Second,
}
/// A certified edge embedded in a face without pretending it is a closed trim.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct EmbeddedFaceCurve {
/// Unsplit face that contains the intersection chord.
pub face: FaceId,
/// Shared model-space intersection edge.
pub edge: EdgeId,
/// Surface-parameter image from edge start to edge end.
pub pcurve: Curve2Id,
/// Native pcurve interval, oriented from edge start to edge end.
pub interval: Interval,
}
/// Validated analytic B-rep arrangement for one certified finite trace.
#[derive(Debug, Clone, PartialEq)]
pub struct CertifiedTrimmedSurfacePair3 {
/// Strict analytic B-rep containing three closed faces.
pub brep: ExactBRep,
/// The same edge used by the two split-face loops and embedded in the other face.
pub intersection_edge: EdgeId,
/// Input member whose rectangular face was split.
pub split_surface: SurfacePairMember,
/// Two deterministic trimmed faces on `split_surface`.
pub split_faces: [FaceId; 2],
/// Rectangular face that the finite chord does not partition.
pub unsplit_face: FaceId,
/// Explicit interior attachment on `unsplit_face`.
pub embedded_curve: EmbeddedFaceCurve,
/// Original certified bounded trace; no endpoint is widened by construction.
pub trace: TransverseSurfaceSurfaceTrace3,
/// Conservative global carrier-to-surface residual bound.
pub max_surface_residual_upper_bound: Scalar,
/// Certified patch pairs processed by the intersection query.
pub visited_patch_pairs: u32,
/// Bounded boundary queries used by the intersection query.
pub boundary_queries: u32,
}
/// Validated arrangement for a chord that partitions BOTH patches.
///
/// Four closed trimmed faces share one intersection edge: two on each
/// input surface. Unlike [`CertifiedTrimmedSurfacePair3`] there is no
/// asymmetry to record -- neither patch merely contains the chord.
#[derive(Debug, Clone, PartialEq)]
pub struct CertifiedDualTrimmedSurfacePair3 {
/// Strict analytic B-rep containing four closed faces.
pub brep: ExactBRep,
/// The edge shared by all four trimmed loops.
pub intersection_edge: EdgeId,
/// Two deterministic trimmed faces on the first input surface.
pub first_faces: [FaceId; 2],
/// Two deterministic trimmed faces on the second input surface.
pub second_faces: [FaceId; 2],
/// Original certified bounded trace; no endpoint is widened.
pub trace: TransverseSurfaceSurfaceTrace3,
/// Conservative global carrier-to-surface residual bound.
pub max_surface_residual_upper_bound: Scalar,
/// Certified patch pairs processed by the intersection query.
pub visited_patch_pairs: u32,
/// Bounded boundary queries used by the intersection query.
pub boundary_queries: u32,
}
/// Why valid geometry could not be promoted to a closed trimmed arrangement.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SurfacePairSplitUnresolvedReason {
/// The underlying certified query retained uncertainty.
IntersectionUnresolved,
/// The query did not produce exactly one finite trace.
UnsupportedTraceCount,
/// Endpoint ownership did not identify exactly one partitioned rectangle.
///
/// The arrangement may still be constructible; this crate has not
/// implemented it. Distinct from [`Self::NoPartitionExists`], which is a
/// proof that no arrangement exists at all.
UnsupportedEndpointOwnership,
/// Proven: this trace cannot partition either patch, so no split exists.
///
/// A trace that stops strictly inside a patch is a slit, not a partition:
/// the face stays simply connected, exactly as a slot cut halfway into a
/// sheet leaves one piece. When that holds on *both* patches, neither can
/// become two closed trimmed faces and retrying with different options or
/// tighter tolerances cannot change the answer.
///
/// This is terminal. Callers should stop rather than escalate.
NoPartitionExists,
/// The certified carrier residual exceeded explicit policy.
ResidualExceedsPolicy,
/// Representative parameters or carrier were finite but degenerate for topology.
DegenerateRepresentative,
}
/// Certified topology-integration outcome.
///
/// The success payload stays inline deliberately: boxing it would add an
/// infallible allocation after all certified construction allocations have
/// already been made fallible and bounded.
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum CertifiedSurfacePairSplit3 {
/// Conservative proof that the bounded patches do not intersect.
Empty {
/// Certified patch pairs processed.
visited_patch_pairs: u32,
/// Bounded boundary queries used.
boundary_queries: u32,
},
/// One finite trace integrated into a strict trimmed arrangement.
Split(CertifiedTrimmedSurfacePair3),
/// A chord that partitions BOTH patches, so each becomes two faces.
///
/// Distinct from [`Self::Split`] because there is no unsplit face and
/// no embedded curve: the chord is a real trim boundary on both sides,
/// not an interior annotation on one. Representing it as `Split` would
/// force naming one patch the 'owner' and lying about the other.
DualSplit(CertifiedDualTrimmedSurfacePair3),
/// Geometry remains usable, but no B-rep split was invented.
Unresolved {
/// Original intersection evidence retained without widening.
intersection: CertifiedSurfaceSurfaceIntersection3,
/// Topology-specific refusal reason.
reason: SurfacePairSplitUnresolvedReason,
},
}