use crate::classification::{parameter_point_in_face, PolygonClass};
use crate::curve::KNOT_IDENTITY_TOL;
use crate::spatial::{Aabb, Bvh};
use crate::tolerance::{
assembler_weld, merge_scale, solid_scale, COINCIDENCE_DISTANCE_FLOOR, WELD_FLOOR,
};
use crate::topology::{BrepSolid, EdgeRecord, FaceRecord};
use crate::{
build_pcurve_on_surface, build_pcurve_on_surface_marched, classify_surface_pair_cached,
fit_polyline, intersect_curve_surface,
intersect_curves, intersect_surfaces, intersect_surfaces_supplemental, project_point_to_curve,
project_point_to_surface, project_point_to_surface_seeded, KnotVector, NurbsCurve, NurbsSurface,
SurfaceClassifyData,
SurfaceIntersectionOptions, SurfacePairClassification, SurfacePairRelation, Vec2, Vec3, Vec4,
};
use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use web_time::Instant;
#[derive(Default)]
struct ImprintProfile {
enabled: bool,
pairs: u64,
marched_pairs: u64,
classify: Duration,
cosurface: Duration,
lies_on: Duration,
planar_iso: Duration,
analytic: Duration,
seeds: Duration,
march: Duration,
clip_and_fit: Duration,
process_curve: Duration,
}
impl ImprintProfile {
fn new() -> Self {
Self {
enabled: std::env::var("BREP_PROFILE").is_ok(),
..Self::default()
}
}
fn lap(&self, started: &mut Option<Instant>) -> Duration {
if !self.enabled {
return Duration::ZERO;
}
let now = Instant::now();
let elapsed = started.map(|s| now - s).unwrap_or_default();
*started = Some(now);
elapsed
}
fn report(&self) {
if !self.enabled {
return;
}
let ms = |d: Duration| d.as_secs_f64() * 1_000.0;
eprintln!(
"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}",
self.pairs,
self.marched_pairs,
ms(self.classify),
ms(self.cosurface),
ms(self.lies_on),
ms(self.planar_iso),
ms(self.analytic),
ms(self.seeds),
ms(self.march),
ms(self.clip_and_fit),
ms(self.process_curve),
);
}
}
#[derive(Clone, Debug, Deserialize)]
pub struct ImprintOptions {
#[serde(default = "default_tolerance")]
pub tolerance: f64,
#[serde(default = "default_maximum_fit_points")]
pub maximum_fit_points: usize,
#[serde(default)]
pub local_fit: bool,
#[serde(default)]
pub fit_chunk_points: Option<usize>,
pub maximum_ssi_step: Option<f64>,
}
fn default_tolerance() -> f64 {
1e-7
}
fn default_maximum_fit_points() -> usize {
80
}
const PAIR_ANGULAR_TOLERANCE: f64 = 1e-4;
const NEAR_TANGENT_STEP_FRACTION: f64 = 1e-3;
const SHARED_SECTION_BAND_FRACTION: f64 = 1e-3;
fn march_maximum_step(
classification: &SurfacePairClassification,
requested: Option<f64>,
scale: f64,
) -> Option<f64> {
let grazes = matches!(
classification.relation,
SurfacePairRelation::NearTangent | SurfacePairRelation::Singular
) && classification.minimum_normal_cross <= PAIR_ANGULAR_TOLERANCE;
if !grazes {
return requested;
}
Some(
requested.unwrap_or(
classification
.minimum_separation
.max(NEAR_TANGENT_STEP_FRACTION * scale),
) * 0.5,
)
}
impl Default for ImprintOptions {
fn default() -> Self {
Self {
tolerance: default_tolerance(),
maximum_fit_points: default_maximum_fit_points(),
local_fit: false,
fit_chunk_points: None,
maximum_ssi_step: None,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ImprintVertex {
pub id: u64,
pub point: Vec3,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct FacePcurve {
pub operand: u8,
pub face_id: u64,
pub pcurve: NurbsCurve,
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Hash, Serialize)]
pub struct FaceKey {
pub operand: u8,
pub face_id: u64,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ImprintPieceRecord {
pub id: u64,
pub curve: NurbsCurve,
pub t0: f64,
pub t1: f64,
pub start_vertex_id: u64,
pub end_vertex_id: u64,
pub pcurves: Vec<FacePcurve>,
pub support_faces: [FaceKey; 2],
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shared_edge: Option<(u8, u64, bool)>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct EdgeSplitRecord {
pub operand: u8,
pub edge_id: u64,
pub parameters: Vec<f64>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct FaceImprints {
pub operand: u8,
pub face_id: u64,
pub piece_ids: Vec<u64>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ImprintResultRecord {
pub vertices: Vec<ImprintVertex>,
pub pieces: Vec<ImprintPieceRecord>,
pub by_face: Vec<FaceImprints>,
pub edge_splits: Vec<EdgeSplitRecord>,
#[serde(default)]
pub barrier_edges: Vec<(u8, u64)>,
#[serde(default)]
pub tangent_nodes: Vec<Vec3>,
#[serde(default)]
pub section_evidence: bool,
}
#[path = "imprint/support.rs"]
mod support;
#[path = "imprint/builder.rs"]
mod builder;
#[path = "imprint/junctions.rs"]
mod junctions;
#[path = "imprint/self_touch.rs"]
mod self_touch;
#[path = "imprint/sections.rs"]
mod sections;
#[path = "imprint/tangent_contact.rs"]
mod tangent_contact;
#[path = "imprint/driver.rs"]
mod driver;
use builder::*;
use junctions::*;
use sections::*;
use support::*;
use tangent_contact::classify_tangent_contact;
pub use driver::build_imprints;
pub(crate) use self_touch::self_touch_edge_splits;