diff --git a/OpenNest.Core/PartGeometry.cs b/OpenNest.Core/PartGeometry.cs
index 2289c43b..bb36d5d5 100644
--- a/OpenNest.Core/PartGeometry.cs
+++ b/OpenNest.Core/PartGeometry.cs
@@ -7,12 +7,21 @@ namespace OpenNest
{
public static class PartGeometry
{
+ ///
+ /// Nesting material of a program, without lead-ins and lead-outs: a lead is a cut
+ /// into scrap, not part material, and chaining it into the perimeter leaves an open
+ /// spur, so the offset contour no longer closes and every slide contact against it
+ /// blocks.
+ ///
+ private static List MaterialEntities(CNC.Program program) =>
+ ConvertProgram.ToGeometry(program)
+ .Where(e => e.Layer != SpecialLayers.Leadin && e.Layer != SpecialLayers.Leadout)
+ .ForNesting();
+
public static List GetPartLines(Part part, double chordTolerance = 0.001)
{
- var entities = ConvertProgram.ToGeometry(part.Program);
- var shapes = ShapeBuilder.GetShapes(
- entities.ForNesting()
- );
+ var entities = MaterialEntities(part.Program);
+ var shapes = ShapeBuilder.GetShapes(entities);
var lines = new List();
foreach (var shape in shapes)
@@ -55,10 +64,8 @@ namespace OpenNest
public static List GetOffsetPerimeterEntities(CNC.Program program, double spacing)
{
PerfCounters.CountOffsetPerimeterEntities();
- var geoEntities = ConvertProgram.ToGeometry(program);
- var profile = new ShapeProfile(
- geoEntities.ForNesting().ToList()
- );
+ var geoEntities = MaterialEntities(program);
+ var profile = new ShapeProfile(geoEntities);
var offsetShape = profile.Perimeter.OffsetOutward(spacing);
if (offsetShape == null)
@@ -73,10 +80,8 @@ namespace OpenNest
///
public static List GetOffsetPartEntities(Part part, double spacing)
{
- var geoEntities = ConvertProgram.ToGeometry(part.Program);
- var profile = new ShapeProfile(
- geoEntities.ForNesting().ToList()
- );
+ var geoEntities = MaterialEntities(part.Program);
+ var profile = new ShapeProfile(geoEntities);
var entities = new List();
var perimeter = profile.Perimeter.OffsetOutward(spacing);
@@ -106,10 +111,8 @@ namespace OpenNest
///
public static List GetPerimeterEntities(Part part)
{
- var geoEntities = ConvertProgram.ToGeometry(part.Program);
- var profile = new ShapeProfile(
- geoEntities.ForNesting().ToList()
- );
+ var geoEntities = MaterialEntities(part.Program);
+ var profile = new ShapeProfile(geoEntities);
return CopyEntitiesAtLocation(profile.Perimeter.Entities, part.Location);
}
@@ -120,10 +123,8 @@ namespace OpenNest
///
public static List GetPartEntities(Part part)
{
- var geoEntities = ConvertProgram.ToGeometry(part.Program);
- var profile = new ShapeProfile(
- geoEntities.ForNesting().ToList()
- );
+ var geoEntities = MaterialEntities(part.Program);
+ var profile = new ShapeProfile(geoEntities);
var entities = CopyEntitiesAtLocation(profile.Perimeter.Entities, part.Location);
foreach (var cutout in profile.Cutouts)
@@ -165,10 +166,8 @@ namespace OpenNest
Vector vectorDirection
)
{
- var entities = ConvertProgram.ToGeometry(part.Program);
- var shapes = ShapeBuilder.GetShapes(
- entities.ForNesting()
- );
+ var entities = MaterialEntities(part.Program);
+ var shapes = ShapeBuilder.GetShapes(entities);
var lines = new List();
foreach (var shape in shapes)
diff --git a/OpenNest.Tests/Fill/CompactorTests.cs b/OpenNest.Tests/Fill/CompactorTests.cs
index 398dafef..beb824ee 100644
--- a/OpenNest.Tests/Fill/CompactorTests.cs
+++ b/OpenNest.Tests/Fill/CompactorTests.cs
@@ -312,6 +312,35 @@ namespace OpenNest.Tests.Fill
AssertClearance(moving, obstacle, 2);
}
+ [Theory]
+ [InlineData(PushDirection.Right)]
+ [InlineData(PushDirection.Up)]
+ public void Push_WithSpacing_ContactWithLeadInPartDoesNotBlockOtherDirections(
+ PushDirection next
+ )
+ {
+ // Leads are cut into scrap, not material: chained into the obstacle's perimeter
+ // they left its offset contour open, so every contact with it blocked.
+ var workArea = new Box(0, 0, 100, 100);
+ var obstacle = MakeRectPart(20, 20, 40, 10);
+ obstacle.ApplyLeadIns(new OpenNest.CNC.CuttingStrategy.CuttingParameters
+ {
+ ExternalLeadIn = new OpenNest.CNC.CuttingStrategy.LineLeadIn { Length = 0.25 },
+ ExternalLeadOut = new OpenNest.CNC.CuttingStrategy.LineLeadOut { Length = 0.25 },
+ }, new Vector(0, 0));
+ var moving = MakeRectPart(30, 60, 5, 5);
+ var parts = new List { moving };
+ var obstacles = new List { obstacle };
+
+ Assert.True(Compactor.Push(parts, obstacles, workArea, 2, PushDirection.Down) > 0);
+ Assert.Equal(32, moving.BoundingBox.Bottom, 7);
+
+ var distance = Compactor.Push(parts, obstacles, workArea, 2, next);
+
+ Assert.True(distance > 1, $"Push {next} after contact moved only {distance:R}");
+ AssertClearance(moving, obstacle, 2);
+ }
+
[Fact]
public void Push_WithSpacing_ContactStillBlocksTheSameDirection()
{
diff --git a/OpenNest.Tests/Geometry/PartGeometryDirectionalPreparationTests.cs b/OpenNest.Tests/Geometry/PartGeometryDirectionalPreparationTests.cs
index 679a81b4..5c3e3575 100644
--- a/OpenNest.Tests/Geometry/PartGeometryDirectionalPreparationTests.cs
+++ b/OpenNest.Tests/Geometry/PartGeometryDirectionalPreparationTests.cs
@@ -45,13 +45,13 @@ public class PartGeometryDirectionalPreparationTests
}
[Fact]
- public void MaterialIncludesDisplayAndLeads_ButExcludesRapidAndScribe()
+ public void MaterialIncludesDisplay_ButExcludesLeadsRapidAndScribe()
{
var material = RectangleProgram(false);
AddRectangle(material, 15, 2, 3, 2, LayerType.Display);
+ var clean = MakePart(material);
AddRectangle(material, 21, 3, 2, 4, LayerType.Leadin);
AddRectangle(material, 26, 1, 4, 3, LayerType.Leadout);
- var clean = MakePart(material);
AddRectangle(material, 100, 120, 7, 9, LayerType.Scribe);
material.MoveTo(-200, -300);
material.MoveTo(400, 500);
@@ -59,9 +59,9 @@ public class PartGeometryDirectionalPreparationTests
foreach (var direction in new[] { PushDirection.Left, PushDirection.Right, PushDirection.Up, PushDirection.Down })
{
var expected = FrozenGetPartLines(clean, direction);
- Assert.Equal(4, expected.Count);
+ Assert.Equal(2, expected.Count);
AssertLines(expected, PartGeometry.GetPartLines(marked, direction));
- AssertMatches(marked, direction, new Vector(3, -2), 0.001);
+ AssertMatches(clean, direction, new Vector(3, -2), 0.001);
}
AssertLines(PartGeometry.GetPartLines(clean), PartGeometry.GetPartLines(marked));
}