From e0bc97a7194e60a6dfdce7b83f733da88334ddc8 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Mon, 2 Nov 2026 09:33:57 -0500 Subject: [PATCH] fix(cutting): plan support-tabbed perimeters that have a lead-out Every part the support grid could not hold level got a tabbed perimeter with the outside lead-out after its tab, and planning refused any lead-out after its own open cut, so with a lead-out configured no such part could be planned. Pre-post review already proves such cuts release nothing (the lead-out stays out of the tab gap); planning now runs the same proof, so planning and review agree and a lead-out that reaches into the tab is still refused. Co-Authored-By: Claude Opus 5.5 --- .../CNC/CuttingPlanning/ReleasedContourState.cs | 11 +++++------ OpenNest.Core/Diagnostics/PostVerificationAnalyzer.cs | 3 +-- docs/cutting-planner.md | 5 +++-- docs/post-verification.md | 4 ++-- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/OpenNest.Core/CNC/CuttingPlanning/ReleasedContourState.cs b/OpenNest.Core/CNC/CuttingPlanning/ReleasedContourState.cs index 87db463b..d448b378 100644 --- a/OpenNest.Core/CNC/CuttingPlanning/ReleasedContourState.cs +++ b/OpenNest.Core/CNC/CuttingPlanning/ReleasedContourState.cs @@ -101,7 +101,7 @@ public sealed class ReleasedContourState internal void AnalyzeMoves(IReadOnlyList moves, bool cutoff, List findings, int plate, int part, CancellationToken token, Func isFixed = null, List fixedFindings = null, - bool proveOpenCuts = false, int contoursBefore = 0, List raisedFindings = null) + int contoursBefore = 0, List raisedFindings = null) { var current = new CutGroup(); var unfinished = new List(); @@ -261,14 +261,13 @@ public sealed class ReleasedContourState Finish(); loose.Leads.AddRange(leadIns); // Open cuts that provably enclose nothing retain all their material, however many - // pieces a tabbed cutout was cut in. Otherwise they need manual review. Planning proves - // them too (its own tabbed cutouts are cut in pieces without lead-outs), but only - // pre-post review lets the proof clear a lead-out: planning keeps refusing to certify - // its own tabs followed by a lead-out. + // pieces a tabbed cutout was cut in, and so does a tabbed perimeter whose lead-out stays + // out of its tab: planning and pre-post review prove them alike. Otherwise they need + // manual review. var retained = !cutoff && (unfinished.Count > 1 || deferred.Count > 0) && unfinished.All(LeadsClearOpenEnds) && OpenCutsEncloseNothing(closed, unfinished.Concat(otherOpen).Append(loose).ToList(), token); - for (var i = deferred.Count - 1; i >= 0 && !(retained && proveOpenCuts); i--) + for (var i = deferred.Count - 1; i >= 0 && !retained; i--) deferred[i].Target.Insert(deferred[i].Index, deferred[i].Finding); // Only one open fragment per part can be the planner's own certified tab, so a // further fragment that a fixed emission cut makes this uncertainty that emission's. diff --git a/OpenNest.Core/Diagnostics/PostVerificationAnalyzer.cs b/OpenNest.Core/Diagnostics/PostVerificationAnalyzer.cs index 1c67bc09..6ced4fe4 100644 --- a/OpenNest.Core/Diagnostics/PostVerificationAnalyzer.cs +++ b/OpenNest.Core/Diagnostics/PostVerificationAnalyzer.cs @@ -144,8 +144,7 @@ public static class PostVerificationAnalyzer && move.Layer is LayerType.Cut or LayerType.Display && move.Curve.Length > PostVerificationGeometry.Epsilon)) Incomplete("Placed program has no cutting contour motions for this drawing."); - obstacles.AnalyzeMoves(moves, cutoff, findings, plateNumber, partNumber, cancellationToken, - proveOpenCuts: true); + obstacles.AnalyzeMoves(moves, cutoff, findings, plateNumber, partNumber, cancellationToken); if (clamps is { Count: > 0 }) foreach (var move in moves) { diff --git a/docs/cutting-planner.md b/docs/cutting-planner.md index 88ac3775..ef314094 100644 --- a/docs/cutting-planner.md +++ b/docs/cutting-planner.md @@ -508,8 +508,9 @@ or retraced cut) is kept as given with a fixed-lead warning instead; see below. no-parameter route retains its narrower compatibility contract and still refuses fixed-program violations. A planned lead-out that may bridge a tab, or a malformed emitted arc, is refused, not automatically repaired. Tabbed lead-outs leave from the -trimmed cut end, but a lead-out after an open contour still needs manual review of -its retention gap, so confirmed-parameters planning refuses it in a planned contour. +trimmed cut end; planning runs pre-post review's open-cut proof on them (see +[post verification](post-verification.md)), so a support-tabbed or tabbed perimeter with a +lead-out that stays out of its tab plans like any other, and one that reaches into it is refused. ### Fixed emissions warn diff --git a/docs/post-verification.md b/docs/post-verification.md index 87169a81..a8f5e0b3 100644 --- a/docs/post-verification.md +++ b/docs/post-verification.md @@ -155,8 +155,8 @@ end it leaves, and a lead-in never behind the start it enters. Anything else (pi meet exactly, a lead that bridges or curls into a tab, cuts from edge to edge) is reported as incomplete, needing manual review. Kerf, tab strength and other parts' cuts are not modeled. Plan Cutting runs the same proof, so its own [tabbed cutouts](tabbed-cutouts.md) (pieces without -lead-outs) pass, but keeps its stricter rule and does not certify its own tabs followed by a -lead-out. A program that still has a tabbed cutout's `Tab` motions (never planned) is +lead-outs) pass, and so do its tabbed perimeters followed by a lead-out that stays out of the +tab. A program that still has a tabbed cutout's `Tab` motions (never planned) is incomplete: a post may cut through them. ## Explicit override