Tapered extrude: the end check of a profile with holes takes the section a swallowed line leaves

A tapered extrusion of a region with holes checks, at its two ends, that each hole's section
stays inside the outer loop's and clear of the others. Those sections were made with the
start section's rule, which refuses a line the offset swallows ("draft extrusion that starts
before its profile where the taper closes a side"). At the far end such a line is ordinary: a
short line between two concave corners whose wall closes on the way up. The letter g of
"tres long" (8 mm, joined 2 mm with a 3 degree taper) has one, and the whole text was refused
with a message about the start that does not apply.

The end check now takes the section as it is there (offset_loop's swallowed lines left out, their
neighbours meeting where they were); whether the closing wall is built stays the draft's to
say. The g builds at 3 and 15 degrees.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jyviko
2026-10-30 08:42:30 -04:00
committed by Irakli Kourtis
parent cf40a3e6c6
commit 5732b62ec3
+13 -2
View File
@@ -827,12 +827,23 @@ fn smlib_tapered(plane: &Plane, region: &Region2, from: f64, to: f64, foot: f64,
return Err(KernelError::Invalid("a tapered extrusion needs a taper".into()));
};
let holes: Vec<NumberedLoop> = joined.holes.iter().map(|h| section(h, from, -taper)).collect::<Result<_>>()?;
// A loop's section `at` as it is there: a line the offset swallows is gone, its neighbours
// meeting where it was (its wall closes on the way; whether that wall is built is the
// draft's to say).
let shape = |l: &NumberedLoop, at: f64, taper: f64| -> Result<NumberedLoop> {
let ccw = l.oriented(false);
if at == 0.0 || taper == 0.0 {
return Ok(ccw);
}
let segs: Vec<Seg2> = offset_loop(&ccw.lp, at * taper.tan())?.into_iter().flatten().collect();
Ok(NumberedLoop { lp: Loop2 { segs }, from: ccw.from })
};
// The sections where the walls are farthest moved, the two ends: each hole stays inside the
// outer loop and clear of the other holes there (the walls are planes, cones and their like,
// which move monotonically in between).
for at in [from, to] {
let o = section(&joined.outer, at, taper)?;
let hs: Vec<NumberedLoop> = joined.holes.iter().map(|h| section(h, at, -taper)).collect::<Result<_>>()?;
let o = shape(&joined.outer, at, taper)?;
let hs: Vec<NumberedLoop> = joined.holes.iter().map(|h| shape(h, at, -taper)).collect::<Result<_>>()?;
let pts = |l: &NumberedLoop| l.lp.segs.iter().flat_map(|s| s.polyline(1e-3)).collect::<Vec<Vec2>>();
// Apart: no point of either inside the other.
let apart = |a: &NumberedLoop, b: &NumberedLoop| !pts(a).iter().any(|p| b.lp.contains(*p)) && !pts(b).iter().any(|p| a.lp.contains(*p));