Files
OpenNest/OpenNest/Controls/CuttingPanel.cs
T
aj aec0523062 style: apply CSharpier formatting to all C# sources
Repo-wide sweep with the pinned CSharpier 1.3.0 tool. Whitespace and
line-wrapping only; OpenNest.Engine.Tests (109) and OpenNest.IO.Tests
pass after reformat, full solution builds 0 errors.

Added .csharpierignore so csproj/config XML keeps its existing layout
(CSharpier's XML wrapping churns attributes with zero benefit).

Formatting is now enforceable: dotnet csharpier check . passes.
2026-09-20 16:41:50 -04:00

726 lines
25 KiB
C#

using System;
using System.Drawing;
using System.Windows.Forms;
using OpenNest.CNC.CuttingStrategy;
namespace OpenNest.Controls
{
public class CuttingPanel : Panel
{
private static readonly string[] LeadInTypes =
{
"None",
"Line",
"Arc",
"Line + Arc",
"Clean Hole",
"Line + Line",
};
private static readonly string[] LeadOutTypes = { "None", "Line", "Arc" };
private readonly TabControl tabControl;
private readonly ComboBox cboExternalLeadIn,
cboExternalLeadOut;
private readonly ComboBox cboInternalLeadIn,
cboInternalLeadOut;
private readonly ComboBox cboArcCircleLeadIn,
cboArcCircleLeadOut;
private readonly Panel pnlExternalLeadIn,
pnlExternalLeadOut;
private readonly Panel pnlInternalLeadIn,
pnlInternalLeadOut;
private readonly Panel pnlArcCircleLeadIn,
pnlArcCircleLeadOut;
private readonly CheckBox chkTabsEnabled;
private readonly NumericUpDown nudTabWidth;
private readonly RadioButton rbTabAll;
private readonly RadioButton rbAutoTab;
private readonly NumericUpDown nudAutoTabMin;
private readonly NumericUpDown nudAutoTabMax;
private readonly NumericUpDown nudPierceClearance;
private readonly CheckBox chkRoundLeadInAngles;
private readonly NumericUpDown nudLeadInAngleIncrement;
private readonly Button btnAutoAssign;
private bool suppressEvents;
public event EventHandler ParametersChanged;
public event EventHandler AutoAssignClicked;
public bool ShowAutoAssign
{
get => btnAutoAssign.Visible;
set => btnAutoAssign.Visible = value;
}
public ContourType? ActiveContourType
{
get
{
return tabControl.SelectedIndex switch
{
0 => ContourType.External,
1 => ContourType.Internal,
2 => ContourType.ArcCircle,
_ => null,
};
}
set
{
if (value == null)
return;
var index = value.Value switch
{
ContourType.External => 0,
ContourType.Internal => 1,
ContourType.ArcCircle => 2,
_ => -1,
};
if (index >= 0 && tabControl.SelectedIndex != index)
tabControl.SelectedIndex = index;
}
}
public CuttingPanel()
{
AutoScroll = true;
BackColor = Color.White;
// Tab control for contour types — wrapped in a fixed-height panel for Dock.Top
tabControl = new TabControl { Dock = DockStyle.Fill };
var tabExternal = new TabPage("External") { Padding = new Padding(4) };
var tabInternal = new TabPage("Internal") { Padding = new Padding(4) };
var tabArcCircle = new TabPage("Arc / Circle") { Padding = new Padding(4) };
SetupTab(
tabExternal,
out cboExternalLeadIn,
out pnlExternalLeadIn,
out cboExternalLeadOut,
out pnlExternalLeadOut
);
SetupTab(
tabInternal,
out cboInternalLeadIn,
out pnlInternalLeadIn,
out cboInternalLeadOut,
out pnlInternalLeadOut
);
SetupTab(
tabArcCircle,
out cboArcCircleLeadIn,
out pnlArcCircleLeadIn,
out cboArcCircleLeadOut,
out pnlArcCircleLeadOut
);
tabControl.Controls.Add(tabExternal);
tabControl.Controls.Add(tabInternal);
tabControl.Controls.Add(tabArcCircle);
var tabWrapper = new Panel { Dock = DockStyle.Top, Height = 340 };
tabWrapper.Controls.Add(tabControl);
// Tabs section
var tabsPanel = new CollapsiblePanel
{
HeaderText = "Tabs",
Dock = DockStyle.Top,
ExpandedHeight = 160,
IsExpanded = false,
};
chkTabsEnabled = new CheckBox
{
Text = "Enable Tabs",
Location = new Point(12, 4),
AutoSize = true,
};
tabsPanel.ContentPanel.Controls.Add(chkTabsEnabled);
tabsPanel.ContentPanel.Controls.Add(
new Label
{
Text = "Tab Size:",
Location = new Point(160, 6),
AutoSize = true,
}
);
nudTabWidth = CreateNumeric(225, 3, 0.25, 0.0625);
nudTabWidth.Enabled = false;
tabsPanel.ContentPanel.Controls.Add(nudTabWidth);
rbTabAll = new RadioButton
{
Text = "Tab all parts",
Location = new Point(28, 28),
AutoSize = true,
Enabled = false,
Checked = true,
};
tabsPanel.ContentPanel.Controls.Add(rbTabAll);
rbAutoTab = new RadioButton
{
Text = "Auto-tab when smallest part dimension is between:",
Location = new Point(28, 50),
AutoSize = true,
Enabled = false,
};
tabsPanel.ContentPanel.Controls.Add(rbAutoTab);
tabsPanel.ContentPanel.Controls.Add(
new Label
{
Text = "Min:",
Location = new Point(44, 76),
AutoSize = true,
}
);
nudAutoTabMin = CreateNumeric(77, 73, 0, 0.0625);
nudAutoTabMin.Enabled = false;
tabsPanel.ContentPanel.Controls.Add(nudAutoTabMin);
tabsPanel.ContentPanel.Controls.Add(
new Label
{
Text = "Max:",
Location = new Point(210, 76),
AutoSize = true,
}
);
nudAutoTabMax = CreateNumeric(245, 73, 0, 0.0625);
nudAutoTabMax.Enabled = false;
tabsPanel.ContentPanel.Controls.Add(nudAutoTabMax);
chkTabsEnabled.CheckedChanged += (s, e) =>
{
var enabled = chkTabsEnabled.Checked;
nudTabWidth.Enabled = enabled;
rbTabAll.Enabled = enabled;
rbAutoTab.Enabled = enabled;
nudAutoTabMin.Enabled = enabled && rbAutoTab.Checked;
nudAutoTabMax.Enabled = enabled && rbAutoTab.Checked;
OnParametersChanged();
};
rbTabAll.CheckedChanged += (s, e) =>
{
nudAutoTabMin.Enabled = chkTabsEnabled.Checked && rbAutoTab.Checked;
nudAutoTabMax.Enabled = chkTabsEnabled.Checked && rbAutoTab.Checked;
OnParametersChanged();
};
// Pierce section
var piercePanel = new CollapsiblePanel
{
HeaderText = "Pierce",
Dock = DockStyle.Top,
ExpandedHeight = 90,
IsExpanded = true,
};
piercePanel.ContentPanel.Controls.Add(
new Label
{
Text = "Pierce Clearance:",
Location = new Point(12, 6),
AutoSize = true,
}
);
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
{
Text = "Auto-Assign Lead-ins",
Dock = DockStyle.Top,
Height = 32,
Visible = false,
};
btnAutoAssign.Click += (s, e) => AutoAssignClicked?.Invoke(this, EventArgs.Empty);
var btnWrapper = new Panel
{
Dock = DockStyle.Top,
Height = 36,
Padding = new Padding(4, 2, 4, 2),
};
btnWrapper.Controls.Add(btnAutoAssign);
// Add in reverse order — Dock.Top stacks top-down
Controls.Add(btnWrapper);
Controls.Add(piercePanel);
Controls.Add(tabsPanel);
Controls.Add(tabWrapper);
// Wire up change events
PopulateDropdowns();
WireChangeEvents();
}
public CuttingParameters BuildParameters()
{
return new CuttingParameters
{
ExternalLeadIn = BuildLeadIn(cboExternalLeadIn, pnlExternalLeadIn),
ExternalLeadOut = BuildLeadOut(cboExternalLeadOut, pnlExternalLeadOut),
InternalLeadIn = BuildLeadIn(cboInternalLeadIn, pnlInternalLeadIn),
InternalLeadOut = BuildLeadOut(cboInternalLeadOut, pnlInternalLeadOut),
ArcCircleLeadIn = BuildLeadIn(cboArcCircleLeadIn, pnlArcCircleLeadIn),
ArcCircleLeadOut = BuildLeadOut(cboArcCircleLeadOut, pnlArcCircleLeadOut),
TabsEnabled = chkTabsEnabled.Checked && rbTabAll.Checked,
TabConfig = new NormalTab { Size = (double)nudTabWidth.Value },
PierceClearance = (double)nudPierceClearance.Value,
RoundLeadInAngles = chkRoundLeadInAngles.Checked,
LeadInAngleIncrement = (double)nudLeadInAngleIncrement.Value,
AutoTabMinSize =
chkTabsEnabled.Checked && rbAutoTab.Checked ? (double)nudAutoTabMin.Value : 0,
AutoTabMaxSize =
chkTabsEnabled.Checked && rbAutoTab.Checked ? (double)nudAutoTabMax.Value : 0,
};
}
public void LoadFromParameters(CuttingParameters p)
{
suppressEvents = true;
LoadLeadIn(cboExternalLeadIn, pnlExternalLeadIn, p.ExternalLeadIn);
LoadLeadOut(cboExternalLeadOut, pnlExternalLeadOut, p.ExternalLeadOut);
LoadLeadIn(cboInternalLeadIn, pnlInternalLeadIn, p.InternalLeadIn);
LoadLeadOut(cboInternalLeadOut, pnlInternalLeadOut, p.InternalLeadOut);
LoadLeadIn(cboArcCircleLeadIn, pnlArcCircleLeadIn, p.ArcCircleLeadIn);
LoadLeadOut(cboArcCircleLeadOut, pnlArcCircleLeadOut, p.ArcCircleLeadOut);
var hasAutoTab = p.AutoTabMinSize > 0 || p.AutoTabMaxSize > 0;
chkTabsEnabled.Checked = p.TabsEnabled || hasAutoTab;
rbAutoTab.Checked = hasAutoTab;
rbTabAll.Checked = !hasAutoTab;
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;
suppressEvents = false;
}
private void OnParametersChanged()
{
if (!suppressEvents)
ParametersChanged?.Invoke(this, EventArgs.Empty);
}
private static void SetupTab(
TabPage tab,
out ComboBox leadInCombo,
out Panel leadInPanel,
out ComboBox leadOutCombo,
out Panel leadOutPanel
)
{
var grpLeadIn = new GroupBox
{
Text = "Lead-In",
Location = new Point(4, 4),
Size = new Size(340, 148),
};
tab.Controls.Add(grpLeadIn);
grpLeadIn.Controls.Add(
new Label
{
Text = "Type:",
Location = new Point(8, 22),
AutoSize = true,
}
);
leadInCombo = new ComboBox
{
DropDownStyle = ComboBoxStyle.DropDownList,
Location = new Point(90, 19),
Size = new Size(230, 24),
};
grpLeadIn.Controls.Add(leadInCombo);
leadInPanel = new Panel
{
Location = new Point(8, 48),
Size = new Size(320, 92),
AutoScroll = true,
};
grpLeadIn.Controls.Add(leadInPanel);
var grpLeadOut = new GroupBox
{
Text = "Lead-Out",
Location = new Point(4, 156),
Size = new Size(340, 132),
};
tab.Controls.Add(grpLeadOut);
grpLeadOut.Controls.Add(
new Label
{
Text = "Type:",
Location = new Point(8, 22),
AutoSize = true,
}
);
leadOutCombo = new ComboBox
{
DropDownStyle = ComboBoxStyle.DropDownList,
Location = new Point(90, 19),
Size = new Size(230, 24),
};
grpLeadOut.Controls.Add(leadOutCombo);
leadOutPanel = new Panel
{
Location = new Point(8, 48),
Size = new Size(320, 76),
AutoScroll = true,
};
grpLeadOut.Controls.Add(leadOutPanel);
}
private void PopulateDropdowns()
{
foreach (
var combo in new[] { cboExternalLeadIn, cboInternalLeadIn, cboArcCircleLeadIn }
)
{
combo.Items.AddRange(LeadInTypes);
combo.SelectedIndex = 0;
}
foreach (
var combo in new[] { cboExternalLeadOut, cboInternalLeadOut, cboArcCircleLeadOut }
)
{
combo.Items.AddRange(LeadOutTypes);
combo.SelectedIndex = 0;
}
}
private void WireChangeEvents()
{
cboExternalLeadIn.SelectedIndexChanged += OnLeadInTypeChanged;
cboInternalLeadIn.SelectedIndexChanged += OnLeadInTypeChanged;
cboArcCircleLeadIn.SelectedIndexChanged += OnLeadInTypeChanged;
cboExternalLeadOut.SelectedIndexChanged += OnLeadOutTypeChanged;
cboInternalLeadOut.SelectedIndexChanged += OnLeadOutTypeChanged;
cboArcCircleLeadOut.SelectedIndexChanged += OnLeadOutTypeChanged;
}
private void OnLeadInTypeChanged(object sender, EventArgs e)
{
var combo = (ComboBox)sender;
var panel = GetLeadInPanel(combo);
if (panel != null)
BuildLeadInParamControls(panel, combo.SelectedIndex);
OnParametersChanged();
}
private void OnLeadOutTypeChanged(object sender, EventArgs e)
{
var combo = (ComboBox)sender;
var panel = GetLeadOutPanel(combo);
if (panel != null)
BuildLeadOutParamControls(panel, combo.SelectedIndex);
OnParametersChanged();
}
private Panel GetLeadInPanel(ComboBox combo)
{
if (combo == cboExternalLeadIn)
return pnlExternalLeadIn;
if (combo == cboInternalLeadIn)
return pnlInternalLeadIn;
if (combo == cboArcCircleLeadIn)
return pnlArcCircleLeadIn;
return null;
}
private Panel GetLeadOutPanel(ComboBox combo)
{
if (combo == cboExternalLeadOut)
return pnlExternalLeadOut;
if (combo == cboInternalLeadOut)
return pnlInternalLeadOut;
if (combo == cboArcCircleLeadOut)
return pnlArcCircleLeadOut;
return null;
}
private void BuildLeadInParamControls(Panel panel, int typeIndex)
{
panel.Controls.Clear();
var y = 0;
switch (typeIndex)
{
case 1:
AddNumericField(panel, "Length:", 0.25, ref y, "Length");
AddNumericField(panel, "Approach Angle:", 90, ref y, "ApproachAngle");
break;
case 2:
AddNumericField(panel, "Radius:", 0.25, ref y, "Radius");
break;
case 3:
AddNumericField(panel, "Line Length:", 0.25, ref y, "LineLength");
AddNumericField(panel, "Arc Radius:", 0.125, ref y, "ArcRadius");
AddNumericField(panel, "Approach Angle:", 135, ref y, "ApproachAngle");
break;
case 4:
AddNumericField(panel, "Line Length:", 0.25, ref y, "LineLength");
AddNumericField(panel, "Arc Radius:", 0.125, ref y, "ArcRadius");
AddNumericField(panel, "Kerf:", 0.06, ref y, "Kerf");
break;
case 5:
AddNumericField(panel, "Length 1:", 0.25, ref y, "Length1");
AddNumericField(panel, "Angle 1:", 90, ref y, "Angle1");
AddNumericField(panel, "Length 2:", 0.25, ref y, "Length2");
AddNumericField(panel, "Angle 2:", 90, ref y, "Angle2");
break;
}
}
private void BuildLeadOutParamControls(Panel panel, int typeIndex)
{
panel.Controls.Clear();
var y = 0;
switch (typeIndex)
{
case 1:
AddNumericField(panel, "Length:", 0.25, ref y, "Length");
AddNumericField(panel, "Approach Angle:", 90, ref y, "ApproachAngle");
break;
case 2:
AddNumericField(panel, "Radius:", 0.25, ref y, "Radius");
break;
}
}
private void AddNumericField(
Panel panel,
string label,
double defaultValue,
ref int y,
string tag
)
{
panel.Controls.Add(
new Label
{
Text = label,
Location = new Point(0, y + 3),
AutoSize = true,
}
);
var nud = CreateNumeric(130, y, defaultValue, 0.0625);
nud.Tag = tag;
nud.ValueChanged += (s, e) => OnParametersChanged();
panel.Controls.Add(nud);
y += 30;
}
private static NumericUpDown CreateNumeric(
int x,
int y,
double defaultValue,
double increment
)
{
return new NumericUpDown
{
Location = new Point(x, y),
Size = new Size(120, 22),
DecimalPlaces = 4,
Increment = (decimal)increment,
Minimum = 0,
Maximum = 9999,
Value = (decimal)defaultValue,
};
}
private static void LoadLeadIn(ComboBox combo, Panel panel, LeadIn leadIn)
{
switch (leadIn)
{
case LineLeadIn line:
combo.SelectedIndex = 1;
SetParam(panel, "Length", line.Length);
SetParam(panel, "ApproachAngle", line.ApproachAngle);
break;
case ArcLeadIn arc:
combo.SelectedIndex = 2;
SetParam(panel, "Radius", arc.Radius);
break;
case LineArcLeadIn lineArc:
combo.SelectedIndex = 3;
SetParam(panel, "LineLength", lineArc.LineLength);
SetParam(panel, "ArcRadius", lineArc.ArcRadius);
SetParam(panel, "ApproachAngle", lineArc.ApproachAngle);
break;
case CleanHoleLeadIn cleanHole:
combo.SelectedIndex = 4;
SetParam(panel, "LineLength", cleanHole.LineLength);
SetParam(panel, "ArcRadius", cleanHole.ArcRadius);
SetParam(panel, "Kerf", cleanHole.Kerf);
break;
case LineLineLeadIn lineLine:
combo.SelectedIndex = 5;
SetParam(panel, "Length1", lineLine.Length1);
SetParam(panel, "Angle1", lineLine.ApproachAngle1);
SetParam(panel, "Length2", lineLine.Length2);
SetParam(panel, "Angle2", lineLine.ApproachAngle2);
break;
default:
combo.SelectedIndex = 0;
break;
}
}
private static void LoadLeadOut(ComboBox combo, Panel panel, LeadOut leadOut)
{
switch (leadOut)
{
case LineLeadOut line:
combo.SelectedIndex = 1;
SetParam(panel, "Length", line.Length);
SetParam(panel, "ApproachAngle", line.ApproachAngle);
break;
case ArcLeadOut arc:
combo.SelectedIndex = 2;
SetParam(panel, "Radius", arc.Radius);
break;
default:
combo.SelectedIndex = 0;
break;
}
}
private static LeadIn BuildLeadIn(ComboBox combo, Panel panel)
{
return combo.SelectedIndex switch
{
1 => new LineLeadIn
{
Length = GetParam(panel, "Length", 0.25),
ApproachAngle = GetParam(panel, "ApproachAngle", 90),
},
2 => new ArcLeadIn { Radius = GetParam(panel, "Radius", 0.25) },
3 => new LineArcLeadIn
{
LineLength = GetParam(panel, "LineLength", 0.25),
ArcRadius = GetParam(panel, "ArcRadius", 0.125),
ApproachAngle = GetParam(panel, "ApproachAngle", 135),
},
4 => new CleanHoleLeadIn
{
LineLength = GetParam(panel, "LineLength", 0.25),
ArcRadius = GetParam(panel, "ArcRadius", 0.125),
Kerf = GetParam(panel, "Kerf", 0.06),
},
5 => new LineLineLeadIn
{
Length1 = GetParam(panel, "Length1", 0.25),
ApproachAngle1 = GetParam(panel, "Angle1", 90),
Length2 = GetParam(panel, "Length2", 0.25),
ApproachAngle2 = GetParam(panel, "Angle2", 90),
},
_ => new NoLeadIn(),
};
}
private static LeadOut BuildLeadOut(ComboBox combo, Panel panel)
{
return combo.SelectedIndex switch
{
1 => new LineLeadOut
{
Length = GetParam(panel, "Length", 0.25),
ApproachAngle = GetParam(panel, "ApproachAngle", 90),
},
2 => new ArcLeadOut { Radius = GetParam(panel, "Radius", 0.25) },
_ => new NoLeadOut(),
};
}
private static void SetParam(Panel panel, string tag, double value)
{
foreach (Control c in panel.Controls)
{
if (c is NumericUpDown nud && (string)nud.Tag == tag)
{
nud.Value = (decimal)value;
return;
}
}
}
private static double GetParam(Panel panel, string tag, double defaultValue)
{
foreach (Control c in panel.Controls)
{
if (c is NumericUpDown nud && (string)nud.Tag == tag)
return (double)nud.Value;
}
return defaultValue;
}
}
}