Oracle: 25/29 (14 and 15 now match Fusion). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
141 lines
4.9 KiB
Rust
141 lines
4.9 KiB
Rust
#![allow(clippy::many_single_char_names)]
|
|
|
|
use super::faces_classification::FacesClassification;
|
|
use super::loops_store::*;
|
|
use rustc_hash::FxHashMap as HashMap;
|
|
use std::ops::Deref;
|
|
use truck_meshalgo::prelude::*;
|
|
use truck_topology::*;
|
|
|
|
fn create_parameter_boundary<P, C, S>(
|
|
face: &Face<P, C, S>,
|
|
wire: &Wire<P, C>,
|
|
polys: &mut HashMap<EdgeID<C>, PolylineCurve<P>>,
|
|
tol: f64,
|
|
) -> Option<PolylineCurve<Point2>>
|
|
where
|
|
P: Copy,
|
|
C: BoundedCurve<Point = P> + ParameterDivision1D<Point = P>,
|
|
S: Clone + SearchParameter<D2, Point = P> + SearchNearestParameter<D2, Point = P>,
|
|
{
|
|
let surface = face.surface();
|
|
let pt = wire.front_vertex().unwrap().point();
|
|
// SolveCraft: intersection-curve points lie on the surface only within the tolerance; fall
|
|
// back to the nearest parameter when the exact search fails.
|
|
let p: Point2 = surface
|
|
.search_parameter(pt, None, 100)
|
|
.or_else(|| surface.search_nearest_parameter(pt, None, 100))?
|
|
.into();
|
|
let vec = wire.edge_iter().try_fold(vec![p], |mut vec, edge| {
|
|
let poly = polys.entry(edge.id()).or_insert_with(|| {
|
|
let curve = edge.curve();
|
|
let div = curve.parameter_division(curve.range_tuple(), tol).1;
|
|
PolylineCurve(div)
|
|
});
|
|
let mut p = *vec.last().unwrap();
|
|
let closure = |q: &P| -> Option<Point2> {
|
|
p = surface
|
|
.search_parameter(*q, Some(p.into()), 100)
|
|
.or_else(|| surface.search_nearest_parameter(*q, Some(p.into()), 100))?
|
|
.into();
|
|
Some(p)
|
|
};
|
|
let add: Option<Vec<Point2>> = match edge.orientation() {
|
|
true => poly.iter().skip(1).map(closure).collect(),
|
|
false => poly.iter().rev().skip(1).map(closure).collect(),
|
|
};
|
|
vec.append(&mut add?);
|
|
Some(vec)
|
|
})?;
|
|
Some(PolylineCurve(vec))
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
struct WireChunk<'a, C> {
|
|
poly: PolylineCurve<Point2>,
|
|
wire: &'a BoundaryWire<Point3, C>,
|
|
}
|
|
|
|
type FaceWithShapesOpStatus<C, S> = (Face<Point3, C, S>, ShapesOpStatus);
|
|
fn divide_one_face<C, S>(
|
|
face: &Face<Point3, C, S>,
|
|
loops: &Loops<Point3, C>,
|
|
tol: f64,
|
|
) -> Option<Vec<FaceWithShapesOpStatus<C, S>>>
|
|
where
|
|
C: BoundedCurve<Point = Point3> + ParameterDivision1D<Point = Point3>,
|
|
S: Clone + SearchParameter<D2, Point = Point3> + SearchNearestParameter<D2, Point = Point3>,
|
|
{
|
|
let (mut pre_faces, mut negative_wires) = (Vec::new(), Vec::new());
|
|
let mut map = HashMap::default();
|
|
loops.iter().try_for_each(|wire| {
|
|
let poly = crate::tr!(create_parameter_boundary(face, wire, &mut map, tol), "parameter boundary");
|
|
match poly.area() > 0.0 {
|
|
true => pre_faces.push(vec![WireChunk { poly, wire }]),
|
|
false => negative_wires.push(WireChunk { poly, wire }),
|
|
}
|
|
Some(())
|
|
})?;
|
|
negative_wires.into_iter().try_for_each(|chunk| {
|
|
let pt = chunk.poly.front();
|
|
let op = crate::tr!(pre_faces.iter_mut().find(|face| face[0].poly.include(pt)), "loop not inside any face piece");
|
|
op.push(chunk);
|
|
Some(())
|
|
})?;
|
|
let vec: Vec<_> = pre_faces
|
|
.into_iter()
|
|
.map(|pre_face| {
|
|
let surface = face.surface();
|
|
let op = pre_face
|
|
.iter()
|
|
.find(|chunk| chunk.wire.status() != ShapesOpStatus::Unknown);
|
|
let status = match op {
|
|
Some(chunk) => chunk.wire.status(),
|
|
None => ShapesOpStatus::Unknown,
|
|
};
|
|
let wires: Vec<Wire<Point3, C>> = pre_face
|
|
.into_iter()
|
|
.map(|chunk| chunk.wire.deref().clone())
|
|
.collect();
|
|
let mut new_face = Face::debug_new(wires, surface);
|
|
if !face.orientation() {
|
|
new_face.invert();
|
|
}
|
|
(new_face, status)
|
|
})
|
|
.collect();
|
|
Some(vec)
|
|
}
|
|
|
|
pub fn divide_faces<C, S>(
|
|
shell: &Shell<Point3, C, S>,
|
|
loops_store: &LoopsStore<Point3, C>,
|
|
tol: f64,
|
|
) -> Option<FacesClassification<Point3, C, S>>
|
|
where
|
|
C: BoundedCurve<Point = Point3> + ParameterDivision1D<Point = Point3>,
|
|
S: Clone + SearchParameter<D2, Point = Point3> + SearchNearestParameter<D2, Point = Point3>,
|
|
{
|
|
let mut res = FacesClassification::<Point3, C, S>::default();
|
|
shell
|
|
.iter()
|
|
.zip(loops_store)
|
|
.try_for_each(|(face, loops)| {
|
|
if loops
|
|
.iter()
|
|
.all(|wire| wire.status() == ShapesOpStatus::Unknown)
|
|
{
|
|
res.push(face.clone(), ShapesOpStatus::Unknown);
|
|
} else {
|
|
let vec = crate::tr!(divide_one_face(face, loops, tol), "divide one face");
|
|
vec.into_iter()
|
|
.for_each(|(face, status)| res.push(face, status));
|
|
}
|
|
Some(())
|
|
})?;
|
|
Some(res)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|