Topology counts: a horn torus pinched to a point is one face (smoothness ignores an edge's ends and points without a normal), with a degenerate loop at the pinch counted as Fusion does (two edges and a vertex); oracle 50 passes, 76/76

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
bherbruckandClaude Opus 5.5 committed 2026-10-08 15:41:02 +00:00
1 parent 109b8acbb5
commit 38a6ae3d38
2 files changed
+56 -4

No files matched your search

+21
View File
@@ -1276,3 +1276,24 @@ fn tangent_holes_pinch_alike() {
let m = measure(&b).unwrap();
assert_eq!(m.merged.faces, 14, "{:?}", m.merged);
}
/// A d10 tube bent at R5: the inside of the bend is a horn torus pinched to a point. One torus
/// face, with a degenerate loop at the pinch (counted as Fusion does: 5 faces, 6 edges, 4 vertices).
#[test]
fn tight_bend_counts() {
use crate::build::PathSeg;
let region = Region2 { outer: Loop2::circle(Vec2::ZERO, 5.0), holes: vec![] };
let path = [
PathSeg::Line { a: Vec3::ZERO, b: Vec3::new(0.0, 0.0, 20.0) },
PathSeg::Arc {
a: Vec3::new(0.0, 0.0, 20.0),
center: Vec3::new(5.0, 0.0, 20.0),
axis: Vec3::new(0.0, 1.0, 0.0),
angle: std::f64::consts::FRAC_PI_2,
},
PathSeg::Line { a: Vec3::new(5.0, 0.0, 25.0), b: Vec3::new(25.0, 0.0, 25.0) },
];
let b = sweep(&Plane::XY, &region, &path).unwrap();
let m = measure(&b).unwrap().merged;
assert_eq!((m.faces, m.edges, m.vertices), (5, 6, 4), "{m:?}");
}
+35 -4
View File
@@ -426,16 +426,26 @@ pub fn merged_topology(b: &Body, mesh: &Mesh) -> Result<TopoCounts> {
let (Some(fa), Some(fb), Some(e)) = (flist.get(a), flist.get(b), edge) else { return false };
let (sa, sb) = (fa.oriented_surface(), fb.oriented_surface());
let step = (e.len() / 8).max(1);
e.iter().step_by(step).all(|p| {
// Points where a surface has no normal (a horn torus pinched to its axis) don't count;
// nor do an edge's ends when it has points between (they can be such a pinch).
let inner: &[Vec3] = if e.len() > 2 { e.get(1..e.len() - 1).unwrap_or(e) } else { e };
let mut seen = 0;
let smooth = inner.iter().step_by(step).all(|p| {
let q = crate::body::p3(*p);
match (sa.search_nearest_parameter(q, None, 50), sb.search_nearest_parameter(q, None, 50)) {
(Some((u, v)), Some((s, t))) => {
let (x, y) = (sa.normal(u, v), sb.normal(s, t));
x.x * y.x + x.y * y.y + x.z * y.z > 1.0 - 1e-3
let d = x.x * y.x + x.y * y.y + x.z * y.z;
if !d.is_finite() {
return true;
}
seen += 1;
d > 1.0 - 1e-3
}
_ => false,
}
})
});
smooth && seen > 0
};
let mut fp: Vec<usize> = (0..nf).collect();
for (ei, faces) in &edge_faces {
@@ -472,6 +482,7 @@ pub fn merged_topology(b: &Body, mesh: &Mesh) -> Result<TopoCounts> {
}
// Remaining edges, keyed by merged face pair.
let mut remaining: Vec<(usize, (usize, usize), (usize, usize))> = Vec::new(); // (edge, verts, face pair)
let mut pinches: Vec<Vec3> = Vec::new();
let mut eids: Vec<&usize> = edge_faces.keys().collect();
eids.sort();
for e in eids {
@@ -480,6 +491,20 @@ pub fn merged_topology(b: &Body, mesh: &Mesh) -> Result<TopoCounts> {
continue;
}
let gs: Vec<usize> = faces.iter().map(|f| find(&mut fp, *f)).collect();
// A free-form face pinched to a point (a horn torus at its axis): a zero-length edge
// inside one face. Fusion keeps a degenerate loop there (two edges and a vertex).
if let Some(g) = gs.first()
&& gs.iter().all(|x| x == g)
&& matches!(surfs.get(*g), Some(Surf::Other(_)))
&& let Some(pl) = poly(*e)
&& let Some(p0) = pl.first()
&& pl.iter().all(|p| p.dist(*p0) < tol * 10.0)
{
if !pinches.iter().any(|q: &Vec3| q.dist(*p0) < tol * 10.0) {
pinches.push(*p0);
}
continue;
}
let pair = match gs[..] {
[a, b] if a == b => continue,
[a, b] => (a.min(b), a.max(b)),
@@ -534,7 +559,13 @@ pub fn merged_topology(b: &Body, mesh: &Mesh) -> Result<TopoCounts> {
}
}
}
Ok(TopoCounts { faces: groups.len(), edges: edge_groups.len() + apexes, vertices: real_vertices + closed_loops + apexes, face_types })
let pinch = pinches.len();
Ok(TopoCounts {
faces: groups.len(),
edges: edge_groups.len() + apexes + 2 * pinch,
vertices: real_vertices + closed_loops + apexes + pinch,
face_types,
})
}
/// A cylindrical face of a body near a point: where its axis is, which way, its radius, the