refactor: extract OpenNest.IO class library from WinForms project
Move DxfImporter, DxfExporter, NestReader, NestWriter, ProgramReader, and Extensions into a new OpenNest.IO class library. The WinForms project now references OpenNest.IO instead of ACadSharp directly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,300 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using ACadSharp;
|
||||
using ACadSharp.Entities;
|
||||
using ACadSharp.IO;
|
||||
using ACadSharp.Tables;
|
||||
using CSMath;
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Math;
|
||||
|
||||
namespace OpenNest.IO
|
||||
{
|
||||
using AcadArc = ACadSharp.Entities.Arc;
|
||||
using AcadCircle = ACadSharp.Entities.Circle;
|
||||
using AcadLine = ACadSharp.Entities.Line;
|
||||
using Layer = ACadSharp.Tables.Layer;
|
||||
|
||||
public class DxfExporter
|
||||
{
|
||||
private CadDocument doc;
|
||||
private XYZ curpos;
|
||||
private Mode mode;
|
||||
private readonly Layer cutLayer;
|
||||
private readonly Layer rapidLayer;
|
||||
private readonly Layer plateLayer;
|
||||
|
||||
public DxfExporter()
|
||||
{
|
||||
doc = new CadDocument();
|
||||
|
||||
cutLayer = new Layer("Cut");
|
||||
cutLayer.Color = new Color(1);
|
||||
|
||||
rapidLayer = new Layer("Rapid");
|
||||
rapidLayer.Color = new Color(5);
|
||||
|
||||
plateLayer = new Layer("Plate");
|
||||
plateLayer.Color = new Color(4);
|
||||
}
|
||||
|
||||
public void ExportProgram(Program program, Stream stream)
|
||||
{
|
||||
doc = new CadDocument();
|
||||
EnsureLayers();
|
||||
AddProgram(program);
|
||||
using (var writer = new DxfWriter(stream, doc, false))
|
||||
{
|
||||
writer.Write();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ExportProgram(Program program, string path)
|
||||
{
|
||||
Stream stream = null;
|
||||
var success = false;
|
||||
|
||||
try
|
||||
{
|
||||
stream = File.Create(path);
|
||||
ExportProgram(program, stream);
|
||||
success = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Debug.Fail("DxfExporter.ExportProgram failed to write program to file: " + path);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (stream != null)
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void ExportPlate(Plate plate, Stream stream)
|
||||
{
|
||||
doc = new CadDocument();
|
||||
EnsureLayers();
|
||||
AddPlateOutline(plate);
|
||||
|
||||
foreach (var part in plate.Parts)
|
||||
{
|
||||
var endpt = part.Location.ToAcadXYZ();
|
||||
AddLine(curpos, endpt, rapidLayer);
|
||||
curpos = part.Location.ToAcadXYZ();
|
||||
AddProgram(part.Program);
|
||||
}
|
||||
|
||||
using (var writer = new DxfWriter(stream, doc, false))
|
||||
{
|
||||
writer.Write();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ExportPlate(Plate plate, string path)
|
||||
{
|
||||
Stream stream = null;
|
||||
var success = false;
|
||||
|
||||
try
|
||||
{
|
||||
stream = File.Create(path);
|
||||
ExportPlate(plate, stream);
|
||||
success = true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
Debug.Fail("DxfExporter.ExportPlate failed to write plate to file: " + path);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (stream != null)
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private void EnsureLayers()
|
||||
{
|
||||
doc.Layers.Add(cutLayer);
|
||||
doc.Layers.Add(rapidLayer);
|
||||
doc.Layers.Add(plateLayer);
|
||||
}
|
||||
|
||||
private void AddLine(XYZ start, XYZ end, Layer layer)
|
||||
{
|
||||
var ln = new AcadLine();
|
||||
ln.StartPoint = start;
|
||||
ln.EndPoint = end;
|
||||
ln.Layer = layer;
|
||||
doc.Entities.Add(ln);
|
||||
}
|
||||
|
||||
private void AddPlateOutline(Plate plate)
|
||||
{
|
||||
XYZ pt1;
|
||||
XYZ pt2;
|
||||
XYZ pt3;
|
||||
XYZ pt4;
|
||||
|
||||
switch (plate.Quadrant)
|
||||
{
|
||||
case 1:
|
||||
pt1 = new XYZ(0, 0, 0);
|
||||
pt2 = new XYZ(0, plate.Size.Height, 0);
|
||||
pt3 = new XYZ(plate.Size.Width, plate.Size.Height, 0);
|
||||
pt4 = new XYZ(plate.Size.Width, 0, 0);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
pt1 = new XYZ(0, 0, 0);
|
||||
pt2 = new XYZ(0, plate.Size.Height, 0);
|
||||
pt3 = new XYZ(-plate.Size.Width, plate.Size.Height, 0);
|
||||
pt4 = new XYZ(-plate.Size.Width, 0, 0);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
pt1 = new XYZ(0, 0, 0);
|
||||
pt2 = new XYZ(0, -plate.Size.Height, 0);
|
||||
pt3 = new XYZ(-plate.Size.Width, -plate.Size.Height, 0);
|
||||
pt4 = new XYZ(-plate.Size.Width, 0, 0);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
pt1 = new XYZ(0, 0, 0);
|
||||
pt2 = new XYZ(0, -plate.Size.Height, 0);
|
||||
pt3 = new XYZ(plate.Size.Width, -plate.Size.Height, 0);
|
||||
pt4 = new XYZ(plate.Size.Width, 0, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
AddLine(pt1, pt2, plateLayer);
|
||||
AddLine(pt2, pt3, plateLayer);
|
||||
AddLine(pt3, pt4, plateLayer);
|
||||
AddLine(pt4, pt1, plateLayer);
|
||||
|
||||
var m1 = new XYZ(pt1.X + plate.EdgeSpacing.Left, pt1.Y + plate.EdgeSpacing.Bottom, 0);
|
||||
var m2 = new XYZ(m1.X, pt2.Y - plate.EdgeSpacing.Top, 0);
|
||||
var m3 = new XYZ(pt3.X - plate.EdgeSpacing.Right, m2.Y, 0);
|
||||
var m4 = new XYZ(m3.X, m1.Y, 0);
|
||||
|
||||
AddLine(m1, m2, plateLayer);
|
||||
AddLine(m2, m3, plateLayer);
|
||||
AddLine(m3, m4, plateLayer);
|
||||
AddLine(m4, m1, plateLayer);
|
||||
}
|
||||
|
||||
private void AddProgram(Program program)
|
||||
{
|
||||
mode = program.Mode;
|
||||
|
||||
for (var i = 0; i < program.Length; ++i)
|
||||
{
|
||||
var code = program[i];
|
||||
|
||||
switch (code.Type)
|
||||
{
|
||||
case CodeType.ArcMove:
|
||||
var arc = (ArcMove)code;
|
||||
AddArcMove(arc);
|
||||
break;
|
||||
|
||||
case CodeType.LinearMove:
|
||||
var line = (LinearMove)code;
|
||||
AddLinearMove(line);
|
||||
break;
|
||||
|
||||
case CodeType.RapidMove:
|
||||
var rapid = (RapidMove)code;
|
||||
AddRapidMove(rapid);
|
||||
break;
|
||||
|
||||
case CodeType.SubProgramCall:
|
||||
var tmpmode = mode;
|
||||
var subpgm = (CNC.SubProgramCall)code;
|
||||
AddProgram(subpgm.Program);
|
||||
mode = tmpmode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddLinearMove(LinearMove line)
|
||||
{
|
||||
var pt = line.EndPoint.ToAcadXYZ();
|
||||
|
||||
if (mode == Mode.Incremental)
|
||||
pt = new XYZ(pt.X + curpos.X, pt.Y + curpos.Y, 0);
|
||||
|
||||
AddLine(curpos, pt, cutLayer);
|
||||
curpos = pt;
|
||||
}
|
||||
|
||||
private void AddRapidMove(RapidMove rapid)
|
||||
{
|
||||
var pt = rapid.EndPoint.ToAcadXYZ();
|
||||
|
||||
if (mode == Mode.Incremental)
|
||||
pt = new XYZ(pt.X + curpos.X, pt.Y + curpos.Y, 0);
|
||||
|
||||
AddLine(curpos, pt, rapidLayer);
|
||||
curpos = pt;
|
||||
}
|
||||
|
||||
private void AddArcMove(ArcMove arc)
|
||||
{
|
||||
var center = arc.CenterPoint.ToAcadXYZ();
|
||||
var endpt = arc.EndPoint.ToAcadXYZ();
|
||||
|
||||
if (mode == Mode.Incremental)
|
||||
{
|
||||
endpt = new XYZ(endpt.X + curpos.X, endpt.Y + curpos.Y, 0);
|
||||
center = new XYZ(center.X + curpos.X, center.Y + curpos.Y, 0);
|
||||
}
|
||||
|
||||
var startAngle = System.Math.Atan2(
|
||||
curpos.Y - center.Y,
|
||||
curpos.X - center.X);
|
||||
|
||||
var endAngle = System.Math.Atan2(
|
||||
endpt.Y - center.Y,
|
||||
endpt.X - center.X);
|
||||
|
||||
if (arc.Rotation == OpenNest.RotationType.CW)
|
||||
Generic.Swap(ref startAngle, ref endAngle);
|
||||
|
||||
var dx = endpt.X - center.X;
|
||||
var dy = endpt.Y - center.Y;
|
||||
|
||||
var radius = System.Math.Sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (startAngle.IsEqualTo(endAngle))
|
||||
{
|
||||
var circle = new AcadCircle();
|
||||
circle.Center = center;
|
||||
circle.Radius = radius;
|
||||
circle.Layer = cutLayer;
|
||||
doc.Entities.Add(circle);
|
||||
}
|
||||
else
|
||||
{
|
||||
var arc2 = new AcadArc();
|
||||
arc2.Center = center;
|
||||
arc2.Radius = radius;
|
||||
arc2.StartAngle = startAngle;
|
||||
arc2.EndAngle = endAngle;
|
||||
arc2.Layer = cutLayer;
|
||||
doc.Entities.Add(arc2);
|
||||
}
|
||||
|
||||
curpos = endpt;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using ACadSharp;
|
||||
using ACadSharp.IO;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.IO
|
||||
{
|
||||
public class DxfImporter
|
||||
{
|
||||
public int SplinePrecision { get; set; }
|
||||
|
||||
public DxfImporter()
|
||||
{
|
||||
}
|
||||
|
||||
private List<Entity> GetGeometry(CadDocument doc)
|
||||
{
|
||||
var entities = new List<Entity>();
|
||||
var lines = new List<Line>();
|
||||
var arcs = new List<Arc>();
|
||||
|
||||
foreach (var entity in doc.Entities)
|
||||
{
|
||||
switch (entity)
|
||||
{
|
||||
case ACadSharp.Entities.Line line:
|
||||
lines.Add(line.ToOpenNest());
|
||||
break;
|
||||
|
||||
case ACadSharp.Entities.Arc arc:
|
||||
arcs.Add(arc.ToOpenNest());
|
||||
break;
|
||||
|
||||
case ACadSharp.Entities.Circle circle:
|
||||
entities.Add(circle.ToOpenNest());
|
||||
break;
|
||||
|
||||
case ACadSharp.Entities.Spline spline:
|
||||
lines.AddRange(spline.ToOpenNest());
|
||||
break;
|
||||
|
||||
case ACadSharp.Entities.LwPolyline lwPolyline:
|
||||
lines.AddRange(lwPolyline.ToOpenNest());
|
||||
break;
|
||||
|
||||
case ACadSharp.Entities.Polyline polyline:
|
||||
lines.AddRange(polyline.ToOpenNest());
|
||||
break;
|
||||
|
||||
case ACadSharp.Entities.Ellipse ellipse:
|
||||
lines.AddRange(ellipse.ToOpenNest(SplinePrecision));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Helper.Optimize(lines);
|
||||
Helper.Optimize(arcs);
|
||||
|
||||
entities.AddRange(lines);
|
||||
entities.AddRange(arcs);
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
public bool GetGeometry(Stream stream, out List<Entity> geometry)
|
||||
{
|
||||
var success = false;
|
||||
|
||||
try
|
||||
{
|
||||
using (var reader = new DxfReader(stream))
|
||||
{
|
||||
var doc = reader.Read();
|
||||
geometry = GetGeometry(doc);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
geometry = new List<Entity>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (stream != null)
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public bool GetGeometry(string path, out List<Entity> geometry)
|
||||
{
|
||||
var success = false;
|
||||
|
||||
try
|
||||
{
|
||||
using (var reader = new DxfReader(path))
|
||||
{
|
||||
var doc = reader.Read();
|
||||
geometry = GetGeometry(doc);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex.Message);
|
||||
geometry = new List<Entity>();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,276 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using ACadSharp.Entities;
|
||||
using CSMath;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.IO
|
||||
{
|
||||
internal static class Extensions
|
||||
{
|
||||
public static Vector ToOpenNest(this XY v)
|
||||
{
|
||||
return new Vector(v.X, v.Y);
|
||||
}
|
||||
|
||||
public static Vector ToOpenNest(this XYZ v)
|
||||
{
|
||||
return new Vector(v.X, v.Y);
|
||||
}
|
||||
|
||||
public static Geometry.Arc ToOpenNest(this ACadSharp.Entities.Arc arc)
|
||||
{
|
||||
var result = new Geometry.Arc(
|
||||
arc.Center.X, arc.Center.Y, arc.Radius,
|
||||
arc.StartAngle,
|
||||
arc.EndAngle)
|
||||
{
|
||||
Layer = arc.Layer.ToOpenNest()
|
||||
};
|
||||
result.ApplyDxfProperties(arc);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Geometry.Circle ToOpenNest(this ACadSharp.Entities.Circle circle)
|
||||
{
|
||||
var result = new Geometry.Circle(
|
||||
circle.Center.X, circle.Center.Y,
|
||||
circle.Radius)
|
||||
{
|
||||
Layer = circle.Layer.ToOpenNest()
|
||||
};
|
||||
result.ApplyDxfProperties(circle);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Geometry.Line ToOpenNest(this ACadSharp.Entities.Line line)
|
||||
{
|
||||
var result = new Geometry.Line(
|
||||
line.StartPoint.X, line.StartPoint.Y,
|
||||
line.EndPoint.X, line.EndPoint.Y)
|
||||
{
|
||||
Layer = line.Layer.ToOpenNest()
|
||||
};
|
||||
result.ApplyDxfProperties(line);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<Geometry.Line> ToOpenNest(this Spline spline)
|
||||
{
|
||||
var lines = new List<Geometry.Line>();
|
||||
var pts = spline.ControlPoints;
|
||||
|
||||
if (pts.Count == 0)
|
||||
return lines;
|
||||
|
||||
var layer = spline.Layer.ToOpenNest();
|
||||
var color = spline.ResolveColor();
|
||||
var lineTypeName = spline.ResolveLineTypeName();
|
||||
var lastPoint = pts[0].ToOpenNest();
|
||||
|
||||
for (var i = 1; i < pts.Count; i++)
|
||||
{
|
||||
var nextPoint = pts[i].ToOpenNest();
|
||||
|
||||
lines.Add(new Geometry.Line(lastPoint, nextPoint)
|
||||
{
|
||||
Layer = layer, Color = color, LineTypeName = lineTypeName
|
||||
});
|
||||
|
||||
lastPoint = nextPoint;
|
||||
}
|
||||
|
||||
if (spline.IsClosed)
|
||||
lines.Add(new Geometry.Line(lastPoint, pts[0].ToOpenNest())
|
||||
{
|
||||
Layer = layer, Color = color, LineTypeName = lineTypeName
|
||||
});
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
public static List<Geometry.Line> ToOpenNest(this Polyline polyline)
|
||||
{
|
||||
var lines = new List<Geometry.Line>();
|
||||
|
||||
if (polyline.Vertices.Count == 0)
|
||||
return lines;
|
||||
|
||||
var layer = polyline.Layer.ToOpenNest();
|
||||
var color = polyline.ResolveColor();
|
||||
var lineTypeName = polyline.ResolveLineTypeName();
|
||||
var lastPoint = polyline.Vertices[0].Location.ToOpenNest();
|
||||
|
||||
for (var i = 1; i < polyline.Vertices.Count; i++)
|
||||
{
|
||||
var nextPoint = polyline.Vertices[i].Location.ToOpenNest();
|
||||
|
||||
lines.Add(new Geometry.Line(lastPoint, nextPoint)
|
||||
{
|
||||
Layer = layer, Color = color, LineTypeName = lineTypeName
|
||||
});
|
||||
|
||||
lastPoint = nextPoint;
|
||||
}
|
||||
|
||||
var isClosed = (polyline.Flags & PolylineFlags.ClosedPolylineOrClosedPolygonMeshInM) != 0;
|
||||
|
||||
if (isClosed)
|
||||
lines.Add(new Geometry.Line(lastPoint, polyline.Vertices[0].Location.ToOpenNest())
|
||||
{
|
||||
Layer = layer, Color = color, LineTypeName = lineTypeName
|
||||
});
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
public static List<Geometry.Line> ToOpenNest(this LwPolyline polyline)
|
||||
{
|
||||
var lines = new List<Geometry.Line>();
|
||||
|
||||
if (polyline.Vertices.Count == 0)
|
||||
return lines;
|
||||
|
||||
var layer = polyline.Layer.ToOpenNest();
|
||||
var color = polyline.ResolveColor();
|
||||
var lineTypeName = polyline.ResolveLineTypeName();
|
||||
var lastPoint = polyline.Vertices[0].ToOpenNest();
|
||||
|
||||
for (var i = 1; i < polyline.Vertices.Count; i++)
|
||||
{
|
||||
var nextPoint = polyline.Vertices[i].ToOpenNest();
|
||||
|
||||
lines.Add(new Geometry.Line(lastPoint, nextPoint)
|
||||
{
|
||||
Layer = layer, Color = color, LineTypeName = lineTypeName
|
||||
});
|
||||
|
||||
lastPoint = nextPoint;
|
||||
}
|
||||
|
||||
var isClosed = (polyline.Flags & LwPolylineFlags.Closed) != 0;
|
||||
|
||||
if (isClosed)
|
||||
lines.Add(new Geometry.Line(lastPoint, polyline.Vertices[0].ToOpenNest())
|
||||
{
|
||||
Layer = layer, Color = color, LineTypeName = lineTypeName
|
||||
});
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
public static List<Geometry.Line> ToOpenNest(this ACadSharp.Entities.Ellipse ellipse, int precision = 200)
|
||||
{
|
||||
var lines = new List<Geometry.Line>();
|
||||
|
||||
var center = new Vector(ellipse.Center.X, ellipse.Center.Y);
|
||||
var majorAxis = new Vector(ellipse.MajorAxisEndPoint.X, ellipse.MajorAxisEndPoint.Y);
|
||||
var majorLength = System.Math.Sqrt(majorAxis.X * majorAxis.X + majorAxis.Y * majorAxis.Y);
|
||||
var minorLength = majorLength * ellipse.RadiusRatio;
|
||||
var rotation = System.Math.Atan2(majorAxis.Y, majorAxis.X);
|
||||
|
||||
var startParam = ellipse.StartParameter;
|
||||
var endParam = ellipse.EndParameter;
|
||||
|
||||
if (endParam <= startParam)
|
||||
endParam += System.Math.PI * 2.0;
|
||||
|
||||
var step = (endParam - startParam) / precision;
|
||||
|
||||
var points = new List<Vector>();
|
||||
|
||||
for (var i = 0; i <= precision; i++)
|
||||
{
|
||||
var t = startParam + step * i;
|
||||
var x = majorLength * System.Math.Cos(t);
|
||||
var y = minorLength * System.Math.Sin(t);
|
||||
|
||||
// Rotate by the major axis angle and translate to center
|
||||
var cos = System.Math.Cos(rotation);
|
||||
var sin = System.Math.Sin(rotation);
|
||||
var px = center.X + x * cos - y * sin;
|
||||
var py = center.Y + x * sin + y * cos;
|
||||
|
||||
points.Add(new Vector(px, py));
|
||||
}
|
||||
|
||||
var layer = ellipse.Layer.ToOpenNest();
|
||||
var color = ellipse.ResolveColor();
|
||||
var lineTypeName = ellipse.ResolveLineTypeName();
|
||||
|
||||
for (var i = 0; i < points.Count - 1; i++)
|
||||
{
|
||||
lines.Add(new Geometry.Line(points[i], points[i + 1])
|
||||
{
|
||||
Layer = layer, Color = color, LineTypeName = lineTypeName
|
||||
});
|
||||
}
|
||||
|
||||
// Close the ellipse if it's a full ellipse
|
||||
if (lines.Count >= 2)
|
||||
{
|
||||
var first = lines.First();
|
||||
var last = lines.Last();
|
||||
lines.Add(new Geometry.Line(last.EndPoint, first.StartPoint)
|
||||
{
|
||||
Layer = layer, Color = color, LineTypeName = lineTypeName
|
||||
});
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
public static Geometry.Layer ToOpenNest(this ACadSharp.Tables.Layer layer)
|
||||
{
|
||||
return new Geometry.Layer(layer.Name)
|
||||
{
|
||||
Color = Color.FromArgb(layer.Color.R, layer.Color.G, layer.Color.B),
|
||||
IsVisible = layer.IsOn,
|
||||
LineTypeName = layer.LineType?.Name
|
||||
};
|
||||
}
|
||||
|
||||
public static Color ResolveColor(this ACadSharp.Entities.Entity entity)
|
||||
{
|
||||
var color = entity.Color;
|
||||
|
||||
if (color.IsByLayer)
|
||||
color = entity.Layer.Color;
|
||||
|
||||
return Color.FromArgb(color.R, color.G, color.B);
|
||||
}
|
||||
|
||||
public static string ResolveLineTypeName(this ACadSharp.Entities.Entity entity)
|
||||
{
|
||||
var lt = entity.LineType;
|
||||
|
||||
if (lt == null || string.Equals(lt.Name, "ByLayer", System.StringComparison.OrdinalIgnoreCase))
|
||||
return entity.Layer.LineType?.Name ?? "Continuous";
|
||||
|
||||
return lt.Name;
|
||||
}
|
||||
|
||||
public static void ApplyDxfProperties(this Geometry.Entity target, ACadSharp.Entities.Entity source)
|
||||
{
|
||||
target.Color = source.ResolveColor();
|
||||
target.LineTypeName = source.ResolveLineTypeName();
|
||||
}
|
||||
|
||||
public static Vector ToOpenNest(this LwPolyline.Vertex v)
|
||||
{
|
||||
return new Vector(v.Location.X, v.Location.Y);
|
||||
}
|
||||
|
||||
public static XY ToAcad(this Vector v)
|
||||
{
|
||||
return new XY(v.X, v.Y);
|
||||
}
|
||||
|
||||
public static XYZ ToAcadXYZ(this Vector v)
|
||||
{
|
||||
return new XYZ(v.X, v.Y, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,473 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Geometry;
|
||||
using OpenNest.Math;
|
||||
|
||||
namespace OpenNest.IO
|
||||
{
|
||||
public sealed class NestReader
|
||||
{
|
||||
private ZipArchive zipArchive;
|
||||
private Dictionary<int, Plate> plateDict;
|
||||
private Dictionary<int, Drawing> drawingDict;
|
||||
private Dictionary<int, Program> programDict;
|
||||
private Dictionary<int, Program> plateProgramDict;
|
||||
private Stream stream;
|
||||
private Nest nest;
|
||||
|
||||
private NestReader()
|
||||
{
|
||||
plateDict = new Dictionary<int, Plate>();
|
||||
drawingDict = new Dictionary<int, Drawing>();
|
||||
programDict = new Dictionary<int, Program>();
|
||||
plateProgramDict = new Dictionary<int, Program>();
|
||||
nest = new Nest();
|
||||
}
|
||||
|
||||
public NestReader(string file)
|
||||
: this()
|
||||
{
|
||||
stream = new FileStream(file, FileMode.Open, FileAccess.Read);
|
||||
zipArchive = new ZipArchive(stream, ZipArchiveMode.Read);
|
||||
}
|
||||
|
||||
public NestReader(Stream stream)
|
||||
: this()
|
||||
{
|
||||
this.stream = stream;
|
||||
zipArchive = new ZipArchive(stream, ZipArchiveMode.Read);
|
||||
}
|
||||
|
||||
public Nest Read()
|
||||
{
|
||||
const string plateExtensionPattern = "plate-\\d\\d\\d";
|
||||
const string programExtensionPattern = "program-\\d\\d\\d";
|
||||
|
||||
foreach (var entry in zipArchive.Entries)
|
||||
{
|
||||
var memstream = new MemoryStream();
|
||||
using (var entryStream = entry.Open())
|
||||
{
|
||||
entryStream.CopyTo(memstream);
|
||||
}
|
||||
|
||||
memstream.Position = 0;
|
||||
|
||||
switch (entry.FullName)
|
||||
{
|
||||
case "info":
|
||||
ReadNestInfo(memstream);
|
||||
continue;
|
||||
|
||||
case "drawing-info":
|
||||
ReadDrawingInfo(memstream);
|
||||
continue;
|
||||
|
||||
case "plate-info":
|
||||
ReadPlateInfo(memstream);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Regex.IsMatch(entry.FullName, programExtensionPattern))
|
||||
{
|
||||
ReadProgram(memstream, entry.FullName);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Regex.IsMatch(entry.FullName, plateExtensionPattern))
|
||||
{
|
||||
ReadPlate(memstream, entry.FullName);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
LinkProgramsToDrawings();
|
||||
LinkPartsToPlates();
|
||||
|
||||
AddPlatesToNest();
|
||||
AddDrawingsToNest();
|
||||
|
||||
zipArchive.Dispose();
|
||||
stream.Close();
|
||||
|
||||
return nest;
|
||||
}
|
||||
|
||||
private void ReadNestInfo(Stream stream)
|
||||
{
|
||||
var reader = XmlReader.Create(stream);
|
||||
var spacing = new Spacing();
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (!reader.IsStartElement())
|
||||
continue;
|
||||
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "Nest":
|
||||
nest.Name = reader["name"];
|
||||
break;
|
||||
|
||||
case "Units":
|
||||
Units units;
|
||||
TryParseEnum<Units>(reader.ReadString(), out units);
|
||||
nest.Units = units;
|
||||
break;
|
||||
|
||||
case "Customer":
|
||||
nest.Customer = reader.ReadString();
|
||||
break;
|
||||
|
||||
case "DateCreated":
|
||||
nest.DateCreated = DateTime.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "DateLastModified":
|
||||
nest.DateLastModified = DateTime.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Notes":
|
||||
nest.Notes = Uri.UnescapeDataString(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Size":
|
||||
nest.PlateDefaults.Size = OpenNest.Geometry.Size.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Thickness":
|
||||
nest.PlateDefaults.Thickness = double.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Quadrant":
|
||||
nest.PlateDefaults.Quadrant = int.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "PartSpacing":
|
||||
nest.PlateDefaults.PartSpacing = double.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Name":
|
||||
nest.PlateDefaults.Material.Name = reader.ReadString();
|
||||
break;
|
||||
|
||||
case "Grade":
|
||||
nest.PlateDefaults.Material.Grade = reader.ReadString();
|
||||
break;
|
||||
|
||||
case "Density":
|
||||
nest.PlateDefaults.Material.Density = double.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Left":
|
||||
spacing.Left = double.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Right":
|
||||
spacing.Right = double.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Top":
|
||||
spacing.Top = double.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Bottom":
|
||||
spacing.Bottom = double.Parse(reader.ReadString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reader.Close();
|
||||
nest.PlateDefaults.EdgeSpacing = spacing;
|
||||
}
|
||||
|
||||
private void ReadDrawingInfo(Stream stream)
|
||||
{
|
||||
var reader = XmlReader.Create(stream);
|
||||
Drawing drawing = null;
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (!reader.IsStartElement())
|
||||
continue;
|
||||
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "Drawing":
|
||||
var id = int.Parse(reader["id"]);
|
||||
var name = reader["name"];
|
||||
|
||||
drawingDict.Add(id, (drawing = new Drawing(name)));
|
||||
break;
|
||||
|
||||
case "Customer":
|
||||
drawing.Customer = reader.ReadString();
|
||||
break;
|
||||
|
||||
case "Color":
|
||||
{
|
||||
var parts = reader.ReadString().Split(',');
|
||||
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
byte r = byte.Parse(parts[0]);
|
||||
byte g = byte.Parse(parts[1]);
|
||||
byte b = byte.Parse(parts[2]);
|
||||
|
||||
drawing.Color = Color.FromArgb(r, g, b);
|
||||
}
|
||||
else if (parts.Length == 4)
|
||||
{
|
||||
byte a = byte.Parse(parts[0]);
|
||||
byte r = byte.Parse(parts[1]);
|
||||
byte g = byte.Parse(parts[2]);
|
||||
byte b = byte.Parse(parts[3]);
|
||||
|
||||
drawing.Color = Color.FromArgb(a, r, g, b);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "Required":
|
||||
drawing.Quantity.Required = int.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Name":
|
||||
drawing.Material.Name = reader.ReadString();
|
||||
break;
|
||||
|
||||
case "Grade":
|
||||
drawing.Material.Grade = reader.ReadString();
|
||||
break;
|
||||
|
||||
case "Density":
|
||||
drawing.Material.Density = double.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Path":
|
||||
drawing.Source.Path = reader.ReadString();
|
||||
break;
|
||||
|
||||
case "Offset":
|
||||
{
|
||||
var parts = reader.ReadString().Split(',');
|
||||
|
||||
if (parts.Length != 2)
|
||||
continue;
|
||||
|
||||
drawing.Source.Offset = new Vector(double.Parse(parts[0]), double.Parse(parts[1]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
private void ReadPlateInfo(Stream stream)
|
||||
{
|
||||
var reader = XmlReader.Create(stream);
|
||||
var spacing = new Spacing();
|
||||
Plate plate = null;
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (!reader.IsStartElement())
|
||||
continue;
|
||||
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "Plate":
|
||||
var id = int.Parse(reader["id"]);
|
||||
|
||||
if (plate != null)
|
||||
plate.EdgeSpacing = spacing;
|
||||
|
||||
plateDict.Add(id, (plate = new Plate()));
|
||||
break;
|
||||
|
||||
case "Size":
|
||||
plate.Size = OpenNest.Geometry.Size.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Qty":
|
||||
plate.Quantity = int.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Thickness":
|
||||
plate.Thickness = double.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Quadrant":
|
||||
plate.Quadrant = int.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "PartSpacing":
|
||||
plate.PartSpacing = double.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Name":
|
||||
plate.Material.Name = reader.ReadString();
|
||||
break;
|
||||
|
||||
case "Grade":
|
||||
plate.Material.Grade = reader.ReadString();
|
||||
break;
|
||||
|
||||
case "Density":
|
||||
plate.Material.Density = double.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Left":
|
||||
spacing.Left = double.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Right":
|
||||
spacing.Right = double.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Top":
|
||||
spacing.Top = double.Parse(reader.ReadString());
|
||||
break;
|
||||
|
||||
case "Bottom":
|
||||
spacing.Bottom = double.Parse(reader.ReadString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (plate != null)
|
||||
plate.EdgeSpacing = spacing;
|
||||
}
|
||||
|
||||
private void ReadProgram(Stream stream, string name)
|
||||
{
|
||||
var id = GetProgramId(name);
|
||||
var reader = new ProgramReader(stream);
|
||||
var pgm = reader.Read();
|
||||
programDict.Add(id, pgm);
|
||||
}
|
||||
|
||||
private void ReadPlate(Stream stream, string name)
|
||||
{
|
||||
var id = GetPlateId(name);
|
||||
var reader = new ProgramReader(stream);
|
||||
var pgm = reader.Read();
|
||||
plateProgramDict.Add(id, pgm);
|
||||
}
|
||||
|
||||
private void LinkProgramsToDrawings()
|
||||
{
|
||||
foreach (var drawingItem in drawingDict)
|
||||
{
|
||||
Program pgm;
|
||||
|
||||
if (programDict.TryGetValue(drawingItem.Key, out pgm))
|
||||
drawingItem.Value.Program = pgm;
|
||||
}
|
||||
}
|
||||
|
||||
private void LinkPartsToPlates()
|
||||
{
|
||||
foreach (var plateProgram in plateProgramDict)
|
||||
{
|
||||
var parts = CreateParts(plateProgram.Value);
|
||||
|
||||
Plate plate;
|
||||
|
||||
if (!plateDict.TryGetValue(plateProgram.Key, out plate))
|
||||
plate = new Plate();
|
||||
|
||||
plate.Parts.AddRange(parts);
|
||||
plateDict[plateProgram.Key] = plate;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddPlatesToNest()
|
||||
{
|
||||
var plates = plateDict.OrderBy(i => i.Key).Select(i => i.Value).ToList();
|
||||
nest.Plates.AddRange(plates);
|
||||
}
|
||||
|
||||
private void AddDrawingsToNest()
|
||||
{
|
||||
var drawings = drawingDict.OrderBy(i => i.Key).Select(i => i.Value).ToList();
|
||||
drawings.ForEach(d => nest.Drawings.Add(d));
|
||||
}
|
||||
|
||||
private List<Part> CreateParts(Program pgm)
|
||||
{
|
||||
var parts = new List<Part>();
|
||||
var pos = Vector.Zero;
|
||||
|
||||
for (int i = 0; i < pgm.Codes.Count; i++)
|
||||
{
|
||||
var code = pgm.Codes[i];
|
||||
|
||||
switch (code.Type)
|
||||
{
|
||||
case CodeType.RapidMove:
|
||||
pos = ((RapidMove)code).EndPoint;
|
||||
break;
|
||||
|
||||
case CodeType.SubProgramCall:
|
||||
var subpgm = (SubProgramCall)code;
|
||||
var dwg = drawingDict[subpgm.Id];
|
||||
var part = new Part(dwg);
|
||||
part.Rotate(Angle.ToRadians(subpgm.Rotation));
|
||||
part.Offset(pos);
|
||||
parts.Add(part);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
private int GetPlateId(string name)
|
||||
{
|
||||
return int.Parse(name.Replace("plate-", ""));
|
||||
}
|
||||
|
||||
private int GetProgramId(string name)
|
||||
{
|
||||
return int.Parse(name.Replace("program-", ""));
|
||||
}
|
||||
|
||||
public static T ParseEnum<T>(string value)
|
||||
{
|
||||
return (T)Enum.Parse(typeof(T), value, true);
|
||||
}
|
||||
|
||||
public static bool TryParseEnum<T>(string value, out T e)
|
||||
{
|
||||
try
|
||||
{
|
||||
e = ParseEnum<T>(value);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
e = ParseEnum<T>(typeof(T).GetEnumValues().GetValue(0).ToString());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private enum NestInfoSection
|
||||
{
|
||||
None,
|
||||
DefaultPlate,
|
||||
Material,
|
||||
EdgeSpacing,
|
||||
Source
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,426 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Math;
|
||||
|
||||
namespace OpenNest.IO
|
||||
{
|
||||
public sealed class NestWriter
|
||||
{
|
||||
/// <summary>
|
||||
/// Number of decimal places the output is round to.
|
||||
/// This number must have more decimal places than Tolerance.Epsilon
|
||||
/// </summary>
|
||||
private const int OutputPrecision = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Fixed-point format string that avoids scientific notation.
|
||||
/// ProgramReader treats 'E' as a code letter, so "6.66E-08" would be
|
||||
/// split into X:"6.66" and E:"-08", corrupting the parsed value.
|
||||
/// </summary>
|
||||
private const string CoordinateFormat = "0.##########";
|
||||
|
||||
private readonly Nest nest;
|
||||
private ZipArchive zipArchive;
|
||||
private Dictionary<int, Drawing> drawingDict;
|
||||
|
||||
public NestWriter(Nest nest)
|
||||
{
|
||||
this.drawingDict = new Dictionary<int, Drawing>();
|
||||
this.nest = nest;
|
||||
}
|
||||
|
||||
public bool Write(string file)
|
||||
{
|
||||
this.nest.DateLastModified = DateTime.Now;
|
||||
|
||||
SetDrawingIds();
|
||||
|
||||
using (var fileStream = new FileStream(file, FileMode.Create))
|
||||
using (zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Create))
|
||||
{
|
||||
AddNestInfo();
|
||||
AddPlates();
|
||||
AddPlateInfo();
|
||||
AddDrawings();
|
||||
AddDrawingInfo();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SetDrawingIds()
|
||||
{
|
||||
int id = 1;
|
||||
|
||||
foreach (var drawing in nest.Drawings)
|
||||
{
|
||||
drawingDict.Add(id, drawing);
|
||||
id++;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddNestInfo()
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
var writer = XmlWriter.Create(stream, new XmlWriterSettings()
|
||||
{
|
||||
Indent = true
|
||||
});
|
||||
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteStartElement("Nest");
|
||||
writer.WriteAttributeString("name", nest.Name);
|
||||
|
||||
writer.WriteElementString("Units", nest.Units.ToString());
|
||||
writer.WriteElementString("Customer", nest.Customer);
|
||||
writer.WriteElementString("DateCreated", nest.DateCreated.ToString());
|
||||
writer.WriteElementString("DateLastModified", nest.DateLastModified.ToString());
|
||||
|
||||
writer.WriteStartElement("DefaultPlate");
|
||||
writer.WriteElementString("Size", nest.PlateDefaults.Size.ToString());
|
||||
writer.WriteElementString("Thickness", nest.PlateDefaults.Thickness.ToString());
|
||||
writer.WriteElementString("Quadrant", nest.PlateDefaults.Quadrant.ToString());
|
||||
writer.WriteElementString("PartSpacing", nest.PlateDefaults.PartSpacing.ToString());
|
||||
|
||||
writer.WriteStartElement("Material");
|
||||
writer.WriteElementString("Name", nest.PlateDefaults.Material.Name);
|
||||
writer.WriteElementString("Grade", nest.PlateDefaults.Material.Grade);
|
||||
writer.WriteElementString("Density", nest.PlateDefaults.Material.Density.ToString());
|
||||
writer.WriteEndElement();
|
||||
|
||||
writer.WriteStartElement("EdgeSpacing");
|
||||
writer.WriteElementString("Left", nest.PlateDefaults.EdgeSpacing.Left.ToString());
|
||||
writer.WriteElementString("Top", nest.PlateDefaults.EdgeSpacing.Top.ToString());
|
||||
writer.WriteElementString("Right", nest.PlateDefaults.EdgeSpacing.Right.ToString());
|
||||
writer.WriteElementString("Bottom", nest.PlateDefaults.EdgeSpacing.Bottom.ToString());
|
||||
writer.WriteEndElement();
|
||||
|
||||
writer.WriteElementString("Notes", Uri.EscapeDataString(nest.Notes));
|
||||
|
||||
writer.WriteEndElement(); // DefaultPlate
|
||||
writer.WriteEndElement(); // Nest
|
||||
|
||||
writer.WriteEndDocument();
|
||||
|
||||
writer.Flush();
|
||||
writer.Close();
|
||||
|
||||
stream.Position = 0;
|
||||
|
||||
var entry = zipArchive.CreateEntry("info");
|
||||
using (var entryStream = entry.Open())
|
||||
{
|
||||
stream.CopyTo(entryStream);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddPlates()
|
||||
{
|
||||
int num = 1;
|
||||
|
||||
foreach (var plate in nest.Plates)
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
var name = string.Format("plate-{0}", num.ToString().PadLeft(3, '0'));
|
||||
|
||||
WritePlate(stream, plate);
|
||||
|
||||
var entry = zipArchive.CreateEntry(name);
|
||||
using (var entryStream = entry.Open())
|
||||
{
|
||||
stream.CopyTo(entryStream);
|
||||
}
|
||||
|
||||
num++;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddPlateInfo()
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
var writer = XmlWriter.Create(stream, new XmlWriterSettings()
|
||||
{
|
||||
Indent = true
|
||||
});
|
||||
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteStartElement("Plates");
|
||||
writer.WriteAttributeString("count", nest.Plates.Count.ToString());
|
||||
|
||||
for (int i = 0; i < nest.Plates.Count; ++i)
|
||||
{
|
||||
var plate = nest.Plates[i];
|
||||
|
||||
writer.WriteStartElement("Plate");
|
||||
writer.WriteAttributeString("id", (i + 1).ToString());
|
||||
|
||||
writer.WriteElementString("Quadrant", plate.Quadrant.ToString());
|
||||
writer.WriteElementString("Thickness", plate.Thickness.ToString());
|
||||
writer.WriteElementString("Size", plate.Size.ToString());
|
||||
writer.WriteElementString("Qty", plate.Quantity.ToString());
|
||||
writer.WriteElementString("PartSpacing", plate.PartSpacing.ToString());
|
||||
|
||||
writer.WriteStartElement("Material");
|
||||
writer.WriteElementString("Name", plate.Material.Name);
|
||||
writer.WriteElementString("Grade", plate.Material.Grade);
|
||||
writer.WriteElementString("Density", plate.Material.Density.ToString());
|
||||
writer.WriteEndElement();
|
||||
|
||||
writer.WriteStartElement("EdgeSpacing");
|
||||
writer.WriteElementString("Left", plate.EdgeSpacing.Left.ToString());
|
||||
writer.WriteElementString("Top", plate.EdgeSpacing.Top.ToString());
|
||||
writer.WriteElementString("Right", plate.EdgeSpacing.Right.ToString());
|
||||
writer.WriteElementString("Bottom", plate.EdgeSpacing.Bottom.ToString());
|
||||
writer.WriteEndElement();
|
||||
|
||||
writer.WriteEndElement(); // Plate
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
writer.WriteEndElement(); // Plates
|
||||
writer.WriteEndDocument();
|
||||
|
||||
writer.Flush();
|
||||
writer.Close();
|
||||
|
||||
stream.Position = 0;
|
||||
|
||||
var entry = zipArchive.CreateEntry("plate-info");
|
||||
using (var entryStream = entry.Open())
|
||||
{
|
||||
stream.CopyTo(entryStream);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDrawings()
|
||||
{
|
||||
int num = 1;
|
||||
|
||||
foreach (var dwg in nest.Drawings)
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
var name = string.Format("program-{0}", num.ToString().PadLeft(3, '0'));
|
||||
|
||||
WriteDrawing(stream, dwg);
|
||||
|
||||
var entry = zipArchive.CreateEntry(name);
|
||||
using (var entryStream = entry.Open())
|
||||
{
|
||||
stream.CopyTo(entryStream);
|
||||
}
|
||||
|
||||
num++;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDrawingInfo()
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
var writer = XmlWriter.Create(stream, new XmlWriterSettings()
|
||||
{
|
||||
Indent = true
|
||||
});
|
||||
|
||||
writer.WriteStartDocument();
|
||||
writer.WriteStartElement("Drawings");
|
||||
writer.WriteAttributeString("count", nest.Drawings.Count.ToString());
|
||||
|
||||
int id = 1;
|
||||
|
||||
foreach (var drawing in nest.Drawings)
|
||||
{
|
||||
writer.WriteStartElement("Drawing");
|
||||
writer.WriteAttributeString("id", id.ToString());
|
||||
writer.WriteAttributeString("name", drawing.Name);
|
||||
|
||||
writer.WriteElementString("Customer", drawing.Customer);
|
||||
writer.WriteElementString("Color", string.Format("{0}, {1}, {2}, {3}", drawing.Color.A, drawing.Color.R, drawing.Color.G, drawing.Color.B));
|
||||
|
||||
writer.WriteStartElement("Quantity");
|
||||
writer.WriteElementString("Required", drawing.Quantity.Required.ToString());
|
||||
writer.WriteElementString("Nested", drawing.Quantity.Nested.ToString());
|
||||
writer.WriteEndElement();
|
||||
|
||||
writer.WriteStartElement("Material");
|
||||
writer.WriteElementString("Name", drawing.Material.Name);
|
||||
writer.WriteElementString("Grade", drawing.Material.Grade);
|
||||
writer.WriteElementString("Density", drawing.Material.Density.ToString());
|
||||
writer.WriteEndElement();
|
||||
|
||||
writer.WriteStartElement("Source");
|
||||
writer.WriteElementString("Path", drawing.Source.Path);
|
||||
writer.WriteElementString("Offset", string.Format("{0}, {1}",
|
||||
drawing.Source.Offset.X,
|
||||
drawing.Source.Offset.Y));
|
||||
writer.WriteEndElement(); // Source
|
||||
|
||||
writer.WriteEndElement(); // Drawing
|
||||
|
||||
id++;
|
||||
}
|
||||
|
||||
writer.WriteEndElement(); // Drawings
|
||||
writer.WriteEndDocument();
|
||||
|
||||
writer.Flush();
|
||||
writer.Close();
|
||||
|
||||
stream.Position = 0;
|
||||
|
||||
var entry = zipArchive.CreateEntry("drawing-info");
|
||||
using (var entryStream = entry.Open())
|
||||
{
|
||||
stream.CopyTo(entryStream);
|
||||
}
|
||||
}
|
||||
|
||||
private void WritePlate(Stream stream, Plate plate)
|
||||
{
|
||||
var writer = new StreamWriter(stream);
|
||||
writer.AutoFlush = true;
|
||||
writer.WriteLine("G90");
|
||||
|
||||
foreach (var part in plate.Parts)
|
||||
{
|
||||
var match = drawingDict.Where(dwg => dwg.Value == part.BaseDrawing).FirstOrDefault();
|
||||
var id = match.Key;
|
||||
|
||||
writer.WriteLine("G00X{0}Y{1}",
|
||||
part.Location.X.ToString(CoordinateFormat),
|
||||
part.Location.Y.ToString(CoordinateFormat));
|
||||
writer.WriteLine("G65P{0}R{1}", id, Angle.ToDegrees(part.Rotation));
|
||||
}
|
||||
|
||||
stream.Position = 0;
|
||||
}
|
||||
|
||||
private void WriteDrawing(Stream stream, Drawing drawing)
|
||||
{
|
||||
var program = drawing.Program;
|
||||
var writer = new StreamWriter(stream);
|
||||
writer.AutoFlush = true;
|
||||
|
||||
writer.WriteLine(program.Mode == Mode.Absolute ? "G90" : "G91");
|
||||
|
||||
for (int i = 0; i < drawing.Program.Length; ++i)
|
||||
{
|
||||
var code = drawing.Program[i];
|
||||
writer.WriteLine(GetCodeString(code));
|
||||
}
|
||||
|
||||
stream.Position = 0;
|
||||
}
|
||||
|
||||
private string GetCodeString(ICode code)
|
||||
{
|
||||
switch (code.Type)
|
||||
{
|
||||
case CodeType.ArcMove:
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var arcMove = (ArcMove)code;
|
||||
|
||||
var x = System.Math.Round(arcMove.EndPoint.X, OutputPrecision).ToString(CoordinateFormat);
|
||||
var y = System.Math.Round(arcMove.EndPoint.Y, OutputPrecision).ToString(CoordinateFormat);
|
||||
var i = System.Math.Round(arcMove.CenterPoint.X, OutputPrecision).ToString(CoordinateFormat);
|
||||
var j = System.Math.Round(arcMove.CenterPoint.Y, OutputPrecision).ToString(CoordinateFormat);
|
||||
|
||||
if (arcMove.Rotation == RotationType.CW)
|
||||
sb.Append(string.Format("G02X{0}Y{1}I{2}J{3}", x, y, i, j));
|
||||
else
|
||||
sb.Append(string.Format("G03X{0}Y{1}I{2}J{3}", x, y, i, j));
|
||||
|
||||
if (arcMove.Layer != LayerType.Cut)
|
||||
sb.Append(GetLayerString(arcMove.Layer));
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
case CodeType.Comment:
|
||||
{
|
||||
var comment = (Comment)code;
|
||||
return ":" + comment.Value;
|
||||
}
|
||||
|
||||
case CodeType.LinearMove:
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var linearMove = (LinearMove)code;
|
||||
|
||||
sb.Append(string.Format("G01X{0}Y{1}",
|
||||
System.Math.Round(linearMove.EndPoint.X, OutputPrecision).ToString(CoordinateFormat),
|
||||
System.Math.Round(linearMove.EndPoint.Y, OutputPrecision).ToString(CoordinateFormat)));
|
||||
|
||||
if (linearMove.Layer != LayerType.Cut)
|
||||
sb.Append(GetLayerString(linearMove.Layer));
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
case CodeType.RapidMove:
|
||||
{
|
||||
var rapidMove = (RapidMove)code;
|
||||
|
||||
return string.Format("G00X{0}Y{1}",
|
||||
System.Math.Round(rapidMove.EndPoint.X, OutputPrecision).ToString(CoordinateFormat),
|
||||
System.Math.Round(rapidMove.EndPoint.Y, OutputPrecision).ToString(CoordinateFormat));
|
||||
}
|
||||
|
||||
case CodeType.SetFeedrate:
|
||||
{
|
||||
var setFeedrate = (Feedrate)code;
|
||||
return "F" + setFeedrate.Value;
|
||||
}
|
||||
|
||||
case CodeType.SetKerf:
|
||||
{
|
||||
var setKerf = (Kerf)code;
|
||||
|
||||
switch (setKerf.Value)
|
||||
{
|
||||
case KerfType.None: return "G40";
|
||||
case KerfType.Left: return "G41";
|
||||
case KerfType.Right: return "G42";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CodeType.SubProgramCall:
|
||||
{
|
||||
var subProgramCall = (SubProgramCall)code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private string GetLayerString(LayerType layer)
|
||||
{
|
||||
switch (layer)
|
||||
{
|
||||
case LayerType.Display:
|
||||
return ":DISPLAY";
|
||||
|
||||
case LayerType.Leadin:
|
||||
return ":LEADIN";
|
||||
|
||||
case LayerType.Leadout:
|
||||
return ":LEADOUT";
|
||||
|
||||
case LayerType.Scribe:
|
||||
return ":SCRIBE";
|
||||
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,390 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.IO
|
||||
{
|
||||
internal sealed class ProgramReader
|
||||
{
|
||||
private const int BufferSize = 200;
|
||||
|
||||
private int codeIndex;
|
||||
private CodeBlock block;
|
||||
private CodeSection section;
|
||||
private Program program;
|
||||
private StreamReader reader;
|
||||
|
||||
public ProgramReader(Stream stream)
|
||||
{
|
||||
reader = new StreamReader(stream);
|
||||
program = new Program();
|
||||
}
|
||||
|
||||
public Program Read()
|
||||
{
|
||||
string line;
|
||||
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
block = ParseBlock(line);
|
||||
ProcessCurrentBlock();
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
private CodeBlock ParseBlock(string line)
|
||||
{
|
||||
var block = new CodeBlock();
|
||||
Code code = null;
|
||||
for (int i = 0; i < line.Length; ++i)
|
||||
{
|
||||
var c = line[i];
|
||||
if (char.IsLetter(c))
|
||||
block.Add((code = new Code(c)));
|
||||
else if (c == ':')
|
||||
{
|
||||
block.Add((new Code(c, line.Remove(0, i + 1).Trim())));
|
||||
break;
|
||||
}
|
||||
else if (code != null)
|
||||
code.Value += c;
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
private void ProcessCurrentBlock()
|
||||
{
|
||||
var code = GetFirstCode();
|
||||
|
||||
while (code != null)
|
||||
{
|
||||
switch (code.Id)
|
||||
{
|
||||
case ':':
|
||||
program.Codes.Add(new Comment(code.Value));
|
||||
code = GetNextCode();
|
||||
break;
|
||||
|
||||
case 'G':
|
||||
int value = int.Parse(code.Value);
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
section = CodeSection.Line;
|
||||
ReadLine(value == 0);
|
||||
code = GetCurrentCode();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
section = CodeSection.Arc;
|
||||
ReadArc(value == 2 ? RotationType.CW : RotationType.CCW);
|
||||
code = GetCurrentCode();
|
||||
break;
|
||||
|
||||
case 65:
|
||||
section = CodeSection.SubProgram;
|
||||
ReadSubProgram();
|
||||
code = GetCurrentCode();
|
||||
break;
|
||||
|
||||
case 40:
|
||||
program.Codes.Add(new Kerf() { Value = KerfType.None });
|
||||
code = GetNextCode();
|
||||
break;
|
||||
|
||||
case 41:
|
||||
program.Codes.Add(new Kerf() { Value = KerfType.Left });
|
||||
code = GetNextCode();
|
||||
break;
|
||||
|
||||
case 42:
|
||||
program.Codes.Add(new Kerf() { Value = KerfType.Right });
|
||||
code = GetNextCode();
|
||||
break;
|
||||
|
||||
case 90:
|
||||
program.Mode = Mode.Absolute;
|
||||
code = GetNextCode();
|
||||
break;
|
||||
|
||||
case 91:
|
||||
program.Mode = Mode.Incremental;
|
||||
code = GetNextCode();
|
||||
break;
|
||||
|
||||
default:
|
||||
code = GetNextCode();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
program.Codes.Add(new Feedrate() { Value = double.Parse(code.Value) });
|
||||
code = GetNextCode();
|
||||
break;
|
||||
|
||||
default:
|
||||
code = GetNextCode();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadLine(bool isRapid)
|
||||
{
|
||||
var line = new LinearMove();
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
var layer = LayerType.Cut;
|
||||
|
||||
while (section == CodeSection.Line)
|
||||
{
|
||||
var code = GetNextCode();
|
||||
|
||||
if (code == null)
|
||||
{
|
||||
section = CodeSection.Unknown;
|
||||
break;
|
||||
}
|
||||
switch (code.Id)
|
||||
{
|
||||
case 'X':
|
||||
x = double.Parse(code.Value);
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
y = double.Parse(code.Value);
|
||||
break;
|
||||
|
||||
case ':':
|
||||
{
|
||||
var value = code.Value.Trim().ToUpper();
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case "DISPLAY":
|
||||
layer = LayerType.Display;
|
||||
break;
|
||||
|
||||
case "LEADIN":
|
||||
layer = LayerType.Leadin;
|
||||
break;
|
||||
|
||||
case "LEADOUT":
|
||||
layer = LayerType.Leadout;
|
||||
break;
|
||||
|
||||
case "SCRIBE":
|
||||
layer = LayerType.Scribe;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
section = CodeSection.Unknown;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isRapid)
|
||||
program.Codes.Add(new RapidMove(x, y));
|
||||
else
|
||||
program.Codes.Add(new LinearMove(x, y) { Layer = layer });
|
||||
}
|
||||
|
||||
private void ReadArc(RotationType rotation)
|
||||
{
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
double i = 0;
|
||||
double j = 0;
|
||||
var layer = LayerType.Cut;
|
||||
|
||||
while (section == CodeSection.Arc)
|
||||
{
|
||||
var code = GetNextCode();
|
||||
|
||||
if (code == null)
|
||||
{
|
||||
section = CodeSection.Unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (code.Id)
|
||||
{
|
||||
case 'X':
|
||||
x = double.Parse(code.Value);
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
y = double.Parse(code.Value);
|
||||
break;
|
||||
|
||||
case 'I':
|
||||
i = double.Parse(code.Value);
|
||||
break;
|
||||
|
||||
case 'J':
|
||||
j = double.Parse(code.Value);
|
||||
break;
|
||||
|
||||
case ':':
|
||||
{
|
||||
var value = code.Value.Trim().ToUpper();
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case "DISPLAY":
|
||||
layer = LayerType.Display;
|
||||
break;
|
||||
|
||||
case "LEADIN":
|
||||
layer = LayerType.Leadin;
|
||||
break;
|
||||
|
||||
case "LEADOUT":
|
||||
layer = LayerType.Leadout;
|
||||
break;
|
||||
|
||||
case "SCRIBE":
|
||||
layer = LayerType.Scribe;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
section = CodeSection.Unknown;
|
||||
break;
|
||||
}
|
||||
}
|
||||
program.Codes.Add(new ArcMove()
|
||||
{
|
||||
EndPoint = new Vector(x, y),
|
||||
CenterPoint = new Vector(i, j),
|
||||
Rotation = rotation,
|
||||
Layer = layer
|
||||
});
|
||||
}
|
||||
|
||||
private void ReadSubProgram()
|
||||
{
|
||||
var p = 0;
|
||||
var r = 0.0;
|
||||
|
||||
while (section == CodeSection.SubProgram)
|
||||
{
|
||||
var code = GetNextCode();
|
||||
|
||||
if (code == null)
|
||||
{
|
||||
section = CodeSection.Unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (code.Id)
|
||||
{
|
||||
case 'P':
|
||||
p = int.Parse(code.Value);
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
r = double.Parse(code.Value);
|
||||
break;
|
||||
|
||||
default:
|
||||
section = CodeSection.Unknown;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
program.Codes.Add(new SubProgramCall() { Id = p, Rotation = r });
|
||||
}
|
||||
|
||||
private Code GetNextCode()
|
||||
{
|
||||
codeIndex++;
|
||||
|
||||
if (codeIndex >= block.Count)
|
||||
return null;
|
||||
|
||||
return block[codeIndex];
|
||||
}
|
||||
|
||||
private Code GetCurrentCode()
|
||||
{
|
||||
if (codeIndex >= block.Count)
|
||||
return null;
|
||||
|
||||
return block[codeIndex];
|
||||
}
|
||||
|
||||
private Code GetFirstCode()
|
||||
{
|
||||
if (block.Count == 0)
|
||||
return null;
|
||||
|
||||
codeIndex = 0;
|
||||
return block[codeIndex];
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
private class Code
|
||||
{
|
||||
public Code(char id)
|
||||
{
|
||||
Id = id;
|
||||
Value = string.Empty;
|
||||
}
|
||||
|
||||
public Code(char id, string value)
|
||||
{
|
||||
Id = id;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public char Id { get; private set; }
|
||||
|
||||
public string Value { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Id + Value;
|
||||
}
|
||||
}
|
||||
|
||||
private class CodeBlock : List<Code>
|
||||
{
|
||||
public void Add(char id, string value)
|
||||
{
|
||||
Add(new Code(id, value));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
foreach (var code in this)
|
||||
builder.Append(code.ToString() + " ");
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private enum CodeSection
|
||||
{
|
||||
Unknown,
|
||||
Arc,
|
||||
Line,
|
||||
SubProgram
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
<ProjectReference Include="..\OpenNest.Core\OpenNest.Core.csproj" />
|
||||
<ProjectReference Include="..\OpenNest.Engine\OpenNest.Engine.csproj" />
|
||||
<ProjectReference Include="..\OpenNest.Gpu\OpenNest.Gpu.csproj" />
|
||||
<PackageReference Include="ACadSharp" Version="3.1.32" />
|
||||
<ProjectReference Include="..\OpenNest.IO\OpenNest.IO.csproj" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="8.0.10" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user