fix(cutoffs): leave leads out of the open-contour trim hull

When a part's outline is open (a tab gap, say), cut-off trimming falls back
to a convex hull. That hull still included lead-in/lead-out points, so it ran
from the lead's pierce point back to the part and stopped cut-offs far short
of the material. Cutting through a lead is harmless, so the hull now uses the
material contour only, as the closed-outline path already did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ajandClaude Opus 5.5 committed 2026-10-29 14:32:30 -04:00
1 parent 4ae50c8a73
commit 35be95b843
3 files changed
+31 -4

No files matched your search

+6 -3
View File
@@ -535,10 +535,13 @@ namespace OpenNest
// Leads are cutting paths in scrap, not boundary edges. Chaining them
// into the outline can make a closed part appear open and replace its
// recesses with a convex hull. Keep the full geometry for the existing
// conservative fallback when the material contour really is open.
// recesses with a convex hull. A cut-off may cross a lead, so the open-
// contour hull also leaves them out: hulling a lead's pierce point would
// stop cut-offs far short of the part.
var outline = entities.Where(e => e.Layer != SpecialLayers.Leadin &&
e.Layer != SpecialLayers.Leadout).ToList();
if (outline.Count == 0)
return null;
var profile = new Geometry.ShapeProfile(outline);
if (profile.Perimeter.IsClosed())
@@ -548,7 +551,7 @@ namespace OpenNest
return perimeter;
}
var points = entities.CollectPoints();
var points = outline.CollectPoints();
if (points.Count < 3)
return null;
@@ -138,4 +138,28 @@ public class CutOffLeadClearanceTests
Assert.True(Assert.IsType<LinearMove>(codes[1]).EndPoint.Y <= 19.98);
Assert.True(Assert.IsType<RapidMove>(codes[2]).EndPoint.Y >= 25.02);
}
[Fact]
public void OpenContour_FallbackHullIgnoresLeads()
{
// A tab gap leaves the outline open. The fallback hull must follow the material,
// not run from the lead-in's pierce point, or a cut-off passing the part stops far short.
var program = new Program();
program.MoveTo(50, -5);
program.Codes.Add(new LinearMove(40, 5) { Layer = LayerType.Leadin });
program.LineTo(40, 15);
program.LineTo(0, 20);
program.LineTo(0, 0);
program.LineTo(39, 4.875);
var plate = new Plate(100, 100);
var part = new Part(new Drawing("open-tapered", program), new Vector(20, 20));
plate.Parts.Add(part);
var cutoff = new CutOff(new Vector(50, 0), CutOffAxis.Vertical);
plate.CutOffs.Add(cutoff);
plate.RegenerateCutOffs(new CutOffSettings());
var codes = cutoff.Drawing.Program.Codes;
Assert.Equal(4, codes.Count);
// The material's bottom edge crosses x = 50 at y = 23.75.
Assert.InRange(Assert.IsType<LinearMove>(codes[1]).EndPoint.Y, 23, 23.75 - 0.02);
}
}
+1 -1
View File
@@ -20,7 +20,7 @@ A proposed tail shorter than **Minimum tail to keep** is left attached to the sc
Only real parts contribute to the occupied envelope; existing cutoff parts do not. When there is room, the final separator is beyond the furthest occupied extent by the larger of plate part spacing and cutoff part clearance, plus numerical tolerance. Repeated skeleton cuts stop before that separator. The larger margin affects only the separator: repeated cuts keep the existing manual-cutoff clearance behavior.
Cutoff trimming measures clearance from the part contour, excluding tagged lead-in and lead-out moves when building the closed outline. Leads must not turn a recessed outline into a convex hull and leave oversized gaps elsewhere. Genuinely open contours still use the conservative hull/bounds fallback. With a nonzero clearance, a line passing exactly through a corner of the clearance outline is trimmed to the outline like any other line. Automatic strip lines sit on a regular lattice and often land on such corners; they once fell back to the part's bounding box there, and the strip search preferred those lines because the box fallback measured as less cutting. The conservative curve/offset approximation can leave a small extra gap (about 0.0003 in on a straight edge at the default 0.02-in clearance), not an extra quarter inch.
Cutoff trimming measures clearance from the part contour, excluding tagged lead-in and lead-out moves when building the closed outline. Leads must not turn a recessed outline into a convex hull and leave oversized gaps elsewhere. Genuinely open contours (a tab gap, for example) still use the conservative hull/bounds fallback, but the hull is built from the material contour only: a cut-off may cross a lead, and hulling a lead's pierce point stopped cut-offs far short of the part. With a nonzero clearance, a line passing exactly through a corner of the clearance outline is trimmed to the outline like any other line. Automatic strip lines sit on a regular lattice and often land on such corners; they once fell back to the part's bounding box there, and the strip search preferred those lines because the box fallback measured as less cutting. The conservative curve/offset approximation can leave a small extra gap (about 0.0003 in on a straight edge at the default 0.02-in clearance), not an extra quarter inch.
If no safe separator fits inside the sheet, there is no retained-tail claim and the nominal cuts may cover the full sheet length. A proposed tail is not reported as separated unless its generated program spans the full sheet width. An active clamp blocks any proposed full-width line whose X coordinate falls in the clamp's whole footprint plus bottom-edge clearance, including a nominal grid line or an equivalent existing line. A tolerance-close existing line is tested at its own X as well as the proposed X, because that existing line is what would be certified. A same-line manual cut with different limits still blocks the whole plan, even on a clamp. Blocked lines are skipped with a clamp diagnostic; a blocked tail also reports `NoSafeTailSeparator` rather than claiming separation. Removed clamps on a sheet are absent from its active zones and do not block cuts. Stored zones are relative to the sheet's lower-left corner; the planner maps them into the plate's quadrant frame (the same frame as part and cut-off coordinates) before comparing, so the rule holds in all four quadrants. Obstructed or minimum-length-filtered lines produce diagnostics; they are not proof of a completed partition.