1use crate::classification::{parameter_point_in_face, PolygonClass};
2use crate::curve::KNOT_IDENTITY_TOL;
3use crate::spatial::{Aabb, Bvh};
4use crate::tolerance::{
5 assembler_weld, merge_scale, solid_scale, COINCIDENCE_DISTANCE_FLOOR, WELD_FLOOR,
6};
7use crate::topology::{BrepSolid, EdgeRecord, FaceRecord};
8use crate::{
9 build_pcurve_on_surface, build_pcurve_on_surface_marched, classify_surface_pair_cached,
10 fit_polyline, fit_polyline_of_degree, intersect_curve_surface, PolylineFit,
11 intersect_curves, intersect_surfaces, intersect_surfaces_supplemental, project_point_to_curve,
12 project_point_to_surface, project_point_to_surface_seeded, KnotVector, NurbsCurve, NurbsSurface,
13 SurfaceClassifyData,
14 SurfaceIntersectionOptions, SurfacePairClassification, SurfacePairRelation, Vec2, Vec3, Vec4,
15};
16use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
17use serde::{Deserialize, Serialize};
18use std::time::Duration;
19use web_time::Instant;
20
21#[derive(Default)]
24struct ImprintProfile {
25 enabled: bool,
26 pairs: u64,
27 marched_pairs: u64,
28 classify: Duration,
29 cosurface: Duration,
30 lies_on: Duration,
31 planar_iso: Duration,
32 analytic: Duration,
33 seeds: Duration,
34 march: Duration,
35 clip_and_fit: Duration,
36 process_curve: Duration,
37 rescue_march: Duration,
40 clip: Duration,
41 shared_boundary: Duration,
42 fit: Duration,
43 process: Duration,
44}
45
46impl ImprintProfile {
47 fn new() -> Self {
48 Self {
49 enabled: std::env::var("BREP_PROFILE").is_ok(),
50 ..Self::default()
51 }
52 }
53
54 fn lap(&self, started: &mut Option<Instant>) -> Duration {
55 if !self.enabled {
56 return Duration::ZERO;
57 }
58 let now = Instant::now();
59 let elapsed = started.map(|s| now - s).unwrap_or_default();
60 *started = Some(now);
61 elapsed
62 }
63
64 fn report(&self) {
65 if !self.enabled {
66 return;
67 }
68 let ms = |d: Duration| d.as_secs_f64() * 1_000.0;
69 eprintln!(
70 "imprint.profile pairs={} marched={} classify={:.2} cosurface={:.2} lies_on={:.2} planar_iso={:.2} analytic={:.2} seeds={:.2} march={:.2} clip_fit={:.2} process_curve={:.2}",
71 self.pairs,
72 self.marched_pairs,
73 ms(self.classify),
74 ms(self.cosurface),
75 ms(self.lies_on),
76 ms(self.planar_iso),
77 ms(self.analytic),
78 ms(self.seeds),
79 ms(self.march),
80 ms(self.clip_and_fit),
81 ms(self.process_curve),
82 );
83 eprintln!(
84 "imprint.clip_fit rescue_march={:.2} clip={:.2} shared_boundary={:.2} fit={:.2} process={:.2}",
85 ms(self.rescue_march),
86 ms(self.clip),
87 ms(self.shared_boundary),
88 ms(self.fit),
89 ms(self.process),
90 );
91 }
92}
93
94#[derive(Clone, Debug, Deserialize)]
95pub struct ImprintOptions {
96 #[serde(default = "default_tolerance")]
97 pub tolerance: f64,
98 #[serde(default = "default_maximum_fit_points")]
99 pub maximum_fit_points: usize,
100 #[serde(default)]
101 pub local_fit: bool,
102 #[serde(default)]
103 pub fit_chunk_points: Option<usize>,
104 pub maximum_ssi_step: Option<f64>,
105}
106
107fn default_tolerance() -> f64 {
108 1e-7
109}
110
111fn default_maximum_fit_points() -> usize {
112 80
113}
114
115const PAIR_ANGULAR_TOLERANCE: f64 = 1e-4;
120
121const NEAR_TANGENT_STEP_FRACTION: f64 = 1e-3;
126
127const SHARED_SECTION_BAND_FRACTION: f64 = 1e-3;
142
143fn march_maximum_step(
163 classification: &SurfacePairClassification,
164 requested: Option<f64>,
165 scale: f64,
166) -> Option<f64> {
167 let grazes = matches!(
168 classification.relation,
169 SurfacePairRelation::NearTangent | SurfacePairRelation::Singular
170 ) && classification.minimum_normal_cross <= PAIR_ANGULAR_TOLERANCE;
171 if !grazes {
172 return requested;
173 }
174 Some(
175 requested.unwrap_or(
176 classification
177 .minimum_separation
178 .max(NEAR_TANGENT_STEP_FRACTION * scale),
179 ) * 0.5,
180 )
181}
182
183impl Default for ImprintOptions {
184 fn default() -> Self {
185 Self {
186 tolerance: default_tolerance(),
187 maximum_fit_points: default_maximum_fit_points(),
188 local_fit: false,
189 fit_chunk_points: None,
190 maximum_ssi_step: None,
191 }
192 }
193}
194
195#[derive(Clone, Debug, Deserialize, Serialize)]
196pub struct ImprintVertex {
197 pub id: u64,
198 pub point: Vec3,
199}
200
201#[derive(Clone, Debug, Deserialize, Serialize)]
202pub struct FacePcurve {
203 pub operand: u8,
204 pub face_id: u64,
205 pub pcurve: NurbsCurve,
206}
207
208#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Hash, Serialize)]
209pub struct FaceKey {
210 pub operand: u8,
211 pub face_id: u64,
212}
213
214#[derive(Clone, Debug, Deserialize, Serialize)]
215pub struct ImprintPieceRecord {
216 pub id: u64,
217 pub curve: NurbsCurve,
218 pub t0: f64,
219 pub t1: f64,
220 pub start_vertex_id: u64,
221 pub end_vertex_id: u64,
222 pub pcurves: Vec<FacePcurve>,
223 pub support_faces: [FaceKey; 2],
224 #[serde(default, skip_serializing_if = "Option::is_none")]
235 pub shared_edge: Option<(u8, u64, bool)>,
236}
237
238#[derive(Clone, Debug, Deserialize, Serialize)]
239pub struct EdgeSplitRecord {
240 pub operand: u8,
241 pub edge_id: u64,
242 pub parameters: Vec<f64>,
243}
244
245#[derive(Clone, Debug, Deserialize, Serialize)]
246pub struct FaceImprints {
247 pub operand: u8,
248 pub face_id: u64,
249 pub piece_ids: Vec<u64>,
250}
251
252#[derive(Clone, Debug, Deserialize, Serialize)]
253pub struct ImprintResultRecord {
254 pub vertices: Vec<ImprintVertex>,
255 pub pieces: Vec<ImprintPieceRecord>,
256 pub by_face: Vec<FaceImprints>,
257 pub edge_splits: Vec<EdgeSplitRecord>,
258 #[serde(default)]
264 pub barrier_edges: Vec<(u8, u64)>,
265 #[serde(default)]
273 pub tangent_nodes: Vec<Vec3>,
274 #[serde(default)]
280 pub section_evidence: bool,
281 #[serde(default)]
287 pub cosurface_pairs: Vec<(FaceKey, FaceKey)>,
288}
289
290#[path = "imprint/ruled_torus.rs"]
291mod ruled_torus;
292pub(crate) use ruled_torus::{circular_revolution_profile, construct_ruled_torus, RuledTorusBoundary};
293
294#[path = "imprint/support.rs"]
295mod support;
296#[path = "imprint/builder.rs"]
297mod builder;
298#[path = "imprint/junctions.rs"]
299mod junctions;
300#[path = "imprint/self_touch.rs"]
301mod self_touch;
302#[path = "imprint/sections.rs"]
303mod sections;
304#[path = "imprint/tangent_contact.rs"]
305mod tangent_contact;
306#[path = "imprint/driver.rs"]
307mod driver;
308
309#[path = "imprint/gate_census.rs"]
310mod gate_census;
311
312use builder::*;
313use junctions::*;
314use sections::*;
315use support::*;
316
317#[path = "imprint/section_graph.rs"]
318mod section_graph;
319pub(crate) use section_graph::{plane_section_graph, plane_section_graph_range, plane_section_graph_range_bounded};
320
321pub(crate) fn planar_carrier_section(
324 first: &NurbsSurface,
325 second: &NurbsSurface,
326 tolerance: f64,
327) -> Result<Option<NurbsCurve>, crate::KernelRefusal> {
328 support::planar_iso_intersection(first,second,tolerance)
329}
330
331use tangent_contact::classify_tangent_contact;
332
333pub use driver::build_imprints;
334pub(crate) use driver::build_carrier_imprints;
335pub(crate) use self_touch::self_touch_edge_splits;