kernel: two planar supports are one when SMLib would call them coincident

same_support took two planes as one support only within 4096 ulps (normals 9e-13 apart,
origins about 1e-12). Faces SMLib rebuilds through a boolean lie within its own resolution of
the plane they were made on, not within ulps: a cap at z = 10 notched by a cut came back with
its origin 2.1e-11 off and its frame turned 1.36e-12 rad, so its CertifiedExtrudeCap was
dropped and the sketch on it reported "the top cap of Extrude1 no longer exists".

Supports are now compared at SMLib's own coincidence rule, SmPlane::CoincidenceCheck: normals
within the angle the tolerance makes across the plane (`tolerance / size`) and each origin within
the tolerance of the other plane, the tolerance being the record's `tolerance` (SMLib's per-brep
XSectTol3d, the one its booleans test surfaces for coincidence with). The body's size stands
for the plane's extent (at least as large, so no looser); rounding stays the floor. The normal
window the candidates are searched in follows the same angle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jyviko
2026-10-29 01:00:48 -04:00
committed by Irakli Kourtis
parent 9be29894e9
commit 2b0357a22d
2 changed files with 52 additions and 23 deletions
+2 -1
View File
@@ -41,13 +41,14 @@ impl Body {
return Ok(None);
}
let actual = super::planar_sources::actual_planes(&self)?;
let at = super::planar_sources::Resolution::of(&self);
let valid: Vec<_> = actual
.iter()
.zip(sources)
.map(|(actual, s)| {
s.as_ref()
.and_then(super::planar_sources::certified_plane)
.filter(|p| actual.is_some_and(|actual| super::planar_sources::same_support(p, &actual)))
.filter(|p| actual.is_some_and(|actual| super::planar_sources::same_support(p, &actual, at)))
})
.collect();
let record = self.record();
+50 -22
View File
@@ -40,23 +40,48 @@ pub(super) fn actual_planes(body: &Body) -> Result<Vec<Option<Plane>>> {
.collect())
}
/// How far apart the normals' x components of two planes `same_support` accepts can be, with
/// room to spare: their unit normals are within about 1e-12 of each other.
const NORMAL_WINDOW: f64 = 1e-9;
/// The resolution two planar supports are compared at: SMLib's own, by which it calls two planes
/// coincident (SmPlane::CoincidenceCheck): a distance within the body's intersection tolerance
/// (the record's `tolerance`, SMLib's XSectTol3d) and normals within the angle that tolerance
/// makes across the body (`tolerance / size`; SMLib takes the plane's own extent, the body's is
/// at least as large, so this is no looser), never finer than rounding. Faces SMLib rebuilt
/// through a boolean lie within it of the plane they were made on (a cut cap at z = 10 came back
/// 2.1e-11 off and 1.4e-12 rad turned), not within a few ulps of it.
#[derive(Clone, Copy, Debug)]
pub(super) struct Resolution {
linear: f64,
angular: f64,
}
/// The entries of `sorted` (ordered by their plane's normal x) whose normal x is within
/// [`NORMAL_WINDOW`] of `nx`: the only ones `same_support` can accept for a plane with that normal.
fn normal_window<T>(sorted: &[T], plane: impl Fn(&T) -> &Plane, nx: f64) -> &[T] {
let first = sorted.partition_point(|t| plane(t).normal().x < nx - NORMAL_WINDOW);
let last = sorted.partition_point(|t| plane(t).normal().x <= nx + NORMAL_WINDOW);
impl Resolution {
pub(super) fn of(body: &Body) -> Self {
let tolerance = body.record().tolerance;
let size = body.size();
let rounding = 4096. * f64::EPSILON;
let linear = if tolerance.is_finite() && tolerance > 0. { tolerance } else { 0. };
let angular = if size.is_finite() && size > 0. { linear / size } else { 0. };
Resolution { linear: linear.max(rounding * (1. + size.max(0.))), angular: angular.max(rounding) }
}
}
/// The entries of `sorted` (ordered by their plane's normal x) whose normal x is within the
/// resolution's angle of `nx` (and rounding): the only ones `same_support` can accept for a plane
/// with that normal.
fn normal_window<T>(sorted: &[T], plane: impl Fn(&T) -> &Plane, nx: f64, at: Resolution) -> &[T] {
let window = 2. * at.angular + 1e-12;
let first = sorted.partition_point(|t| plane(t).normal().x < nx - window);
let last = sorted.partition_point(|t| plane(t).normal().x <= nx + window);
sorted.get(first..last.max(first)).unwrap_or_default()
}
pub(super) fn same_support(a: &Plane, b: &Plane) -> bool {
let error = 4096. * f64::EPSILON * (1. + a.origin.len() + b.origin.len());
a.normal().cross(b.normal()).len() <= 4096. * f64::EPSILON
&& a.normal().dot(b.normal()) > 0.
&& (a.origin - b.origin).dot(a.normal()).abs() <= error
/// Whether `a` and `b` are one oriented plane at resolution `at`: normals the same way within its
/// angle, each origin within its distance of the other plane.
pub(super) fn same_support(a: &Plane, b: &Plane, at: Resolution) -> bool {
let (na, nb) = (a.normal(), b.normal());
na.cross(nb).len() <= at.angular
&& na.dot(nb) > 0.
&& (b.origin - a.origin).dot(na).abs() <= at.linear
&& (a.origin - b.origin).dot(nb).abs() <= at.linear
}
fn base_plane(source: &FaceSource, actual: &Plane) -> Option<Plane> {
@@ -195,6 +220,7 @@ impl Body {
if open.len() > 256 {
return Ok(self);
}
let at = Resolution::of(&self);
let opening_tolerance = if shell.is_some() { inputs.iter().map(|input| input.size()).fold(0., f64::max) * 1e-4 + 1e-6 } else { 0. };
let mapped = guard("exact planar support lineage", || {
let mut competing = Vec::new();
@@ -226,7 +252,7 @@ impl Body {
unknown_supports.push(actual);
continue;
};
if !same_support(&plane, &actual) || (shell.is_some() && open.iter().any(|p| actual.height(*p).abs() <= opening_tolerance)) {
if !same_support(&plane, &actual, at) || (shell.is_some() && open.iter().any(|p| actual.height(*p).abs() <= opening_tolerance)) {
unknown_supports.push(actual);
continue;
}
@@ -254,16 +280,16 @@ impl Body {
if let Some(actual) = actual {
let mut blocked = false;
let nx = actual.normal().x;
for plane in normal_window(&unknown_supports, |p| p, nx) {
for plane in normal_window(&unknown_supports, |p| p, nx, at) {
work = work.checked_sub(1).ok_or_else(|| KernelError::Invalid("unknown planar source comparison budget exceeded".into()))?;
if same_support(plane, &actual) {
if same_support(plane, &actual, at) {
blocked = true;
break;
}
}
for plane in normal_window(&competing, |p| p, nx) {
for plane in normal_window(&competing, |p| p, nx, at) {
work = work.checked_sub(1).ok_or_else(|| KernelError::Invalid("planar tool comparison budget exceeded".into()))?;
if same_support(plane, &actual) {
if same_support(plane, &actual, at) {
blocked = true;
break;
}
@@ -286,9 +312,9 @@ impl Body {
continue;
}
let mut chosen: Option<(Plane, &FaceSource, bool, usize)> = None;
for (plane, source, inner, cost) in normal_window(&candidates, |c| &c.0, nx) {
for (plane, source, inner, cost) in normal_window(&candidates, |c| &c.0, nx, at) {
work = work.checked_sub(1).ok_or_else(|| KernelError::Invalid("planar source comparison budget exceeded".into()))?;
if same_support(plane, &actual) {
if same_support(plane, &actual, at) {
if let Some((previous_plane, previous_source, previous_inner, _)) = chosen {
let Some(remaining) = identity_bytes.checked_sub(*cost) else {
chosen = None;
@@ -444,7 +470,9 @@ mod tests {
.map(|(i, p)| {
let p = (*p)?;
let find = |of: &Body, ps: &[Option<Plane>]| {
ps.iter().position(|q| q.is_some_and(|q| same_support(&q, &p))).and_then(|i| of.face_sources()?[i].clone())
ps.iter()
.position(|q| q.is_some_and(|q| same_support(&q, &p, Resolution::of(&combined))))
.and_then(|i| of.face_sources()?[i].clone())
};
let in_a = face_points(&combined, i).iter().all(|q| q.x <= a_right + 1e-9);
if in_a {
@@ -496,7 +524,7 @@ mod tests {
if matches!(s, Some(FaceSource::ExtrudeSide { coalesced: true, plane: None, .. })) {
assert!(cup.face_plane(i).is_none(), "loss witness is not source authority");
} else if s.is_some() {
assert!(same_support(&cup.face_plane(i).unwrap(), &cup.planar_face(i).unwrap()));
assert!(same_support(&cup.face_plane(i).unwrap(), &cup.planar_face(i).unwrap(), Resolution::of(&cup)));
}
}
let moved = transform_matrix(&cup, [[0., 1., 0., 0.], [-1., 0., 0., 0.], [0., 0., 1., 0.], [100., 200., 300., 1.]]).unwrap();