From a8e42fb4b5b8caaa3391d40491a8ad89d4cf5384 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Mon, 6 Apr 2026 09:10:06 -0400 Subject: [PATCH] feat: use nest template for BOM import spacing defaults, editable per group BOM import now loads the nest template to populate plate size, part spacing, edge spacing, and quadrant instead of hard-coding defaults. Spacing columns are shown per material+thickness group on the Groups tab so each combo can be adjusted independently. Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest/Forms/BomImportForm.Designer.cs | 84 ++++++++-------- OpenNest/Forms/BomImportForm.cs | 118 +++++++++++++++++------ 2 files changed, 133 insertions(+), 69 deletions(-) diff --git a/OpenNest/Forms/BomImportForm.Designer.cs b/OpenNest/Forms/BomImportForm.Designer.cs index c213982..1361777 100644 --- a/OpenNest/Forms/BomImportForm.Designer.cs +++ b/OpenNest/Forms/BomImportForm.Designer.cs @@ -50,9 +50,9 @@ namespace OpenNest.Forms ((System.ComponentModel.ISupportInitialize)dgvGroups).BeginInit(); pnlBottom.SuspendLayout(); SuspendLayout(); - // + // // grpInput - // + // grpInput.Controls.Add(tbl); grpInput.Dock = System.Windows.Forms.DockStyle.Top; grpInput.Location = new System.Drawing.Point(0, 0); @@ -62,9 +62,9 @@ namespace OpenNest.Forms grpInput.TabIndex = 0; grpInput.TabStop = false; grpInput.Text = "Input"; - // + // // tbl - // + // tbl.ColumnCount = 3; tbl.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); tbl.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); @@ -92,9 +92,9 @@ namespace OpenNest.Forms tbl.RowStyles.Add(new System.Windows.Forms.RowStyle()); tbl.Size = new System.Drawing.Size(792, 172); tbl.TabIndex = 0; - // + // // lblJobName - // + // lblJobName.Anchor = System.Windows.Forms.AnchorStyles.Left; lblJobName.AutoSize = true; lblJobName.Location = new System.Drawing.Point(6, 13); @@ -103,9 +103,9 @@ namespace OpenNest.Forms lblJobName.Size = new System.Drawing.Size(63, 15); lblJobName.TabIndex = 0; lblJobName.Text = "Job Name:"; - // + // // txtJobName - // + // tbl.SetColumnSpan(txtJobName, 2); txtJobName.Dock = System.Windows.Forms.DockStyle.Fill; txtJobName.Location = new System.Drawing.Point(79, 9); @@ -113,9 +113,9 @@ namespace OpenNest.Forms txtJobName.Name = "txtJobName"; txtJobName.Size = new System.Drawing.Size(707, 23); txtJobName.TabIndex = 1; - // + // // lblBomFile - // + // lblBomFile.Anchor = System.Windows.Forms.AnchorStyles.Left; lblBomFile.AutoSize = true; lblBomFile.Location = new System.Drawing.Point(6, 45); @@ -124,9 +124,9 @@ namespace OpenNest.Forms lblBomFile.Size = new System.Drawing.Size(58, 15); lblBomFile.TabIndex = 2; lblBomFile.Text = "BOM File:"; - // + // // txtBomFile - // + // txtBomFile.Dock = System.Windows.Forms.DockStyle.Fill; txtBomFile.Location = new System.Drawing.Point(79, 41); txtBomFile.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); @@ -134,9 +134,9 @@ namespace OpenNest.Forms txtBomFile.ReadOnly = true; txtBomFile.Size = new System.Drawing.Size(669, 23); txtBomFile.TabIndex = 3; - // + // // btnBrowseBom - // + // btnBrowseBom.Location = new System.Drawing.Point(751, 40); btnBrowseBom.Margin = new System.Windows.Forms.Padding(0, 5, 3, 3); btnBrowseBom.Name = "btnBrowseBom"; @@ -144,9 +144,9 @@ namespace OpenNest.Forms btnBrowseBom.TabIndex = 4; btnBrowseBom.Text = "..."; btnBrowseBom.Click += BrowseBom_Click; - // + // // lblDxfFolder - // + // lblDxfFolder.Anchor = System.Windows.Forms.AnchorStyles.Left; lblDxfFolder.AutoSize = true; lblDxfFolder.Location = new System.Drawing.Point(6, 78); @@ -155,9 +155,9 @@ namespace OpenNest.Forms lblDxfFolder.Size = new System.Drawing.Size(67, 15); lblDxfFolder.TabIndex = 5; lblDxfFolder.Text = "DXF Folder:"; - // + // // txtDxfFolder - // + // txtDxfFolder.Dock = System.Windows.Forms.DockStyle.Fill; txtDxfFolder.Location = new System.Drawing.Point(79, 74); txtDxfFolder.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); @@ -165,9 +165,9 @@ namespace OpenNest.Forms txtDxfFolder.ReadOnly = true; txtDxfFolder.Size = new System.Drawing.Size(669, 23); txtDxfFolder.TabIndex = 6; - // + // // btnBrowseDxf - // + // btnBrowseDxf.Location = new System.Drawing.Point(751, 73); btnBrowseDxf.Margin = new System.Windows.Forms.Padding(0, 5, 3, 3); btnBrowseDxf.Name = "btnBrowseDxf"; @@ -175,9 +175,9 @@ namespace OpenNest.Forms btnBrowseDxf.TabIndex = 7; btnBrowseDxf.Text = "..."; btnBrowseDxf.Click += BrowseDxf_Click; - // + // // lblPlateSize - // + // lblPlateSize.Anchor = System.Windows.Forms.AnchorStyles.Left; lblPlateSize.AutoSize = true; lblPlateSize.Location = new System.Drawing.Point(6, 112); @@ -186,9 +186,9 @@ namespace OpenNest.Forms lblPlateSize.Size = new System.Drawing.Size(59, 15); lblPlateSize.TabIndex = 8; lblPlateSize.Text = "Plate Size:"; - // + // // platePanel - // + // platePanel.AutoSize = true; platePanel.Controls.Add(txtPlateWidth); platePanel.Controls.Add(lblPlateX); @@ -199,17 +199,17 @@ namespace OpenNest.Forms platePanel.Size = new System.Drawing.Size(156, 29); platePanel.TabIndex = 9; platePanel.WrapContents = false; - // + // // txtPlateWidth - // + // txtPlateWidth.Location = new System.Drawing.Point(3, 3); txtPlateWidth.Name = "txtPlateWidth"; txtPlateWidth.Size = new System.Drawing.Size(60, 23); txtPlateWidth.TabIndex = 0; txtPlateWidth.Text = "60"; - // + // // lblPlateX - // + // lblPlateX.Anchor = System.Windows.Forms.AnchorStyles.Left; lblPlateX.AutoSize = true; lblPlateX.Location = new System.Drawing.Point(69, 7); @@ -217,17 +217,17 @@ namespace OpenNest.Forms lblPlateX.Size = new System.Drawing.Size(18, 15); lblPlateX.TabIndex = 1; lblPlateX.Text = " x "; - // + // // txtPlateLength - // + // txtPlateLength.Location = new System.Drawing.Point(93, 3); txtPlateLength.Name = "txtPlateLength"; txtPlateLength.Size = new System.Drawing.Size(60, 23); txtPlateLength.TabIndex = 2; txtPlateLength.Text = "120"; - // + // // btnAnalyze - // + // btnAnalyze.Anchor = System.Windows.Forms.AnchorStyles.Right; tbl.SetColumnSpan(btnAnalyze, 2); btnAnalyze.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold); @@ -291,9 +291,9 @@ namespace OpenNest.Forms dgvGroups.RowHeadersVisible = false; dgvGroups.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; dgvGroups.TabIndex = 0; - // + // // pnlBottom - // + // pnlBottom.Controls.Add(lblSummary); pnlBottom.Controls.Add(btnCreateNests); pnlBottom.Controls.Add(btnClose); @@ -303,9 +303,9 @@ namespace OpenNest.Forms pnlBottom.Padding = new System.Windows.Forms.Padding(10); pnlBottom.Size = new System.Drawing.Size(804, 50); pnlBottom.TabIndex = 2; - // + // // lblSummary - // + // lblSummary.AutoSize = true; lblSummary.Dock = System.Windows.Forms.DockStyle.Left; lblSummary.ForeColor = System.Drawing.Color.Gray; @@ -314,9 +314,9 @@ namespace OpenNest.Forms lblSummary.Size = new System.Drawing.Size(0, 15); lblSummary.TabIndex = 0; lblSummary.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - // + // // btnCreateNests - // + // btnCreateNests.Dock = System.Windows.Forms.DockStyle.Right; btnCreateNests.Enabled = false; btnCreateNests.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold); @@ -327,9 +327,9 @@ namespace OpenNest.Forms btnCreateNests.TabIndex = 1; btnCreateNests.Text = "Create Nests"; btnCreateNests.Click += CreateNests_Click; - // + // // btnClose - // + // btnClose.DialogResult = System.Windows.Forms.DialogResult.Cancel; btnClose.Dock = System.Windows.Forms.DockStyle.Right; btnClose.Location = new System.Drawing.Point(714, 10); @@ -338,9 +338,9 @@ namespace OpenNest.Forms btnClose.TabIndex = 2; btnClose.Text = "Close"; btnClose.Click += BtnClose_Click; - // + // // BomImportForm - // + // AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; CancelButton = btnClose; diff --git a/OpenNest/Forms/BomImportForm.cs b/OpenNest/Forms/BomImportForm.cs index 64019a4..190722f 100644 --- a/OpenNest/Forms/BomImportForm.cs +++ b/OpenNest/Forms/BomImportForm.cs @@ -16,8 +16,9 @@ namespace OpenNest.Forms public partial class BomImportForm : Form { private List _parts; - private Dictionary _plateSizes; + private Dictionary _groupSettings; private bool _suppressRegroup; + private Nest.PlateSettings _templateDefaults; public Form MdiParentForm { get; set; } @@ -25,7 +26,38 @@ namespace OpenNest.Forms { InitializeComponent(); _parts = new List(); - _plateSizes = new Dictionary(); + _groupSettings = new Dictionary(); + _templateDefaults = LoadTemplateDefaults(); + ApplyTemplateDefaults(); + } + + private Nest.PlateSettings LoadTemplateDefaults() + { + var templatePath = Properties.Settings.Default.NestTemplatePath; + if (File.Exists(templatePath)) + { + try + { + var nest = new NestReader(templatePath).Read(); + return nest.PlateDefaults; + } + catch { } + } + + // Fallback defaults matching CreateDefaultNest + return new Nest.PlateSettings + { + Size = new Geometry.Size(100, 100), + Quadrant = 1, + PartSpacing = 1, + EdgeSpacing = new Spacing(1, 1, 1, 1), + }; + } + + private void ApplyTemplateDefaults() + { + txtPlateWidth.Text = _templateDefaults.Size.Width.ToString("0.####"); + txtPlateLength.Text = _templateDefaults.Size.Length.ToString("0.####"); } #region File Browsing @@ -154,7 +186,7 @@ namespace OpenNest.Forms _parts.Add(row); } - _plateSizes.Clear(); + _groupSettings.Clear(); } #endregion @@ -244,11 +276,11 @@ namespace OpenNest.Forms private void RebuildGroups() { - // Save existing plate sizes before rebuilding - SavePlateSizes(); + // Save existing settings before rebuilding + SaveGroupSettings(); - var defaultWidth = double.TryParse(txtPlateWidth.Text, out var w) ? w : 60; - var defaultLength = double.TryParse(txtPlateLength.Text, out var l) ? l : 120; + var defaultWidth = double.TryParse(txtPlateWidth.Text, out var w) ? w : _templateDefaults.Size.Width; + var defaultLength = double.TryParse(txtPlateLength.Text, out var l) ? l : _templateDefaults.Size.Length; var groups = _parts .Where(p => p.IsEditable @@ -270,6 +302,11 @@ namespace OpenNest.Forms table.Columns.Add("Total Qty", typeof(int)); table.Columns.Add("Plate Width", typeof(double)); table.Columns.Add("Plate Length", typeof(double)); + table.Columns.Add("Part Spacing", typeof(double)); + table.Columns.Add("Edge Left", typeof(double)); + table.Columns.Add("Edge Bottom", typeof(double)); + table.Columns.Add("Edge Right", typeof(double)); + table.Columns.Add("Edge Top", typeof(double)); foreach (var group in groups) { @@ -277,23 +314,27 @@ namespace OpenNest.Forms var thickness = group.Key.Thickness; var key = GroupKey(material, thickness); - var plateWidth = _plateSizes.TryGetValue(key, out var size) ? size.Width : defaultWidth; - var plateLength = _plateSizes.TryGetValue(key, out _) ? size.Length : defaultLength; + var existing = _groupSettings.TryGetValue(key, out var gs); table.Rows.Add( material, thickness, group.Count(), group.Sum(p => p.Qty ?? 0), - plateWidth, - plateLength + existing ? gs.PlateWidth : defaultWidth, + existing ? gs.PlateLength : defaultLength, + existing ? gs.PartSpacing : _templateDefaults.PartSpacing, + existing ? gs.EdgeLeft : _templateDefaults.EdgeSpacing.Left, + existing ? gs.EdgeBottom : _templateDefaults.EdgeSpacing.Bottom, + existing ? gs.EdgeRight : _templateDefaults.EdgeSpacing.Right, + existing ? gs.EdgeTop : _templateDefaults.EdgeSpacing.Top ); } dgvGroups.DataSource = table; // Material, Thickness, Parts, Total Qty are read-only - if (dgvGroups.Columns.Count >= 6) + if (dgvGroups.Columns.Count > 0) { dgvGroups.Columns["Material"].ReadOnly = true; dgvGroups.Columns["Thickness"].ReadOnly = true; @@ -304,22 +345,28 @@ namespace OpenNest.Forms btnCreateNests.Enabled = table.Rows.Count > 0; } - private void SavePlateSizes() + private void SaveGroupSettings() { if (dgvGroups.DataSource is not DataTable table) return; - _plateSizes.Clear(); + _groupSettings.Clear(); foreach (DataRow row in table.Rows) { var material = row["Material"]?.ToString() ?? ""; var thickness = row["Thickness"] is double t ? t : 0; var key = GroupKey(material, thickness); - var width = row["Plate Width"] is double pw ? pw : 60; - var length = row["Plate Length"] is double pl ? pl : 120; - - _plateSizes[key] = (width, length); + _groupSettings[key] = new GroupSettings + { + PlateWidth = row["Plate Width"] is double pw ? pw : _templateDefaults.Size.Width, + PlateLength = row["Plate Length"] is double pl ? pl : _templateDefaults.Size.Length, + PartSpacing = row["Part Spacing"] is double ps ? ps : _templateDefaults.PartSpacing, + EdgeLeft = row["Edge Left"] is double el ? el : _templateDefaults.EdgeSpacing.Left, + EdgeBottom = row["Edge Bottom"] is double eb ? eb : _templateDefaults.EdgeSpacing.Bottom, + EdgeRight = row["Edge Right"] is double er ? er : _templateDefaults.EdgeSpacing.Right, + EdgeTop = row["Edge Top"] is double et ? et : _templateDefaults.EdgeSpacing.Top, + }; } } @@ -356,11 +403,11 @@ namespace OpenNest.Forms if (_parts == null || _parts.Count == 0) return; - // Save latest plate size edits - SavePlateSizes(); + // Save latest group edits + SaveGroupSettings(); - var defaultWidth = double.TryParse(txtPlateWidth.Text, out var dw) ? dw : 60; - var defaultLength = double.TryParse(txtPlateLength.Text, out var dl) ? dl : 120; + var defaultWidth = double.TryParse(txtPlateWidth.Text, out var dw) ? dw : _templateDefaults.Size.Width; + var defaultLength = double.TryParse(txtPlateLength.Text, out var dl) ? dl : _templateDefaults.Size.Length; var groups = _parts .Where(p => p.IsEditable @@ -391,8 +438,14 @@ namespace OpenNest.Forms var thickness = group.Key.Thickness; var key = GroupKey(material, thickness); - var plateWidth = _plateSizes.TryGetValue(key, out var size) ? size.Width : defaultWidth; - var plateLength = _plateSizes.TryGetValue(key, out _) ? size.Length : defaultLength; + var hasSettings = _groupSettings.TryGetValue(key, out var gs); + var plateWidth = hasSettings ? gs.PlateWidth : defaultWidth; + var plateLength = hasSettings ? gs.PlateLength : defaultLength; + var partSpacing = hasSettings ? gs.PartSpacing : _templateDefaults.PartSpacing; + var edgeLeft = hasSettings ? gs.EdgeLeft : _templateDefaults.EdgeSpacing.Left; + var edgeBottom = hasSettings ? gs.EdgeBottom : _templateDefaults.EdgeSpacing.Bottom; + var edgeRight = hasSettings ? gs.EdgeRight : _templateDefaults.EdgeSpacing.Right; + var edgeTop = hasSettings ? gs.EdgeTop : _templateDefaults.EdgeSpacing.Top; var nestName = $"{jobName} - {thickness:0.###} {material}"; var nest = new Nest(nestName); @@ -401,9 +454,9 @@ namespace OpenNest.Forms nest.PlateDefaults.Size = new Geometry.Size(plateWidth, plateLength); nest.Thickness = thickness; nest.Material = new Material(material); - nest.PlateDefaults.Quadrant = 1; - nest.PlateDefaults.PartSpacing = 1; - nest.PlateDefaults.EdgeSpacing = new Spacing(1, 1, 1, 1); + nest.PlateDefaults.Quadrant = _templateDefaults.Quadrant; + nest.PlateDefaults.PartSpacing = partSpacing; + nest.PlateDefaults.EdgeSpacing = new Spacing(edgeLeft, edgeBottom, edgeRight, edgeTop); foreach (var part in group) { @@ -486,4 +539,15 @@ namespace OpenNest.Forms public string Status { get; set; } public bool IsEditable { get; set; } } + + internal class GroupSettings + { + public double PlateWidth { get; set; } + public double PlateLength { get; set; } + public double PartSpacing { get; set; } + public double EdgeLeft { get; set; } + public double EdgeBottom { get; set; } + public double EdgeRight { get; set; } + public double EdgeTop { get; set; } + } }