diff --git a/OpenNest.IO.Tests/DisplayPerimeterPersistenceTests.cs b/OpenNest.IO.Tests/DisplayPerimeterPersistenceTests.cs
new file mode 100644
index 00000000..8c3f6680
--- /dev/null
+++ b/OpenNest.IO.Tests/DisplayPerimeterPersistenceTests.cs
@@ -0,0 +1,55 @@
+using OpenNest.Bending;
+using OpenNest.CNC;
+using OpenNest.Geometry;
+using OpenNest.TabbedCutouts;
+
+namespace OpenNest.IO.Tests;
+
+/// A supplied blank's display perimeter must survive save/reload as never-cut geometry.
+public class DisplayPerimeterPersistenceTests
+{
+ private static Drawing Door()
+ {
+ var outline = new List
+ {
+ new Line(0, 0, 30, 0), new Line(30, 0, 30, 20), new Line(30, 20, 0, 20), new Line(0, 20, 0, 0),
+ };
+ foreach (var line in outline)
+ line.Layer = SpecialLayers.DisplayPerimeter;
+ var entities = new List(outline)
+ {
+ new Line(5, 5, 10, 5), new Line(10, 5, 10, 10), new Line(10, 10, 5, 10), new Line(5, 10, 5, 5),
+ };
+ return CadImporter.BuildDrawing(new CadImportResult { Entities = entities, Name = "door" },
+ entities, new List(), 1, null, null, null, new List());
+ }
+
+ private static int DisplayPerimeterMoves(Program program) =>
+ program.Codes.Count(code => code is LinearMove { Layer: LayerType.DisplayPerimeter } or ArcMove { Layer: LayerType.DisplayPerimeter });
+
+ [Fact]
+ public void DisplayPerimeter_SurvivesSaveAndReloadOnTheDrawingAndItsPlacements()
+ {
+ var drawing = Door();
+ Assert.Equal(4, DisplayPerimeterMoves(drawing.Program));
+ var nest = new Nest("door") { Units = Units.Inches };
+ nest.Drawings.Add(drawing);
+ var plate = new Plate(new Size(20, 30));
+ plate.Parts.Add(new Part(drawing, Vector.Zero));
+ nest.Plates.Add(plate);
+ using var stream = new MemoryStream();
+ Assert.True(new NestWriter(nest).Write(stream));
+
+ var reader = new NestReader(new MemoryStream(stream.ToArray()));
+ var restored = reader.Read();
+
+ Assert.Empty(reader.Warnings);
+ var reread = Assert.Single(restored.Drawings);
+ Assert.Equal(4, DisplayPerimeterMoves(reread.Program));
+ var outline = CadImporter.EditableEntities(reread).Where(e => e.Length > 19.9).ToList();
+ Assert.Equal(4, outline.Count);
+ Assert.All(outline, e => Assert.Same(SpecialLayers.DisplayPerimeter, e.Layer));
+ var part = Assert.Single(restored.Plates.SelectMany(p => p.Parts));
+ Assert.Equal(4, DisplayPerimeterMoves(part.Program));
+ }
+}
diff --git a/OpenNest.IO/EntitySerializer.cs b/OpenNest.IO/EntitySerializer.cs
index 0e1e5ae6..5dd07b4a 100644
--- a/OpenNest.IO/EntitySerializer.cs
+++ b/OpenNest.IO/EntitySerializer.cs
@@ -191,6 +191,8 @@ namespace OpenNest.IO
return SpecialLayers.Break;
if (string.Equals(name, SpecialLayers.Tab.Name, StringComparison.OrdinalIgnoreCase))
return SpecialLayers.Tab;
+ if (string.Equals(name, SpecialLayers.DisplayPerimeter.Name, StringComparison.OrdinalIgnoreCase))
+ return SpecialLayers.DisplayPerimeter;
return new Layer(name);
}
diff --git a/OpenNest.IO/NestWriter.cs b/OpenNest.IO/NestWriter.cs
index f0435b61..9ead35b8 100644
--- a/OpenNest.IO/NestWriter.cs
+++ b/OpenNest.IO/NestWriter.cs
@@ -720,6 +720,9 @@ namespace OpenNest.IO
case LayerType.Tab:
return ":TAB";
+ case LayerType.DisplayPerimeter:
+ return ":DISPLAYPERIMETER";
+
default:
return string.Empty;
}
diff --git a/OpenNest.IO/ProgramReader.cs b/OpenNest.IO/ProgramReader.cs
index 773d4783..d1154705 100644
--- a/OpenNest.IO/ProgramReader.cs
+++ b/OpenNest.IO/ProgramReader.cs
@@ -288,6 +288,10 @@ namespace OpenNest.IO
layer = LayerType.Tab;
break;
+ case "DISPLAYPERIMETER":
+ layer = LayerType.DisplayPerimeter;
+ break;
+
case "SUPPRESSED":
suppressed = true;
break;
@@ -394,6 +398,10 @@ namespace OpenNest.IO
layer = LayerType.Tab;
break;
+ case "DISPLAYPERIMETER":
+ layer = LayerType.DisplayPerimeter;
+ break;
+
case "SUPPRESSED":
suppressed = true;
break;