feat: persist cutting parameters and add pierce clearance UI

Save/restore cutting parameters as JSON in user settings so values
survive between sessions. Add pierce clearance numeric input to the
CuttingParametersForm.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-30 19:35:51 -04:00
parent e860ca3f4a
commit 428dbdb03c
6 changed files with 228 additions and 4 deletions

View File

@@ -114,7 +114,7 @@ namespace OpenNest.Forms
this.bottomPanel.Controls.Add(this.acceptButton);
this.bottomPanel.Controls.Add(this.cancelButton);
this.bottomPanel.Dock = System.Windows.Forms.DockStyle.Bottom;
this.bottomPanel.Location = new System.Drawing.Point(0, 406);
this.bottomPanel.Location = new System.Drawing.Point(0, 466);
this.bottomPanel.Name = "bottomPanel";
this.bottomPanel.Size = new System.Drawing.Size(380, 50);
this.bottomPanel.TabIndex = 1;
@@ -125,7 +125,7 @@ namespace OpenNest.Forms
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelButton;
this.ClientSize = new System.Drawing.Size(380, 456);
this.ClientSize = new System.Drawing.Size(380, 516);
this.Controls.Add(this.tabControl);
this.Controls.Add(this.bottomPanel);
this.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));

View File

@@ -22,8 +22,20 @@ namespace OpenNest.Forms
private CheckBox chkTabsEnabled;
private NumericUpDown nudTabWidth;
private NumericUpDown nudPierceClearance;
public CuttingParameters Parameters { get; set; } = new CuttingParameters();
private bool hasCustomParameters;
private CuttingParameters parameters = new CuttingParameters();
public CuttingParameters Parameters
{
get => parameters;
set
{
parameters = value;
hasCustomParameters = true;
}
}
public CuttingParametersForm()
{
@@ -54,9 +66,33 @@ namespace OpenNest.Forms
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// If caller didn't provide custom parameters, try loading saved ones
if (!hasCustomParameters)
{
var json = Properties.Settings.Default.CuttingParametersJson;
if (!string.IsNullOrEmpty(json))
{
try { Parameters = CuttingParametersSerializer.Deserialize(json); }
catch { /* use defaults on corrupt data */ }
}
}
LoadFromParameters(Parameters);
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if (DialogResult == System.Windows.Forms.DialogResult.OK)
{
var json = CuttingParametersSerializer.Serialize(BuildParameters());
Properties.Settings.Default.CuttingParametersJson = json;
Properties.Settings.Default.Save();
}
}
private static void SetupTab(TabPage tab,
out ComboBox leadInCombo, out Panel leadInPanel,
out ComboBox leadOutCombo, out Panel leadOutPanel)
@@ -164,6 +200,35 @@ namespace OpenNest.Forms
grpTabs.Controls.Add(nudTabWidth);
Controls.Add(grpTabs);
var grpPierce = new GroupBox
{
Text = "Pierce",
Location = new System.Drawing.Point(4, 410),
Size = new System.Drawing.Size(372, 55),
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right
};
grpPierce.Controls.Add(new Label
{
Text = "Pierce Clearance:",
Location = new System.Drawing.Point(12, 23),
AutoSize = true
});
nudPierceClearance = new NumericUpDown
{
Location = new System.Drawing.Point(130, 20),
Size = new System.Drawing.Size(100, 22),
DecimalPlaces = 4,
Increment = 0.0625m,
Minimum = 0,
Maximum = 9999,
Value = 0.0625m
};
grpPierce.Controls.Add(nudPierceClearance);
Controls.Add(grpPierce);
}
private void PopulateDropdowns()
@@ -305,6 +370,8 @@ namespace OpenNest.Forms
chkTabsEnabled.Checked = p.TabsEnabled;
if (p.TabConfig != null)
nudTabWidth.Value = (decimal)p.TabConfig.Size;
nudPierceClearance.Value = (decimal)p.PierceClearance;
}
private static void LoadLeadIn(ComboBox combo, Panel panel, LeadIn leadIn)
@@ -379,7 +446,8 @@ namespace OpenNest.Forms
ArcCircleLeadIn = BuildLeadIn(cboArcCircleLeadIn, pnlArcCircleLeadIn),
ArcCircleLeadOut = BuildLeadOut(cboArcCircleLeadOut, pnlArcCircleLeadOut),
TabsEnabled = chkTabsEnabled.Checked,
TabConfig = new NormalTab { Size = (double)nudTabWidth.Value }
TabConfig = new NormalTab { Size = (double)nudTabWidth.Value },
PierceClearance = (double)nudPierceClearance.Value
};
return p;
}

View File

