From b970629a59e6c13925b5bf2f8379bcf9cbf34d71 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Mon, 23 Mar 2026 22:00:57 -0400 Subject: [PATCH] feat: add material library resolution, assist gas support, and UI fixes - Add MaterialLibraryResolver for Cincinnati post processor to resolve G89 library files from material/thickness/gas configuration - Add Nest.AssistGas property with serialization support in nest format - Add etch library support with separate gas configuration - Fix CutOff tests to match AwayFromOrigin default cut direction - Fix plate info label not updating after ResizePlateToFitParts - Add cutoff and remnants toolbar button icons Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest.Core/Nest.cs | 2 + OpenNest.IO/NestFormat.cs | 1 + OpenNest.IO/NestReader.cs | 1 + OpenNest.IO/NestWriter.cs | 1 + .../CincinnatiPostConfig.cs | 41 ++++- .../CincinnatiPostProcessor.cs | 32 ++-- .../MaterialLibraryResolver.cs | 42 +++++ .../MaterialLibraryResolverTests.cs | 160 ++++++++++++++++++ OpenNest.Tests/CutOffTests.cs | 20 +-- OpenNest/Forms/EditNestForm.cs | 1 + OpenNest/Forms/MainForm.Designer.cs | 29 +++- OpenNest/Properties/Resources.Designer.cs | 20 +++ OpenNest/Properties/Resources.resx | 6 + OpenNest/Resources/cutoff.png | Bin 0 -> 994 bytes OpenNest/Resources/remnants.png | Bin 0 -> 1219 bytes 15 files changed, 327 insertions(+), 29 deletions(-) create mode 100644 OpenNest.Posts.Cincinnati/MaterialLibraryResolver.cs create mode 100644 OpenNest.Tests/Cincinnati/MaterialLibraryResolverTests.cs create mode 100644 OpenNest/Resources/cutoff.png create mode 100644 OpenNest/Resources/remnants.png diff --git a/OpenNest.Core/Nest.cs b/OpenNest.Core/Nest.cs index 7169100..511f28a 100644 --- a/OpenNest.Core/Nest.cs +++ b/OpenNest.Core/Nest.cs @@ -36,6 +36,8 @@ namespace OpenNest public string Notes { get; set; } + public string AssistGas { get; set; } = ""; + public Units Units { get; set; } public DateTime DateCreated { get; set; } diff --git a/OpenNest.IO/NestFormat.cs b/OpenNest.IO/NestFormat.cs index c21b550..3c8c086 100644 --- a/OpenNest.IO/NestFormat.cs +++ b/OpenNest.IO/NestFormat.cs @@ -23,6 +23,7 @@ namespace OpenNest.IO public string DateCreated { get; init; } = ""; public string DateLastModified { get; init; } = ""; public string Notes { get; init; } = ""; + public string AssistGas { get; init; } = ""; public PlateDefaultsDto PlateDefaults { get; init; } = new(); public List Drawings { get; init; } = new(); public List Plates { get; init; } = new(); diff --git a/OpenNest.IO/NestReader.cs b/OpenNest.IO/NestReader.cs index 6072d9a..805d8d9 100644 --- a/OpenNest.IO/NestReader.cs +++ b/OpenNest.IO/NestReader.cs @@ -160,6 +160,7 @@ namespace OpenNest.IO nest.DateCreated = DateTime.Parse(dto.DateCreated); nest.DateLastModified = DateTime.Parse(dto.DateLastModified); nest.Notes = dto.Notes; + nest.AssistGas = dto.AssistGas ?? ""; // Plate defaults var pd = dto.PlateDefaults; diff --git a/OpenNest.IO/NestWriter.cs b/OpenNest.IO/NestWriter.cs index f8bfea2..2316b30 100644 --- a/OpenNest.IO/NestWriter.cs +++ b/OpenNest.IO/NestWriter.cs @@ -77,6 +77,7 @@ namespace OpenNest.IO DateCreated = nest.DateCreated.ToString("o"), DateLastModified = nest.DateLastModified.ToString("o"), Notes = nest.Notes ?? "", + AssistGas = nest.AssistGas ?? "", PlateDefaults = BuildPlateDefaultsDto(), Drawings = BuildDrawingDtos(), Plates = BuildPlateDtos() diff --git a/OpenNest.Posts.Cincinnati/CincinnatiPostConfig.cs b/OpenNest.Posts.Cincinnati/CincinnatiPostConfig.cs index bf26be8..34092e6 100644 --- a/OpenNest.Posts.Cincinnati/CincinnatiPostConfig.cs +++ b/OpenNest.Posts.Cincinnati/CincinnatiPostConfig.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace OpenNest.Posts.Cincinnati { /// @@ -153,16 +155,29 @@ namespace OpenNest.Posts.Cincinnati public G89Mode ProcessParameterMode { get; set; } = G89Mode.LibraryFile; /// - /// Gets or sets the default G89 library file path. - /// Default: empty string + /// Gets or sets the default assist gas when Nest.AssistGas is empty. + /// Default: "O2" /// - public string DefaultLibraryFile { get; set; } = ""; + public string DefaultAssistGas { get; set; } = "O2"; /// - /// Gets or sets whether to repeat G89 before each feature. - /// Default: true + /// Gets or sets the gas used for etch operations. + /// Independent of the cutting assist gas — etch typically requires a specific gas. + /// Default: "N2" /// - public bool RepeatG89BeforeEachFeature { get; set; } = true; + public string DefaultEtchGas { get; set; } = "N2"; + + /// + /// Gets or sets the material-to-library mapping for cut operations. + /// Each entry maps (material, thickness, gas) to a G89 library file. + /// + public List MaterialLibraries { get; set; } = new(); + + /// + /// Gets or sets the gas-to-library mapping for etch operations. + /// Each entry maps a gas type to a G89 etch library file. + /// + public List EtchLibraries { get; set; } = new(); /// /// Gets or sets whether to use exact stop mode (G61). @@ -272,4 +287,18 @@ namespace OpenNest.Posts.Cincinnati /// public int SheetLengthVariable { get; set; } = 111; } + + public class MaterialLibraryEntry + { + public string Material { get; set; } = ""; + public double Thickness { get; set; } + public string Gas { get; set; } = ""; + public string Library { get; set; } = ""; + } + + public class EtchLibraryEntry + { + public string Gas { get; set; } = ""; + public string Library { get; set; } = ""; + } } diff --git a/OpenNest.Posts.Cincinnati/CincinnatiPostProcessor.cs b/OpenNest.Posts.Cincinnati/CincinnatiPostProcessor.cs index a80f42f..41db93c 100644 --- a/OpenNest.Posts.Cincinnati/CincinnatiPostProcessor.cs +++ b/OpenNest.Posts.Cincinnati/CincinnatiPostProcessor.cs @@ -68,7 +68,18 @@ namespace OpenNest.Posts.Cincinnati .Where(p => p.Parts.Count > 0) .ToList(); - // 3. Build part sub-program registry (if enabled) + // 3. Resolve gas and library files + var resolver = new MaterialLibraryResolver(Config); + var gas = MaterialLibraryResolver.ResolveGas(nest, Config); + var etchLibrary = resolver.ResolveEtchLibrary(Config.DefaultEtchGas); + + // Resolve cut library from first plate for preamble + var firstPlate = plates.FirstOrDefault(); + var initialCutLibrary = firstPlate != null + ? resolver.ResolveCutLibrary(firstPlate.Material?.Name ?? "", firstPlate.Thickness, gas) + : ""; + + // 4. Build part sub-program registry (if enabled) Dictionary<(int, long), int> partSubprograms = null; List<(int subNum, string name, Program program)> subprogramEntries = null; @@ -100,21 +111,21 @@ namespace OpenNest.Posts.Cincinnati } } - // 4. Create writers + // 5. Create writers var preamble = new CincinnatiPreambleWriter(Config); var sheetWriter = new CincinnatiSheetWriter(Config, vars); - // 5. Build material description from first plate - var material = plates.FirstOrDefault()?.Material; + // 6. Build material description from first plate + var material = firstPlate?.Material; var materialDesc = material != null ? $"{material.Name}{(string.IsNullOrEmpty(material.Grade) ? "" : $", {material.Grade}")}" : ""; - // 6. Write to stream + // 7. Write to stream using var writer = new StreamWriter(outputStream, Encoding.UTF8, 1024, leaveOpen: true); // Main program - preamble.WriteMainProgram(writer, nest.Name ?? "NEST", materialDesc, plates.Count); + preamble.WriteMainProgram(writer, nest.Name ?? "NEST", materialDesc, plates.Count, initialCutLibrary); // Variable declaration subprogram preamble.WriteVariableDeclaration(writer, vars); @@ -122,17 +133,18 @@ namespace OpenNest.Posts.Cincinnati // Sheet subprograms for (var i = 0; i < plates.Count; i++) { + var plate = plates[i]; var sheetIndex = i + 1; var subNumber = Config.SheetSubprogramStart + i; - sheetWriter.Write(writer, plates[i], nest.Name ?? "NEST", sheetIndex, subNumber, - partSubprograms); + var cutLibrary = resolver.ResolveCutLibrary(plate.Material?.Name ?? "", plate.Thickness, gas); + sheetWriter.Write(writer, plate, nest.Name ?? "NEST", sheetIndex, subNumber, + cutLibrary, etchLibrary, partSubprograms); } // Part sub-programs (if enabled) if (subprogramEntries != null) { var partSubWriter = new CincinnatiPartSubprogramWriter(Config); - var firstPlate = plates.FirstOrDefault(); var sheetDiagonal = firstPlate != null ? System.Math.Sqrt(firstPlate.Size.Width * firstPlate.Size.Width + firstPlate.Size.Length * firstPlate.Size.Length) @@ -141,7 +153,7 @@ namespace OpenNest.Posts.Cincinnati foreach (var (subNum, name, pgm) in subprogramEntries) { partSubWriter.Write(writer, pgm, name, subNum, - Config.DefaultLibraryFile ?? "", sheetDiagonal); + initialCutLibrary, etchLibrary, sheetDiagonal); } } diff --git a/OpenNest.Posts.Cincinnati/MaterialLibraryResolver.cs b/OpenNest.Posts.Cincinnati/MaterialLibraryResolver.cs new file mode 100644 index 0000000..6fca9e3 --- /dev/null +++ b/OpenNest.Posts.Cincinnati/MaterialLibraryResolver.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace OpenNest.Posts.Cincinnati; + +public sealed class MaterialLibraryResolver +{ + private const double ThicknessTolerance = 0.001; + + private readonly List _materialLibraries; + private readonly List _etchLibraries; + + public MaterialLibraryResolver(CincinnatiPostConfig config) + { + _materialLibraries = config.MaterialLibraries ?? new List(); + _etchLibraries = config.EtchLibraries ?? new List(); + } + + public string ResolveCutLibrary(string materialName, double thickness, string gas) + { + var entry = _materialLibraries.FirstOrDefault(e => + string.Equals(e.Material, materialName, StringComparison.OrdinalIgnoreCase) && + System.Math.Abs(e.Thickness - thickness) <= ThicknessTolerance && + string.Equals(e.Gas, gas, StringComparison.OrdinalIgnoreCase)); + + return entry?.Library ?? ""; + } + + public string ResolveEtchLibrary(string gas) + { + var entry = _etchLibraries.FirstOrDefault(e => + string.Equals(e.Gas, gas, StringComparison.OrdinalIgnoreCase)); + + return entry?.Library ?? ""; + } + + public static string ResolveGas(Nest nest, CincinnatiPostConfig config) + { + return !string.IsNullOrEmpty(nest.AssistGas) ? nest.AssistGas : config.DefaultAssistGas; + } +} diff --git a/OpenNest.Tests/Cincinnati/MaterialLibraryResolverTests.cs b/OpenNest.Tests/Cincinnati/MaterialLibraryResolverTests.cs new file mode 100644 index 0000000..1a9732c --- /dev/null +++ b/OpenNest.Tests/Cincinnati/MaterialLibraryResolverTests.cs @@ -0,0 +1,160 @@ +using OpenNest.Posts.Cincinnati; + +namespace OpenNest.Tests.Cincinnati; + +public class MaterialLibraryResolverTests +{ + private static CincinnatiPostConfig ConfigWithLibraries() => new() + { + DefaultAssistGas = "O2", + DefaultEtchGas = "N2", + MaterialLibraries = new() + { + new MaterialLibraryEntry { Material = "Mild Steel", Thickness = 0.250, Gas = "O2", Library = "MS250O2.lib" }, + new MaterialLibraryEntry { Material = "Mild Steel", Thickness = 0.250, Gas = "N2", Library = "MS250N2.lib" }, + new MaterialLibraryEntry { Material = "Aluminum", Thickness = 0.125, Gas = "N2", Library = "AL125N2.lib" }, + new MaterialLibraryEntry { Material = "Stainless Steel", Thickness = 0.375, Gas = "AIR", Library = "SS375AIR.lib" } + }, + EtchLibraries = new() + { + new EtchLibraryEntry { Gas = "N2", Library = "EtchN2.lib" }, + new EtchLibraryEntry { Gas = "O2", Library = "EtchO2.lib" }, + new EtchLibraryEntry { Gas = "AIR", Library = "EtchAIR.lib" } + } + }; + + [Fact] + public void ResolveCutLibrary_ExactMatch() + { + var resolver = new MaterialLibraryResolver(ConfigWithLibraries()); + var result = resolver.ResolveCutLibrary("Mild Steel", 0.250, "O2"); + Assert.Equal("MS250O2.lib", result); + } + + [Fact] + public void ResolveCutLibrary_CaseInsensitiveMaterial() + { + var resolver = new MaterialLibraryResolver(ConfigWithLibraries()); + var result = resolver.ResolveCutLibrary("mild steel", 0.250, "O2"); + Assert.Equal("MS250O2.lib", result); + } + + [Fact] + public void ResolveCutLibrary_CaseInsensitiveGas() + { + var resolver = new MaterialLibraryResolver(ConfigWithLibraries()); + var result = resolver.ResolveCutLibrary("Mild Steel", 0.250, "o2"); + Assert.Equal("MS250O2.lib", result); + } + + [Fact] + public void ResolveCutLibrary_ThicknessWithinTolerance() + { + var resolver = new MaterialLibraryResolver(ConfigWithLibraries()); + var result = resolver.ResolveCutLibrary("Mild Steel", 0.2505, "O2"); + Assert.Equal("MS250O2.lib", result); + } + + [Fact] + public void ResolveCutLibrary_ThicknessOutsideTolerance_ReturnsEmpty() + { + var resolver = new MaterialLibraryResolver(ConfigWithLibraries()); + var result = resolver.ResolveCutLibrary("Mild Steel", 0.260, "O2"); + Assert.Equal("", result); + } + + [Fact] + public void ResolveCutLibrary_NoMatch_ReturnsEmpty() + { + var resolver = new MaterialLibraryResolver(ConfigWithLibraries()); + var result = resolver.ResolveCutLibrary("Titanium", 0.250, "O2"); + Assert.Equal("", result); + } + + [Fact] + public void ResolveCutLibrary_WrongGas_ReturnsEmpty() + { + var resolver = new MaterialLibraryResolver(ConfigWithLibraries()); + var result = resolver.ResolveCutLibrary("Mild Steel", 0.250, "AIR"); + Assert.Equal("", result); + } + + [Fact] + public void ResolveCutLibrary_DifferentGasSameMaterial() + { + var resolver = new MaterialLibraryResolver(ConfigWithLibraries()); + var o2 = resolver.ResolveCutLibrary("Mild Steel", 0.250, "O2"); + var n2 = resolver.ResolveCutLibrary("Mild Steel", 0.250, "N2"); + Assert.Equal("MS250O2.lib", o2); + Assert.Equal("MS250N2.lib", n2); + } + + [Fact] + public void ResolveCutLibrary_EmptyList_ReturnsEmpty() + { + var config = new CincinnatiPostConfig { MaterialLibraries = new() }; + var resolver = new MaterialLibraryResolver(config); + var result = resolver.ResolveCutLibrary("Mild Steel", 0.250, "O2"); + Assert.Equal("", result); + } + + [Fact] + public void ResolveEtchLibrary_ExactMatch() + { + var resolver = new MaterialLibraryResolver(ConfigWithLibraries()); + var result = resolver.ResolveEtchLibrary("N2"); + Assert.Equal("EtchN2.lib", result); + } + + [Fact] + public void ResolveEtchLibrary_CaseInsensitive() + { + var resolver = new MaterialLibraryResolver(ConfigWithLibraries()); + var result = resolver.ResolveEtchLibrary("n2"); + Assert.Equal("EtchN2.lib", result); + } + + [Fact] + public void ResolveEtchLibrary_NoMatch_ReturnsEmpty() + { + var resolver = new MaterialLibraryResolver(ConfigWithLibraries()); + var result = resolver.ResolveEtchLibrary("Argon"); + Assert.Equal("", result); + } + + [Fact] + public void ResolveEtchLibrary_EmptyList_ReturnsEmpty() + { + var config = new CincinnatiPostConfig { EtchLibraries = new() }; + var resolver = new MaterialLibraryResolver(config); + var result = resolver.ResolveEtchLibrary("N2"); + Assert.Equal("", result); + } + + [Fact] + public void ResolveGas_UsesNestAssistGas_WhenSet() + { + var nest = new Nest("Test") { AssistGas = "N2" }; + var config = new CincinnatiPostConfig { DefaultAssistGas = "O2" }; + var result = MaterialLibraryResolver.ResolveGas(nest, config); + Assert.Equal("N2", result); + } + + [Fact] + public void ResolveGas_FallsBackToConfig_WhenNestEmpty() + { + var nest = new Nest("Test") { AssistGas = "" }; + var config = new CincinnatiPostConfig { DefaultAssistGas = "O2" }; + var result = MaterialLibraryResolver.ResolveGas(nest, config); + Assert.Equal("O2", result); + } + + [Fact] + public void ResolveGas_FallsBackToConfig_WhenNestNull() + { + var nest = new Nest("Test"); + var config = new CincinnatiPostConfig { DefaultAssistGas = "AIR" }; + var result = MaterialLibraryResolver.ResolveGas(nest, config); + Assert.Equal("AIR", result); + } +} diff --git a/OpenNest.Tests/CutOffTests.cs b/OpenNest.Tests/CutOffTests.cs index 440afb3..cd7a164 100644 --- a/OpenNest.Tests/CutOffTests.cs +++ b/OpenNest.Tests/CutOffTests.cs @@ -154,10 +154,10 @@ public class CutOffTests // Plate(100, 50) = Width=100, Length=50. Vertical cut runs along Y (Width axis). // BoundingBox Y extent = Size.Width = 100. With 2" overtravel = 102. - // Default TowardOrigin: RapidMove to far end (102), LinearMove to near end (0). - var rapidMoves = cutoff.Drawing.Program.Codes.OfType().ToList(); - Assert.Single(rapidMoves); - Assert.Equal(102.0, rapidMoves[0].EndPoint.Y, 5); + // Default AwayFromOrigin: RapidMove to near end (0), LinearMove to far end (102). + var linearMoves = cutoff.Drawing.Program.Codes.OfType().ToList(); + Assert.Single(linearMoves); + Assert.Equal(102.0, linearMoves[0].EndPoint.Y, 5); } [Fact] @@ -171,11 +171,10 @@ public class CutOffTests }; cutoff.Regenerate(plate, settings); + // AwayFromOrigin: RapidMove to near end (StartLimit=20), LinearMove to far end (100). var rapidMoves = cutoff.Drawing.Program.Codes.OfType().ToList(); Assert.Single(rapidMoves); - var linearMoves = cutoff.Drawing.Program.Codes.OfType().ToList(); - Assert.Single(linearMoves); - Assert.Equal(20.0, linearMoves[0].EndPoint.Y, 5); + Assert.Equal(20.0, rapidMoves[0].EndPoint.Y, 5); } [Fact] @@ -189,9 +188,10 @@ public class CutOffTests }; cutoff.Regenerate(plate, settings); - var rapidMoves = cutoff.Drawing.Program.Codes.OfType().ToList(); - Assert.Single(rapidMoves); - Assert.Equal(80.0, rapidMoves[0].EndPoint.Y, 5); + // AwayFromOrigin: RapidMove to near end (0), LinearMove to far end (EndLimit=80). + var linearMoves = cutoff.Drawing.Program.Codes.OfType().ToList(); + Assert.Single(linearMoves); + Assert.Equal(80.0, linearMoves[0].EndPoint.Y, 5); } [Fact] diff --git a/OpenNest/Forms/EditNestForm.cs b/OpenNest/Forms/EditNestForm.cs index 3f01578..0645fb7 100644 --- a/OpenNest/Forms/EditNestForm.cs +++ b/OpenNest/Forms/EditNestForm.cs @@ -458,6 +458,7 @@ namespace OpenNest.Forms PlateView.ZoomToPlate(); PlateView.Refresh(); UpdatePlateList(); + UpdatePlateHeader(); } public void SelectAllParts() diff --git a/OpenNest/Forms/MainForm.Designer.cs b/OpenNest/Forms/MainForm.Designer.cs index 3af69f4..171b662 100644 --- a/OpenNest/Forms/MainForm.Designer.cs +++ b/OpenNest/Forms/MainForm.Designer.cs @@ -149,6 +149,8 @@ engineComboBox = new System.Windows.Forms.ToolStripComboBox(); btnAutoNest = new System.Windows.Forms.ToolStripButton(); btnShowRemnants = new System.Windows.Forms.ToolStripButton(); + toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator(); + btnCutOff = new System.Windows.Forms.ToolStripButton(); pEPToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); openNestToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); menuStrip1.SuspendLayout(); @@ -917,7 +919,7 @@ // toolStrip1 // toolStrip1.AutoSize = false; - toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { btnNew, btnOpen, btnSave, btnSaveAs, toolStripSeparator1, btnZoomOut, btnZoomIn, btnZoomToFit, toolStripSeparator4, engineLabel, engineComboBox, btnAutoNest, btnShowRemnants }); + toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { btnNew, btnOpen, btnSave, btnSaveAs, toolStripSeparator1, btnZoomOut, btnZoomIn, btnZoomToFit, toolStripSeparator4, engineLabel, engineComboBox, btnAutoNest, btnShowRemnants, toolStripSeparator5, btnCutOff }); toolStrip1.Location = new System.Drawing.Point(0, 24); toolStrip1.Name = "toolStrip1"; toolStrip1.Size = new System.Drawing.Size(1281, 40); @@ -1044,12 +1046,31 @@ // // btnShowRemnants // - btnShowRemnants.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + btnShowRemnants.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + btnShowRemnants.Image = Properties.Resources.remnants; + btnShowRemnants.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; btnShowRemnants.Name = "btnShowRemnants"; - btnShowRemnants.Size = new System.Drawing.Size(64, 37); + btnShowRemnants.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + btnShowRemnants.Size = new System.Drawing.Size(38, 37); btnShowRemnants.Text = "Remnants"; btnShowRemnants.Click += ShowRemnants_Click; // + // toolStripSeparator5 + // + toolStripSeparator5.Name = "toolStripSeparator5"; + toolStripSeparator5.Size = new System.Drawing.Size(6, 40); + // + // btnCutOff + // + btnCutOff.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; + btnCutOff.Image = Properties.Resources.cutoff; + btnCutOff.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + btnCutOff.Name = "btnCutOff"; + btnCutOff.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + btnCutOff.Size = new System.Drawing.Size(38, 37); + btnCutOff.Text = "Sheet Cut-Off"; + btnCutOff.Click += CutOff_Click; + // // pEPToolStripMenuItem // pEPToolStripMenuItem.Name = "pEPToolStripMenuItem"; @@ -1213,6 +1234,8 @@ private System.Windows.Forms.ToolStripComboBox engineComboBox; private System.Windows.Forms.ToolStripButton btnAutoNest; private System.Windows.Forms.ToolStripButton btnShowRemnants; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator5; + private System.Windows.Forms.ToolStripButton btnCutOff; private System.Windows.Forms.ToolStripMenuItem mnuPlateCutOff; } } \ No newline at end of file diff --git a/OpenNest/Properties/Resources.Designer.cs b/OpenNest/Properties/Resources.Designer.cs index bb7d609..953b82e 100644 --- a/OpenNest/Properties/Resources.Designer.cs +++ b/OpenNest/Properties/Resources.Designer.cs @@ -80,6 +80,16 @@ namespace OpenNest.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap cutoff { + get { + object obj = ResourceManager.GetObject("cutoff", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// @@ -160,6 +170,16 @@ namespace OpenNest.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap remnants { + get { + object obj = ResourceManager.GetObject("remnants", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/OpenNest/Properties/Resources.resx b/OpenNest/Properties/Resources.resx index cbba870..b84a2fc 100644 --- a/OpenNest/Properties/Resources.resx +++ b/OpenNest/Properties/Resources.resx @@ -175,4 +175,10 @@ ..\Resources\zoom_out.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\cutoff.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\remnants.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/OpenNest/Resources/cutoff.png b/OpenNest/Resources/cutoff.png new file mode 100644 index 0000000000000000000000000000000000000000..3e5ae18b55ca8f4df53420f3425b04bcc64b1dea GIT binary patch literal 994 zcmV<810DQ{P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D19wS8K~zXfwU%2? zT3Hmw``zXnO)_aFnK~V9r)rC~Dk{jW+~p$o103KS4i`ZYMNmMwI@)<_6%wB`=Ra9% z;`GIVKIl$X!e+0%f9vw!JLiR}sz0f!I{RGqLRD1_o^cRhmVT(J`sDT~;3>vL!2SI_ zzJC42_U-x_*Ect~xfx+J8e=>jkuN`_M0 zL*?)ojgvN-tqvq*7oF|^jng(~md%$C7bOme0vlfZH61{X6p)kqbCG z@1a(2qFgzIoZm$>k%l)AfzD*Z@JBMBR6RQp#FClsJ*YR)BtW*vrJ|nf?pKjf3Wz3B z{{oEG=_R5bY#2Vn?GGUmPa&fe`JlsEgR>AbIj(=T)^Rh{%{<-dsWE! z5>iqgVL^n;7sQsuiN%lF9{}|FSX|a(!)S%g?T0@sa2DxofhFq;3Q&q=q~tuylk{(M z`=GZtFt1tV3QPoadp*4S@CjPIh12!~A{=ErmE}Vn+W|;Qk)IK2E=6y&y0E4*%u!`%E5RyK^#o9(b~`}xvE;%Ou^3T*BjE*VMF8;HW{^0BN*6o35r0o`7IGT^+^ z#p}24v9zkgn$E=0xqKl6A_+v38JO)}#F7#}B~(JX^(z|&XjV2cGq=D2P6TwjJ^b?P z41RyJz$t51*0H{2hRN!L&E;i<+1)-EEOuyhCTKpbV{Tr9SF`W%^UGJ0OGI{ecgL57 znoWwLi#Hey`HdmFyu9Ld`j4#NAFvk literal 0 HcmV?d00001 diff --git a/OpenNest/Resources/remnants.png b/OpenNest/Resources/remnants.png new file mode 100644 index 0000000000000000000000000000000000000000..7a882a680346daf0827a30bcc8f66b2dee64f9b4 GIT binary patch literal 1219 zcmV;!1U&nRP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1XxK#K~zXfwbpA) zQ)e8<@uSYm+@_1hOy;I>H)FPW8OkIY(CHjoreb7dMg?&&u2n>I2m_>pmVzh&Ct8{1 zK14+>iU@^*f)skVl+qR{wiI4%x|k(fwm0^>6ysiLd4vDtm-C$G~J`wFDin=;mG0zmJ^UV2M&j0((_DUGRh_J&%b}uJA4CixRPFL6@4Ss=tpWW*so(4 zt)|z0iC%j-{bN@!jMk1n>$6oO4qv5b`1<(z=K{_e9Bm`2a1ODT7Z6*qfVispq&2;X ztjUe+=1(Z=2;x#lD5|y{$kmI;&@4fEb1`vO-ypK^Rm}GG|4;Euz+`VBvgmc93Kvq< ztfalSj?5Y#luZGsJIl$hNg(G+GzE1hxT!sdylw?4b?*{axrA`lTn22dt^uQ6!`|HK zC@Z9l4LxGeVCR?0R2sBpB$j$p+kTdAV=MV}KagLQOif!Zu@wtBQvNo<=jSkByX_j# z+ZqVTT})0v0*VWVNch>G(pxglR34Cm-cGFXVo^P>2c3`La>;i`G;%bAVA zI>8C_)<*Uy7Lbw`%;~{)WkD{NAxo}yIcp9R)$u*!esaX>t;FqwRkueJo9{f&}bUV9} z-B6Y)@Jo7&&yP>zvsi)ntOML>(ouh-fql}Itc?^9O%uGLE>j~P1`3G!4*q=f7m2bM zii(QaFI$OU(tCuYd(ft9q^qZmh>R_4Iyj5H32Ts~__03hWx9-N*MQEb#yw0R@WeU- zj(by7QZU|&)5Wpuk}gCPMRYom0fPyv#ZLaURK8A}hsPm-brAx(fr(CFQ1fYofQLk2 zb%a2!Dh;j1fT~!@&J%MvUy_dcRx9d;X6|U(xzpT1NqGS~k1fMJ)Co-RnRe(KSQRYb zd2kBrk2-ttK9aQUY)gI(U&S=S6rLPDy_F#O3U(zf!spl=Hpjezck~QaeJ9YVpP2Huicct=mg>xjUYiPPDZG@CDe6xixayug-Y0-Iu{;w5>B_2JW58!(gp zLEZBKXC6O(jLmLg=)Mt))r7@nesZ;%pNwtJIp@8Jq4D`Axp&`)ZPYTp{^?%N1SX?g h0+X4izzhAW_zyy