fix: geometry simplifier arc connectivity and ellipse support
Three bugs prevented the simplifier from working on ellipse geometry: 1. Sweep angle check blocked initial fit — the 5-degree minimum sweep was inside TryFit(), killing candidates before the extension loop could accumulate enough segments. Moved to TryFitArcAt() after extension. 2. Layer reference equality split runs — entities from separate DXF ellipses had different Layer object instances for the same layer "0", splitting them into independent runs. Changed to compare Layer.Name. 3. Symmetrize replaced arcs with mirrored copies whose endpoints didn't match the target's original geometry, creating ~0.014 gaps. Now only applies mirrored arcs when endpoints are within tolerance of the target's boundary points. Also: default tolerance 0.02 -> 0.004, Export DXF button in CadConverterForm for debugging simplified geometry. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
12
OpenNest/Forms/CadConverterForm.Designer.cs
generated
12
OpenNest/Forms/CadConverterForm.Designer.cs
generated
@@ -29,6 +29,7 @@ namespace OpenNest.Forms
|
||||
lblEntityCount = new System.Windows.Forms.Label();
|
||||
btnSplit = new System.Windows.Forms.Button();
|
||||
btnSimplify = new System.Windows.Forms.Button();
|
||||
btnExportDxf = new System.Windows.Forms.Button();
|
||||
chkShowOriginal = new System.Windows.Forms.CheckBox();
|
||||
lblDetect = new System.Windows.Forms.Label();
|
||||
cboBendDetector = new System.Windows.Forms.ComboBox();
|
||||
@@ -130,6 +131,7 @@ namespace OpenNest.Forms
|
||||
detailBar.Controls.Add(lblEntityCount);
|
||||
detailBar.Controls.Add(btnSplit);
|
||||
detailBar.Controls.Add(btnSimplify);
|
||||
detailBar.Controls.Add(btnExportDxf);
|
||||
detailBar.Controls.Add(chkShowOriginal);
|
||||
detailBar.Controls.Add(lblDetect);
|
||||
detailBar.Controls.Add(cboBendDetector);
|
||||
@@ -227,6 +229,15 @@ namespace OpenNest.Forms
|
||||
btnSimplify.Margin = new System.Windows.Forms.Padding(4, 0, 0, 0);
|
||||
btnSimplify.Click += new System.EventHandler(this.OnSimplifyClick);
|
||||
//
|
||||
// btnExportDxf
|
||||
//
|
||||
btnExportDxf.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
btnExportDxf.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||
btnExportDxf.Text = "Export DXF";
|
||||
btnExportDxf.AutoSize = true;
|
||||
btnExportDxf.Margin = new System.Windows.Forms.Padding(4, 0, 0, 0);
|
||||
btnExportDxf.Click += new System.EventHandler(this.OnExportDxfClick);
|
||||
//
|
||||
// chkShowOriginal
|
||||
//
|
||||
chkShowOriginal.AutoSize = true;
|
||||
@@ -334,6 +345,7 @@ namespace OpenNest.Forms
|
||||
private System.Windows.Forms.TextBox txtCustomer;
|
||||
private System.Windows.Forms.Button btnSplit;
|
||||
private System.Windows.Forms.Button btnSimplify;
|
||||
private System.Windows.Forms.Button btnExportDxf;
|
||||
private System.Windows.Forms.CheckBox chkShowOriginal;
|
||||
private System.Windows.Forms.ComboBox cboBendDetector;
|
||||
private System.Windows.Forms.Label lblQty;
|
||||
|
||||
@@ -436,6 +436,60 @@ namespace OpenNest.Forms
|
||||
entityView1.Invalidate();
|
||||
}
|
||||
|
||||
private void OnExportDxfClick(object sender, EventArgs e)
|
||||
{
|
||||
var item = CurrentItem;
|
||||
if (item == null) return;
|
||||
|
||||
using var dlg = new SaveFileDialog
|
||||
{
|
||||
Filter = "DXF Files|*.dxf",
|
||||
FileName = Path.ChangeExtension(item.Name, ".dxf"),
|
||||
};
|
||||
|
||||
if (dlg.ShowDialog() != DialogResult.OK) return;
|
||||
|
||||
var doc = new ACadSharp.CadDocument();
|
||||
foreach (var entity in item.Entities)
|
||||
{
|
||||
switch (entity)
|
||||
{
|
||||
case Geometry.Line line:
|
||||
doc.Entities.Add(new ACadSharp.Entities.Line
|
||||
{
|
||||
StartPoint = new CSMath.XYZ(line.StartPoint.X, line.StartPoint.Y, 0),
|
||||
EndPoint = new CSMath.XYZ(line.EndPoint.X, line.EndPoint.Y, 0),
|
||||
});
|
||||
break;
|
||||
|
||||
case Geometry.Arc arc:
|
||||
var startAngle = arc.StartAngle;
|
||||
var endAngle = arc.EndAngle;
|
||||
if (arc.IsReversed)
|
||||
OpenNest.Math.Generic.Swap(ref startAngle, ref endAngle);
|
||||
doc.Entities.Add(new ACadSharp.Entities.Arc
|
||||
{
|
||||
Center = new CSMath.XYZ(arc.Center.X, arc.Center.Y, 0),
|
||||
Radius = arc.Radius,
|
||||
StartAngle = startAngle,
|
||||
EndAngle = endAngle,
|
||||
});
|
||||
break;
|
||||
|
||||
case Geometry.Circle circle:
|
||||
doc.Entities.Add(new ACadSharp.Entities.Circle
|
||||
{
|
||||
Center = new CSMath.XYZ(circle.Center.X, circle.Center.Y, 0),
|
||||
Radius = circle.Radius,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
using var writer = new ACadSharp.IO.DxfWriter(dlg.FileName, doc, false);
|
||||
writer.Write();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Output
|
||||
|
||||
@@ -7,12 +7,8 @@ using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Forms;
|
||||
|
||||
public class SimplifierViewerForm : Form
|
||||
public partial class SimplifierViewerForm : Form
|
||||
{
|
||||
private ListView listView;
|
||||
private System.Windows.Forms.NumericUpDown numTolerance;
|
||||
private Label lblCount;
|
||||
private Button btnApply;
|
||||
private EntityView entityView;
|
||||
private GeometrySimplifier simplifier;
|
||||
private List<Shape> shapes;
|
||||
@@ -22,85 +18,10 @@ public class SimplifierViewerForm : Form
|
||||
|
||||
public SimplifierViewerForm()
|
||||
{
|
||||
Text = "Geometry Simplifier";
|
||||
FormBorderStyle = FormBorderStyle.SizableToolWindow;
|
||||
ShowInTaskbar = false;
|
||||
TopMost = true;
|
||||
StartPosition = FormStartPosition.Manual;
|
||||
Size = new System.Drawing.Size(420, 450);
|
||||
Font = new Font("Segoe UI", 9f);
|
||||
|
||||
InitializeControls();
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeControls()
|
||||
{
|
||||
// Bottom panel
|
||||
var bottomPanel = new FlowLayoutPanel
|
||||
{
|
||||
Dock = DockStyle.Bottom,
|
||||
Height = 36,
|
||||
Padding = new Padding(4, 6, 4, 4),
|
||||
WrapContents = false,
|
||||
};
|
||||
|
||||
var lblTolerance = new Label
|
||||
{
|
||||
Text = "Tolerance:",
|
||||
AutoSize = true,
|
||||
Margin = new Padding(0, 3, 2, 0),
|
||||
};
|
||||
|
||||
numTolerance = new System.Windows.Forms.NumericUpDown
|
||||
{
|
||||
Minimum = 0.001m,
|
||||
Maximum = 5.000m,
|
||||
DecimalPlaces = 3,
|
||||
Increment = 0.05m,
|
||||
Value = 0.500m,
|
||||
Width = 70,
|
||||
};
|
||||
numTolerance.ValueChanged += OnToleranceChanged;
|
||||
|
||||
lblCount = new Label
|
||||
{
|
||||
Text = "0 of 0 selected",
|
||||
AutoSize = true,
|
||||
Margin = new Padding(8, 3, 4, 0),
|
||||
};
|
||||
|
||||
btnApply = new Button
|
||||
{
|
||||
Text = "Apply",
|
||||
FlatStyle = FlatStyle.Flat,
|
||||
Width = 60,
|
||||
Margin = new Padding(4, 0, 0, 0),
|
||||
};
|
||||
btnApply.Click += OnApplyClick;
|
||||
|
||||
bottomPanel.Controls.AddRange(new Control[] { lblTolerance, numTolerance, lblCount, btnApply });
|
||||
|
||||
// ListView
|
||||
listView = new ListView
|
||||
{
|
||||
Dock = DockStyle.Fill,
|
||||
View = View.Details,
|
||||
FullRowSelect = true,
|
||||
CheckBoxes = true,
|
||||
GridLines = true,
|
||||
};
|
||||
listView.Columns.Add("Lines", 50);
|
||||
listView.Columns.Add("Radius", 70);
|
||||
listView.Columns.Add("Deviation", 75);
|
||||
listView.Columns.Add("Location", 100);
|
||||
listView.ItemSelectionChanged += OnItemSelected;
|
||||
listView.ItemChecked += OnItemChecked;
|
||||
|
||||
Controls.Add(listView);
|
||||
Controls.Add(bottomPanel);
|
||||
}
|
||||
|
||||
public void LoadShapes(List<Shape> shapes, EntityView view, double tolerance = 0.5)
|
||||
public void LoadShapes(List<Shape> shapes, EntityView view, double tolerance = 0.004)
|
||||
{
|
||||
this.shapes = shapes;
|
||||
this.entityView = view;
|
||||
@@ -119,6 +40,11 @@ public class SimplifierViewerForm : Form
|
||||
var shapeCandidates = simplifier.Analyze(shapes[i]);
|
||||
foreach (var c in shapeCandidates)
|
||||
c.ShapeIndex = i;
|
||||
|
||||
var axis = GeometrySimplifier.DetectMirrorAxis(shapes[i]);
|
||||
if (axis.IsValid)
|
||||
simplifier.Symmetrize(shapeCandidates, axis);
|
||||
|
||||
candidates.AddRange(shapeCandidates);
|
||||
}
|
||||
RefreshList();
|
||||
|
||||
Reference in New Issue
Block a user