@@ -0,0 +1,138 @@
using OpenNest.CNC.CuttingStrategy;
using System.Text.Json;
namespace OpenNest.Forms
{
internal static class CuttingParametersSerializer
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = false,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
public static string Serialize(CuttingParameters p)
{
var dto = new CuttingParametersDto
{
ExternalLeadIn = ToDto(p.ExternalLeadIn),
ExternalLeadOut = ToLeadOutDto(p.ExternalLeadOut),
InternalLeadIn = ToDto(p.InternalLeadIn),
InternalLeadOut = ToLeadOutDto(p.InternalLeadOut),
ArcCircleLeadIn = ToDto(p.ArcCircleLeadIn),
ArcCircleLeadOut = ToLeadOutDto(p.ArcCircleLeadOut),
TabsEnabled = p.TabsEnabled,
TabWidth = p.TabConfig?.Size ?? 0.25,
PierceClearance = p.PierceClearance
};
return JsonSerializer.Serialize(dto, JsonOptions);
}
public static CuttingParameters Deserialize(string json)
{
var dto = JsonSerializer.Deserialize<CuttingParametersDto>(json, JsonOptions);
if (dto == null)
return new CuttingParameters();
return new CuttingParameters
{
ExternalLeadIn = FromDto(dto.ExternalLeadIn),
ExternalLeadOut = FromLeadOutDto(dto.ExternalLeadOut),
InternalLeadIn = FromDto(dto.InternalLeadIn),
InternalLeadOut = FromLeadOutDto(dto.InternalLeadOut),
ArcCircleLeadIn = FromDto(dto.ArcCircleLeadIn),
ArcCircleLeadOut = FromLeadOutDto(dto.ArcCircleLeadOut),
TabsEnabled = dto.TabsEnabled,
TabConfig = new NormalTab { Size = dto.TabWidth },
PierceClearance = dto.PierceClearance
};
}
private static LeadInDto ToDto(LeadIn leadIn)
{
return leadIn switch
{
LineLeadIn line => new LeadInDto { Type = "Line", Length = line.Length, ApproachAngle = line.ApproachAngle },
ArcLeadIn arc => new LeadInDto { Type = "Arc", Radius = arc.Radius },
LineArcLeadIn la => new LeadInDto { Type = "LineArc", LineLength = la.LineLength, ArcRadius = la.ArcRadius, ApproachAngle = la.ApproachAngle },
CleanHoleLeadIn ch => new LeadInDto { Type = "CleanHole", LineLength = ch.LineLength, ArcRadius = ch.ArcRadius, Kerf = ch.Kerf },
LineLineLeadIn ll => new LeadInDto { Type = "LineLine", Length1 = ll.Length1, Angle1 = ll.ApproachAngle1, Length2 = ll.Length2, Angle2 = ll.ApproachAngle2 },
_ => new LeadInDto { Type = "None" }
};
}
private static LeadIn FromDto(LeadInDto dto)
{
if (dto == null) return new NoLeadIn();
return dto.Type switch
{
"Line" => new LineLeadIn { Length = dto.Length, ApproachAngle = dto.ApproachAngle },
"Arc" => new ArcLeadIn { Radius = dto.Radius },
"LineArc" => new LineArcLeadIn { LineLength = dto.LineLength, ArcRadius = dto.ArcRadius, ApproachAngle = dto.ApproachAngle },
"CleanHole" => new CleanHoleLeadIn { LineLength = dto.LineLength, ArcRadius = dto.ArcRadius, Kerf = dto.Kerf },
"LineLine" => new LineLineLeadIn { Length1 = dto.Length1, ApproachAngle1 = dto.Angle1, Length2 = dto.Length2, ApproachAngle2 = dto.Angle2 },
_ => new NoLeadIn()
};
}
private static LeadOutDto ToLeadOutDto(LeadOut leadOut)
{
return leadOut switch
{
LineLeadOut line => new LeadOutDto { Type = "Line", Length = line.Length, ApproachAngle = line.ApproachAngle },
ArcLeadOut arc => new LeadOutDto { Type = "Arc", Radius = arc.Radius },
MicrotabLeadOut mt => new LeadOutDto { Type = "Microtab", GapSize = mt.GapSize },
_ => new LeadOutDto { Type = "None" }
};
}
private static LeadOut FromLeadOutDto(LeadOutDto dto)
{
if (dto == null) return new NoLeadOut();
return dto.Type switch
{
"Line" => new LineLeadOut { Length = dto.Length, ApproachAngle = dto.ApproachAngle },
"Arc" => new ArcLeadOut { Radius = dto.Radius },
"Microtab" => new MicrotabLeadOut { GapSize = dto.GapSize },
_ => new NoLeadOut()
};
}
private class CuttingParametersDto
{
public LeadInDto ExternalLeadIn { get; set; }
public LeadOutDto ExternalLeadOut { get; set; }
public LeadInDto InternalLeadIn { get; set; }
public LeadOutDto InternalLeadOut { get; set; }
public LeadInDto ArcCircleLeadIn { get; set; }
public LeadOutDto ArcCircleLeadOut { get; set; }
public bool TabsEnabled { get; set; }
public double TabWidth { get; set; }
public double PierceClearance { get; set; }
}
private class LeadInDto
{
public string Type { get; set; } = "None";
public double Length { get; set; }
public double ApproachAngle { get; set; }
public double Radius { get; set; }
public double LineLength { get; set; }
public double ArcRadius { get; set; }
public double Kerf { get; set; }
public double Length1 { get; set; }
public double Angle1 { get; set; }
public double Length2 { get; set; }
public double Angle2 { get; set; }
}
private class LeadOutDto
{
public string Type { get; set; } = "None";
public double Length { get; set; }
public double ApproachAngle { get; set; }
public double Radius { get; set; }
public double GapSize { get; set; }
}
}
}

View File

@@ -226,5 +226,17 @@ namespace OpenNest.Properties {
this["DisabledStrategies"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string CuttingParametersJson {
get {
return ((string)(this["CuttingParametersJson"]));
}
set {
this["CuttingParametersJson"] = value;
}
}
}
}

View File

@@ -53,5 +53,8 @@
<Setting Name="DisabledStrategies" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="CuttingParametersJson" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
</Settings>
</SettingsFile>

View File

@@ -56,6 +56,9 @@
<setting name="LastPierceTime" serializeAs="String">
<value>0</value>
</setting>
<setting name="CuttingParametersJson" serializeAs="String">
<value/>
</setting>
</OpenNest.Properties.Settings>
<OpenNest.Resources.Settings>
<setting name="MainFormLocation" serializeAs="String">