axiolid_construct/
trimmed_intersection.rs1use axiolid_contracts::GeomResult;
9use axiolid_nurbs::{intersect_surface_surface_certified, CertifiedSurfaceSurfaceIntersection3};
10use axiolid_surface::BSplineSurface;
11
12pub use crate::trimmed_intersection_types::{
13 CertifiedDualTrimmedSurfacePair3, CertifiedSurfacePairSplit3, CertifiedSurfacePairSplitOptions,
14 CertifiedTrimmedSurfacePair3, EmbeddedFaceCurve, SurfacePairMember,
15 SurfacePairSplitUnresolvedReason,
16};
17
18use crate::trimmed_intersection_assembly::{assemble, assemble_dual};
19use crate::trimmed_intersection_classify::{classify, Classification};
20
21pub fn split_surface_pair_certified(
34 first: &BSplineSurface,
35 second: &BSplineSurface,
36 options: CertifiedSurfacePairSplitOptions,
37) -> GeomResult<CertifiedSurfacePairSplit3> {
38 let intersection =
39 intersect_surface_surface_certified(first, second, options.intersection_options())?;
40 let (mut traces, visited_patch_pairs, boundary_queries) = match intersection {
41 CertifiedSurfaceSurfaceIntersection3::Unresolved { .. } => {
42 return Ok(CertifiedSurfacePairSplit3::Unresolved {
43 intersection,
44 reason: SurfacePairSplitUnresolvedReason::IntersectionUnresolved,
45 });
46 }
47 CertifiedSurfaceSurfaceIntersection3::Complete {
48 traces,
49 visited_patch_pairs,
50 boundary_queries,
51 } => (traces, visited_patch_pairs, boundary_queries),
52 other => {
53 return Ok(CertifiedSurfacePairSplit3::Unresolved {
54 intersection: other,
55 reason: SurfacePairSplitUnresolvedReason::IntersectionUnresolved,
56 });
57 }
58 };
59 if traces.is_empty() {
60 return Ok(CertifiedSurfacePairSplit3::Empty {
61 visited_patch_pairs,
62 boundary_queries: u32::from(boundary_queries),
63 });
64 }
65 if traces.len() != 1 {
66 return Ok(unresolved_complete(
67 traces,
68 visited_patch_pairs,
69 boundary_queries,
70 SurfacePairSplitUnresolvedReason::UnsupportedTraceCount,
71 ));
72 }
73 let Some(trace_ref) = traces.first() else {
74 return Ok(CertifiedSurfacePairSplit3::Empty {
75 visited_patch_pairs,
76 boundary_queries: u32::from(boundary_queries),
77 });
78 };
79 let residual_upper_bound = trace_ref
80 .start
81 .residual_upper_bound
82 .max(trace_ref.end.residual_upper_bound);
83 if !residual_upper_bound.is_finite() || residual_upper_bound > options.max_surface_residual() {
84 return Ok(unresolved_complete(
85 traces,
86 visited_patch_pairs,
87 boundary_queries,
88 SurfacePairSplitUnresolvedReason::ResidualExceedsPolicy,
89 ));
90 }
91 let classification = match classify(first, second, trace_ref) {
95 Ok(classification) => classification,
96 Err(reason) => {
97 return Ok(unresolved_complete(
98 traces,
99 visited_patch_pairs,
100 boundary_queries,
101 reason,
102 ));
103 }
104 };
105 let Some(trace) = traces.pop() else {
106 return Ok(CertifiedSurfacePairSplit3::Empty {
107 visited_patch_pairs,
108 boundary_queries: u32::from(boundary_queries),
109 });
110 };
111 match classification {
114 Classification::Single(single) => {
115 let split = assemble(
116 first,
117 second,
118 trace,
119 single,
120 residual_upper_bound,
121 visited_patch_pairs,
122 boundary_queries,
123 )?;
124 Ok(CertifiedSurfacePairSplit3::Split(split))
125 }
126 Classification::Dual(dual) => {
127 let split = assemble_dual(
128 first,
129 second,
130 trace,
131 dual,
132 residual_upper_bound,
133 visited_patch_pairs,
134 boundary_queries,
135 )?;
136 Ok(CertifiedSurfacePairSplit3::DualSplit(split))
137 }
138 }
139}
140
141fn unresolved_complete(
142 traces: Vec<axiolid_nurbs::TransverseSurfaceSurfaceTrace3>,
143 visited_patch_pairs: u32,
144 boundary_queries: u8,
145 reason: SurfacePairSplitUnresolvedReason,
146) -> CertifiedSurfacePairSplit3 {
147 CertifiedSurfacePairSplit3::Unresolved {
148 intersection: CertifiedSurfaceSurfaceIntersection3::Complete {
149 traces,
150 visited_patch_pairs,
151 boundary_queries,
152 },
153 reason,
154 }
155}