fix: shell a body whose curved edge is rounded smaller than the wall (#476)

Moved in by more than its radius, a round collapses and its neighbours meet
along a new edge. That edge was always built as a straight line keyed by its
ends, so on a disc whose circular edge is rounded the four arcs where the floor
meets the side became one chord used by four faces, and the shell failed with
"This shell is not oriented and closed" (#319).

The new edge now runs where the edge's face meets the face across the round
(the round's far side, crossed as a strip), through the edge's middle moved
onto both: a line or a circular arc, checked to lie on both moved faces, and
refused otherwise. Twins are the new edges with the same ends through the same
middle.

Fixes #319

Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Irakli KourtisandClaude Opus 5.5 authored and GitHub committed 2026-10-12 21:48:27 -04:00
1 parent 0fe032438f
commit ccc36dc1f9
2 files changed
+117 -24

No files matched your search

+26
View File
@@ -2123,3 +2123,29 @@ fn shell_open_beside_a_round_as_large_as_the_wall_is_refused() {
let f = fillet(&b, &loop_, 1.5).unwrap();
assert!(shell(&f, &bottom, 2.0).unwrap().validity().is_empty());
}
/// A disc whose circular edge is rounded smaller than the wall (#319): moved in by the
/// thickness the round collapses to a sharp edge, where the floor meets the side along a
/// circle (two arcs with the same ends), not a straight line.
#[test]
fn shell_of_a_disc_rounded_smaller_than_the_wall() {
use std::f64::consts::PI;
let (rd, h, r) = (15.0, 5.0, 1.5);
let disc = cylinder(Vec3::new(0.0, 0.0, -h), Vec3::Z, rd, h).unwrap();
// The round takes a corner of area r²(1 − π/4) whose centroid is r(10 − 3π)/(3(4 − π))
// in from the rim, swept round the axis (Pappus).
let corner = r * r * (1.0 - PI / 4.0) * 2.0 * PI * (rd - r * (10.0 - 3.0 * PI) / (3.0 * (4.0 - PI)));
let (top, bottom) = ([Vec3::ZERO], [Vec3::new(0.0, 0.0, -h)]);
for (edge_z, open) in [(-h, &top), (-h, &bottom), (0.0, &bottom)] {
let rounded = fillet(&disc, &[Vec3::new(0.0, rd, edge_z), Vec3::new(0.0, -rd, edge_z)], r).unwrap();
let v0 = PI * rd * rd * h - corner;
for t in [1.6, 2.0] {
let s = shell(&rounded, open, t).unwrap_or_else(|e| panic!("round at z {edge_z}, t {t}: {e}"));
assert!(s.validity().is_empty(), "round at z {edge_z}, t {t}: {:?}", s.validity());
// The cavity: a plain cylinder the thickness in from the side and the closed end.
let want = v0 - PI * (rd - t).powi(2) * (h - t);
let v = measure(&s).unwrap().volume;
assert!(rel(v, want) < 1e-3, "round at z {edge_z}, t {t}: {v} vs {want}");
}
}
}