feat: add LeadInAssigner for auto-assigning lead-ins to plate parts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
40
OpenNest.Engine/LeadInAssigner.cs
Normal file
40
OpenNest.Engine/LeadInAssigner.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using OpenNest.Engine.Sequencing;
|
||||||
|
using OpenNest.Geometry;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace OpenNest.Engine
|
||||||
|
{
|
||||||
|
public class LeadInAssigner
|
||||||
|
{
|
||||||
|
public IPartSequencer Sequencer { get; set; }
|
||||||
|
|
||||||
|
public void Assign(Plate plate)
|
||||||
|
{
|
||||||
|
var parameters = plate.CuttingParameters;
|
||||||
|
if (parameters == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var sequenced = Sequencer.Sequence(plate.Parts.ToList(), plate);
|
||||||
|
var currentPoint = PlateHelper.GetExitPoint(plate);
|
||||||
|
|
||||||
|
foreach (var sp in sequenced)
|
||||||
|
{
|
||||||
|
var part = sp.Part;
|
||||||
|
|
||||||
|
if (part.LeadInsLocked)
|
||||||
|
{
|
||||||
|
currentPoint = part.Location;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part.HasManualLeadIns)
|
||||||
|
part.RemoveLeadIns();
|
||||||
|
|
||||||
|
var localApproach = currentPoint - part.Location;
|
||||||
|
part.ApplyLeadIns(parameters, localApproach);
|
||||||
|
|
||||||
|
currentPoint = part.Location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
98
OpenNest.Tests/LeadInAssignerTests.cs
Normal file
98
OpenNest.Tests/LeadInAssignerTests.cs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
using OpenNest.CNC;
|
||||||
|
using OpenNest.CNC.CuttingStrategy;
|
||||||
|
using OpenNest.Engine;
|
||||||
|
using OpenNest.Engine.Sequencing;
|
||||||
|
using OpenNest.Geometry;
|
||||||
|
|
||||||
|
namespace OpenNest.Tests;
|
||||||
|
|
||||||
|
public class LeadInAssignerTests
|
||||||
|
{
|
||||||
|
private static Part MakeSquarePartAt(double x, double y)
|
||||||
|
{
|
||||||
|
var pgm = new Program();
|
||||||
|
pgm.Codes.Add(new RapidMove(new Vector(0, 0)));
|
||||||
|
pgm.Codes.Add(new LinearMove(new Vector(0, 10)));
|
||||||
|
pgm.Codes.Add(new LinearMove(new Vector(10, 10)));
|
||||||
|
pgm.Codes.Add(new LinearMove(new Vector(10, 0)));
|
||||||
|
pgm.Codes.Add(new LinearMove(new Vector(0, 0)));
|
||||||
|
var drawing = new Drawing("test", pgm);
|
||||||
|
return new Part(drawing, new Vector(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Assign_SetsHasManualLeadInsOnAllParts()
|
||||||
|
{
|
||||||
|
var plate = new Plate(60, 120);
|
||||||
|
plate.Parts.Add(MakeSquarePartAt(10, 10));
|
||||||
|
plate.Parts.Add(MakeSquarePartAt(30, 30));
|
||||||
|
plate.CuttingParameters = new CuttingParameters
|
||||||
|
{
|
||||||
|
ExternalLeadIn = new LineLeadIn { Length = 0.5, ApproachAngle = 90 }
|
||||||
|
};
|
||||||
|
|
||||||
|
var assigner = new LeadInAssigner { Sequencer = new LeftSideSequencer() };
|
||||||
|
assigner.Assign(plate);
|
||||||
|
|
||||||
|
Assert.All(plate.Parts, p => Assert.True(p.HasManualLeadIns));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Assign_SkipsLockedParts()
|
||||||
|
{
|
||||||
|
var plate = new Plate(60, 120);
|
||||||
|
var lockedPart = MakeSquarePartAt(10, 10);
|
||||||
|
lockedPart.LeadInsLocked = true;
|
||||||
|
lockedPart.HasManualLeadIns = true;
|
||||||
|
var originalProgram = lockedPart.Program;
|
||||||
|
|
||||||
|
plate.Parts.Add(lockedPart);
|
||||||
|
plate.Parts.Add(MakeSquarePartAt(30, 30));
|
||||||
|
plate.CuttingParameters = new CuttingParameters
|
||||||
|
{
|
||||||
|
ExternalLeadIn = new LineLeadIn { Length = 0.5, ApproachAngle = 90 }
|
||||||
|
};
|
||||||
|
|
||||||
|
var assigner = new LeadInAssigner { Sequencer = new LeftSideSequencer() };
|
||||||
|
assigner.Assign(plate);
|
||||||
|
|
||||||
|
Assert.Same(originalProgram, lockedPart.Program);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Assign_RemovesExistingLeadInsBeforeReapply()
|
||||||
|
{
|
||||||
|
var plate = new Plate(60, 120);
|
||||||
|
plate.Parts.Add(MakeSquarePartAt(10, 10));
|
||||||
|
plate.CuttingParameters = new CuttingParameters
|
||||||
|
{
|
||||||
|
ExternalLeadIn = new LineLeadIn { Length = 0.5, ApproachAngle = 90 }
|
||||||
|
};
|
||||||
|
|
||||||
|
var assigner = new LeadInAssigner { Sequencer = new LeftSideSequencer() };
|
||||||
|
assigner.Assign(plate);
|
||||||
|
var countAfterFirst = plate.Parts[0].Program.Codes.Count;
|
||||||
|
|
||||||
|
assigner.Assign(plate);
|
||||||
|
var countAfterSecond = plate.Parts[0].Program.Codes.Count;
|
||||||
|
|
||||||
|
Assert.Equal(countAfterFirst, countAfterSecond);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Assign_PartsContainLeadinLayerCodes()
|
||||||
|
{
|
||||||
|
var plate = new Plate(60, 120);
|
||||||
|
plate.Parts.Add(MakeSquarePartAt(10, 10));
|
||||||
|
plate.CuttingParameters = new CuttingParameters
|
||||||
|
{
|
||||||
|
ExternalLeadIn = new LineLeadIn { Length = 0.5, ApproachAngle = 90 }
|
||||||
|
};
|
||||||
|
|
||||||
|
var assigner = new LeadInAssigner { Sequencer = new LeftSideSequencer() };
|
||||||
|
assigner.Assign(plate);
|
||||||
|
|
||||||
|
var hasLeadin = plate.Parts[0].Program.Codes.OfType<LinearMove>().Any(m => m.Layer == LayerType.Leadin);
|
||||||
|
Assert.True(hasLeadin);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user