From e1bcb7498f617a1c5d2f3f2990949581f4362324 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Tue, 24 Mar 2026 20:43:40 -0400 Subject: [PATCH] feat: add SplitDxfWriter for split DXF output with bend data Co-Authored-By: Claude Sonnet 4.6 --- OpenNest.IO/SplitDxfWriter.cs | 177 +++++++++++++++++++++++++++++ OpenNest/Forms/CadConverterForm.cs | 14 ++- 2 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 OpenNest.IO/SplitDxfWriter.cs diff --git a/OpenNest.IO/SplitDxfWriter.cs b/OpenNest.IO/SplitDxfWriter.cs new file mode 100644 index 0000000..6b24ede --- /dev/null +++ b/OpenNest.IO/SplitDxfWriter.cs @@ -0,0 +1,177 @@ +using ACadSharp; +using ACadSharp.Entities; +using ACadSharp.IO; +using ACadSharp.Tables; +using CSMath; +using OpenNest.Bending; +using OpenNest.Converters; +using OpenNest.Geometry; +using System.Collections.Generic; +using System.IO; + +// Disambiguate Entity — both ACadSharp.Entities and OpenNest.Geometry define it +using GeoEntity = OpenNest.Geometry.Entity; + +namespace OpenNest.IO +{ + public class SplitDxfWriter + { + private const double DefaultEtchLength = 1.0; + + public double EtchLength { get; set; } = DefaultEtchLength; + + public void Write(string path, Drawing drawing) + { + var doc = new CadDocument(); + + var cutLayer = new ACadSharp.Tables.Layer("CUT") { Color = new Color(7) }; + var bendLayer = new ACadSharp.Tables.Layer("BEND") { Color = new Color(2) }; + var etchLayer = new ACadSharp.Tables.Layer("ETCH") { Color = new Color(3) }; + doc.Layers.Add(cutLayer); + doc.Layers.Add(bendLayer); + doc.Layers.Add(etchLayer); + + var centerLineType = new LineType("CENTERX2"); + doc.LineTypes.Add(centerLineType); + + WriteProgramEntities(doc, drawing.Program, cutLayer); + + if (drawing.Bends != null) + { + foreach (var bend in drawing.Bends) + { + WriteBendLine(doc, bend, bendLayer, centerLineType); + WriteEtchLines(doc, bend, etchLayer); + } + } + + using var stream = File.Create(path); + using var writer = new DxfWriter(stream, doc, false); + writer.Write(); + } + + private static void WriteProgramEntities(CadDocument doc, CNC.Program program, ACadSharp.Tables.Layer layer) + { + var geometry = ConvertProgram.ToGeometry(program); + WriteGeometryEntities(doc, geometry, layer); + } + + private static void WriteGeometryEntities(CadDocument doc, List geometry, ACadSharp.Tables.Layer layer) + { + foreach (var entity in geometry) + { + // Skip rapid moves + if (entity.Layer == SpecialLayers.Rapid) + continue; + + switch (entity) + { + case OpenNest.Geometry.Line line: + doc.Entities.Add(new ACadSharp.Entities.Line + { + StartPoint = new XYZ(line.StartPoint.X, line.StartPoint.Y, 0), + EndPoint = new XYZ(line.EndPoint.X, line.EndPoint.Y, 0), + Layer = layer + }); + break; + + case OpenNest.Geometry.Arc arc: + var startAngle = arc.StartAngle; + var endAngle = arc.EndAngle; + if (arc.IsReversed) + OpenNest.Math.Generic.Swap(ref startAngle, ref endAngle); + + doc.Entities.Add(new ACadSharp.Entities.Arc + { + Center = new XYZ(arc.Center.X, arc.Center.Y, 0), + Radius = arc.Radius, + StartAngle = startAngle, + EndAngle = endAngle, + Layer = layer + }); + break; + + case OpenNest.Geometry.Circle circle: + doc.Entities.Add(new ACadSharp.Entities.Circle + { + Center = new XYZ(circle.Center.X, circle.Center.Y, 0), + Radius = circle.Radius, + Layer = layer + }); + break; + + case OpenNest.Geometry.Shape shape: + WriteGeometryEntities(doc, shape.Entities, layer); + break; + } + } + } + + private static void WriteBendLine(CadDocument doc, Bend bend, ACadSharp.Tables.Layer layer, LineType lineType) + { + var line = new ACadSharp.Entities.Line + { + StartPoint = new XYZ(bend.StartPoint.X, bend.StartPoint.Y, 0), + EndPoint = new XYZ(bend.EndPoint.X, bend.EndPoint.Y, 0), + Layer = layer, + LineType = lineType + }; + doc.Entities.Add(line); + + if (!string.IsNullOrEmpty(bend.NoteText)) + { + var midX = (bend.StartPoint.X + bend.EndPoint.X) / 2; + var midY = (bend.StartPoint.Y + bend.EndPoint.Y) / 2; + + var mtext = new MText + { + InsertPoint = new XYZ(midX, midY + 0.5, 0), + Value = bend.NoteText, + Height = 0.1, + Layer = layer + }; + doc.Entities.Add(mtext); + } + } + + private void WriteEtchLines(CadDocument doc, Bend bend, ACadSharp.Tables.Layer layer) + { + if (bend.Direction != BendDirection.Up) + return; + + var start = bend.StartPoint; + var end = bend.EndPoint; + var length = bend.Length; + + if (length < EtchLength * 3.0) + { + doc.Entities.Add(new ACadSharp.Entities.Line + { + StartPoint = new XYZ(start.X, start.Y, 0), + EndPoint = new XYZ(end.X, end.Y, 0), + Layer = layer + }); + } + else + { + var angle = start.AngleTo(end); + var dx = System.Math.Cos(angle) * EtchLength; + var dy = System.Math.Sin(angle) * EtchLength; + + doc.Entities.Add(new ACadSharp.Entities.Line + { + StartPoint = new XYZ(start.X, start.Y, 0), + EndPoint = new XYZ(start.X + dx, start.Y + dy, 0), + Layer = layer + }); + + doc.Entities.Add(new ACadSharp.Entities.Line + { + StartPoint = new XYZ(end.X, end.Y, 0), + EndPoint = new XYZ(end.X - dx, end.Y - dy, 0), + Layer = layer + }); + } + } + } +} diff --git a/OpenNest/Forms/CadConverterForm.cs b/OpenNest/Forms/CadConverterForm.cs index 48fc4d0..2841c55 100644 --- a/OpenNest/Forms/CadConverterForm.cs +++ b/OpenNest/Forms/CadConverterForm.cs @@ -249,17 +249,19 @@ namespace OpenNest.Forms var index = fileList.SelectedIndex; var newItems = new List(); + var splitWriter = new SplitDxfWriter(); + for (var i = 0; i < form.ResultDrawings.Count; i++) { + var splitDrawing = form.ResultDrawings[i]; + + // Assign bends from the source item — spatial filtering is a future enhancement + splitDrawing.Bends.AddRange(item.Bends); + var splitName = $"{baseName}_split{i + 1}.dxf"; var splitPath = GetUniquePath(Path.Combine(writableDir, splitName)); - // TODO: Use SplitDxfWriter when implemented - // For now, export via DxfExporter - var exporter = new DxfExporter(); - using var stream = new FileStream(splitPath, FileMode.Create); - exporter.ExportProgram(form.ResultDrawings[i].Program, stream); - + splitWriter.Write(splitPath, splitDrawing); newItems.Add(splitPath); }