mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-27 15:01:07 -04:00
Plan Cutting is now the only automatic lead-in assignment, and parts the support grid cannot hold level are reported wherever they lack a steadying tab. - Remove Assign Lead-ins (plate and nest), LeadInAssigner, the lead-in tool's Auto-Assign button and the Auto Tab size range; old settings with autoTab sizes still load - Manual Place Lead-in tabs the clicked entry when support tabs are on and the grid cannot hold the part (SupportTabSettings) - New PartSupport finding: Plan Cutting replay and best-effort plans warn when a drawing pin, a lead-in that does not fit or no single tab leaves a part unsteady; the pre-post review checks each placed program's outside tab gap. These warnings replace the advisory weak-tab notes; only the inside-corner tab note stays advisory, and never for a pin - Pinned perimeters keep their pin but carry the support analysis - Draw grid points as pixel-snapped squares without anti-aliasing - Post fixtures build lead-ins through a test-only TestLeadIns helper - Docs: support tabs, pre-post verification and planner notes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
125 lines
5.2 KiB
C#
125 lines
5.2 KiB
C#
using OpenNest.CNC;
|
|
using OpenNest.CNC.CuttingStrategy;
|
|
using OpenNest.Diagnostics;
|
|
using OpenNest.Engine.CuttingPlanning;
|
|
using OpenNest.Geometry;
|
|
using OpenNest.Support;
|
|
using OpenNest.Tests.CuttingPlanning;
|
|
|
|
namespace OpenNest.Tests.Support;
|
|
|
|
/// <summary>
|
|
/// Parts the support grid cannot hold level: hand-placed lead-ins tab them, Plan Cutting warns
|
|
/// when it cannot steady them, and the pre-post review warns about any without a steadying tab.
|
|
/// </summary>
|
|
public class SupportReviewTests
|
|
{
|
|
// One row of grid points at y = 2.25 runs under the 10 x 1 strip at (2, 2), below its center.
|
|
private static readonly SupportGrid OneRow = new(2, 4, 0, 2.25, stabilityMargin: 0.1);
|
|
|
|
private static CuttingParameters Parameters(bool tabs = false) => new()
|
|
{
|
|
ExternalLeadIn = new LineLeadIn { Length = 0.3, ApproachAngle = 90 },
|
|
SupportTabs = true,
|
|
TabsEnabled = tabs,
|
|
TabConfig = new NormalTab { Size = 0.2 },
|
|
};
|
|
|
|
private static (Nest Nest, Plate Plate, Part Part) Strip(SupportBed bed, double height = 1)
|
|
{
|
|
var nest = new Nest("support");
|
|
nest.SetSupportBed(bed);
|
|
var plate = new Plate(20, 20) { CuttingParameters = Parameters() };
|
|
nest.Plates.Add(plate);
|
|
var part = new Part(new Drawing("strip", LeadPathValidationTests.Rectangle(0, 0, 10, height)), new Vector(2, 2));
|
|
plate.Parts.Add(part);
|
|
return (nest, plate, part);
|
|
}
|
|
|
|
private static void PlaceLeadIn(Part part, CuttingParameters parameters, Vector point, Line edge) =>
|
|
part.ApplySingleLeadIn(parameters, point, edge, ContourType.External);
|
|
|
|
[Fact]
|
|
public void ManualLeadIn_TabsAnUnsupportedPartWithoutChangingTheSettings()
|
|
{
|
|
var (_, plate, part) = Strip(OneRow);
|
|
var parameters = Parameters();
|
|
|
|
var used = SupportTabSettings.ForManualLeadIn(parameters, plate, part);
|
|
|
|
Assert.NotSame(parameters, used);
|
|
Assert.True(used.TabsEnabled);
|
|
Assert.False(parameters.TabsEnabled);
|
|
}
|
|
|
|
[Fact]
|
|
public void ManualLeadIn_KeepsTheSettingsForASupportedPartOrWithSupportTabsOff()
|
|
{
|
|
var (_, plate, part) = Strip(new SupportGrid(0.5, 0.5, 0.25, 0.25, stabilityMargin: 0.1));
|
|
var parameters = Parameters();
|
|
Assert.Same(parameters, SupportTabSettings.ForManualLeadIn(parameters, plate, part));
|
|
|
|
var (_, tipping, strip) = Strip(OneRow);
|
|
parameters.SupportTabs = false;
|
|
Assert.Same(parameters, SupportTabSettings.ForManualLeadIn(parameters, tipping, strip));
|
|
}
|
|
|
|
[Fact]
|
|
public void PrePost_WarnsAboutAnUnsupportedPartWithoutATab()
|
|
{
|
|
var (nest, _, part) = Strip(OneRow);
|
|
PlaceLeadIn(part, Parameters(), new Vector(5, 1), new Line(new Vector(0, 1), new Vector(10, 1)));
|
|
|
|
var finding = Assert.Single(PostVerificationAnalyzer.Analyze(nest).Findings,
|
|
f => f.Kind == PostVerificationKind.PartSupport);
|
|
|
|
Assert.Equal(1, finding.PartNumber);
|
|
Assert.Contains("no tab", finding.Message);
|
|
Assert.Equal(new Vector(7, 2.5), finding.Location);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrePost_AcceptsATabThatSteadiesThePart_AndWarnsAboutOneThatDoesNot()
|
|
{
|
|
var (steadied, _, top) = Strip(OneRow);
|
|
PlaceLeadIn(top, Parameters(tabs: true), new Vector(5, 1), new Line(new Vector(0, 1), new Vector(10, 1)));
|
|
var (unsteady, _, bottom) = Strip(OneRow);
|
|
PlaceLeadIn(bottom, Parameters(tabs: true), new Vector(5, 0), new Line(new Vector(10, 0), new Vector(0, 0)));
|
|
|
|
Assert.DoesNotContain(PostVerificationAnalyzer.Analyze(steadied).Findings,
|
|
f => f.Kind == PostVerificationKind.PartSupport);
|
|
var finding = Assert.Single(PostVerificationAnalyzer.Analyze(unsteady).Findings,
|
|
f => f.Kind == PostVerificationKind.PartSupport);
|
|
Assert.Contains("does not steady it", finding.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void PrePost_IgnoresPartsWithoutSupportTabsOrABed()
|
|
{
|
|
var (nest, _, part) = Strip(OneRow);
|
|
var parameters = Parameters();
|
|
parameters.SupportTabs = false;
|
|
// A placed lead-in keeps its settings on the part, which the review reads first.
|
|
PlaceLeadIn(part, parameters, new Vector(5, 1), new Line(new Vector(0, 1), new Vector(10, 1)));
|
|
Assert.DoesNotContain(PostVerificationAnalyzer.Analyze(nest).Findings, f => f.Kind == PostVerificationKind.PartSupport);
|
|
|
|
parameters.SupportTabs = true;
|
|
nest.SetSupportBed(null);
|
|
Assert.DoesNotContain(PostVerificationAnalyzer.Analyze(nest).Findings, f => f.Kind == PostVerificationKind.PartSupport);
|
|
}
|
|
|
|
[Fact]
|
|
public void PlanCutting_WarnsWhenNoSingleTabCanSteadyThePart()
|
|
{
|
|
// The 10 x 0.8 strip lies between grid rows: it rests on nothing, so a tab only hangs it.
|
|
var (_, plate, _) = Strip(new SupportGrid(1, 1, 0, 0.9, stabilityMargin: 0.1), height: 0.8);
|
|
|
|
var result = CuttingPlanService.Plan(CuttingPlanRequest.ForPlate(plate, Vector.Zero,
|
|
confirmedParameters: Parameters()));
|
|
|
|
Assert.Equal(CuttingPlanStatus.BestEffort, result.Status);
|
|
var finding = Assert.Single(result.Findings, f => f.Kind == PostVerificationKind.PartSupport);
|
|
Assert.Contains("no single tab can steady it", finding.Message);
|
|
}
|
|
}
|