diff --git a/PepLib.Core.Tests/IO/DrawingDxfExporterTests.cs b/PepLib.Core.Tests/IO/DrawingDxfExporterTests.cs index bb6492c..fdb68f8 100644 --- a/PepLib.Core.Tests/IO/DrawingDxfExporterTests.cs +++ b/PepLib.Core.Tests/IO/DrawingDxfExporterTests.cs @@ -64,4 +64,62 @@ public class DrawingDxfExporterTests Assert.InRange(arc.StartAngle, 0, 360); Assert.InRange(arc.EndAngle, 0, 360); } + + [Fact] + public void Export_NonCutLinearMove_IsDrawnOnDedicatedLayerInsteadOfDropped() + { + var drawing = new Drawing(); + drawing.Info = new DrawingInfo { Name = "TEST" }; + var loop = new Loop { Name = "TEST.loop-000" }; + loop.Add(new RapidMove(new Vector(0, 0))); + loop.Add(new LinearMove(new Vector(10, 0)) { Type = EntityType.InternalLeadin }); + drawing.Loops.Add(loop); + + using var stream = DrawingDxfExporter.Export(drawing); + + stream.Position = 0; + using var reader = new ACadSharp.IO.DxfReader(stream); + var doc = reader.Read(); + var line = doc.Entities.OfType().Single(); + Assert.Equal("InternalLeadin", line.Layer.Name); + } + + [Fact] + public void Export_NonCutCircularMove_IsDrawnOnDedicatedLayerInsteadOfCutLayer() + { + var drawing = new Drawing(); + drawing.Info = new DrawingInfo { Name = "TEST" }; + var loop = new Loop { Name = "TEST.loop-000" }; + loop.Add(new RapidMove(new Vector(5, 0))); + loop.Add(new CircularMove(new Vector(-5, 5), new Vector(-5, 0), RotationType.CCW) { Type = EntityType.ExternalLeadout }); + drawing.Loops.Add(loop); + + using var stream = DrawingDxfExporter.Export(drawing); + + stream.Position = 0; + using var reader = new ACadSharp.IO.DxfReader(stream); + var doc = reader.Read(); + var arc = doc.Entities.OfType().Single(); + Assert.Equal("ExternalLeadout", arc.Layer.Name); + Assert.NotEqual("TEST.loop-000", arc.Layer.Name); + } + + [Fact] + public void Export_CutLinearMove_StaysOnLoopNamedLayer() + { + var drawing = new Drawing(); + drawing.Info = new DrawingInfo { Name = "TEST" }; + var loop = new Loop { Name = "TEST.loop-000" }; + loop.Add(new RapidMove(new Vector(0, 0))); + loop.Add(new LinearMove(new Vector(10, 0))); + drawing.Loops.Add(loop); + + using var stream = DrawingDxfExporter.Export(drawing); + + stream.Position = 0; + using var reader = new ACadSharp.IO.DxfReader(stream); + var doc = reader.Read(); + var line = doc.Entities.OfType().Single(); + Assert.Equal("TEST.loop-000", line.Layer.Name); + } } diff --git a/PepLib.Core.Tests/IO/ProgramReaderTests.cs b/PepLib.Core.Tests/IO/ProgramReaderTests.cs new file mode 100644 index 0000000..5c1806f --- /dev/null +++ b/PepLib.Core.Tests/IO/ProgramReaderTests.cs @@ -0,0 +1,44 @@ +using System.Text; +using PepLib.Codes; +using PepLib.Models; +using Xunit; + +namespace PepLib.Core.Tests.IO; + +public class ProgramReaderTests +{ + // ProgramReader reads fixed 200-byte ASCII records (no newlines), matching + // the raw record format found inside real .pep loop entries. + private const int RecordSize = 200; + + private static Stream RecordStream(params string[] lines) + { + var sb = new StringBuilder(); + foreach (var line in lines) + sb.Append(line.PadRight(RecordSize)); + + return new MemoryStream(Encoding.ASCII.GetBytes(sb.ToString())); + } + + [Fact] + public void Load_LinearMoveWithScribeMarker_SetsTypeToScribe() + { + using var stream = RecordStream("N50G1X1Y0:SCRIBE"); + + var program = Program.Load(stream); + + var move = Assert.IsType(Assert.Single(program)); + Assert.Equal(EntityType.Scribe, move.Type); + } + + [Fact] + public void Load_CircularMoveWithScribeMarker_SetsTypeToScribe() + { + using var stream = RecordStream("N50G2X1Y0I0.5J0:SCRIBE"); + + var program = Program.Load(stream); + + var move = Assert.IsType(Assert.Single(program)); + Assert.Equal(EntityType.Scribe, move.Type); + } +} diff --git a/PepLib.Core/IO/DrawingDxfExporter.cs b/PepLib.Core/IO/DrawingDxfExporter.cs index 461d1c7..dfc6ddf 100644 --- a/PepLib.Core/IO/DrawingDxfExporter.cs +++ b/PepLib.Core/IO/DrawingDxfExporter.cs @@ -30,16 +30,17 @@ public static class DrawingDxfExporter } } + var layers = new Dictionary(); + foreach (var loop in drawing.Loops) { var loopNumber = GetLoopNumber(loop.Name); if (subprogramIds.Contains(loopNumber)) continue; // drawn inline at SubProgramCall positions - var layer = new Layer(loop.Name); - doc.Layers.Add(layer); + var cutLayer = GetOrCreateLayer(doc, layers, loop.Name); - DrawLoop(doc, drawing, loop, layer, new Vector()); + DrawLoop(doc, drawing, loop, cutLayer, layers, new Vector()); } var buffer = new MemoryStream(); @@ -51,7 +52,7 @@ public static class DrawingDxfExporter return new MemoryStream(buffer.ToArray()); } - private static Vector DrawLoop(CadDocument doc, Drawing drawing, Loop loop, Layer layer, Vector startPos) + private static Vector DrawLoop(CadDocument doc, Drawing drawing, Loop loop, Layer cutLayer, Dictionary layers, Vector startPos) { var pos = startPos; @@ -65,21 +66,14 @@ public static class DrawingDxfExporter case CodeType.LinearMove: var lm = (LinearMove)code; - if (lm.Type == EntityType.Cut) + var lineEnd = Advance(pos, lm.EndPoint, loop.Mode); + doc.Entities.Add(new AcadLine { - var lineEnd = Advance(pos, lm.EndPoint, loop.Mode); - doc.Entities.Add(new AcadLine - { - StartPoint = ToXYZ(pos), - EndPoint = ToXYZ(lineEnd), - Layer = layer - }); - pos = lineEnd; - } - else - { - pos = Advance(pos, lm.EndPoint, loop.Mode); - } + StartPoint = ToXYZ(pos), + EndPoint = ToXYZ(lineEnd), + Layer = LayerFor(doc, layers, cutLayer, lm.Type) + }); + pos = lineEnd; break; case CodeType.CircularMove: @@ -97,13 +91,15 @@ public static class DrawingDxfExporter if (cm.Rotation == RotationType.CW) (startAngle, endAngle) = (endAngle, startAngle); + var arcLayer = LayerFor(doc, layers, cutLayer, cm.Type); + if (Math.Abs(startAngle - endAngle) < 1e-10) { doc.Entities.Add(new AcadCircle { Center = ToXYZ(arcCenter), Radius = radius, - Layer = layer + Layer = arcLayer }); } else @@ -114,7 +110,7 @@ public static class DrawingDxfExporter Radius = radius, StartAngle = startAngle, EndAngle = endAngle, - Layer = layer + Layer = arcLayer }); } @@ -125,7 +121,7 @@ public static class DrawingDxfExporter var call = (SubProgramCall)code; var subLoop = drawing.Loops.FirstOrDefault(l => l.Name == drawing.GetLoopName(call.LoopId)); if (subLoop != null) - DrawLoop(doc, drawing, subLoop, layer, pos); + DrawLoop(doc, drawing, subLoop, cutLayer, layers, pos); // pos unchanged — subprograms are closed shapes that return to start break; } @@ -134,6 +130,20 @@ public static class DrawingDxfExporter return pos; } + private static Layer LayerFor(CadDocument doc, Dictionary layers, Layer cutLayer, EntityType type) => + type == EntityType.Cut ? cutLayer : GetOrCreateLayer(doc, layers, type.ToString()); + + private static Layer GetOrCreateLayer(CadDocument doc, Dictionary layers, string name) + { + if (layers.TryGetValue(name, out var existing)) + return existing; + + var layer = new Layer(name); + doc.Layers.Add(layer); + layers[name] = layer; + return layer; + } + private static int GetLoopNumber(string loopName) { var idx = loopName.LastIndexOf(".loop-", StringComparison.OrdinalIgnoreCase); diff --git a/PepLib.Core/IO/ProgramReader.cs b/PepLib.Core/IO/ProgramReader.cs index e9cded1..72823ae 100644 --- a/PepLib.Core/IO/ProgramReader.cs +++ b/PepLib.Core/IO/ProgramReader.cs @@ -189,6 +189,10 @@ namespace PepLib.IO case "DISPLAY": type = EntityType.Display; break; + + case "SCRIBE": + type = EntityType.Scribe; + break; } break; } @@ -266,6 +270,10 @@ namespace PepLib.IO case "DISPLAY": type = EntityType.Display; break; + + case "SCRIBE": + type = EntityType.Scribe; + break; } break; }