Compare commits

...

4 Commits

Author SHA1 Message Date
aj 250fdefaea refactor: merge DxfImporter and DxfExporter into single static Dxf class
Consolidated two stateless classes into one unified API: Dxf.Import(),
Dxf.GetGeometry(), Dxf.ExportPlate(), Dxf.ExportProgram(). Export
state moved into a private ExportContext. Removed bool+out pattern
from GetGeometry in favor of returning empty list on failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 08:17:49 -04:00
aj e92208b8c0 fix: remove import spline precision setting entirely
Spline import now uses SplineConverter (arc-based) so the configurable
precision parameter is obsolete. Removed the setting from the options
dialog, DxfImporter property, Settings files, and all callsites.
Hardcoded 200 as the sampling density for the intermediate point
evaluation that feeds into SplineConverter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 08:05:33 -04:00
aj 297ebee45b fix: stop plate list changes from forcing tab switch
PlateListChanged handler was setting tabControl1.SelectedIndex = 0,
which forced the UI to the plates tab whenever a sentinel plate was
auto-created during part placement, disrupting the workflow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 08:00:09 -04:00
aj 1eba3e7cde fix: improve DrawingListBox rendering and scroll stability
Add LightGray separator lines between items to visually distinguish
adjacent quantity bars. Preserve scroll position and selection when
updating the drawing list by saving/restoring TopIndex and SelectedItem.
Use incremental item removal instead of full list rebuild when hiding
depleted drawings. Wrap list modifications in BeginUpdate/EndUpdate to
reduce flicker.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 07:53:53 -04:00
27 changed files with 446 additions and 586 deletions
+2 -3
View File
@@ -25,14 +25,13 @@ public static class NestRunner
// 1. Import DXFs → Drawings
var drawings = new List<Drawing>();
var importer = new DxfImporter();
foreach (var part in request.Parts)
{
if (!File.Exists(part.DxfPath))
throw new FileNotFoundException($"DXF file not found: {part.DxfPath}", part.DxfPath);
if (!importer.GetGeometry(part.DxfPath, out var geometry) || geometry.Count == 0)
var geometry = Dxf.GetGeometry(part.DxfPath);
if (geometry.Count == 0)
throw new InvalidOperationException($"Failed to import DXF: {part.DxfPath}");
var normalized = ShapeProfile.NormalizeEntities(geometry);
+2 -8
View File
@@ -241,17 +241,11 @@ static class NestConsole
static Drawing ImportDxf(string path)
{
var importer = new DxfImporter();
if (!importer.GetGeometry(path, out var geometry))
{
Console.Error.WriteLine($"Error: failed to read DXF file: {path}");
return null;
}
var geometry = Dxf.GetGeometry(path);
if (geometry.Count == 0)
{
Console.Error.WriteLine($"Error: no geometry found in DXF file: {path}");
Console.Error.WriteLine($"Error: failed to read DXF file or no geometry found: {path}");
return null;
}
+378
View File
@@ -0,0 +1,378 @@
using ACadSharp;
using ACadSharp.IO;
using CSMath;
using OpenNest.CNC;
using OpenNest.Geometry;
using OpenNest.Math;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace OpenNest.IO
{
using AcadArc = ACadSharp.Entities.Arc;
using AcadCircle = ACadSharp.Entities.Circle;
using AcadLine = ACadSharp.Entities.Line;
using Layer = ACadSharp.Tables.Layer;
public static class Dxf
{
#region Import
/// <summary>
/// Imports a DXF file, returning both converted entities and the raw CadDocument
/// for bend detection. The CadDocument is NOT disposed — caller can use it for
/// additional analysis (e.g., MText extraction for bend notes).
/// </summary>
public static DxfImportResult Import(string path)
{
using var reader = new DxfReader(path);
var doc = reader.Read();
return new DxfImportResult
{
Entities = ConvertEntities(doc),
Document = doc
};
}
public static List<Entity> GetGeometry(string path)
{
try
{
using var reader = new DxfReader(path);
var doc = reader.Read();
return ConvertEntities(doc);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return new List<Entity>();
}
}
public static List<Entity> GetGeometry(Stream stream)
{
try
{
using var reader = new DxfReader(stream);
var doc = reader.Read();
return ConvertEntities(doc);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return new List<Entity>();
}
}
#endregion
#region Export
public static void ExportProgram(Program program, string path)
{
using var stream = File.Create(path);
ExportProgram(program, stream);
}
public static void ExportProgram(Program program, Stream stream)
{
var ctx = new ExportContext();
ctx.AddProgram(program);
using var writer = new DxfWriter(stream, ctx.Document, false);
writer.Write();
}
public static void ExportPlate(Plate plate, string path)
{
using var stream = File.Create(path);
ExportPlate(plate, stream);
}
public static void ExportPlate(Plate plate, Stream stream)
{
var ctx = new ExportContext();
ctx.AddPlateOutline(plate);
foreach (var part in plate.Parts)
{
var endpt = part.Location.ToAcadXYZ();
ctx.AddLine(ctx.CurPos, endpt, ctx.RapidLayer);
ctx.CurPos = part.Location.ToAcadXYZ();
ctx.AddProgram(part.Program);
}
using var writer = new DxfWriter(stream, ctx.Document, false);
writer.Write();
}
#endregion
#region Private
private static List<Entity> ConvertEntities(CadDocument doc)
{
var entities = new List<Entity>();
var lines = new List<Line>();
var arcs = new List<Arc>();
foreach (var entity in doc.Entities)
{
if (IsNonCutLayer(entity.Layer?.Name))
continue;
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:
foreach (var e in spline.ToOpenNest())
{
if (e is Line l) lines.Add(l);
else if (e is Arc a) arcs.Add(a);
}
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:
foreach (var e in ellipse.ToOpenNest())
{
if (e is Line l) lines.Add(l);
else if (e is Arc a) arcs.Add(a);
}
break;
}
}
GeometryOptimizer.Optimize(lines);
GeometryOptimizer.Optimize(arcs);
entities.AddRange(lines);
entities.AddRange(arcs);
return entities;
}
private static bool IsNonCutLayer(string layerName)
{
return string.Equals(layerName, "BEND", StringComparison.OrdinalIgnoreCase)
|| string.Equals(layerName, "ETCH", StringComparison.OrdinalIgnoreCase);
}
private class ExportContext
{
public CadDocument Document { get; }
public XYZ CurPos { get; set; }
public Layer CutLayer { get; }
public Layer RapidLayer { get; }
public Layer PlateLayer { get; }
private Mode mode;
public ExportContext()
{
Document = new CadDocument();
CutLayer = new Layer("Cut") { Color = new Color(1) };
RapidLayer = new Layer("Rapid") { Color = new Color(5) };
PlateLayer = new Layer("Plate") { Color = new Color(4) };
Document.Layers.Add(CutLayer);
Document.Layers.Add(RapidLayer);
Document.Layers.Add(PlateLayer);
}
public void AddLine(XYZ start, XYZ end, Layer layer)
{
var ln = new AcadLine
{
StartPoint = start,
EndPoint = end,
Layer = layer
};
Document.Entities.Add(ln);
}
public void AddPlateOutline(Plate plate)
{
XYZ pt1, pt2, pt3, pt4;
switch (plate.Quadrant)
{
case 1:
pt1 = new XYZ(0, 0, 0);
pt2 = new XYZ(0, plate.Size.Width, 0);
pt3 = new XYZ(plate.Size.Length, plate.Size.Width, 0);
pt4 = new XYZ(plate.Size.Length, 0, 0);
break;
case 2:
pt1 = new XYZ(0, 0, 0);
pt2 = new XYZ(0, plate.Size.Width, 0);
pt3 = new XYZ(-plate.Size.Length, plate.Size.Width, 0);
pt4 = new XYZ(-plate.Size.Length, 0, 0);
break;
case 3:
pt1 = new XYZ(0, 0, 0);
pt2 = new XYZ(0, -plate.Size.Width, 0);
pt3 = new XYZ(-plate.Size.Length, -plate.Size.Width, 0);
pt4 = new XYZ(-plate.Size.Length, 0, 0);
break;
case 4:
pt1 = new XYZ(0, 0, 0);
pt2 = new XYZ(0, -plate.Size.Width, 0);
pt3 = new XYZ(plate.Size.Length, -plate.Size.Width, 0);
pt4 = new XYZ(plate.Size.Length, 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);
}
public 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:
AddArcMove((ArcMove)code);
break;
case CodeType.LinearMove:
AddLinearMove((LinearMove)code);
break;
case CodeType.RapidMove:
AddRapidMove((RapidMove)code);
break;
case CodeType.SubProgramCall:
var tmpmode = mode;
AddProgram(((SubProgramCall)code).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 == 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
{
Center = center,
Radius = radius,
Layer = CutLayer
};
Document.Entities.Add(circle);
}
else
{
var acadArc = new AcadArc
{
Center = center,
Radius = radius,
StartAngle = startAngle,
EndAngle = endAngle,
Layer = CutLayer
};
Document.Entities.Add(acadArc);
}
CurPos = endpt;
}
}
#endregion
}
}
-297
View File
@@ -1,297 +0,0 @@
using ACadSharp;
using ACadSharp.IO;
using CSMath;
using OpenNest.CNC;
using OpenNest.Math;
using System.Diagnostics;
using System.IO;
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.Width, 0);
pt3 = new XYZ(plate.Size.Length, plate.Size.Width, 0);
pt4 = new XYZ(plate.Size.Length, 0, 0);
break;
case 2:
pt1 = new XYZ(0, 0, 0);
pt2 = new XYZ(0, plate.Size.Width, 0);
pt3 = new XYZ(-plate.Size.Length, plate.Size.Width, 0);
pt4 = new XYZ(-plate.Size.Length, 0, 0);
break;
case 3:
pt1 = new XYZ(0, 0, 0);
pt2 = new XYZ(0, -plate.Size.Width, 0);
pt3 = new XYZ(-plate.Size.Length, -plate.Size.Width, 0);
pt4 = new XYZ(-plate.Size.Length, 0, 0);
break;
case 4:
pt1 = new XYZ(0, 0, 0);
pt2 = new XYZ(0, -plate.Size.Width, 0);
pt3 = new XYZ(plate.Size.Length, -plate.Size.Width, 0);
pt4 = new XYZ(plate.Size.Length, 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;
}
}
}
-155
View File
@@ -1,155 +0,0 @@
using ACadSharp;
using ACadSharp.IO;
using OpenNest.Geometry;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
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)
{
// Skip bend/etch entities — bends are converted to Bend objects
// separately via bend detection, and etch marks are generated from
// bends during DXF export. Neither should be treated as cut geometry.
if (IsNonCutLayer(entity.Layer?.Name))
continue;
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:
foreach (var e in spline.ToOpenNest(SplinePrecision))
{
if (e is Line l) lines.Add(l);
else if (e is Arc a) arcs.Add(a);
}
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:
foreach (var e in ellipse.ToOpenNest())
{
if (e is Line l) lines.Add(l);
else if (e is Arc a) arcs.Add(a);
}
break;
}
}
GeometryOptimizer.Optimize(lines);
GeometryOptimizer.Optimize(arcs);
entities.AddRange(lines);
entities.AddRange(arcs);
return entities;
}
/// <summary>
/// Imports a DXF file, returning both converted entities and the raw CadDocument
/// for bend detection. The CadDocument is NOT disposed — caller can use it for
/// additional analysis (e.g., MText extraction for bend notes).
/// </summary>
public DxfImportResult Import(string path)
{
using var reader = new DxfReader(path);
var doc = reader.Read();
var entities = GetGeometry(doc);
return new DxfImportResult
{
Entities = entities,
Document = doc
};
}
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;
}
private static bool IsNonCutLayer(string layerName)
{
return string.Equals(layerName, "BEND", System.StringComparison.OrdinalIgnoreCase)
|| string.Equals(layerName, "ETCH", System.StringComparison.OrdinalIgnoreCase);
}
}
}
+2 -2
View File
@@ -57,7 +57,7 @@ namespace OpenNest.IO
return result;
}
public static List<Geometry.Entity> ToOpenNest(this Spline spline, int precision)
public static List<Geometry.Entity> ToOpenNest(this Spline spline)
{
var layer = spline.Layer.ToOpenNest();
var color = spline.ResolveColor();
@@ -67,7 +67,7 @@ namespace OpenNest.IO
List<XYZ> curvePoints;
try
{
curvePoints = spline.PolygonalVertexes(precision > 0 ? precision : 200);
curvePoints = spline.PolygonalVertexes(200);
}
catch (Exception ex)
{
+2 -5
View File
@@ -96,13 +96,10 @@ namespace OpenNest.Mcp.Tools
if (!File.Exists(path))
return $"Error: file not found: {path}";
var importer = new DxfImporter();
if (!importer.GetGeometry(path, out var geometry))
return "Error: failed to read DXF file";
var geometry = Dxf.GetGeometry(path);
if (geometry.Count == 0)
return "Error: no geometry found in DXF file";
return "Error: failed to read DXF file or no geometry found";
var normalized = ShapeProfile.NormalizeEntities(geometry);
var pgm = ConvertGeometry.ToProgram(normalized);
+1 -2
View File
@@ -70,8 +70,7 @@ public class NestRunnerTests
var pgm = ConvertGeometry.ToProgram(shape);
var path = Path.Combine(Path.GetTempPath(), $"test-{Guid.NewGuid()}.dxf");
var exporter = new DxfExporter();
exporter.ExportProgram(pgm, path);
Dxf.ExportProgram(pgm, path);
return path;
}
@@ -35,8 +35,7 @@ public class SolidWorksBendDetectorTests
var path = Path.Combine(AppContext.BaseDirectory, "Bending", "TestData", "4526 A14 PT11 Test.dxf");
Assert.True(File.Exists(path), $"Test DXF not found: {path}");
var importer = new OpenNest.IO.DxfImporter { SplinePrecision = 200 };
var result = importer.Import(path);
var result = OpenNest.IO.Dxf.Import(path);
// EllipseConverter now produces arcs directly during import,
// so the imported entities should contain Arc instances from the ellipses
@@ -61,8 +60,7 @@ public class SolidWorksBendDetectorTests
var path = Path.Combine(AppContext.BaseDirectory, "Bending", "TestData", "4526 A14 PT11.dxf");
Assert.True(File.Exists(path), $"Test DXF not found: {path}");
var importer = new OpenNest.IO.DxfImporter();
var result = importer.Import(path);
var result = OpenNest.IO.Dxf.Import(path);
// The DXF has 2 trimmed ellipses forming an oblong slot.
// Trimmed ellipses must not generate a closing chord line.
@@ -32,8 +32,7 @@ public class BestFitOverlapTests
if (!File.Exists(DxfPath))
return null;
var importer = new DxfImporter();
importer.GetGeometry(DxfPath, out var geometry);
var geometry = Dxf.GetGeometry(DxfPath);
var pgm = ConvertGeometry.ToProgram(geometry);
return new Drawing("PT16", pgm);
}
+1 -2
View File
@@ -20,8 +20,7 @@ public class EngineOverlapTests
if (!System.IO.File.Exists(DxfPath))
return null;
var importer = new DxfImporter();
importer.GetGeometry(DxfPath, out var geometry);
var geometry = Dxf.GetGeometry(DxfPath);
var pgm = ConvertGeometry.ToProgram(geometry);
return new Drawing("PT15", pgm);
}
@@ -228,8 +228,7 @@ public class EllipseConverterTests
using (var writer = new ACadSharp.IO.DxfWriter(stream, doc, false))
writer.Write();
var importer = new OpenNest.IO.DxfImporter { SplinePrecision = 200 };
var result = importer.Import(tempPath);
var result = OpenNest.IO.Dxf.Import(tempPath);
var arcCount = result.Entities.Count(e => e is Arc);
var lineCount = result.Entities.Count(e => e is Line);
@@ -138,8 +138,7 @@ public class GeometrySimplifierTests
if (!File.Exists(path))
return; // skip if file not available
var importer = new DxfImporter();
var result = importer.Import(path);
var result = Dxf.Import(path);
var shapes = ShapeBuilder.GetShapes(result.Entities);
var simplifier = new GeometrySimplifier { Tolerance = 0.004 };
+3 -6
View File
@@ -11,17 +11,14 @@ public class DxfRoundtripTests
private static List<Entity> ExportAndReimport(List<Entity> geometry)
{
var program = ConvertGeometry.ToProgram(geometry);
var exporter = new DxfExporter();
var importer = new DxfImporter();
using var exportStream = new MemoryStream();
exporter.ExportProgram(program, exportStream);
Dxf.ExportProgram(program, exportStream);
var bytes = exportStream.ToArray();
var importStream = new MemoryStream(bytes);
var success = importer.GetGeometry(importStream, out var reimported);
var reimported = Dxf.GetGeometry(importStream);
Assert.True(success, "Failed to re-import exported DXF");
Assert.NotEmpty(reimported);
return reimported;
}
@@ -369,8 +369,7 @@ public class DrawingSplitterTests
var writer = new OpenNest.IO.SplitDxfWriter();
writer.Write(tempPath, results[0]);
var reimporter = new OpenNest.IO.DxfImporter();
var reimportResult = reimporter.Import(tempPath);
var reimportResult = OpenNest.IO.Dxf.Import(tempPath);
var afterArcs = reimportResult.Entities.OfType<Arc>().Count();
var afterCircles = reimportResult.Entities.OfType<Circle>().Count();
@@ -185,8 +185,7 @@ public class SplitDxfWriterEtchLayerTests
writer.Write(tempPath, splitDrawing);
// Re-import via DxfImporter (same path as CadConverterForm)
var importer = new DxfImporter();
var result = importer.Import(tempPath);
var result = Dxf.Import(tempPath);
// ETCH entities should be filtered during import (like BEND)
var etchEntities = result.Entities
@@ -23,8 +23,7 @@ public class StrategyOverlapTests
if (!System.IO.File.Exists(DxfPath))
return null;
var importer = new DxfImporter();
importer.GetGeometry(DxfPath, out var geometry);
var geometry = Dxf.GetGeometry(DxfPath);
var pgm = ConvertGeometry.ToProgram(geometry);
return new Drawing("PT15", pgm);
}
+2 -2
View File
@@ -104,7 +104,6 @@ int RunDataCollection(string dir, string dbPath, string saveDir, double s, strin
if (backfilled > 0)
Console.WriteLine($"Backfilled PerimeterToAreaRatio for {backfilled} existing parts");
var importer = new DxfImporter();
var colorIndex = 0;
var processed = 0;
var skippedGeometry = 0;
@@ -129,7 +128,8 @@ int RunDataCollection(string dir, string dbPath, string saveDir, double s, strin
continue;
}
if (!importer.GetGeometry(file, out var entities))
var entities = Dxf.GetGeometry(file);
if (entities.Count == 0)
{
Console.WriteLine(" - SKIP (no geometry)");
skippedGeometry++;
+4
View File
@@ -125,6 +125,10 @@ namespace OpenNest.Controls
pt.Y += 18;
e.Graphics.DrawString(text3, Font, detailBrush, pt);
}
using var separatorPen = new Pen(Color.LightGray);
var separatorY = e.Bounds.Bottom - 1;
e.Graphics.DrawLine(separatorPen, e.Bounds.X, separatorY, e.Bounds.Right, separatorY);
}
protected override void OnMouseMove(MouseEventArgs e)
+1 -2
View File
@@ -382,7 +382,6 @@ namespace OpenNest.Forms
}
var jobName = txtJobName.Text.Trim();
var importer = new DxfImporter();
var nestsCreated = 0;
var importErrors = new List<string>();
@@ -416,7 +415,7 @@ namespace OpenNest.Forms
try
{
var result = importer.Import(part.DxfPath);
var result = Dxf.Import(part.DxfPath);
var drawingName = Path.GetFileNameWithoutExtension(part.DxfPath);
var drawing = new Drawing(drawingName);
+2 -6
View File
@@ -74,9 +74,7 @@ namespace OpenNest.Forms
{
try
{
var importer = new DxfImporter();
importer.SplinePrecision = Settings.Default.ImportSplinePrecision;
var result = importer.Import(file);
var result = Dxf.Import(file);
if (result.Entities.Count == 0)
return;
@@ -384,9 +382,7 @@ namespace OpenNest.Forms
newItems.Add(splitPath);
// Re-import geometry but keep bends from the split drawing
var importer = new DxfImporter();
importer.SplinePrecision = Settings.Default.ImportSplinePrecision;
var result = importer.Import(splitPath);
var result = Dxf.Import(splitPath);
var splitItem = new FileListItem
{
+30 -9
View File
@@ -259,6 +259,11 @@ namespace OpenNest.Forms
public void UpdateDrawingList()
{
var topIndex = drawingListBox1.TopIndex;
var selected = drawingListBox1.SelectedItem;
drawingListBox1.BeginUpdate();
drawingListBox1.Items.Clear();
foreach (var dwg in Nest.Drawings.OrderBy(d => d.Name).ToList())
@@ -268,6 +273,14 @@ namespace OpenNest.Forms
drawingListBox1.Items.Add(dwg);
}
if (selected != null && drawingListBox1.Items.Contains(selected))
drawingListBox1.SelectedItem = selected;
if (topIndex < drawingListBox1.Items.Count)
drawingListBox1.TopIndex = topIndex;
drawingListBox1.EndUpdate();
}
public void Save()
@@ -349,9 +362,8 @@ namespace OpenNest.Forms
{
if (dlg.FilterIndex == 1)
{
var exporter = new DxfExporter();
var success = exporter.ExportPlate(PlateView.Plate, dlg.FileName);
return success;
Dxf.ExportPlate(PlateView.Plate, dlg.FileName);
return true;
}
else if (dlg.FilterIndex == 2)
{
@@ -527,8 +539,7 @@ namespace OpenNest.Forms
var plate = PlateView.Plate;
var name = string.Format("{0}-P{1}.dxf", Nest.Name, PlateManager.CurrentIndex + 1);
var path = Path.Combine(Path.GetTempPath(), name);
var exporter = new DxfExporter();
exporter.ExportPlate(plate, path);
Dxf.ExportPlate(plate, path);
Process.Start(path);
}
@@ -886,7 +897,6 @@ namespace OpenNest.Forms
private void PlateManager_PlateListChanged(object sender, EventArgs e)
{
tabControl1.SelectedIndex = 0;
UpdatePlateList();
UpdatePlateHeader();
UpdateRemovePlateButton();
@@ -935,9 +945,20 @@ namespace OpenNest.Forms
drawingListBox1.Invoke(new MethodInvoker(() =>
{
if (hideNestedButton.Checked)
UpdateDrawingList();
else
drawingListBox1.Refresh();
{
drawingListBox1.BeginUpdate();
for (var i = drawingListBox1.Items.Count - 1; i >= 0; i--)
{
var dwg = (Drawing)drawingListBox1.Items[i];
if (dwg.Quantity.Required > 0 && dwg.Quantity.Remaining == 0)
drawingListBox1.Items.RemoveAt(i);
}
drawingListBox1.EndUpdate();
}
drawingListBox1.Invalidate();
}));
}
+7 -50
View File
@@ -33,11 +33,9 @@
this.label1 = new System.Windows.Forms.Label();
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
this.numericUpDown1 = new OpenNest.Controls.NumericUpDown();
this.label2 = new System.Windows.Forms.Label();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.textBox1 = new System.Windows.Forms.TextBox();
this.label3 = new System.Windows.Forms.Label();
this.numericUpDown2 = new OpenNest.Controls.NumericUpDown();
this.button1 = new System.Windows.Forms.Button();
this.saveButton = new System.Windows.Forms.Button();
this.cancelButton = new System.Windows.Forms.Button();
@@ -46,7 +44,6 @@
this.strategyGroupBox = new System.Windows.Forms.GroupBox();
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();
this.tableLayoutPanel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.numericUpDown2)).BeginInit();
this.bottomPanel1.SuspendLayout();
this.SuspendLayout();
//
@@ -87,17 +84,7 @@
this.numericUpDown1.Suffix = "";
this.numericUpDown1.TabIndex = 4;
this.toolTip1.SetToolTip(this.numericUpDown1, "The amount to round the plate size up.");
//
// label2
//
this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(3, 92);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(145, 16);
this.label2.TabIndex = 5;
this.label2.Text = "Import spline precision:\r\n";
//
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 4;
@@ -107,19 +94,16 @@
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100F));
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.textBox1, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.label2, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.label3, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.checkBox1, 0, 3);
this.tableLayoutPanel1.Controls.Add(this.numericUpDown2, 1, 2);
this.tableLayoutPanel1.Controls.Add(this.checkBox1, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.numericUpDown1, 1, 1);
this.tableLayoutPanel1.Controls.Add(this.button1, 3, 0);
this.tableLayoutPanel1.Location = new System.Drawing.Point(12, 12);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 4;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.RowCount = 3;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.34F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(684, 160);
this.tableLayoutPanel1.TabIndex = 0;
//
@@ -141,31 +125,7 @@
this.label3.Size = new System.Drawing.Size(145, 16);
this.label3.TabIndex = 0;
this.label3.Text = "Nest Template Path:";
//
// numericUpDown2
//
this.numericUpDown2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
this.numericUpDown2.Location = new System.Drawing.Point(154, 89);
this.numericUpDown2.Maximum = new decimal(new int[] {
360,
0,
0,
0});
this.numericUpDown2.Minimum = new decimal(new int[] {
3,
0,
0,
0});
this.numericUpDown2.Name = "numericUpDown2";
this.numericUpDown2.Size = new System.Drawing.Size(130, 22);
this.numericUpDown2.Suffix = "";
this.numericUpDown2.TabIndex = 6;
this.numericUpDown2.Value = new decimal(new int[] {
200,
0,
0,
0});
//
//
// button1
//
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
@@ -259,7 +219,6 @@
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit();
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.numericUpDown2)).EndInit();
this.bottomPanel1.ResumeLayout(false);
this.ResumeLayout(false);
@@ -273,8 +232,6 @@
private System.Windows.Forms.Label label1;
private Controls.NumericUpDown numericUpDown1;
private System.Windows.Forms.ToolTip toolTip1;
private System.Windows.Forms.Label label2;
private Controls.NumericUpDown numericUpDown2;
private Controls.BottomPanel bottomPanel1;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.TextBox textBox1;
-2
View File
@@ -67,7 +67,6 @@ namespace OpenNest.Forms
textBox1.Text = Settings.Default.NestTemplatePath;
checkBox1.Checked = Settings.Default.CreateNewNestOnOpen;
numericUpDown1.Value = (decimal)Settings.Default.AutoSizePlateFactor;
numericUpDown2.Value = (decimal)Settings.Default.ImportSplinePrecision;
var disabledNames = ParseDisabledStrategies(Settings.Default.DisabledStrategies);
foreach (DataGridViewRow row in strategyGrid.Rows)
@@ -79,7 +78,6 @@ namespace OpenNest.Forms
Settings.Default.NestTemplatePath = textBox1.Text;
Settings.Default.CreateNewNestOnOpen = checkBox1.Checked;
Settings.Default.AutoSizePlateFactor = (double)numericUpDown1.Value;
Settings.Default.ImportSplinePrecision = (int)numericUpDown2.Value;
var disabledNames = new List<string>();
foreach (DataGridViewRow row in strategyGrid.Rows)
+1 -13
View File
@@ -118,19 +118,7 @@ namespace OpenNest.Properties {
this["NestNumber"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("200")]
public int ImportSplinePrecision {
get {
return ((int)(this["ImportSplinePrecision"]));
}
set {
this["ImportSplinePrecision"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("Inches")]
-3
View File
@@ -26,9 +26,6 @@
<Setting Name="NestNumber" Type="System.Int32" Scope="User">
<Value Profile="(Default)">1</Value>
</Setting>
<Setting Name="ImportSplinePrecision" Type="System.Int32" Scope="User">
<Value Profile="(Default)">200</Value>
</Setting>
<Setting Name="DefaultUnit" Type="OpenNest.Units" Scope="User">
<Value Profile="(Default)">Inches</Value>
</Setting>
-3
View File
@@ -32,9 +32,6 @@
<setting name="NestNumber" serializeAs="String">
<value>1</value>
</setting>
<setting name="ImportSplinePrecision" serializeAs="String">
<value>200</value>
</setting>
<setting name="DefaultUnit" serializeAs="String">
<value>Inches</value>
</setting>