//! Where projected nodes go: onto a target (one B-rep face or a union of faces), onto the curve //! where two targets meet, or onto the point where three meet. //! //! A curve or corner node converges by tangent planes: project the node onto each target, //! intersect the targets' tangent planes there, move to the point of that intersection nearest //! the node, repeat. For two transversal surfaces this is Newton's method on the curve; it needs //! no B-rep edge between the faces, so it tolerates small gaps and overlaps in the CAD (the nodes //! end on the curve where the two surfaces, extended, meet). Faces meeting at a shallow angle //! fall back to alternating projections. use crate::tet::geometry::Tessellation; use crate::vec3::{P, add, cross, dist, dot, norm, scale, sub}; /// Rounds of the curve and corner iterations. const ROUNDS: usize = 60; /// Below this `|n_a × n_b|` two targets are treated as tangent (sin 3°). const TANGENT_SIN: f64 = 0.052; /// What the projection needs of the geometry. pub trait Geometry { /// The closest point on B-rep face `face`. fn project(&self, face: u32, p: P) -> Option
; /// The unit normal of B-rep face `face` near `p`. fn normal(&self, face: u32, p: P) -> Option
; } impl Geometry for Tessellation { fn project(&self, face: u32, p: P) -> Option
{ self.project_trimmed(face, p) } fn normal(&self, face: u32, p: P) -> Option
{
self.face_normal(face, p)
}
}
/// Where a node may go: targets by index into the projection's target list.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Constraint {
Free,
Surface(usize),
Curve(usize, usize),
Corner(usize, usize, usize),
}
impl Constraint {
/// The constraint of a node on the targets `on` (distinct, ascending).
pub(crate) fn of(on: &[usize]) -> Constraint {
match *on {
[] => Constraint::Free,
[a] => Constraint::Surface(a),
[a, b] => Constraint::Curve(a, b),
[a, b, c, ..] => Constraint::Corner(a, b, c),
}
}
}
/// The closest point to `p` on any face of `faces`, with its normal and face.
pub(crate) fn on_target(g: &dyn Geometry, faces: &[u32], p: P) -> Option<(P, P, u32)> {
let mut best: Option<(f64, P, u32)> = None;
for &f in faces {
if let Some(q) = g.project(f, p) {
let d = dist(p, q);
if best.is_none_or(|b| d < b.0) {
best = Some((d, q, f));
}
}
}
let (_, q, f) = best?;
let n = g.normal(f, q)?;
Some((q, n, f))
}
/// Place `p` by its constraint over `targets` (face lists); `tol` is the convergence distance.
/// A point the geometry cannot place stays where it is.
pub(crate) fn place(g: &dyn Geometry, targets: &[Vec