mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-28 14:51:06 -04:00
With every part drawn the same, it was hard to see which parts the drawn lines had not reached yet. Parts no line reaches are now drawn muted, and update live while dragging. Clicking a part adds it as a one-point line, so a stray part can be picked without drawing a line to it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
199 lines
8.4 KiB
C#
199 lines
8.4 KiB
C#
using OpenNest.Engine.Sequencing;
|
|
using OpenNest.Geometry;
|
|
|
|
namespace OpenNest.Tests.Sequencing;
|
|
|
|
public class DrawnPathSequencingTests
|
|
{
|
|
private static Vector[][] Lines(params Vector[][] lines) => lines;
|
|
|
|
[Fact]
|
|
public void Apply_CutsPartsInTheOrderTheSnakePathReachesThem()
|
|
{
|
|
// Two rows of three 10x10 squares; the path runs along the bottom row
|
|
// left to right, then back along the top row right to left.
|
|
var drawing = TestHelpers.MakeSquareDrawing();
|
|
var parts = new[]
|
|
{
|
|
new Part(drawing, new Vector(0, 0)), new Part(drawing, new Vector(20, 0)), new Part(drawing, new Vector(40, 0)),
|
|
new Part(drawing, new Vector(0, 20)), new Part(drawing, new Vector(20, 20)), new Part(drawing, new Vector(40, 20)),
|
|
};
|
|
var plate = TestHelpers.MakePlate(60, 120, parts[4], parts[0], parts[5], parts[2], parts[3], parts[1]);
|
|
var nested = drawing.Quantity.Nested;
|
|
var path = new[] { new Vector(-5, 5), new Vector(55, 5), new Vector(55, 25), new Vector(-5, 25) };
|
|
|
|
Assert.True(DrawnPathSequencing.Apply(plate, Lines(path)));
|
|
|
|
Assert.Equal(new[] { parts[0], parts[1], parts[2], parts[5], parts[4], parts[3] }, plate.Parts);
|
|
Assert.Equal(nested, drawing.Quantity.Nested);
|
|
}
|
|
|
|
[Fact]
|
|
public void Apply_CutsLineByLineAndTheJumpBetweenLinesClaimsNothing()
|
|
{
|
|
// Both rows run left to right. The jump from the bottom row's end back to the
|
|
// top row's start crosses the middle part of each row but is not a line.
|
|
var drawing = TestHelpers.MakeSquareDrawing();
|
|
var bottom = new[] { new Part(drawing, new Vector(0, 0)), new Part(drawing, new Vector(20, 0)), new Part(drawing, new Vector(40, 0)) };
|
|
var top = new[] { new Part(drawing, new Vector(0, 20)), new Part(drawing, new Vector(20, 20)), new Part(drawing, new Vector(40, 20)) };
|
|
var plate = TestHelpers.MakePlate(60, 120, top[2], bottom[1], top[0], bottom[2], top[1], bottom[0]);
|
|
|
|
var lines = Lines(
|
|
new[] { new Vector(-5, 5), new Vector(55, 5) },
|
|
new[] { new Vector(-5, 25), new Vector(55, 25) });
|
|
|
|
Assert.True(DrawnPathSequencing.Apply(plate, lines));
|
|
|
|
Assert.Equal(bottom.Concat(top), plate.Parts);
|
|
}
|
|
|
|
[Fact]
|
|
public void CutOrder_PreviewsApplyWithoutReorderingThePlate()
|
|
{
|
|
var drawing = TestHelpers.MakeSquareDrawing();
|
|
var a = new Part(drawing, new Vector(0, 0));
|
|
var b = new Part(drawing, new Vector(20, 0));
|
|
var c = new Part(drawing, new Vector(40, 0));
|
|
var plate = TestHelpers.MakePlate(60, 120, c, a, b);
|
|
var reorders = 0;
|
|
plate.PartsReordered += (_, _) => reorders++;
|
|
var lines = Lines(new[] { new Vector(-5, 5), new Vector(55, 5) });
|
|
|
|
var preview = DrawnPathSequencing.CutOrder(plate, lines);
|
|
|
|
Assert.Equal(new[] { a, b, c }, preview);
|
|
Assert.Equal(new[] { c, a, b }, plate.Parts);
|
|
Assert.Equal(0, reorders);
|
|
Assert.Null(DrawnPathSequencing.CutOrder(plate, Lines(new[] { new Vector(0, 0), new Vector(0, 0) })));
|
|
|
|
Assert.True(DrawnPathSequencing.Apply(plate, lines));
|
|
Assert.Equal(preview, plate.Parts);
|
|
}
|
|
|
|
[Fact]
|
|
public void Apply_PartGoesToTheNearestLineEvenWhenDrawnLater()
|
|
{
|
|
var drawing = TestHelpers.MakeSquareDrawing();
|
|
var a = new Part(drawing, new Vector(0, 0));
|
|
var b = new Part(drawing, new Vector(20, 0));
|
|
var stray = new Part(drawing, new Vector(40, 30));
|
|
var plate = TestHelpers.MakePlate(60, 120, stray, b, a);
|
|
|
|
// The first line passes 'stray' at a distance; the second runs right through it.
|
|
var lines = Lines(
|
|
new[] { new Vector(5, 5), new Vector(45, 5), new Vector(45, 20) },
|
|
new[] { new Vector(45, 50), new Vector(45, 30) });
|
|
|
|
Assert.True(DrawnPathSequencing.Apply(plate, lines));
|
|
|
|
Assert.Equal(new[] { a, b, stray }, plate.Parts);
|
|
}
|
|
|
|
[Fact]
|
|
public void Apply_CutsPartsNoLineCrossesLast()
|
|
{
|
|
// Four stacked squares; the line starts in the second and runs up through the
|
|
// fourth. The bottom square is nearest the line's start but the line never reaches it.
|
|
var drawing = TestHelpers.MakeSquareDrawing();
|
|
var stack = Enumerable.Range(0, 4).Select(i => new Part(drawing, new Vector(0, i * 15))).ToArray();
|
|
var plate = TestHelpers.MakePlate(60, 120, stack[3], stack[1], stack[0], stack[2]);
|
|
|
|
Assert.True(DrawnPathSequencing.Apply(plate, Lines(new[] { new Vector(5, 20), new Vector(5, 52) })));
|
|
|
|
Assert.Equal(new[] { stack[1], stack[2], stack[3], stack[0] }, plate.Parts);
|
|
}
|
|
|
|
[Fact]
|
|
public void Apply_ClickCutsThePartItLandsIn()
|
|
{
|
|
var drawing = TestHelpers.MakeSquareDrawing();
|
|
var a = new Part(drawing, new Vector(0, 0));
|
|
var b = new Part(drawing, new Vector(20, 0));
|
|
var c = new Part(drawing, new Vector(40, 0));
|
|
var plate = TestHelpers.MakePlate(60, 120, a, b, c);
|
|
|
|
// A line through 'b' and 'a', then a click on 'c'.
|
|
var lines = Lines(
|
|
new[] { new Vector(25, 5), new Vector(5, 5) },
|
|
new[] { new Vector(45, 5) });
|
|
|
|
Assert.True(DrawnPathSequencing.Apply(plate, lines));
|
|
|
|
Assert.Equal(new[] { b, a, c }, plate.Parts);
|
|
}
|
|
|
|
[Fact]
|
|
public void Reaches_OnlyPartsALineCrossesOrAClickLandsIn()
|
|
{
|
|
var drawing = TestHelpers.MakeSquareDrawing();
|
|
var crossed = new Part(drawing, new Vector(0, 0));
|
|
var clicked = new Part(drawing, new Vector(20, 0));
|
|
var missed = new Part(drawing, new Vector(40, 0));
|
|
var lines = Lines(
|
|
new[] { new Vector(5, -5), new Vector(5, 15) },
|
|
new[] { new Vector(25, 5) });
|
|
|
|
Assert.True(DrawnPathSequencing.Reaches(lines, crossed));
|
|
Assert.True(DrawnPathSequencing.Reaches(lines, clicked));
|
|
Assert.False(DrawnPathSequencing.Reaches(lines, missed));
|
|
Assert.False(DrawnPathSequencing.Reaches(Lines(), crossed));
|
|
}
|
|
|
|
[Fact]
|
|
public void Apply_PartPassedTwiceFollowsTheNearerPass()
|
|
{
|
|
var drawing = TestHelpers.MakeSquareDrawing();
|
|
var start = new Part(drawing, new Vector(0, 0));
|
|
var passed = new Part(drawing, new Vector(40, 0));
|
|
var top = new Part(drawing, new Vector(40, 40));
|
|
var plate = TestHelpers.MakePlate(60, 120, passed, top, start);
|
|
|
|
// The path skirts 'passed' on the way up to 'top', then comes back through it.
|
|
var path = new[] { new Vector(5, 5), new Vector(45, 15), new Vector(45, 45), new Vector(45, 5) };
|
|
|
|
Assert.True(DrawnPathSequencing.Apply(plate, Lines(path)));
|
|
Assert.Equal(new[] { start, top, passed }, plate.Parts);
|
|
|
|
// Reversed, the path runs through 'passed' first.
|
|
Assert.True(DrawnPathSequencing.Apply(plate, Lines(path.Reverse().ToArray())));
|
|
Assert.Equal(new[] { passed, top, start }, plate.Parts);
|
|
}
|
|
|
|
[Fact]
|
|
public void Apply_KeepsCutFirstPartsAheadOfTheRest()
|
|
{
|
|
var drawing = TestHelpers.MakeSquareDrawing();
|
|
var coupon = TestHelpers.MakeSquareDrawing();
|
|
coupon.CutFirst = true;
|
|
var a = new Part(drawing, new Vector(0, 0));
|
|
var b = new Part(drawing, new Vector(20, 0));
|
|
var test = new Part(coupon, new Vector(40, 0));
|
|
var plate = TestHelpers.MakePlate(60, 120, a, b, test);
|
|
|
|
Assert.True(DrawnPathSequencing.Apply(plate, Lines(new[] { new Vector(55, 5), new Vector(-5, 5) })));
|
|
|
|
Assert.Equal(new[] { test, b, a }, plate.Parts);
|
|
}
|
|
|
|
[Fact]
|
|
public void Apply_IgnoresLinesWithoutLength()
|
|
{
|
|
var drawing = TestHelpers.MakeSquareDrawing();
|
|
var a = new Part(drawing, new Vector(20, 0));
|
|
var b = new Part(drawing, new Vector(0, 0));
|
|
var plate = TestHelpers.MakePlate(60, 120, a, b);
|
|
var reorders = 0;
|
|
plate.PartsReordered += (_, _) => reorders++;
|
|
|
|
Assert.False(DrawnPathSequencing.Apply(plate, Lines()));
|
|
Assert.False(DrawnPathSequencing.Apply(plate, Lines(System.Array.Empty<Vector>())));
|
|
Assert.False(DrawnPathSequencing.Apply(plate, Lines(new[] { new Vector(3, 3), new Vector(3, 3) })));
|
|
Assert.Equal(new[] { a, b }, plate.Parts);
|
|
Assert.Equal(0, reorders);
|
|
|
|
// A zero-length line drawn first does not count as line one.
|
|
Assert.True(DrawnPathSequencing.Apply(plate, Lines(new[] { new Vector(30, 5), new Vector(30, 5) }, new[] { new Vector(-5, 5), new Vector(35, 5) })));
|
|
Assert.Equal(new[] { b, a }, plate.Parts);
|
|
}
|
|
}
|