From de6877ac4889a879c62bfa2de837c34865abffdf Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Thu, 9 Apr 2026 12:41:33 -0400 Subject: [PATCH] feat: add option to round lead-in angles for circle holes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../CuttingStrategy/ContourCuttingStrategy.cs | 16 ++++++++ .../CNC/CuttingStrategy/CuttingParameters.cs | 3 ++ OpenNest/Controls/CuttingPanel.cs | 38 ++++++++++++++++++- OpenNest/Forms/CuttingParametersSerializer.cs | 6 +++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/OpenNest.Core/CNC/CuttingStrategy/ContourCuttingStrategy.cs b/OpenNest.Core/CNC/CuttingStrategy/ContourCuttingStrategy.cs index 7416b70..ad747ce 100644 --- a/OpenNest.Core/CNC/CuttingStrategy/ContourCuttingStrategy.cs +++ b/OpenNest.Core/CNC/CuttingStrategy/ContourCuttingStrategy.cs @@ -255,7 +255,23 @@ namespace OpenNest.CNC.CuttingStrategy var leadOut = SelectLeadOut(contourType); 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); + } program.Codes.AddRange(leadIn.Generate(point, normal, winding)); diff --git a/OpenNest.Core/CNC/CuttingStrategy/CuttingParameters.cs b/OpenNest.Core/CNC/CuttingStrategy/CuttingParameters.cs index 0283348..15a1e09 100644 --- a/OpenNest.Core/CNC/CuttingStrategy/CuttingParameters.cs +++ b/OpenNest.Core/CNC/CuttingStrategy/CuttingParameters.cs @@ -23,6 +23,9 @@ namespace OpenNest.CNC.CuttingStrategy 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 AutoTabMaxSize { get; set; } diff --git a/OpenNest/Controls/CuttingPanel.cs b/OpenNest/Controls/CuttingPanel.cs index d763040..e817592 100644 --- a/OpenNest/Controls/CuttingPanel.cs +++ b/OpenNest/Controls/CuttingPanel.cs @@ -28,6 +28,9 @@ namespace OpenNest.Controls private readonly NumericUpDown nudAutoTabMax; private readonly NumericUpDown nudPierceClearance; + private readonly CheckBox chkRoundLeadInAngles; + private readonly NumericUpDown nudLeadInAngleIncrement; + private readonly Button btnAutoAssign; private bool suppressEvents; @@ -162,7 +165,7 @@ namespace OpenNest.Controls { HeaderText = "Pierce", Dock = DockStyle.Top, - ExpandedHeight = 60, + ExpandedHeight = 90, IsExpanded = true }; @@ -176,6 +179,34 @@ namespace OpenNest.Controls nudPierceClearance = CreateNumeric(130, 3, 0.0625, 0.0625); 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 btnAutoAssign = new Button { @@ -218,6 +249,8 @@ namespace OpenNest.Controls TabsEnabled = chkTabsEnabled.Checked, TabConfig = new NormalTab { Size = (double)nudTabWidth.Value }, PierceClearance = (double)nudPierceClearance.Value, + RoundLeadInAngles = chkRoundLeadInAngles.Checked, + LeadInAngleIncrement = (double)nudLeadInAngleIncrement.Value, AutoTabMinSize = (double)nudAutoTabMin.Value, AutoTabMaxSize = (double)nudAutoTabMax.Value }; @@ -238,6 +271,9 @@ namespace OpenNest.Controls if (p.TabConfig != null) nudTabWidth.Value = (decimal)p.TabConfig.Size; nudPierceClearance.Value = (decimal)p.PierceClearance; + chkRoundLeadInAngles.Checked = p.RoundLeadInAngles; + nudLeadInAngleIncrement.Value = (decimal)p.LeadInAngleIncrement; + nudLeadInAngleIncrement.Enabled = p.RoundLeadInAngles; nudAutoTabMin.Value = (decimal)p.AutoTabMinSize; nudAutoTabMax.Value = (decimal)p.AutoTabMaxSize; diff --git a/OpenNest/Forms/CuttingParametersSerializer.cs b/OpenNest/Forms/CuttingParametersSerializer.cs index a6e7d20..f66a225 100644 --- a/OpenNest/Forms/CuttingParametersSerializer.cs +++ b/OpenNest/Forms/CuttingParametersSerializer.cs @@ -24,6 +24,8 @@ namespace OpenNest.Forms TabsEnabled = p.TabsEnabled, TabWidth = p.TabConfig?.Size ?? 0.25, PierceClearance = p.PierceClearance, + RoundLeadInAngles = p.RoundLeadInAngles, + LeadInAngleIncrement = p.LeadInAngleIncrement, AutoTabMinSize = p.AutoTabMinSize, AutoTabMaxSize = p.AutoTabMaxSize }; @@ -47,6 +49,8 @@ namespace OpenNest.Forms TabsEnabled = dto.TabsEnabled, TabConfig = new NormalTab { Size = dto.TabWidth }, PierceClearance = dto.PierceClearance, + RoundLeadInAngles = dto.RoundLeadInAngles, + LeadInAngleIncrement = dto.LeadInAngleIncrement > 0 ? dto.LeadInAngleIncrement : 5.0, AutoTabMinSize = dto.AutoTabMinSize, AutoTabMaxSize = dto.AutoTabMaxSize }; @@ -111,6 +115,8 @@ namespace OpenNest.Forms public bool TabsEnabled { get; set; } public double TabWidth { get; set; } public double PierceClearance { get; set; } + public bool RoundLeadInAngles { get; set; } + public double LeadInAngleIncrement { get; set; } public double AutoTabMinSize { get; set; } public double AutoTabMaxSize { get; set; } }