Files
PepApi.Core/PepLib.Core/IO/DrawingDxfExporter.cs
T
ajandClaude Sonnet 5 154574721e
Build PepApi image / build-and-push (push) Successful in 45s
feat: separate DXF entities onto per-type layers, recognize SCRIBE marker
DrawLoop previously drew every entity onto the single loop layer, so
scribe/display marks were indistinguishable from cuts in the exported
DXF. Non-cut entity types now get their own layer, and ProgramReader
now parses the SCRIBE marker into EntityType.Scribe so scribed moves
route to their own layer instead of falling through as untyped cuts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 09:49:53 -04:00

159 lines
5.5 KiB
C#

using ACadSharp;
using ACadSharp.IO;
using ACadSharp.Tables;
using CSMath;
using PepLib.Codes;
using PepLib.Enums;
using PepLib.Geometry;
using PepLib.Models;
using AcadArc = ACadSharp.Entities.Arc;
using AcadCircle = ACadSharp.Entities.Circle;
using AcadLine = ACadSharp.Entities.Line;
namespace PepLib.IO;
public static class DrawingDxfExporter
{
public static Stream Export(Drawing drawing)
{
var doc = new CadDocument();
doc.Header.Version = ACadVersion.AC1018;
// Collect loop IDs that are called as subprograms so we skip drawing them as top-level
var subprogramIds = new HashSet<int>();
foreach (var loop in drawing.Loops)
{
foreach (var code in loop)
{
if (code.CodeType() == CodeType.SubProgramCall)
subprogramIds.Add(((SubProgramCall)code).LoopId);
}
}
var layers = new Dictionary<string, Layer>();
foreach (var loop in drawing.Loops)
{
var loopNumber = GetLoopNumber(loop.Name);
if (subprogramIds.Contains(loopNumber))
continue; // drawn inline at SubProgramCall positions
var cutLayer = GetOrCreateLayer(doc, layers, loop.Name);
DrawLoop(doc, drawing, loop, cutLayer, layers, new Vector());
}
var buffer = new MemoryStream();
using (var writer = new DxfWriter(buffer, doc, false))
{
writer.Write();
}
return new MemoryStream(buffer.ToArray());
}
private static Vector DrawLoop(CadDocument doc, Drawing drawing, Loop loop, Layer cutLayer, Dictionary<string, Layer> layers, Vector startPos)
{
var pos = startPos;
foreach (var code in loop)
{
switch (code.CodeType())
{
case CodeType.RapidMove:
pos = Advance(pos, ((RapidMove)code).EndPoint, loop.Mode);
break;
case CodeType.LinearMove:
var lm = (LinearMove)code;
var lineEnd = Advance(pos, lm.EndPoint, loop.Mode);
doc.Entities.Add(new AcadLine
{
StartPoint = ToXYZ(pos),
EndPoint = ToXYZ(lineEnd),
Layer = LayerFor(doc, layers, cutLayer, lm.Type)
});
pos = lineEnd;
break;
case CodeType.CircularMove:
var cm = (CircularMove)code;
var arcEnd = Advance(pos, cm.EndPoint, loop.Mode);
var arcCenter = Advance(pos, cm.CenterPoint, loop.Mode);
var dx = pos.X - arcCenter.X;
var dy = pos.Y - arcCenter.Y;
var radius = Math.Sqrt(dx * dx + dy * dy);
var startAngle = Math.Atan2(pos.Y - arcCenter.Y, pos.X - arcCenter.X);
var endAngle = Math.Atan2(arcEnd.Y - arcCenter.Y, arcEnd.X - arcCenter.X);
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 = arcLayer
});
}
else
{
doc.Entities.Add(new AcadArc
{
Center = ToXYZ(arcCenter),
Radius = radius,
StartAngle = startAngle,
EndAngle = endAngle,
Layer = arcLayer
});
}
pos = arcEnd;
break;
case CodeType.SubProgramCall:
var call = (SubProgramCall)code;
var subLoop = drawing.Loops.FirstOrDefault(l => l.Name == drawing.GetLoopName(call.LoopId));
if (subLoop != null)
DrawLoop(doc, drawing, subLoop, cutLayer, layers, pos);
// pos unchanged — subprograms are closed shapes that return to start
break;
}
}
return pos;
}
private static Layer LayerFor(CadDocument doc, Dictionary<string, Layer> layers, Layer cutLayer, EntityType type) =>
type == EntityType.Cut ? cutLayer : GetOrCreateLayer(doc, layers, type.ToString());
private static Layer GetOrCreateLayer(CadDocument doc, Dictionary<string, Layer> 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);
if (idx < 0) return -1;
return int.TryParse(loopName.Substring(idx + 6), out var n) ? n : -1;
}
private static Vector Advance(Vector current, Vector offset, ProgrammingMode mode) =>
mode == ProgrammingMode.Incremental ? current + offset : offset;
private static XYZ ToXYZ(Vector v) => new XYZ(v.X, v.Y, 0);
}