feat: add option to round lead-in angles for circle holes

Snaps lead-in angles on ArcCircle contours to a configurable
increment (default 5°), reducing unique hole variations from
infinite to 72 max. Rounding happens upstream in EmitContour
so the PlateView and post output stay in sync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-09 12:41:33 -04:00
parent 3481764416
commit de6877ac48
4 changed files with 62 additions and 1 deletions
@@ -255,7 +255,23 @@ namespace OpenNest.CNC.CuttingStrategy
var leadOut = SelectLeadOut(contourType); var leadOut = SelectLeadOut(contourType);
if (contourType == ContourType.ArcCircle && entity is Circle circle) if (contourType == ContourType.ArcCircle && entity is Circle circle)
{
if (Parameters.RoundLeadInAngles && Parameters.LeadInAngleIncrement > 0)
{
var increment = Angle.ToRadians(Parameters.LeadInAngleIncrement);
normal = System.Math.Round(normal / increment) * increment;
normal = Angle.NormalizeRad(normal);
// Recompute contour start point on the circle at the rounded angle.
// For ArcCircle, normal points inward (toward center), so outward = normal - PI.
var outwardAngle = normal - System.Math.PI;
point = new Vector(
circle.Center.X + circle.Radius * System.Math.Cos(outwardAngle),
circle.Center.Y + circle.Radius * System.Math.Sin(outwardAngle));
}
leadIn = ClampLeadInForCircle(leadIn, circle, point, normal); leadIn = ClampLeadInForCircle(leadIn, circle, point, normal);
}
program.Codes.AddRange(leadIn.Generate(point, normal, winding)); program.Codes.AddRange(leadIn.Generate(point, normal, winding));
@@ -23,6 +23,9 @@ namespace OpenNest.CNC.CuttingStrategy
public double PierceClearance { get; set; } = 0.0625; public double PierceClearance { get; set; } = 0.0625;
public bool RoundLeadInAngles { get; set; }
public double LeadInAngleIncrement { get; set; } = 5.0;
public double AutoTabMinSize { get; set; } public double AutoTabMinSize { get; set; }
public double AutoTabMaxSize { get; set; } public double AutoTabMaxSize { get; set; }
+37 -1
View File
@@ -28,6 +28,9 @@ namespace OpenNest.Controls
private readonly NumericUpDown nudAutoTabMax; private readonly NumericUpDown nudAutoTabMax;
private readonly NumericUpDown nudPierceClearance; private readonly NumericUpDown nudPierceClearance;
private readonly CheckBox chkRoundLeadInAngles;
private readonly NumericUpDown nudLeadInAngleIncrement;
private readonly Button btnAutoAssign; private readonly Button btnAutoAssign;
private bool suppressEvents; private bool suppressEvents;
@@ -162,7 +165,7 @@ namespace OpenNest.Controls
{ {
HeaderText = "Pierce", HeaderText = "Pierce",
Dock = DockStyle.Top, Dock = DockStyle.Top,
ExpandedHeight = 60, ExpandedHeight = 90,
IsExpanded = true IsExpanded = true
}; };
@@ -176,6 +179,34 @@ namespace OpenNest.Controls
nudPierceClearance = CreateNumeric(130, 3, 0.0625, 0.0625); nudPierceClearance = CreateNumeric(130, 3, 0.0625, 0.0625);
piercePanel.ContentPanel.Controls.Add(nudPierceClearance); piercePanel.ContentPanel.Controls.Add(nudPierceClearance);
chkRoundLeadInAngles = new CheckBox
{
Text = "Round Lead-In Angles",
Location = new Point(12, 32),
AutoSize = true
};
chkRoundLeadInAngles.CheckedChanged += (s, e) =>
{
nudLeadInAngleIncrement.Enabled = chkRoundLeadInAngles.Checked;
OnParametersChanged();
};
piercePanel.ContentPanel.Controls.Add(chkRoundLeadInAngles);
piercePanel.ContentPanel.Controls.Add(new Label
{
Text = "Increment:",
Location = new Point(175, 34),
AutoSize = true
});
nudLeadInAngleIncrement = CreateNumeric(245, 31, 5, 1);
nudLeadInAngleIncrement.DecimalPlaces = 0;
nudLeadInAngleIncrement.Minimum = 1;
nudLeadInAngleIncrement.Maximum = 90;
nudLeadInAngleIncrement.Enabled = false;
nudLeadInAngleIncrement.ValueChanged += (s, e) => OnParametersChanged();
piercePanel.ContentPanel.Controls.Add(nudLeadInAngleIncrement);
// Auto-Assign button — wrapped in a panel for Dock.Top with padding // Auto-Assign button — wrapped in a panel for Dock.Top with padding
btnAutoAssign = new Button btnAutoAssign = new Button
{ {
@@ -218,6 +249,8 @@ namespace OpenNest.Controls
TabsEnabled = chkTabsEnabled.Checked, TabsEnabled = chkTabsEnabled.Checked,
TabConfig = new NormalTab { Size = (double)nudTabWidth.Value }, TabConfig = new NormalTab { Size = (double)nudTabWidth.Value },
PierceClearance = (double)nudPierceClearance.Value, PierceClearance = (double)nudPierceClearance.Value,
RoundLeadInAngles = chkRoundLeadInAngles.Checked,
LeadInAngleIncrement = (double)nudLeadInAngleIncrement.Value,
AutoTabMinSize = (double)nudAutoTabMin.Value, AutoTabMinSize = (double)nudAutoTabMin.Value,
AutoTabMaxSize = (double)nudAutoTabMax.Value AutoTabMaxSize = (double)nudAutoTabMax.Value
}; };
@@ -238,6 +271,9 @@ namespace OpenNest.Controls
if (p.TabConfig != null) if (p.TabConfig != null)
nudTabWidth.Value = (decimal)p.TabConfig.Size; nudTabWidth.Value = (decimal)p.TabConfig.Size;
nudPierceClearance.Value = (decimal)p.PierceClearance; nudPierceClearance.Value = (decimal)p.PierceClearance;
chkRoundLeadInAngles.Checked = p.RoundLeadInAngles;
nudLeadInAngleIncrement.Value = (decimal)p.LeadInAngleIncrement;
nudLeadInAngleIncrement.Enabled = p.RoundLeadInAngles;
nudAutoTabMin.Value = (decimal)p.AutoTabMinSize; nudAutoTabMin.Value = (decimal)p.AutoTabMinSize;
nudAutoTabMax.Value = (decimal)p.AutoTabMaxSize; nudAutoTabMax.Value = (decimal)p.AutoTabMaxSize;
@@ -24,6 +24,8 @@ namespace OpenNest.Forms
TabsEnabled = p.TabsEnabled, TabsEnabled = p.TabsEnabled,
TabWidth = p.TabConfig?.Size ?? 0.25, TabWidth = p.TabConfig?.Size ?? 0.25,
PierceClearance = p.PierceClearance, PierceClearance = p.PierceClearance,
RoundLeadInAngles = p.RoundLeadInAngles,
LeadInAngleIncrement = p.LeadInAngleIncrement,
AutoTabMinSize = p.AutoTabMinSize, AutoTabMinSize = p.AutoTabMinSize,
AutoTabMaxSize = p.AutoTabMaxSize AutoTabMaxSize = p.AutoTabMaxSize
}; };
@@ -47,6 +49,8 @@ namespace OpenNest.Forms
TabsEnabled = dto.TabsEnabled, TabsEnabled = dto.TabsEnabled,
TabConfig = new NormalTab { Size = dto.TabWidth }, TabConfig = new NormalTab { Size = dto.TabWidth },
PierceClearance = dto.PierceClearance, PierceClearance = dto.PierceClearance,
RoundLeadInAngles = dto.RoundLeadInAngles,
LeadInAngleIncrement = dto.LeadInAngleIncrement > 0 ? dto.LeadInAngleIncrement : 5.0,
AutoTabMinSize = dto.AutoTabMinSize, AutoTabMinSize = dto.AutoTabMinSize,
AutoTabMaxSize = dto.AutoTabMaxSize AutoTabMaxSize = dto.AutoTabMaxSize
}; };
@@ -111,6 +115,8 @@ namespace OpenNest.Forms
public bool TabsEnabled { get; set; } public bool TabsEnabled { get; set; }
public double TabWidth { get; set; } public double TabWidth { get; set; }
public double PierceClearance { get; set; } public double PierceClearance { get; set; }
public bool RoundLeadInAngles { get; set; }
public double LeadInAngleIncrement { get; set; }
public double AutoTabMinSize { get; set; } public double AutoTabMinSize { get; set; }
public double AutoTabMaxSize { get; set; } public double AutoTabMaxSize { get; set; }
} }