mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-10 06:25:50 -05:00
A cut-off always runs away from its fixed edge: the origin side, the only edge guaranteed to be a real sheet edge. The part order is proposed as a traveling-salesman path over one point per placement, and a cut-off's point was the middle of its line. The route did not know the tool must arrive at the line's start and leaves at its far end, so the parts before a sheet-splitting line finished level with its middle, and the rapid down to its start crossed parts already cut. Each placement now has an entry and an exit point; they differ only for cut-offs (start and departure). Nearest neighbor and Or-opt go on from each part's exit, and 2-opt costs a reversed run along its own directed legs; with no cut-off on the plate the exits are not passed and the symmetric route is unchanged. The part cut before a cut-off faces its start. Cut-off direction is not chosen here: the order reads each cut-off's start and end from its program as given, and the joint search may still reverse a head-locked far-edge segment.
88 lines
3.8 KiB
C#
88 lines
3.8 KiB
C#
using OpenNest.CNC;
|
|
using OpenNest.CNC.CuttingPlanning;
|
|
using OpenNest.Engine.CuttingPlanning;
|
|
using OpenNest.Engine.Sequencing;
|
|
using OpenNest.Geometry;
|
|
|
|
namespace OpenNest.Tests.CuttingPlanning;
|
|
|
|
/// <summary>
|
|
/// A cut-off is entered at its start, on the origin-side edge, and left at its far end. The route
|
|
/// plans it that way: the parts before a line that splits the sheet finish beside its start, so
|
|
/// the rapid onto it crosses nothing already cut.
|
|
/// </summary>
|
|
public class CutOffArrivalOrderTests
|
|
{
|
|
// Two columns of squares left of a full-height line, which is cut last, with the tool
|
|
// arriving from the top right. Aimed at the line's middle, the route ends at the top of the
|
|
// left column and the rapid down to the line's start crosses the right one. Aimed at the
|
|
// start, it ends at the bottom of the right column, beside it.
|
|
[Fact]
|
|
public void ColumnsBesideASplittingLine_FinishBesideTheLinesStart()
|
|
{
|
|
var nest = new Nest();
|
|
var plate = nest.CreatePlate();
|
|
plate.Size = new Size(48, 8);
|
|
var square = new Drawing("square", Square());
|
|
foreach (var x in new[] { 0.5, 2.5 })
|
|
for (var y = 2.0; y <= 42; y += 4)
|
|
plate.Parts.Add(new Part(square, new Vector(x, y)));
|
|
var line = new CutOff(new Vector(4, 0), CutOffAxis.Vertical);
|
|
plate.CutOffs.Add(line);
|
|
plate.RegenerateCutOffs(new CutOffSettings());
|
|
PlateSequencing.PlaceCutOffs(plate, [line]);
|
|
|
|
var result = CuttingPlanService.Plan(CuttingPlanRequest.ForPlate(plate, new Vector(6, 48),
|
|
confirmedParameters: ExplicitContourTests.Parameters()));
|
|
|
|
Assert.True(result.Status == CuttingPlanStatus.Ready && result.IndependentlyReplayed,
|
|
$"{result.Status}: {string.Join("; ", result.Findings.Select(f => f.Message).Take(3))}");
|
|
var order = result.ProposedOrder.Select(o => o.SourcePart).ToArray();
|
|
Assert.True(order[^1].BaseDrawing.IsCutOff);
|
|
Assert.Equal(new Vector(2.5, 2), order[^2].Location);
|
|
}
|
|
|
|
// A line through open scrap from (10, 0) to (10, 30) precedes nothing, so it is routed like
|
|
// a part: reached at its start, left at its far end. Square C sits beside the start and B
|
|
// beside the end, so the route goes C, the line, B; one that thought the tool stays at the
|
|
// start would go the line, C, B and travel the column twice.
|
|
[Fact]
|
|
public void LineThroughScrap_RouteGoesOnFromItsFarEnd()
|
|
{
|
|
var nest = new Nest();
|
|
var plate = nest.CreatePlate();
|
|
plate.Size = new Size(40, 20);
|
|
var square = new Drawing("square", Square());
|
|
var c = new Part(square, new Vector(12, 1));
|
|
var b = new Part(square, new Vector(12, 31));
|
|
plate.Parts.Add(b);
|
|
plate.Parts.Add(c);
|
|
var line = new CutOff(new Vector(10, 0), CutOffAxis.Vertical) { StartLimit = 0, EndLimit = 30 };
|
|
plate.CutOffs.Add(line);
|
|
plate.RegenerateCutOffs(new CutOffSettings());
|
|
PlateSequencing.PlaceCutOffs(plate, [line]);
|
|
|
|
var result = CuttingPlanService.Plan(CuttingPlanRequest.ForPlate(plate,
|
|
confirmedParameters: ExplicitContourTests.Parameters()));
|
|
|
|
Assert.True(result.Status == CuttingPlanStatus.Ready && result.IndependentlyReplayed,
|
|
$"{result.Status}: {string.Join("; ", result.Findings.Select(f => f.Message).Take(3))}");
|
|
var order = result.ProposedOrder.Select(o => o.SourcePart).ToArray();
|
|
Assert.Equal(3, order.Length);
|
|
Assert.Same(c, order[0]);
|
|
Assert.True(order[1].BaseDrawing.IsCutOff);
|
|
Assert.Same(b, order[2]);
|
|
}
|
|
|
|
private static Program Square()
|
|
{
|
|
var program = new Program();
|
|
program.MoveTo(0, 0);
|
|
program.LineTo(1, 0);
|
|
program.LineTo(1, 1);
|
|
program.LineTo(0, 1);
|
|
program.LineTo(0, 0);
|
|
return program;
|
|
}
|
|
}
|