diff --git a/OpenNest.Data/EdgeSpacingConfig.cs b/OpenNest.Data/EdgeSpacingConfig.cs
new file mode 100644
index 00000000..956bdfcc
--- /dev/null
+++ b/OpenNest.Data/EdgeSpacingConfig.cs
@@ -0,0 +1,10 @@
+namespace OpenNest.Data;
+
+/// A thickness's distance kept from each sheet edge, in the machine's units.
+public class EdgeSpacingConfig
+{
+ public double Left { get; set; }
+ public double Bottom { get; set; }
+ public double Right { get; set; }
+ public double Top { get; set; }
+}
diff --git a/OpenNest.Data/MachineCuttingDefaults.cs b/OpenNest.Data/MachineCuttingDefaults.cs
index 22ff48f6..c3afd6da 100644
--- a/OpenNest.Data/MachineCuttingDefaults.cs
+++ b/OpenNest.Data/MachineCuttingDefaults.cs
@@ -1,4 +1,5 @@
using OpenNest.CNC.CuttingStrategy;
+using OpenNest.Geometry;
namespace OpenNest.Data;
@@ -64,6 +65,53 @@ public static class MachineCuttingDefaults
return true;
}
+ ///
+ /// Gives the nest's plate defaults the part and edge spacing the machine has for its
+ /// material and thickness. Spacing the machine leaves unset stays as it was. Returns false,
+ /// changing nothing, when the machine sets neither.
+ ///
+ public static bool ApplySpacingTo(Nest nest, MachineConfig? machine)
+ {
+ ArgumentNullException.ThrowIfNull(nest);
+ var settings = Find(machine, nest);
+ if (settings?.PartSpacing == null && settings?.EdgeSpacing == null)
+ return false;
+
+ var scale = Scale(machine!.Units, nest.Units);
+ if (settings.PartSpacing is { } partSpacing)
+ nest.PlateDefaults.PartSpacing = partSpacing * scale;
+ if (settings.EdgeSpacing is { } edge)
+ nest.PlateDefaults.EdgeSpacing = new Spacing(edge.Left * scale, edge.Bottom * scale, edge.Right * scale, edge.Top * scale);
+ return true;
+ }
+
+ ///
+ /// Runs , then, when it changed the nest's material, grade or
+ /// thickness, applies the machine's spacing for them (). A
+ /// spacing the edit changed itself is the user's choice and stays.
+ ///
+ public static void ApplySpacingAfter(Nest nest, MachineConfig? machine, Action edit)
+ {
+ ArgumentNullException.ThrowIfNull(nest);
+ ArgumentNullException.ThrowIfNull(edit);
+ var job = (nest.Material?.Name, nest.Material?.Grade, nest.Thickness);
+ var plate = nest.PlateDefaults;
+ var (partBefore, edgeBefore) = (plate.PartSpacing, plate.EdgeSpacing);
+
+ edit();
+
+ if (job == (nest.Material?.Name, nest.Material?.Grade, nest.Thickness))
+ return;
+
+ plate = nest.PlateDefaults;
+ var (partEdited, edgeEdited) = (plate.PartSpacing, plate.EdgeSpacing);
+ ApplySpacingTo(nest, machine);
+ if (partEdited != partBefore)
+ plate.PartSpacing = partEdited;
+ if (!edgeEdited.Equals(edgeBefore))
+ plate.EdgeSpacing = edgeEdited;
+ }
+
///
/// The leads give, in the machine's units, for previewing them:
/// a lead the machine leaves as it was (Line + Line, or Clean Hole on outside cuts) is none.
diff --git a/OpenNest.Data/ThicknessConfig.cs b/OpenNest.Data/ThicknessConfig.cs
index 6d674e8b..88b93ab9 100644
--- a/OpenNest.Data/ThicknessConfig.cs
+++ b/OpenNest.Data/ThicknessConfig.cs
@@ -26,4 +26,10 @@ public class ThicknessConfig
public CutOffConfig CutOff { get; set; } = new();
public List PlateSizes { get; set; } = new();
+
+ /// The part spacing of nests in this thickness; null keeps the nest defaults'.
+ public double? PartSpacing { get; set; }
+
+ /// The edge spacing of nests in this thickness; null keeps the nest defaults'.
+ public EdgeSpacingConfig? EdgeSpacing { get; set; }
}
diff --git a/OpenNest.Tests/Data/MachineCuttingDefaultsTests.cs b/OpenNest.Tests/Data/MachineCuttingDefaultsTests.cs
index 5cb46e29..0bf281fb 100644
--- a/OpenNest.Tests/Data/MachineCuttingDefaultsTests.cs
+++ b/OpenNest.Tests/Data/MachineCuttingDefaultsTests.cs
@@ -1,5 +1,6 @@
using OpenNest.CNC.CuttingStrategy;
using OpenNest.Data;
+using OpenNest.Geometry;
namespace OpenNest.Tests.Data;
@@ -41,6 +42,119 @@ public class MachineCuttingDefaultsTests
private static readonly LeadConfig LineArc = new() { Type = "Line + Arc", Length = 0.125, Angle = 90, Radius = 0.0625 };
+ private static MachineConfig SpacedMachine(UnitSystem units = UnitSystem.Inches)
+ {
+ var machine = Machine(LineArc, units: units);
+ var thickness = machine.Materials[0].Thicknesses[0];
+ thickness.PartSpacing = 0.5;
+ thickness.EdgeSpacing = new EdgeSpacingConfig { Left = 1, Bottom = 2, Right = 3, Top = 4 };
+ return machine;
+ }
+
+ private static Nest SpacedNest(double thickness = 0.25, Units units = Units.Inches)
+ {
+ var nest = NestOf(thickness, "A36", units);
+ nest.PlateDefaults.PartSpacing = 0.1;
+ nest.PlateDefaults.EdgeSpacing = new Spacing(0.2, 0.2, 0.2, 0.2);
+ return nest;
+ }
+
+ [Fact]
+ public void ApplySpacingTo_SetsTheMachinesSpacing()
+ {
+ var nest = SpacedNest();
+
+ Assert.True(MachineCuttingDefaults.ApplySpacingTo(nest, SpacedMachine()));
+
+ Assert.Equal(0.5, nest.PlateDefaults.PartSpacing);
+ Assert.Equal(new Spacing(1, 2, 3, 4), nest.PlateDefaults.EdgeSpacing);
+ }
+
+ [Fact]
+ public void ApplySpacingTo_ConvertsToTheNestsUnits()
+ {
+ var nest = SpacedNest(6.35, Units.Millimeters);
+
+ Assert.True(MachineCuttingDefaults.ApplySpacingTo(nest, SpacedMachine()));
+
+ Assert.Equal(12.7, nest.PlateDefaults.PartSpacing, 6);
+ Assert.Equal(25.4, nest.PlateDefaults.EdgeSpacing.Left, 6);
+ }
+
+ [Fact]
+ public void ApplySpacingTo_KeepsWhatTheThicknessLeavesUnset()
+ {
+ var machine = SpacedMachine();
+ machine.Materials[0].Thicknesses[0].EdgeSpacing = null;
+ var nest = SpacedNest();
+
+ Assert.True(MachineCuttingDefaults.ApplySpacingTo(nest, machine));
+
+ Assert.Equal(0.5, nest.PlateDefaults.PartSpacing);
+ Assert.Equal(new Spacing(0.2, 0.2, 0.2, 0.2), nest.PlateDefaults.EdgeSpacing);
+ }
+
+ [Fact]
+ public void ApplySpacingTo_WithoutSpacingOnFile_ChangesNothing()
+ {
+ var nest = SpacedNest();
+
+ Assert.False(MachineCuttingDefaults.ApplySpacingTo(nest, Machine(LineArc)));
+ Assert.False(MachineCuttingDefaults.ApplySpacingTo(nest, null));
+ Assert.False(MachineCuttingDefaults.ApplySpacingTo(SpacedNest(0.5), SpacedMachine()));
+
+ Assert.Equal(0.1, nest.PlateDefaults.PartSpacing);
+ }
+
+ [Fact]
+ public void ApplySpacingAfter_FollowsAChangedThicknessOnly()
+ {
+ var nest = SpacedNest(0.5);
+
+ MachineCuttingDefaults.ApplySpacingAfter(nest, SpacedMachine(), () => nest.Customer = "Acme");
+ Assert.Equal(0.1, nest.PlateDefaults.PartSpacing);
+
+ MachineCuttingDefaults.ApplySpacingAfter(nest, SpacedMachine(), () => nest.Thickness = 0.25);
+ Assert.Equal(0.5, nest.PlateDefaults.PartSpacing);
+ }
+
+ [Fact]
+ public void ApplySpacingAfter_KeepsSpacingTheEditChanged()
+ {
+ var nest = SpacedNest(0.5);
+
+ MachineCuttingDefaults.ApplySpacingAfter(nest, SpacedMachine(), () =>
+ {
+ nest.Thickness = 0.25;
+ nest.PlateDefaults.PartSpacing = 0.3;
+ });
+
+ Assert.Equal(0.3, nest.PlateDefaults.PartSpacing);
+ Assert.Equal(new Spacing(1, 2, 3, 4), nest.PlateDefaults.EdgeSpacing);
+ }
+
+ [Fact]
+ public void Spacing_SurvivesTheMachineFile()
+ {
+ var directory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+ try
+ {
+ var provider = new LocalJsonProvider(directory);
+ var machine = SpacedMachine();
+ provider.SaveMachine(machine);
+
+ var thickness = provider.GetMachine(machine.Id)!.Materials[0].Thicknesses[0];
+
+ Assert.Equal(0.5, thickness.PartSpacing);
+ Assert.Equal(4, thickness.EdgeSpacing!.Top);
+ }
+ finally
+ {
+ if (Directory.Exists(directory))
+ Directory.Delete(directory, true);
+ }
+ }
+
[Fact]
public void IsRecordedFor_KeepsLeadsEditedAfterTheMachineSettingsWereTaken()
{
diff --git a/OpenNest.WinForms.Tests/Forms/EditNestInfoFormTests.cs b/OpenNest.WinForms.Tests/Forms/EditNestInfoFormTests.cs
index 1f2be4a1..c9e57490 100644
--- a/OpenNest.WinForms.Tests/Forms/EditNestInfoFormTests.cs
+++ b/OpenNest.WinForms.Tests/Forms/EditNestInfoFormTests.cs
@@ -74,6 +74,25 @@ public class EditNestInfoFormTests
});
}
+ [Fact]
+ public void NewNestDefaultsChoice_NeedsTheOfferAndTheCheck()
+ {
+ RunSta(() =>
+ {
+ using var form = new EditNestInfoForm();
+ var box = (CheckBox)form.Controls.Find("useForNewNestsBox", true).Single();
+
+ box.Checked = true;
+ Assert.False(form.UseForNewNests);
+
+ form.OfferNewNestDefaults = true;
+ Assert.True(form.UseForNewNests);
+
+ box.Checked = false;
+ Assert.False(form.UseForNewNests);
+ });
+ }
+
private static Nest AcceptanceNest()
{
var nest = new Nest("Acceptance nest")
diff --git a/OpenNest.WinForms.Tests/Forms/MachineCutsFormsTests.cs b/OpenNest.WinForms.Tests/Forms/MachineCutsFormsTests.cs
index cd3f0e9a..8a930892 100644
--- a/OpenNest.WinForms.Tests/Forms/MachineCutsFormsTests.cs
+++ b/OpenNest.WinForms.Tests/Forms/MachineCutsFormsTests.cs
@@ -10,6 +10,57 @@ namespace OpenNest.WinForms.Tests.Forms;
///
public class MachineCutsFormsTests
{
+ [Fact]
+ public void SpacingTab_SetsOnlyTheCheckedSpacing()
+ {
+ RunSta(() =>
+ {
+ var directory = Path.Combine(Path.GetTempPath(), "OpenNestTests", Guid.NewGuid().ToString());
+ Directory.CreateDirectory(directory);
+ try
+ {
+ var provider = new LocalJsonProvider(directory);
+ provider.SaveMachine(new MachineConfig
+ {
+ Name = "Laser",
+ Materials = { new MaterialConfig { Name = "Mild Steel", Thicknesses = { new ThicknessConfig { Value = 0.25 } } } },
+ });
+
+ using var form = new MachineConfigForm(provider);
+ var node = Descendants(Find(form, "_tree").Nodes).Single(n => n.Tag is ThicknessConfig);
+ typeof(MachineConfigForm)
+ .GetMethod("Tree_AfterSelect", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)!
+ .Invoke(form, new object[] { null, new TreeViewEventArgs(node) });
+ var shown = (ThicknessConfig)node.Tag;
+
+ var page = All(form).Single().TabPages.Cast().Single(p => p.Text == "Spacing");
+ var checks = All(page).ToList();
+ var boxes = All(page).ToList();
+ Assert.Equal(2, checks.Count);
+ Assert.Equal(5, boxes.Count);
+ Assert.All(boxes, b => Assert.False(b.Enabled));
+
+ checks[0].Checked = true;
+ boxes[0].Value = 0.5m;
+ Assert.Equal(0.5, shown.PartSpacing);
+ Assert.Null(shown.EdgeSpacing);
+
+ checks[1].Checked = true;
+ boxes[4].Value = 2m;
+ Assert.Equal(2, shown.EdgeSpacing!.Top);
+
+ checks[0].Checked = false;
+ checks[1].Checked = false;
+ Assert.Null(shown.PartSpacing);
+ Assert.Null(shown.EdgeSpacing);
+ }
+ finally
+ {
+ Directory.Delete(directory, true);
+ }
+ });
+ }
+
[Fact]
public void InsideCutsTab_GivesHolesTheirOwnLeads()
{
diff --git a/OpenNest/Controls/ReleasePanel.cs b/OpenNest/Controls/ReleasePanel.cs
index 24577820..54966cd9 100644
--- a/OpenNest/Controls/ReleasePanel.cs
+++ b/OpenNest/Controls/ReleasePanel.cs
@@ -116,7 +116,7 @@ namespace OpenNest.Controls
nestAfterReleaseCheckBox.Checked = Properties.Settings.Default.ReleaseNestAfter;
nestAfterReleaseCheckBox.Click += NestAfterReleaseCheckBox_Click;
ShowMessage = (text, buttons, icon) => MessageBox.Show(FindForm(), text, "Release", buttons, icon);
- CreateNest = (group, _) => NewNestFactory.CreateAsync(FindForm(), group.Units);
+ CreateNest = (group, _) => NewNestFactory.CreateAsync(FindForm(), group);
EditDrawing = edit =>
{
using var form = new EditLibraryDrawingForm(edit);
diff --git a/OpenNest/Forms/BomImportForm.cs b/OpenNest/Forms/BomImportForm.cs
index b758e552..909a7a93 100644
--- a/OpenNest/Forms/BomImportForm.cs
+++ b/OpenNest/Forms/BomImportForm.cs
@@ -280,6 +280,7 @@ namespace OpenNest.Forms
: _defaults.Size.Length;
var groups = BomImportGroups.Build(_parts);
+ var machine = MachineSettings.DefaultMachine(_defaults, MachineSettings.LoadMachines());
var table = new DataTable();
table.Columns.Add("Material", typeof(string));
@@ -297,6 +298,7 @@ namespace OpenNest.Forms
foreach (var group in groups)
{
var existing = _groupSettings.TryGetValue(group.Key, out var gs);
+ var defaults = GroupDefaults(group, machine);
table.Rows.Add(
group.Material,
@@ -305,11 +307,11 @@ namespace OpenNest.Forms
group.TotalQty,
existing ? gs.PlateWidth : defaultWidth,
existing ? gs.PlateLength : defaultLength,
- existing ? gs.PartSpacing : _defaults.PartSpacing,
- existing ? gs.EdgeLeft : _defaults.EdgeSpacing.Left,
- existing ? gs.EdgeBottom : _defaults.EdgeSpacing.Bottom,
- existing ? gs.EdgeRight : _defaults.EdgeSpacing.Right,
- existing ? gs.EdgeTop : _defaults.EdgeSpacing.Top
+ existing ? gs.PartSpacing : defaults.PartSpacing,
+ existing ? gs.EdgeLeft : defaults.EdgeSpacing.Left,
+ existing ? gs.EdgeBottom : defaults.EdgeSpacing.Bottom,
+ existing ? gs.EdgeRight : defaults.EdgeSpacing.Right,
+ existing ? gs.EdgeTop : defaults.EdgeSpacing.Top
);
}
@@ -327,6 +329,15 @@ namespace OpenNest.Forms
btnCreateNests.Enabled = table.Rows.Count > 0;
}
+ // The saved defaults with the machine's spacing for the group's material and thickness.
+ private NestDefaults GroupDefaults(BomImportGroup group, MachineConfig machine)
+ {
+ var nest = new Nest { Thickness = group.Thickness, Material = new Material(group.Material) };
+ _defaults.ApplyTo(nest);
+ MachineCuttingDefaults.ApplySpacingTo(nest, machine);
+ return NestDefaults.FromNest(nest);
+ }
+
private void SaveGroupSettings()
{
if (dgvGroups.DataSource is not DataTable table)
@@ -416,15 +427,16 @@ namespace OpenNest.Forms
{
if (!_groupSettings.TryGetValue(group.Key, out var plate))
{
+ var defaults = GroupDefaults(group, machine);
plate = new BomGroupPlateSettings
{
PlateWidth = defaultWidth,
PlateLength = defaultLength,
- PartSpacing = _defaults.PartSpacing,
- EdgeLeft = _defaults.EdgeSpacing.Left,
- EdgeBottom = _defaults.EdgeSpacing.Bottom,
- EdgeRight = _defaults.EdgeSpacing.Right,
- EdgeTop = _defaults.EdgeSpacing.Top,
+ PartSpacing = defaults.PartSpacing,
+ EdgeLeft = defaults.EdgeSpacing.Left,
+ EdgeBottom = defaults.EdgeSpacing.Bottom,
+ EdgeRight = defaults.EdgeSpacing.Right,
+ EdgeTop = defaults.EdgeSpacing.Top,
};
}
diff --git a/OpenNest/Forms/EditNestForm.cs b/OpenNest/Forms/EditNestForm.cs
index 9e0f03ca..d373a717 100644
--- a/OpenNest/Forms/EditNestForm.cs
+++ b/OpenNest/Forms/EditNestForm.cs
@@ -826,7 +826,11 @@ namespace OpenNest.Forms
public async Task ShowNestInfoEditorAsync()
{
- var form = new EditNestInfoForm { AllowRename = NestStorage.Settings.IsDatabaseMode };
+ var form = new EditNestInfoForm
+ {
+ AllowRename = NestStorage.Settings.IsDatabaseMode,
+ OfferNewNestDefaults = true,
+ };
form.SetMaterialCatalog(MachineSettings.LoadMaterials());
form.LoadNestInfo(Nest);
_ = ShowReleaseOriginAsync(form);
@@ -843,13 +847,15 @@ namespace OpenNest.Forms
using (var change = BeginChange("Nest Info"))
{
var before = UndoableNestInfo();
- PlateManager.ChangePlateDefaults(() => form.SaveNestInfo(Nest));
+ MachineSettings.SaveNestInfo(PlateManager, form, Nest);
if (UndoableNestInfo() != before)
change.MarkChanged();
// The material, thickness or units may have changed.
MachineSettings.ApplyCutOffDefaults(PlateView, Nest);
change.Complete();
}
+ if (form.UseForNewNests)
+ MachineSettings.SaveNestDefaults(this, NestDefaults.FromNest(Nest), Nest.Machine);
Text = Nest.Name;
drawingListBox1.Units = Nest.Units;
drawingListBox1.Invalidate();
diff --git a/OpenNest/Forms/EditNestInfoForm.Designer.cs b/OpenNest/Forms/EditNestInfoForm.Designer.cs
index bdbebc20..62c99cdc 100644
--- a/OpenNest/Forms/EditNestInfoForm.Designer.cs
+++ b/OpenNest/Forms/EditNestInfoForm.Designer.cs
@@ -47,6 +47,7 @@
this.partSpacingBox = new OpenNest.Controls.NumericUpDown();
this.labelThk = new System.Windows.Forms.Label();
this.groupBox3 = new System.Windows.Forms.GroupBox();
+ this.useForNewNestsBox = new System.Windows.Forms.CheckBox();
this.quadrantSelect1 = new OpenNest.Controls.QuadrantSelect();
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
@@ -631,6 +632,7 @@
//
// tabPage2
//
+ this.tabPage2.Controls.Add(this.useForNewNestsBox);
this.tabPage2.Controls.Add(this.groupBox3);
this.tabPage2.Controls.Add(this.groupBox1);
this.tabPage2.Controls.Add(this.tableLayoutPanel1);
@@ -642,6 +644,16 @@
this.tabPage2.Text = "Defaults";
this.tabPage2.UseVisualStyleBackColor = true;
//
+ // useForNewNestsBox
+ //
+ this.useForNewNestsBox.AutoSize = true;
+ this.useForNewNestsBox.Location = new System.Drawing.Point(12, 288);
+ this.useForNewNestsBox.Name = "useForNewNestsBox";
+ this.useForNewNestsBox.TabIndex = 8;
+ this.useForNewNestsBox.Text = "Use these defaults for new nests";
+ this.useForNewNestsBox.UseVisualStyleBackColor = true;
+ this.useForNewNestsBox.Visible = false;
+ //
// tabPage3
//
this.tabPage3.Controls.Add(this.notesBox);
@@ -772,6 +784,7 @@
private Controls.NumericUpDown partSpacingBox;
private Controls.BottomPanel bottomPanel1;
private System.Windows.Forms.GroupBox groupBox3;
+ private System.Windows.Forms.CheckBox useForNewNestsBox;
private Controls.QuadrantSelect quadrantSelect1;
private System.Windows.Forms.Button applyButton;
private System.Windows.Forms.Label releaseLabel;
diff --git a/OpenNest/Forms/EditNestInfoForm.cs b/OpenNest/Forms/EditNestInfoForm.cs
index 9fc517a9..4474c453 100644
--- a/OpenNest/Forms/EditNestInfoForm.cs
+++ b/OpenNest/Forms/EditNestInfoForm.cs
@@ -15,6 +15,7 @@ namespace OpenNest.Forms
private DateTime dateLastModified;
private List materialCatalog = new();
private string gradeMaterialKey = "";
+ private bool offerNewNestDefaults;
public EditNestInfoForm()
{
@@ -58,6 +59,24 @@ namespace OpenNest.Forms
set { nameBox.ReadOnly = !value; }
}
+ ///
+ /// Shows the Defaults tab's choice to also start new nests from these values. Hidden
+ /// by default: the tab otherwise edits only this nest.
+ ///
+ public bool OfferNewNestDefaults
+ {
+ // Not read back from the box: Visible is false once the dialog has closed.
+ get { return offerNewNestDefaults; }
+ set
+ {
+ offerNewNestDefaults = value;
+ useForNewNestsBox.Visible = value;
+ }
+ }
+
+ /// The user chose to save the Defaults tab for new nests.
+ public bool UseForNewNests => OfferNewNestDefaults && useForNewNestsBox.Checked;
+
///
/// The drawing set release this nest came from (Database mode), shown beside the
/// buttons; blank for none.
diff --git a/OpenNest/Forms/MachineConfigForm.cs b/OpenNest/Forms/MachineConfigForm.cs
index 7e763037..6c14cf00 100644
--- a/OpenNest/Forms/MachineConfigForm.cs
+++ b/OpenNest/Forms/MachineConfigForm.cs
@@ -621,6 +621,7 @@ namespace OpenNest.Forms
tabs.TabPages.Add(CreateTab("Outside Cuts", BuildOutsideCutsTab(thickness)));
tabs.TabPages.Add(CreateTab("Inside Cuts", BuildInsideCutsTab(thickness)));
tabs.TabPages.Add(CreateTab("Cut Off", BuildCutOffTab(thickness.CutOff)));
+ tabs.TabPages.Add(CreateTab("Spacing", BuildSpacingTab(thickness)));
tabs.TabPages.Add(CreateTab("Plate Sizes", BuildPlateSizesTab(thickness)));
RememberTab(tabs, _thicknessTab, t => _thicknessTab = t);
@@ -701,6 +702,76 @@ namespace OpenNest.Forms
return panel;
}
+ // Unchecked leaves the spacing to the nest defaults, so a thickness need not set both.
+ private Control BuildSpacingTab(ThicknessConfig thickness)
+ {
+ var layout = CreateDetailLayout();
+ var row = 0;
+
+ var partBox = new CheckBox
+ {
+ Text = "Use for this thickness",
+ AutoSize = true,
+ Checked = thickness.PartSpacing.HasValue,
+ };
+ var part = CreateNumericBox(thickness.PartSpacing ?? 0, v =>
+ {
+ thickness.PartSpacing = partBox.Checked ? v : null;
+ MarkDirty();
+ }, 4, 1000, 0.0625m);
+ part.Enabled = partBox.Checked;
+ partBox.CheckedChanged += (s, e) =>
+ {
+ part.Enabled = partBox.Checked;
+ thickness.PartSpacing = partBox.Checked ? (double)part.Value : null;
+ MarkDirty();
+ };
+ AddField(layout, ref row, "Part Spacing:", partBox);
+ AddField(layout, ref row, $"Spacing ({LengthUnitSuffix(null)}):", part);
+
+ var edge = thickness.EdgeSpacing;
+ var edgeBox = new CheckBox
+ {
+ Text = "Use for this thickness",
+ AutoSize = true,
+ Checked = edge != null,
+ };
+ var sides = new NumericUpDown[4];
+ void StoreEdge()
+ {
+ thickness.EdgeSpacing = edgeBox.Checked
+ ? new EdgeSpacingConfig
+ {
+ Left = (double)sides[0].Value,
+ Bottom = (double)sides[1].Value,
+ Right = (double)sides[2].Value,
+ Top = (double)sides[3].Value,
+ }
+ : null;
+ MarkDirty();
+ }
+ AddField(layout, ref row, "Edge Spacing:", edgeBox);
+ var values = new[] { edge?.Left ?? 0, edge?.Bottom ?? 0, edge?.Right ?? 0, edge?.Top ?? 0 };
+ var labels = new[] { "Left", "Bottom", "Right", "Top" }
+ .Select(side => $"{side} ({LengthUnitSuffix(null)}):").ToArray();
+ for (var i = 0; i < sides.Length; i++)
+ {
+ sides[i] = CreateNumericBox(values[i], _ => StoreEdge(), 4, 1000, 0.0625m);
+ sides[i].Enabled = edgeBox.Checked;
+ AddField(layout, ref row, labels[i], sides[i]);
+ }
+ edgeBox.CheckedChanged += (s, e) =>
+ {
+ foreach (var side in sides)
+ side.Enabled = edgeBox.Checked;
+ StoreEdge();
+ };
+
+ var panel = new Panel { Dock = DockStyle.Fill, Padding = new Padding(10) };
+ panel.Controls.Add(layout);
+ return panel;
+ }
+
private Control BuildOutsideCutsTab(ThicknessConfig thickness)
{
var editor = CreateLeadsEditor(CNC.CuttingStrategy.ContourType.External);
diff --git a/OpenNest/Forms/MachineSettings.cs b/OpenNest/Forms/MachineSettings.cs
index 90f65a23..3e058927 100644
--- a/OpenNest/Forms/MachineSettings.cs
+++ b/OpenNest/Forms/MachineSettings.cs
@@ -241,6 +241,14 @@ namespace OpenNest.Forms
return null;
}
+ ///
+ /// Saves a Nest Info dialog to : a changed material or thickness
+ /// takes the machine's spacing, and an untouched trailing sheet follows the new defaults.
+ ///
+ public static void SaveNestInfo(PlateManager manager, EditNestInfoForm form, Nest nest) =>
+ manager.ChangePlateDefaults(() => MachineCuttingDefaults.ApplySpacingAfter(
+ nest, FindMachine(nest.Machine), () => form.SaveNestInfo(nest)));
+
///
/// Gives the cut-off clearance, overtravel, minimum segment and
/// direction of the nest's machine for its material and thickness, when the machine has
@@ -317,6 +325,30 @@ namespace OpenNest.Forms
defaults.Save(NestDefaults.DefaultPath);
}
+ /// Saves the nest defaults, telling the user when they could not be saved.
+ public static void SaveNestDefaults(IWin32Window owner, NestDefaults defaults)
+ {
+ try
+ {
+ SaveNestDefaults(defaults);
+ }
+ catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
+ {
+ MessageBox.Show(owner, "The nest defaults could not be saved.\n\n" + ex.Message, "Nest Defaults",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ ///
+ /// Makes values taken from an open nest the defaults new nests start from. The nest's
+ /// machine becomes the default; a nest without one keeps the saved choice.
+ ///
+ public static void SaveNestDefaults(IWin32Window owner, NestDefaults defaults, NestMachine machine)
+ {
+ defaults.MachineId = machine?.Id ?? LoadSavedNestDefaults(out _).MachineId;
+ SaveNestDefaults(owner, defaults);
+ }
+
private static SharedSettingRecord SharedNestDefaults() =>
SharedSettings.Find(SharedSettingKinds.NestDefaults, SharedSettingKinds.DefaultKey);
}
diff --git a/OpenNest/Forms/MainForm.Nest.cs b/OpenNest/Forms/MainForm.Nest.cs
index bf786edd..1693192c 100644
--- a/OpenNest/Forms/MainForm.Nest.cs
+++ b/OpenNest/Forms/MainForm.Nest.cs
@@ -79,8 +79,9 @@ namespace OpenNest.Forms
items.Add(new ToolStripMenuItem("(no machines; see Tools > Machine Configuration)") { Enabled = false });
}
- // Assigning a machine also puts every sheet on that machine's support grid and gives
- // the views its cut-off settings; plates' own cutting parameters are kept.
+ // Assigning a machine also puts every sheet on that machine's support grid, gives the
+ // views its cut-off settings and the nest its spacing; plates' own cutting parameters are
+ // kept, and of the sheets only an untouched trailing one takes the spacing.
private void AssignMachine(MachineConfig machine)
{
var editForm = activeForm;
@@ -101,6 +102,7 @@ namespace OpenNest.Forms
MachineSettings.AssignMachine(nest, machine);
foreach (var view in views)
MachineSettings.ApplyCutOffDefaults(view, nest);
+ editForm.PlateManager.ChangePlateDefaults(() => MachineCuttingDefaults.ApplySpacingTo(nest, machine));
});
foreach (var view in views)
view.Invalidate();
diff --git a/OpenNest/Forms/MainForm.Plate.cs b/OpenNest/Forms/MainForm.Plate.cs
index f361fccd..6e46e9c3 100644
--- a/OpenNest/Forms/MainForm.Plate.cs
+++ b/OpenNest/Forms/MainForm.Plate.cs
@@ -9,7 +9,12 @@ namespace OpenNest.Forms
{
#region Plate Menu Events
- private void SetAsNestDefault_Click(object sender, EventArgs e) => WithActive(f => f.SetCurrentPlateAsNestDefault());
+ // Both this nest's next plates and every new nest start from the current plate.
+ private void SetAsNestDefault_Click(object sender, EventArgs e)
+ {
+ WithActive(f => f.SetCurrentPlateAsNestDefault());
+ SaveCurrentAsDefaults_Click(sender, e);
+ }
private void AddPlate_Click(object sender, EventArgs e)
{
diff --git a/OpenNest/Forms/MainForm.Tools.cs b/OpenNest/Forms/MainForm.Tools.cs
index fb688e80..02d7e2b5 100644
--- a/OpenNest/Forms/MainForm.Tools.cs
+++ b/OpenNest/Forms/MainForm.Tools.cs
@@ -1,5 +1,4 @@
using System;
-using System.IO;
using System.Windows.Forms;
using OpenNest.Actions;
using OpenNest.Data;
@@ -185,20 +184,7 @@ namespace OpenNest.Forms
using (var form = new NestDefaultsForm(defaults, MachineSettings.LoadMachines(), SharedSettings.NestDefaultsSource()))
{
if (form.ShowDialog(this) == DialogResult.OK)
- SaveNestDefaults(form.GetDefaults());
- }
- }
-
- private void SaveNestDefaults(NestDefaults defaults)
- {
- try
- {
- MachineSettings.SaveNestDefaults(defaults);
- }
- catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
- {
- MessageBox.Show(this, "The nest defaults could not be saved.\n\n" + ex.Message, "Nest Defaults",
- MessageBoxButtons.OK, MessageBoxIcon.Error);
+ MachineSettings.SaveNestDefaults(this, form.GetDefaults());
}
}
@@ -207,10 +193,8 @@ namespace OpenNest.Forms
if (activeForm == null)
return;
- var defaults = NestDefaults.FromPlate(activeForm.Nest.Units, activeForm.PlateView.Plate);
- // The nest's machine becomes the default; a nest without one keeps the saved choice.
- defaults.MachineId = activeForm.Nest.Machine?.Id ?? MachineSettings.LoadSavedNestDefaults(out _).MachineId;
- SaveNestDefaults(defaults);
+ MachineSettings.SaveNestDefaults(this, NestDefaults.FromPlate(activeForm.Nest.Units, activeForm.PlateView.Plate),
+ activeForm.Nest.Machine);
}
private void MachineConfig_Click(object sender, EventArgs e)
diff --git a/OpenNest/Forms/NewNestFactory.cs b/OpenNest/Forms/NewNestFactory.cs
index dd5735ef..a1973adc 100644
--- a/OpenNest/Forms/NewNestFactory.cs
+++ b/OpenNest/Forms/NewNestFactory.cs
@@ -19,13 +19,14 @@ namespace OpenNest.Forms
{
private static readonly TimeSpan ServerTimeout = TimeSpan.FromSeconds(3);
- ///
- /// The new nest's units, with the saved plate defaults converted to them (a drawing set
- /// release group drawn in other units); null uses the defaults as saved.
+ ///
+ /// The drawing set release group the nest is for: its units, with the saved plate
+ /// defaults converted to them, its material and thickness, and the machine's spacing for
+ /// those. Null uses the defaults as saved.
///
- public static async Task CreateAsync(IWin32Window owner, Units? units = null)
+ public static async Task CreateAsync(IWin32Window owner, SetReleaseGroup release = null)
{
- var nest = CreateDefaultNest(owner, units);
+ var nest = CreateDefaultNest(release);
var now = DateTime.Now;
nest.DateCreated = now;
@@ -34,15 +35,21 @@ namespace OpenNest.Forms
return nest;
}
- private static Nest CreateDefaultNest(IWin32Window owner, Units? units)
+ private static Nest CreateDefaultNest(SetReleaseGroup release)
{
var nest = new Nest();
var defaults = MachineSettings.LoadNestDefaults();
- (units is { } converted ? defaults.ConvertedTo(converted) : defaults).ApplyTo(nest);
+ (release != null ? defaults.ConvertedTo(release.Units) : defaults).ApplyTo(nest);
// After the defaults so the rail and grid are converted to the new nest's units.
var machine = MachineSettings.DefaultMachine(defaults, MachineSettings.LoadMachines());
MachineSettings.LoadClampSettings(machine).ApplyToNewNest(nest);
MachineSettings.AssignMachine(nest, machine);
+ if (release != null)
+ {
+ nest.Material = new Material(release.MaterialName, release.MaterialGrade);
+ nest.Thickness = release.Thickness;
+ MachineCuttingDefaults.ApplySpacingTo(nest, machine);
+ }
return nest;
}
diff --git a/OpenNest/Forms/SavedNestsForm.cs b/OpenNest/Forms/SavedNestsForm.cs
index 54f8898c..b78e97bc 100644
--- a/OpenNest/Forms/SavedNestsForm.cs
+++ b/OpenNest/Forms/SavedNestsForm.cs
@@ -872,7 +872,7 @@ public sealed class SavedNestsForm : Form
}
// Same default propagation as Nest > Nest Info in the open editor.
using var manager = new PlateManager(nest);
- manager.ChangePlateDefaults(() => dialog.SaveNestInfo(nest));
+ MachineSettings.SaveNestInfo(manager, dialog, nest);
nest.DateLastModified = DateTime.Now;
summaryLabel.Text = "Saving nest information...";
var bytes = await Task.Run(() =>
diff --git a/docs/machine-cutting-defaults.md b/docs/machine-cutting-defaults.md
index 07459b4f..ff775efd 100644
--- a/docs/machine-cutting-defaults.md
+++ b/docs/machine-cutting-defaults.md
@@ -1,7 +1,7 @@
# Machine cutting defaults
A nest's assigned machine (`Nest.Machine`, see [nest file format](nest-file-format.md))
-supplies the starting lead-ins, lead-outs, kerf and cut-off settings for the nest's
+supplies the starting lead-ins, lead-outs, kerf, cut-off settings and spacing for the nest's
material and thickness, from that machine's Tools > Machine Configuration entry (in
Database mode, the server's [shared machines](shared-settings.md) once it holds them).
`MachineCuttingDefaults` (OpenNest.Data) holds the mapping; the desktop applies it.
@@ -47,6 +47,16 @@ which comes from the thickness). Types map as follows:
Lead-outs map None, Line (length, angle) and Arc (radius); any other type is none.
+## Spacing
+
+Each thickness can set a part spacing and an edge spacing (the Spacing tab), each optional:
+one left unset stays at the nest defaults' value. The machine's spacing replaces the nest's
+plate defaults when Nest > Edit or Nest Information changes the material, grade or thickness
+(a spacing edited in the same dialog stays), when Nest > Machine changes the machine, and when a
+drawing set release creates a nest. BOM import starts each group's row from it. File > New has
+no material yet, so a new nest starts from the nest defaults. Plates are kept as they are, except
+an untouched trailing empty sheet, which follows the plate defaults.
+
## Cut-offs
The plate view's part clearance, overtravel, minimum segment length and direction come from
diff --git a/docs/shared-settings.md b/docs/shared-settings.md
index f749735f..3bc5b867 100644
--- a/docs/shared-settings.md
+++ b/docs/shared-settings.md
@@ -13,6 +13,10 @@ building nests and posts the same way:
File mode, the console, MCP and API front ends use only this PC's copies.
+Nest defaults are saved by Tools > Nest Defaults, Tools > Save Current Plate as Defaults,
+Plate > Set as Default, and Nest > Edit's Defaults tab with "Use these defaults for new nests"
+checked. Without that check the tab changes only the open nest.
+
## Which copy is used
Each kind is read from the server once the server holds it, and from this PC's copy until