#![allow(dead_code)]
use brepkit_math::surfaces::CylindricalSurface;
use brepkit_math::tolerance::Tolerance;
use brepkit_math::vec::{Point3, Vec3};
use brepkit_topology::edge::EdgeCurve;
use brepkit_topology::face::{FaceId, FaceSurface};
pub(super) const CLOSED_CURVE_SAMPLES: usize = 32;
#[cfg(not(target_arch = "wasm32"))]
pub(super) const PARALLEL_THRESHOLD: usize = 64;
pub(super) const DEFAULT_BOOLEAN_DEFLECTION: f64 = 0.1;
pub(super) const CLASSIFIER_CYL_SEGMENTS: usize = 16;
pub(super) const CDT_CHORD_THRESHOLD: usize = 5;
pub(super) const CDT_SNAP_FACTOR: f64 = 100.0;
pub(super) const MIN_SOLID_FACES: usize = 3;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BooleanOp {
Fuse,
Cut,
Intersect,
}
#[derive(Clone)]
pub enum FaceSpec {
Planar {
vertices: Vec<Point3>,
normal: Vec3,
d: f64,
inner_wires: Vec<Vec<Point3>>,
},
Surface {
vertices: Vec<Point3>,
surface: FaceSurface,
reversed: bool,
inner_wires: Vec<Vec<Point3>>,
},
CylindricalFace {
vertices: Vec<Point3>,
cylinder: CylindricalSurface,
reversed: bool,
inner_wires: Vec<Vec<Point3>>,
},
}
impl FaceSpec {
#[must_use]
pub fn inner_wires(&self) -> &[Vec<Point3>] {
match self {
Self::Planar { inner_wires, .. }
| Self::Surface { inner_wires, .. }
| Self::CylindricalFace { inner_wires, .. } => inner_wires,
}
}
pub fn inner_wires_mut(&mut self) -> &mut Vec<Vec<Point3>> {
match self {
Self::Planar { inner_wires, .. }
| Self::Surface { inner_wires, .. }
| Self::CylindricalFace { inner_wires, .. } => inner_wires,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct BooleanOptions {
pub deflection: f64,
pub tolerance: Tolerance,
pub unify_faces: bool,
pub heal_after_boolean: bool,
}
impl Default for BooleanOptions {
fn default() -> Self {
Self {
deflection: DEFAULT_BOOLEAN_DEFLECTION,
tolerance: Tolerance::new(),
unify_faces: true,
heal_after_boolean: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Source {
A,
B,
}
pub(super) use brepkit_algo::FaceClass;
pub(super) enum CurveClassification {
Crossings(Vec<Point3>),
FullyContained,
FullyOutside,
}
#[derive(Debug, Clone, Copy)]
pub(super) struct BooleanContext {
#[allow(dead_code)]
pub(super) tol: Tolerance,
pub(super) vertex_merge: f64,
pub(super) classify_tol: f64,
pub(super) degenerate_area: f64,
}
impl BooleanContext {
pub(super) fn from_options(opts: &BooleanOptions) -> Self {
let tol = opts.tolerance;
Self {
tol,
vertex_merge: tol.linear * 1000.0,
classify_tol: tol.linear * 100.0,
degenerate_area: tol.linear * tol.linear,
}
}
}
#[derive(Debug)]
pub(super) struct IntersectionSegment {
pub(super) face_a: FaceId,
pub(super) face_b: FaceId,
pub(super) p0: Point3,
pub(super) p1: Point3,
}
#[derive(Debug)]
pub(super) struct FaceFragment {
pub(super) vertices: Vec<Point3>,
pub(super) normal: Vec3,
pub(super) d: f64,
pub(super) source: Source,
}
pub(super) struct FacePairSide<'a> {
pub(super) fid: FaceId,
pub(super) verts: &'a [Point3],
pub(super) normal: Vec3,
pub(super) d: f64,
}
pub(super) struct FaceSnapshot {
pub(super) id: FaceId,
pub(super) surface: FaceSurface,
pub(super) vertices: Vec<Point3>,
pub(super) normal: Vec3,
pub(super) d: f64,
pub(super) reversed: bool,
}
pub(super) struct AnalyticFragment {
pub(super) vertices: Vec<Point3>,
pub(super) surface: FaceSurface,
pub(super) normal: Vec3,
pub(super) d: f64,
pub(super) source: Source,
pub(super) edge_curves: Vec<Option<EdgeCurve>>,
pub(super) source_reversed: bool,
pub(super) source_face_id: Option<FaceId>,
}
pub(super) type FaceData = Vec<(FaceId, Vec<Point3>, Vec3, f64)>;
#[allow(clippy::match_same_arms)] pub(super) const fn select_fragment(
source: Source,
class: FaceClass,
op: BooleanOp,
) -> Option<bool> {
match (source, class, op) {
(Source::A, FaceClass::Outside, BooleanOp::Fuse | BooleanOp::Cut) => Some(false),
(Source::A, FaceClass::Outside, BooleanOp::Intersect) => None,
(Source::A, FaceClass::Inside, BooleanOp::Fuse | BooleanOp::Cut) => None,
(Source::A, FaceClass::Inside, BooleanOp::Intersect) => Some(false),
(Source::B, FaceClass::Outside, BooleanOp::Fuse) => Some(false),
(Source::B, FaceClass::Outside, BooleanOp::Cut | BooleanOp::Intersect) => None,
(Source::B, FaceClass::Inside, BooleanOp::Fuse) => None,
(Source::B, FaceClass::Inside, BooleanOp::Cut) => Some(true), (Source::B, FaceClass::Inside, BooleanOp::Intersect) => Some(false),
(Source::A, FaceClass::CoplanarSame, BooleanOp::Fuse | BooleanOp::Intersect) => Some(false),
(_, FaceClass::CoplanarSame, _) => None,
(Source::A, FaceClass::CoplanarOpposite, BooleanOp::Cut) => Some(false),
(_, FaceClass::CoplanarOpposite, _) => None,
(Source::A, FaceClass::On, BooleanOp::Fuse | BooleanOp::Cut | BooleanOp::Intersect) => {
Some(false)
}
(_, FaceClass::On, _) => None,
(_, FaceClass::Unknown, _) => {
debug_assert!(
false,
"FaceClass::Unknown must never reach fragment selection"
);
None
}
}
}