Kernel tests: a slot narrower than the taper closes partway up and builds; a closing hole is refused

tangency-labs/smlib#64 builds a tapered wall that closes partway up (its neighbours meet above
its apex), so the 0.2 mm slot of a 10 mm square tapered 10° out over 2 mm now builds where it
was refused. The test, renamed for what it checks, asserts the slot's solid is valid with its
exact volume and keeps the ring's refusal (its 0.3 mm hole closes entirely).

Proof of the volume, t = tan 10°: every wall moves out of the material by z·t at height z. The
square grows to 10 + 2zt, so without the slot ∫₀² (10 + 2zt)² dz = ((10 + 4t)³ − 1000)/(6t).
The slot narrows to 0.2 − 2zt and keeps its length 8 (its end wall and the square's side both
move up by zt); it closes at z = 0.1/t = 0.567 and takes ∫ 8 (0.2 − 2zt) dz over 0..0.1/t =
0.08/t. Volume ((10 + 4t)³ − 1000)/(6t) − 0.08/t = 213.984095421783; measured to 1e-6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jyviko
2026-10-30 06:16:13 -04:00
committed by Irakli Kourtis
parent 01d0d6bb93
commit 96b248d288
+16 -6
View File
@@ -738,10 +738,11 @@ fn tapered_extrude_between_with_a_foot() {
);
}
/// A taper steeper than a profile's details is refused, and says why: a slot narrower than the
/// taper closes; a ring's hole smaller than it closes.
/// A taper steeper than a profile's details: a slot narrower than the taper closes partway up
/// and the solid builds, the slot's walls meeting above where its end wall closes to a point; a
/// ring's hole smaller than the taper closes the whole hole and is refused, saying why.
#[test]
fn tapered_extrude_refuses_a_taper_steeper_than_the_details() {
fn tapered_extrude_closes_a_narrow_slot_and_refuses_a_closing_hole() {
let slot = Loop2::polygon(&[
Vec2::new(0.0, 0.0),
Vec2::new(10.0, 0.0),
@@ -752,9 +753,18 @@ fn tapered_extrude_refuses_a_taper_steeper_than_the_details() {
Vec2::new(4.9, 10.0),
Vec2::new(0.0, 10.0),
]);
let e = extrude_tapered(&Plane::XY, &Region2 { outer: slot, holes: vec![] }, 2.0, 1.0, 10f64.to_radians(), crate::DraftCorner::default())
.unwrap_err();
assert!(e.to_string().contains("the taper closes the profile"), "{e}");
let a = 10f64.to_radians();
let b = extrude_tapered(&Plane::XY, &Region2 { outer: slot, holes: vec![] }, 2.0, 1.0, a, crate::DraftCorner::default()).unwrap();
assert!(b.validity().is_empty(), "{:?}", b.validity());
// Every wall moves out of the material by z·t at height z (t = tan 10°): the 10 mm square
// grows to 10 + 2zt, so the solid without the slot is ∫₀² (10 + 2zt)² dz
// = ((10 + 4t)³ − 1000) / (6t). The slot narrows to 0.2 − 2zt, its length staying 8 (its end
// wall and the square's side both move up by zt), and it closes at z = 0.1/t (0.567, below
// the top): it takes ∫ 8 (0.2 − 2zt) dz over 0..0.1/t = 0.08/t.
let t = a.tan();
let v = ((10.0 + 4.0 * t).powi(3) - 1000.0) / (6.0 * t) - 0.08 / t;
let m = measure(&b).unwrap();
assert!(rel(m.volume, v) < 1e-6, "{} vs {v}", m.volume);
let ring = Region2 { outer: Loop2::circle(Vec2::ZERO, 5.0), holes: vec![Loop2::circle(Vec2::ZERO, 0.3).reversed()] };
let e = extrude_tapered(&Plane::XY, &ring, 2.0, 1.0, 10f64.to_radians(), crate::DraftCorner::default()).unwrap_err();
assert!(e.to_string().contains("the taper closes the profile"), "{e}");