fix(engine): find the fewest clamps to remove, not only neighbouring runs

Recovery only tried contiguous runs of clamps, so a part with a notch over
a middle clamp had all three clamps removed although the two outer ones
were enough (D8: 'the fewest clamps that let it fit'). One probe per clamp
now finds the clamps every removal must include (a part that cannot fit with
only that clamp left needs it off; removal is monotone), and the search
tries every set by size, mandatory clamps plus the optional ones nearest the
origin first. Full-sheet parts resolve with one probe per clamp.

PM 3df03af2-35a6-49c2-9082-fc6208f5b708
This commit is contained in:
aj committed 2026-10-13 18:15:50 -04:00
1 parent 6908e00969
commit 4226c0f902
3 files changed
+87 -13

No files matched your search

@@ -359,4 +359,33 @@ public class ClampRemovalRepairTests
Assert.Equal(8, outcome.Result.Plates.Count);
Assert.All(outcome.Result.Plates, sheet => Assert.Empty(sheet.Stock.ClampZones));
}
// PM 3df03af2 (C11/D8 "the fewest clamps"): the minimal set need not be neighbours. The
// notch over the middle clamp lets it stay while the two outer clamps come off.
[Fact]
public void FewestClampsNeedNotBeNeighbours()
{
var program = new OpenNest.CNC.Program();
program.MoveTo(0, 0);
program.LineTo(8, 0);
program.LineTo(8, 1);
program.LineTo(15, 1);
program.LineTo(15, 0);
program.LineTo(23, 0);
program.LineTo(23, 3);
program.LineTo(0, 3);
program.LineTo(0, 0);
var rail = new ClampRail(1.5, 0.625, new[] { 3.0, 11.5, 20.0 });
var size = new Size(3, 23);
var stock = new NestPlateStock("sheet", size, null, 0, default, 1, null, rail.ResolveFor(size, 1))
{ ClampRail = rail };
var result = NestPipeline.Run(new NestPipelineRequest("Default",
new[] { new NestItem { Drawing = new Drawing("middle-notch", program), Quantity = 1 } },
new[] { stock }));
Assert.True(result.IsValid, string.Join("; ", result.Violations));
Assert.Equal(NestJobStatus.Complete, result.Status);
var sheet = Assert.Single(result.Raw.Plates);
Assert.Equal(new[] { "clamp-2" }, sheet.Stock.ClampZones.Select(z => z.Id));
}
}