diff --git a/OpenNest.Engine.Tests/NestingEngines/IrregularBlockTests.cs b/OpenNest.Engine.Tests/NestingEngines/IrregularBlockTests.cs index 92b79efd..c2327efa 100644 --- a/OpenNest.Engine.Tests/NestingEngines/IrregularBlockTests.cs +++ b/OpenNest.Engine.Tests/NestingEngines/IrregularBlockTests.cs @@ -19,7 +19,7 @@ public class IrregularBlockTests var job = Job(new[] { Part("ell", LShape(9, 7, 3), quantity, RotationPolicy.Automatic) }, new[] { Stock("sheet", 40, 60, spacing: 0.25) }); var types = PartCatalog.Build(job); - using var catalog = new BlockCatalog(0.25, types, NoPairs); + using var catalog = new BlockCatalog(0.25, types, NoPairs, new WorkCounter(), long.MaxValue); Assert.Empty(catalog.Get(types[0], quantity, new Box(0, 0, 60, 40), CancellationToken.None)); Assert.Equal(0, catalog.PreparationCount); } @@ -31,7 +31,7 @@ public class IrregularBlockTests new[] { Stock("sheet", 40, 60, spacing: 0.25) }); var types = PartCatalog.Build(job); var original = types[0].Orientations.ToArray(); - using var catalog = new BlockCatalog(0.25, types, NoPairs); + using var catalog = new BlockCatalog(0.25, types, NoPairs, new WorkCounter(), long.MaxValue); var block = catalog.Get(types[0], 7, new Box(0, 0, 60, 40), CancellationToken.None); Assert.Equal(7, block.Count); Assert.Equal(original, types[0].Orientations); @@ -79,7 +79,7 @@ public class IrregularBlockTests var job = Job(new[] { Part("ell", LShape(9, 7, 3), 44, RotationPolicy.Automatic) }, new[] { stock }); var types = PartCatalog.Build(job); var pairs = PairCatalog.Build(types, 0.25, 40, 30, CancellationToken.None); - using var blocks = new BlockCatalog(0.25, types, pairs); + using var blocks = new BlockCatalog(0.25, types, pairs, new WorkCounter(), long.MaxValue); var before = new FrontierPacker(types, new NoFitCache(0.25), pairs, stock, PackAxis.X, 1, new WorkCounter()).Fill(new[] { 44 }, CancellationToken.None); var after = new FrontierPacker(types, new NoFitCache(0.25), pairs, stock, @@ -97,7 +97,7 @@ public class IrregularBlockTests var stock = Stock("sheet", 3, 3, spacing: 0.25); var job = Job(new[] { Rectangle("box", 2, 2, 3) }, new[] { stock }); var types = PartCatalog.Build(job); - using var blocks = new BlockCatalog(0.25, types, NoPairs); + using var blocks = new BlockCatalog(0.25, types, NoPairs, new WorkCounter(), long.MaxValue); Assert.Empty(blocks.Get(types[0], 3, new Box(0, 0, 3, 3), CancellationToken.None)); Assert.Equal(0, blocks.PreparationCount); var fill = new FrontierPacker(types, new NoFitCache(0.25), NoPairs, stock, @@ -158,7 +158,7 @@ public class IrregularBlockTests { var job = Job(new[] { Rectangle("box", 2, 2, 3) }, new[] { Stock("sheet", 10, 20) }); var types = PartCatalog.Build(job); - using var catalog = new BlockCatalog(0.25, types, NoPairs); + using var catalog = new BlockCatalog(0.25, types, NoPairs, new WorkCounter(), long.MaxValue); Assert.Throws(() => catalog.Get(types[0], 3, new Box(0, 0, 20, 10), new CancellationToken(true))); } diff --git a/OpenNest.Engine/NestingEngines/Irregular/BlockCatalog.cs b/OpenNest.Engine/NestingEngines/Irregular/BlockCatalog.cs index 78c09910..2df13e3d 100644 --- a/OpenNest.Engine/NestingEngines/Irregular/BlockCatalog.cs +++ b/OpenNest.Engine/NestingEngines/Irregular/BlockCatalog.cs @@ -17,6 +17,8 @@ namespace OpenNest.Engine.NestingEngines.Irregular; internal sealed class BlockCatalog : IDisposable { private readonly double spacing; + private readonly WorkCounter counter; + private readonly long budget; private readonly Dictionary drawings = new(); private readonly Dictionary> orientations = new(); private readonly Dictionary attempts = new(); @@ -24,9 +26,11 @@ internal sealed class BlockCatalog : IDisposable private readonly Dictionary<(int Type, int Quantity, double Length, double Width), IReadOnlyList> cache = new(); public BlockCatalog(double spacing, IReadOnlyList types, - IReadOnlyDictionary> pairs) + IReadOnlyDictionary> pairs, WorkCounter counter, long budget) { this.spacing = spacing; + this.counter = counter; + this.budget = budget; foreach (var type in types) { var poses = type.Orientations.ToList(); @@ -65,7 +69,7 @@ internal sealed class BlockCatalog : IDisposable var key = (type.Index, quantity, rectangle.Length, rectangle.Width); if (cache.TryGetValue(key, out var cached)) return cached; - if (rectangle.Area() < 3 * type.Area || attempts.GetValueOrDefault(type.Index) >= 8) + if (rectangle.Area() < 3 * type.Area || attempts.GetValueOrDefault(type.Index) >= 8 || counter.Value >= budget) return Array.Empty(); attempts[type.Index] = attempts.GetValueOrDefault(type.Index) + 1; var result = Build(type, quantity, rectangle, token); @@ -75,6 +79,11 @@ internal sealed class BlockCatalog : IDisposable private IReadOnlyList Build(PartType type, int quantity, Box rectangle, CancellationToken token) { + // PrivatePlateFill.Run below is a full independent NFP-based pack whose own operations + // never touch this solve's effort meter; charge it here using the same factors - demand + // and outline complexity - that drive its real cost, so the shared budget actually sees it. + var vertices = type.Orientations.Count > 0 ? type.Orientations[0].Outline.Count : 1; + counter.Add((long)quantity * quantity * System.Math.Max(1, vertices)); if (!drawings.TryGetValue(type.Index, out var drawing)) drawings[type.Index] = drawing = DrawingJobMapper.CreateDrawing(type.Part); try diff --git a/OpenNest.Engine/NestingEngines/Irregular/IrregularNestingEngine.cs b/OpenNest.Engine/NestingEngines/Irregular/IrregularNestingEngine.cs index aeb106ef..12c12ace 100644 --- a/OpenNest.Engine/NestingEngines/Irregular/IrregularNestingEngine.cs +++ b/OpenNest.Engine/NestingEngines/Irregular/IrregularNestingEngine.cs @@ -48,7 +48,7 @@ public sealed class IrregularNestingEngine : INestingEngine ArgumentNullException.ThrowIfNull(job); token.ThrowIfCancellationRequested(); var types = PartCatalog.Build(job); - using var solver = new Solver(job, types, progress, token); + using var solver = new Solver(job, types, progress, token, WorkBudget); // Pair-only orientations may fit stock even when the sampled single poses do not. // Demand that neither a single nor a pair can fit is reported unplaced. @@ -86,7 +86,8 @@ public sealed class IrregularNestingEngine : INestingEngine NestJob job, IReadOnlyList types, IProgress? progress, - CancellationToken token + CancellationToken token, + long workBudget ) : IDisposable { public void Dispose() @@ -104,7 +105,7 @@ public sealed class IrregularNestingEngine : INestingEngine { var spacing = System.Math.Max(0, stock.PartSpacing); if (!blocks.TryGetValue(spacing, out var found)) - blocks[spacing] = found = new BlockCatalog(spacing, types, PairsFor(stock)); + blocks[spacing] = found = new BlockCatalog(spacing, types, PairsFor(stock), Work, workBudget); return found; } diff --git a/OpenNest/Controls/PlateView.cs b/OpenNest/Controls/PlateView.cs index 47f15b6c..b9738880 100644 --- a/OpenNest/Controls/PlateView.cs +++ b/OpenNest/Controls/PlateView.cs @@ -84,6 +84,12 @@ namespace OpenNest.Controls public event EventHandler PartsReordered; public event EventHandler StatusChanged; + /// + /// Raised when files are dropped on the plate and something is listening (see + /// ); callers decide which paths to act on. + /// + public event EventHandler FilesDropped; + public event EventHandler SelectionChanged { add => selection.SelectionChanged += value; @@ -313,10 +319,18 @@ namespace OpenNest.Controls { if (drgevent.Data.GetData(typeof(Drawing)) != null) drgevent.Effect = DragDropEffects.Copy; + else if (FilesDropped != null && drgevent.Data.GetDataPresent(DataFormats.FileDrop)) + drgevent.Effect = DragDropEffects.Copy; } protected override void OnDragDrop(DragEventArgs drgevent) { + if (drgevent.Data.GetDataPresent(DataFormats.FileDrop)) + { + FilesDropped?.Invoke(this, (string[])drgevent.Data.GetData(DataFormats.FileDrop)); + return; + } + var dwg = drgevent.Data.GetData(typeof(Drawing)) as Drawing; if (dwg == null) diff --git a/OpenNest/Forms/MainForm.Designer.cs b/OpenNest/Forms/MainForm.Designer.cs index eeaf09b0..dba1fd50 100644 --- a/OpenNest/Forms/MainForm.Designer.cs +++ b/OpenNest/Forms/MainForm.Designer.cs @@ -73,27 +73,14 @@ mnuTools = new System.Windows.Forms.ToolStripMenuItem(); mnuToolsMeasureArea = new System.Windows.Forms.ToolStripMenuItem(); mnuToolsBestFitViewer = new System.Windows.Forms.ToolStripMenuItem(); - mnuToolsPatternTile = new System.Windows.Forms.ToolStripMenuItem(); - mnuToolsExpandSpacing = new System.Windows.Forms.ToolStripMenuItem(); - mnuToolsAlign = new System.Windows.Forms.ToolStripMenuItem(); - mnuToolsAlignLeft = new System.Windows.Forms.ToolStripMenuItem(); - mnuToolsAlignRight = new System.Windows.Forms.ToolStripMenuItem(); - mnuToolsAlignTop = new System.Windows.Forms.ToolStripMenuItem(); - mnuToolsAlignBottom = new System.Windows.Forms.ToolStripMenuItem(); - toolStripMenuItem11 = new System.Windows.Forms.ToolStripSeparator(); - mnuToolsAlignHorizontal = new System.Windows.Forms.ToolStripMenuItem(); - mnuToolsAlignVertically = new System.Windows.Forms.ToolStripMenuItem(); - toolStripMenuItem8 = new System.Windows.Forms.ToolStripSeparator(); - mnuToolsEvenlySpaceHorizontal = new System.Windows.Forms.ToolStripMenuItem(); - mnuToolsEvenlySpaceVertical = new System.Windows.Forms.ToolStripMenuItem(); toolStripMenuItem14 = new System.Windows.Forms.ToolStripSeparator(); - mnuSetOffsetIncrement = new System.Windows.Forms.ToolStripMenuItem(); - mnuSetRotationIncrement = new System.Windows.Forms.ToolStripMenuItem(); - toolStripMenuItem15 = new System.Windows.Forms.ToolStripSeparator(); mnuToolsMachineConfig = new System.Windows.Forms.ToolStripMenuItem(); mnuToolsNestDefaults = new System.Windows.Forms.ToolStripMenuItem(); mnuToolsSaveCurrentAsDefaults = new System.Windows.Forms.ToolStripMenuItem(); mnuToolsOptions = new System.Windows.Forms.ToolStripMenuItem(); + toolStripMenuItem15 = new System.Windows.Forms.ToolStripSeparator(); + mnuSetOffsetIncrement = new System.Windows.Forms.ToolStripMenuItem(); + mnuSetRotationIncrement = new System.Windows.Forms.ToolStripMenuItem(); mnuNest = new System.Windows.Forms.ToolStripMenuItem(); mnuNestEdit = new System.Windows.Forms.ToolStripMenuItem(); mnuNestImportDrawing = new System.Windows.Forms.ToolStripMenuItem(); @@ -130,20 +117,35 @@ mnuPlateRotate180 = new System.Windows.Forms.ToolStripMenuItem(); mnuResizeToFitParts = new System.Windows.Forms.ToolStripMenuItem(); toolStripMenuItem13 = new System.Windows.Forms.ToolStripSeparator(); - mnuPlateViewInCad = new System.Windows.Forms.ToolStripMenuItem(); - toolStripMenuItem20 = new System.Windows.Forms.ToolStripSeparator(); + mnuPlateArrange = new System.Windows.Forms.ToolStripMenuItem(); + mnuPlateAlignLeft = new System.Windows.Forms.ToolStripMenuItem(); + mnuPlateAlignRight = new System.Windows.Forms.ToolStripMenuItem(); + mnuPlateAlignTop = new System.Windows.Forms.ToolStripMenuItem(); + mnuPlateAlignBottom = new System.Windows.Forms.ToolStripMenuItem(); + toolStripMenuItem11 = new System.Windows.Forms.ToolStripSeparator(); + mnuPlateAlignHorizontal = new System.Windows.Forms.ToolStripMenuItem(); + mnuPlateAlignVertically = new System.Windows.Forms.ToolStripMenuItem(); + toolStripMenuItem8 = new System.Windows.Forms.ToolStripSeparator(); + mnuPlateEvenlySpaceHorizontal = new System.Windows.Forms.ToolStripMenuItem(); + mnuPlateEvenlySpaceVertical = new System.Windows.Forms.ToolStripMenuItem(); + toolStripMenuItem23 = new System.Windows.Forms.ToolStripSeparator(); + centerPartsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + toolStripMenuItem24 = new System.Windows.Forms.ToolStripSeparator(); + mnuPlatePatternTile = new System.Windows.Forms.ToolStripMenuItem(); + mnuPlateExpandSpacing = new System.Windows.Forms.ToolStripMenuItem(); mnuSequenceParts = new System.Windows.Forms.ToolStripMenuItem(); autoSequenceCurrentPlateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); manualSequenceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); calculateCutTimeToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); - centerPartsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - mnuPlateCutOff = new System.Windows.Forms.ToolStripMenuItem(); - mnuPlateAutomaticCutOff = new System.Windows.Forms.ToolStripMenuItem(); toolStripMenuItem21 = new System.Windows.Forms.ToolStripSeparator(); mnuPlatePlanCutting = new System.Windows.Forms.ToolStripMenuItem(); mnuPlateAssignLeadIns = new System.Windows.Forms.ToolStripMenuItem(); mnuPlatePlaceLeadIn = new System.Windows.Forms.ToolStripMenuItem(); mnuPlateRemoveLeadIns = new System.Windows.Forms.ToolStripMenuItem(); + toolStripMenuItem20 = new System.Windows.Forms.ToolStripSeparator(); + mnuPlateViewInCad = new System.Windows.Forms.ToolStripMenuItem(); + mnuPlateCutOff = new System.Windows.Forms.ToolStripMenuItem(); + mnuPlateAutomaticCutOff = new System.Windows.Forms.ToolStripMenuItem(); mnuWindow = new System.Windows.Forms.ToolStripMenuItem(); mnuWindowCascade = new System.Windows.Forms.ToolStripMenuItem(); mnuWindowTileVertical = new System.Windows.Forms.ToolStripMenuItem(); @@ -494,7 +496,7 @@ // // mnuTools // - mnuTools.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { mnuToolsMeasureArea, mnuToolsBestFitViewer, mnuToolsPatternTile, mnuToolsExpandSpacing, mnuToolsAlign, toolStripMenuItem14, mnuSetOffsetIncrement, mnuSetRotationIncrement, toolStripMenuItem15, mnuToolsMachineConfig, mnuToolsNestDefaults, mnuToolsSaveCurrentAsDefaults, mnuToolsOptions }); + mnuTools.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { mnuToolsMeasureArea, mnuToolsBestFitViewer, toolStripMenuItem14, mnuToolsMachineConfig, mnuToolsNestDefaults, mnuToolsSaveCurrentAsDefaults, mnuToolsOptions, toolStripMenuItem15, mnuSetOffsetIncrement, mnuSetRotationIncrement }); mnuTools.Name = "mnuTools"; mnuTools.Size = new System.Drawing.Size(47, 20); mnuTools.Text = "&Tools"; @@ -513,93 +515,6 @@ mnuToolsBestFitViewer.Text = "Best-Fit Viewer"; mnuToolsBestFitViewer.Click += BestFitViewer_Click; // - // mnuToolsPatternTile - // - this.mnuToolsPatternTile.Name = "mnuToolsPatternTile"; - this.mnuToolsPatternTile.Size = new System.Drawing.Size(214, 22); - this.mnuToolsPatternTile.Text = "Pattern Tile"; - this.mnuToolsPatternTile.Click += PatternTile_Click; - // - // mnuToolsExpandSpacing - // - this.mnuToolsExpandSpacing.Name = "mnuToolsExpandSpacing"; - this.mnuToolsExpandSpacing.Size = new System.Drawing.Size(214, 22); - this.mnuToolsExpandSpacing.Text = "Expand Spacing"; - this.mnuToolsExpandSpacing.Click += ExpandSpacing_Click; - // - // mnuToolsAlign - // - mnuToolsAlign.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { mnuToolsAlignLeft, mnuToolsAlignRight, mnuToolsAlignTop, mnuToolsAlignBottom, toolStripMenuItem11, mnuToolsAlignHorizontal, mnuToolsAlignVertically, toolStripMenuItem8, mnuToolsEvenlySpaceHorizontal, mnuToolsEvenlySpaceVertical }); - mnuToolsAlign.Name = "mnuToolsAlign"; - mnuToolsAlign.Size = new System.Drawing.Size(214, 22); - mnuToolsAlign.Text = "Align Selected"; - // - // mnuToolsAlignLeft - // - mnuToolsAlignLeft.Name = "mnuToolsAlignLeft"; - mnuToolsAlignLeft.Size = new System.Drawing.Size(209, 22); - mnuToolsAlignLeft.Text = "Left"; - mnuToolsAlignLeft.Click += AlignLeft_Click; - // - // mnuToolsAlignRight - // - mnuToolsAlignRight.Name = "mnuToolsAlignRight"; - mnuToolsAlignRight.Size = new System.Drawing.Size(209, 22); - mnuToolsAlignRight.Text = "Right"; - mnuToolsAlignRight.Click += AlignRight_Click; - // - // mnuToolsAlignTop - // - mnuToolsAlignTop.Name = "mnuToolsAlignTop"; - mnuToolsAlignTop.Size = new System.Drawing.Size(209, 22); - mnuToolsAlignTop.Text = "Top"; - mnuToolsAlignTop.Click += AlignTop_Click; - // - // mnuToolsAlignBottom - // - mnuToolsAlignBottom.Name = "mnuToolsAlignBottom"; - mnuToolsAlignBottom.Size = new System.Drawing.Size(209, 22); - mnuToolsAlignBottom.Text = "Bottom"; - mnuToolsAlignBottom.Click += AlignBottom_Click; - // - // toolStripMenuItem11 - // - toolStripMenuItem11.Name = "toolStripMenuItem11"; - toolStripMenuItem11.Size = new System.Drawing.Size(206, 6); - // - // mnuToolsAlignHorizontal - // - mnuToolsAlignHorizontal.Name = "mnuToolsAlignHorizontal"; - mnuToolsAlignHorizontal.Size = new System.Drawing.Size(209, 22); - mnuToolsAlignHorizontal.Text = "Horizontally"; - mnuToolsAlignHorizontal.Click += AlignHorizontal_Click; - // - // mnuToolsAlignVertically - // - mnuToolsAlignVertically.Name = "mnuToolsAlignVertically"; - mnuToolsAlignVertically.Size = new System.Drawing.Size(209, 22); - mnuToolsAlignVertically.Text = "Vertically"; - mnuToolsAlignVertically.Click += AlignVertical_Click; - // - // toolStripMenuItem8 - // - toolStripMenuItem8.Name = "toolStripMenuItem8"; - toolStripMenuItem8.Size = new System.Drawing.Size(206, 6); - // - // mnuToolsEvenlySpaceHorizontal - // - mnuToolsEvenlySpaceHorizontal.Name = "mnuToolsEvenlySpaceHorizontal"; - mnuToolsEvenlySpaceHorizontal.Size = new System.Drawing.Size(209, 22); - mnuToolsEvenlySpaceHorizontal.Text = "Evenly Space Horizontally"; - mnuToolsEvenlySpaceHorizontal.Click += EvenlySpaceHorizontally_Click; - // - // mnuToolsEvenlySpaceVertical - // - mnuToolsEvenlySpaceVertical.Name = "mnuToolsEvenlySpaceVertical"; - mnuToolsEvenlySpaceVertical.Size = new System.Drawing.Size(209, 22); - mnuToolsEvenlySpaceVertical.Text = "Evenly Space Vertically"; - mnuToolsEvenlySpaceVertical.Click += EvenlySpaceVertically_Click; - // // toolStripMenuItem14 // toolStripMenuItem14.Name = "toolStripMenuItem14"; @@ -805,8 +720,8 @@ mnuNestRemoveLeadIns.Click += NestRemoveLeadIns_Click; // // mnuPlate - // - mnuPlate.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { mnuPlateEdit, mnuPlateSetAsDefault, toolStripMenuItem18, mnuPlateAdd, mnuPlateRemove, toolStripMenuItem16, mnuPlateCutOff, mnuPlateAutomaticCutOff, mnuPlateRotate, mnuResizeToFitParts, toolStripMenuItem13, mnuPlateViewInCad, toolStripMenuItem20, mnuSequenceParts, calculateCutTimeToolStripMenuItem1, centerPartsToolStripMenuItem, toolStripMenuItem21, mnuPlatePlanCutting, mnuPlateAssignLeadIns, mnuPlatePlaceLeadIn, mnuPlateRemoveLeadIns }); + // + mnuPlate.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { mnuPlateEdit, mnuPlateSetAsDefault, toolStripMenuItem18, mnuPlateAdd, mnuPlateRemove, toolStripMenuItem16, mnuPlateCutOff, mnuPlateAutomaticCutOff, mnuPlateRotate, mnuResizeToFitParts, toolStripMenuItem13, mnuPlateArrange, mnuSequenceParts, calculateCutTimeToolStripMenuItem1, toolStripMenuItem21, mnuPlatePlanCutting, mnuPlateAssignLeadIns, mnuPlatePlaceLeadIn, mnuPlateRemoveLeadIns, toolStripMenuItem20, mnuPlateViewInCad }); mnuPlate.Name = "mnuPlate"; mnuPlate.Size = new System.Drawing.Size(45, 20); mnuPlate.Text = "&Plate"; @@ -901,64 +816,149 @@ mnuPlateRotate180.Click += Rotate180_Click; // // mnuResizeToFitParts - // + // mnuResizeToFitParts.Name = "mnuResizeToFitParts"; mnuResizeToFitParts.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.P; mnuResizeToFitParts.Size = new System.Drawing.Size(177, 22); mnuResizeToFitParts.Text = "Resize to Fit"; mnuResizeToFitParts.Click += ResizeToFitParts_Click; - // + // // toolStripMenuItem13 - // + // toolStripMenuItem13.Name = "toolStripMenuItem13"; toolStripMenuItem13.Size = new System.Drawing.Size(174, 6); - // - // mnuPlateViewInCad - // - mnuPlateViewInCad.Name = "mnuPlateViewInCad"; - mnuPlateViewInCad.Size = new System.Drawing.Size(177, 22); - mnuPlateViewInCad.Text = "View in CAD"; - mnuPlateViewInCad.Click += OpenInExternalCad_Click; - // - // toolStripMenuItem20 - // - toolStripMenuItem20.Name = "toolStripMenuItem20"; - toolStripMenuItem20.Size = new System.Drawing.Size(174, 6); - // + // + // mnuPlateArrange + // + mnuPlateArrange.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { mnuPlateAlignLeft, mnuPlateAlignRight, mnuPlateAlignTop, mnuPlateAlignBottom, toolStripMenuItem11, mnuPlateAlignHorizontal, mnuPlateAlignVertically, toolStripMenuItem8, mnuPlateEvenlySpaceHorizontal, mnuPlateEvenlySpaceVertical, toolStripMenuItem23, centerPartsToolStripMenuItem, toolStripMenuItem24, mnuPlatePatternTile, mnuPlateExpandSpacing }); + mnuPlateArrange.Name = "mnuPlateArrange"; + mnuPlateArrange.Size = new System.Drawing.Size(177, 22); + mnuPlateArrange.Text = "Arrange"; + // + // mnuPlateAlignLeft + // + mnuPlateAlignLeft.Name = "mnuPlateAlignLeft"; + mnuPlateAlignLeft.Size = new System.Drawing.Size(209, 22); + mnuPlateAlignLeft.Text = "Left"; + mnuPlateAlignLeft.Click += AlignLeft_Click; + // + // mnuPlateAlignRight + // + mnuPlateAlignRight.Name = "mnuPlateAlignRight"; + mnuPlateAlignRight.Size = new System.Drawing.Size(209, 22); + mnuPlateAlignRight.Text = "Right"; + mnuPlateAlignRight.Click += AlignRight_Click; + // + // mnuPlateAlignTop + // + mnuPlateAlignTop.Name = "mnuPlateAlignTop"; + mnuPlateAlignTop.Size = new System.Drawing.Size(209, 22); + mnuPlateAlignTop.Text = "Top"; + mnuPlateAlignTop.Click += AlignTop_Click; + // + // mnuPlateAlignBottom + // + mnuPlateAlignBottom.Name = "mnuPlateAlignBottom"; + mnuPlateAlignBottom.Size = new System.Drawing.Size(209, 22); + mnuPlateAlignBottom.Text = "Bottom"; + mnuPlateAlignBottom.Click += AlignBottom_Click; + // + // toolStripMenuItem11 + // + toolStripMenuItem11.Name = "toolStripMenuItem11"; + toolStripMenuItem11.Size = new System.Drawing.Size(206, 6); + // + // mnuPlateAlignHorizontal + // + mnuPlateAlignHorizontal.Name = "mnuPlateAlignHorizontal"; + mnuPlateAlignHorizontal.Size = new System.Drawing.Size(209, 22); + mnuPlateAlignHorizontal.Text = "Horizontally"; + mnuPlateAlignHorizontal.Click += AlignHorizontal_Click; + // + // mnuPlateAlignVertically + // + mnuPlateAlignVertically.Name = "mnuPlateAlignVertically"; + mnuPlateAlignVertically.Size = new System.Drawing.Size(209, 22); + mnuPlateAlignVertically.Text = "Vertically"; + mnuPlateAlignVertically.Click += AlignVertical_Click; + // + // toolStripMenuItem8 + // + toolStripMenuItem8.Name = "toolStripMenuItem8"; + toolStripMenuItem8.Size = new System.Drawing.Size(206, 6); + // + // mnuPlateEvenlySpaceHorizontal + // + mnuPlateEvenlySpaceHorizontal.Name = "mnuPlateEvenlySpaceHorizontal"; + mnuPlateEvenlySpaceHorizontal.Size = new System.Drawing.Size(209, 22); + mnuPlateEvenlySpaceHorizontal.Text = "Evenly Space Horizontally"; + mnuPlateEvenlySpaceHorizontal.Click += EvenlySpaceHorizontally_Click; + // + // mnuPlateEvenlySpaceVertical + // + mnuPlateEvenlySpaceVertical.Name = "mnuPlateEvenlySpaceVertical"; + mnuPlateEvenlySpaceVertical.Size = new System.Drawing.Size(209, 22); + mnuPlateEvenlySpaceVertical.Text = "Evenly Space Vertically"; + mnuPlateEvenlySpaceVertical.Click += EvenlySpaceVertically_Click; + // + // toolStripMenuItem23 + // + toolStripMenuItem23.Name = "toolStripMenuItem23"; + toolStripMenuItem23.Size = new System.Drawing.Size(206, 6); + // + // centerPartsToolStripMenuItem + // + centerPartsToolStripMenuItem.Name = "centerPartsToolStripMenuItem"; + centerPartsToolStripMenuItem.Size = new System.Drawing.Size(209, 22); + centerPartsToolStripMenuItem.Text = "Center Parts"; + centerPartsToolStripMenuItem.Click += centerPartsToolStripMenuItem_Click; + // + // toolStripMenuItem24 + // + toolStripMenuItem24.Name = "toolStripMenuItem24"; + toolStripMenuItem24.Size = new System.Drawing.Size(206, 6); + // + // mnuPlatePatternTile + // + mnuPlatePatternTile.Name = "mnuPlatePatternTile"; + mnuPlatePatternTile.Size = new System.Drawing.Size(209, 22); + mnuPlatePatternTile.Text = "Pattern Tile"; + mnuPlatePatternTile.Click += PatternTile_Click; + // + // mnuPlateExpandSpacing + // + mnuPlateExpandSpacing.Name = "mnuPlateExpandSpacing"; + mnuPlateExpandSpacing.Size = new System.Drawing.Size(209, 22); + mnuPlateExpandSpacing.Text = "Expand Spacing"; + mnuPlateExpandSpacing.Click += ExpandSpacing_Click; + // // mnuSequenceParts - // + // mnuSequenceParts.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { autoSequenceCurrentPlateToolStripMenuItem, manualSequenceToolStripMenuItem }); mnuSequenceParts.Name = "mnuSequenceParts"; mnuSequenceParts.Size = new System.Drawing.Size(177, 22); mnuSequenceParts.Text = "Sequence Parts"; - // + // // autoSequenceCurrentPlateToolStripMenuItem - // + // autoSequenceCurrentPlateToolStripMenuItem.Name = "autoSequenceCurrentPlateToolStripMenuItem"; autoSequenceCurrentPlateToolStripMenuItem.Size = new System.Drawing.Size(168, 22); autoSequenceCurrentPlateToolStripMenuItem.Text = "Auto Sequence"; autoSequenceCurrentPlateToolStripMenuItem.Click += AutoSequenceCurrentPlate_Click; - // + // // manualSequenceToolStripMenuItem - // + // manualSequenceToolStripMenuItem.Name = "manualSequenceToolStripMenuItem"; manualSequenceToolStripMenuItem.Size = new System.Drawing.Size(168, 22); manualSequenceToolStripMenuItem.Text = "Manual Sequence"; manualSequenceToolStripMenuItem.Click += ManualSequenceParts_Click; - // + // // calculateCutTimeToolStripMenuItem1 - // + // calculateCutTimeToolStripMenuItem1.Name = "calculateCutTimeToolStripMenuItem1"; calculateCutTimeToolStripMenuItem1.Size = new System.Drawing.Size(177, 22); calculateCutTimeToolStripMenuItem1.Text = "Calculate Cut Time"; calculateCutTimeToolStripMenuItem1.Click += CalculatePlateCutTime_Click; - // - // centerPartsToolStripMenuItem - // - centerPartsToolStripMenuItem.Name = "centerPartsToolStripMenuItem"; - centerPartsToolStripMenuItem.Size = new System.Drawing.Size(177, 22); - centerPartsToolStripMenuItem.Text = "Center Parts"; - centerPartsToolStripMenuItem.Click += centerPartsToolStripMenuItem_Click; // // toolStripMenuItem21 // @@ -993,6 +993,18 @@ mnuPlateRemoveLeadIns.Text = "Remove Lead-ins"; mnuPlateRemoveLeadIns.Click += PlateRemoveLeadIns_Click; // + // toolStripMenuItem20 + // + toolStripMenuItem20.Name = "toolStripMenuItem20"; + toolStripMenuItem20.Size = new System.Drawing.Size(174, 6); + // + // mnuPlateViewInCad + // + mnuPlateViewInCad.Name = "mnuPlateViewInCad"; + mnuPlateViewInCad.Size = new System.Drawing.Size(177, 22); + mnuPlateViewInCad.Text = "View in CAD"; + mnuPlateViewInCad.Click += OpenInExternalCad_Click; + // // mnuWindow // mnuWindow.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { mnuWindowCascade, mnuWindowTileVertical, mnuWindowTileHorizontal, toolStripMenuItem10, closeToolStripMenuItem, mnuCloseAll }); @@ -1394,17 +1406,19 @@ private System.Windows.Forms.ToolStripMenuItem mnuViewZoomToPlate; private System.Windows.Forms.ToolStripMenuItem mnuViewZoomToSelected; private System.Windows.Forms.ToolStripMenuItem mnuResizeToFitParts; - private System.Windows.Forms.ToolStripMenuItem mnuToolsAlign; - private System.Windows.Forms.ToolStripMenuItem mnuToolsAlignLeft; - private System.Windows.Forms.ToolStripMenuItem mnuToolsAlignRight; - private System.Windows.Forms.ToolStripMenuItem mnuToolsAlignTop; - private System.Windows.Forms.ToolStripMenuItem mnuToolsAlignBottom; + private System.Windows.Forms.ToolStripMenuItem mnuPlateArrange; + private System.Windows.Forms.ToolStripMenuItem mnuPlateAlignLeft; + private System.Windows.Forms.ToolStripMenuItem mnuPlateAlignRight; + private System.Windows.Forms.ToolStripMenuItem mnuPlateAlignTop; + private System.Windows.Forms.ToolStripMenuItem mnuPlateAlignBottom; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem11; - private System.Windows.Forms.ToolStripMenuItem mnuToolsAlignHorizontal; - private System.Windows.Forms.ToolStripMenuItem mnuToolsAlignVertically; + private System.Windows.Forms.ToolStripMenuItem mnuPlateAlignHorizontal; + private System.Windows.Forms.ToolStripMenuItem mnuPlateAlignVertically; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem8; - private System.Windows.Forms.ToolStripMenuItem mnuToolsEvenlySpaceHorizontal; - private System.Windows.Forms.ToolStripMenuItem mnuToolsEvenlySpaceVertical; + private System.Windows.Forms.ToolStripMenuItem mnuPlateEvenlySpaceHorizontal; + private System.Windows.Forms.ToolStripMenuItem mnuPlateEvenlySpaceVertical; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem23; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem24; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem12; private System.Windows.Forms.ToolStripMenuItem mnuNestPost; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem13; @@ -1440,8 +1454,8 @@ private System.Windows.Forms.ToolStripMenuItem autoSequenceAllPlatesToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem mnuToolsMeasureArea; private System.Windows.Forms.ToolStripMenuItem mnuToolsBestFitViewer; - private System.Windows.Forms.ToolStripMenuItem mnuToolsPatternTile; - private System.Windows.Forms.ToolStripMenuItem mnuToolsExpandSpacing; + private System.Windows.Forms.ToolStripMenuItem mnuPlatePatternTile; + private System.Windows.Forms.ToolStripMenuItem mnuPlateExpandSpacing; private System.Windows.Forms.ToolStripButton btnSaveAs; private System.Windows.Forms.ToolStripMenuItem centerPartsToolStripMenuItem; private System.Windows.Forms.ToolStripStatusLabel gpuStatusLabel; diff --git a/OpenNest/Forms/MainForm.cs b/OpenNest/Forms/MainForm.cs index 913af765..938e7fcb 100644 --- a/OpenNest/Forms/MainForm.cs +++ b/OpenNest/Forms/MainForm.cs @@ -69,6 +69,7 @@ namespace OpenNest.Forms clickUpdateLocation = true; this.SetBevel(false); + AllowDrop = true; LoadPosts(); EnableCheck(); @@ -173,6 +174,7 @@ namespace OpenNest.Forms if (remoteId != Guid.Empty) editForm.Document.BindRemote(remoteId, serverUrl); editForm.MdiParent = this; + editForm.PlateView.FilesDropped += (sender, paths) => OpenDroppedNestFiles(paths); editForm.PlateChanged += (sender, e) => { NavigationEnableCheck(); @@ -232,9 +234,7 @@ namespace OpenNest.Forms mnuNest.Visible = hasValue; mnuPlate.Visible = hasValue; mnuWindow.Visible = hasValue; - mnuToolsAlign.Visible = hasValue; mnuToolsMeasureArea.Visible = hasValue; - mnuToolsExpandSpacing.Visible = hasValue; mnuToolsSaveCurrentAsDefaults.Visible = hasValue; toolStripMenuItem14.Visible = hasValue; @@ -554,6 +554,43 @@ namespace OpenNest.Forms Settings.Default.Save(); } + protected override void OnDragEnter(DragEventArgs e) + { + base.OnDragEnter(e); + e.Effect = HasDroppedNestFile(e.Data) ? DragDropEffects.Copy : DragDropEffects.None; + } + + protected override void OnDragDrop(DragEventArgs e) + { + base.OnDragDrop(e); + if (e.Data.GetData(DataFormats.FileDrop) is string[] paths) + OpenDroppedNestFiles(paths); + } + + private static bool HasDroppedNestFile(IDataObject data) => + data.GetData(DataFormats.FileDrop) is string[] paths + && paths.Any(path => string.Equals( + Path.GetExtension(path), NestFormat.FileExtension, StringComparison.OrdinalIgnoreCase)); + + /// + /// Opens dropped .nest files locally regardless of storage mode. Loaded documents are + /// not bound to a server record, so Save/Save As follow the normal storage-mode rules + /// (File mode writes to disk; Database mode creates a new record on first save). + /// + private void OpenDroppedNestFiles(IEnumerable paths) + { + foreach (var path in paths) + { + if (!string.Equals(Path.GetExtension(path), NestFormat.FileExtension, StringComparison.OrdinalIgnoreCase)) + continue; + + var reader = new NestReader(path); + var nest = reader.Read(); + LoadNest(nest); + ShowNestWarnings(reader); + } + } + protected override void OnClosing(System.ComponentModel.CancelEventArgs e) { base.OnClosing(e); diff --git a/OpenNest/Forms/SavedNestsForm.cs b/OpenNest/Forms/SavedNestsForm.cs index 6da18863..b9cba85d 100644 --- a/OpenNest/Forms/SavedNestsForm.cs +++ b/OpenNest/Forms/SavedNestsForm.cs @@ -72,6 +72,7 @@ public sealed class SavedNestsForm : Form StartPosition = FormStartPosition.CenterParent; ClientSize = new Size(1280, 800); MinimumSize = new Size(800, 560); + WindowState = FormWindowState.Maximized; MinimizeBox = false; ShowInTaskbar = false; @@ -277,6 +278,10 @@ public sealed class SavedNestsForm : Form Load += (_, _) => { + // Keep a maximized dialog's bottom row clear of the taskbar. + var area = Screen.FromControl(Owner ?? this).WorkingArea; + MaximizedBounds = new Rectangle(area.Left, area.Top, area.Width, area.Height - 20); + // Minimum sizes are applied once the container has its real height; setting them // on the default-sized container throws. split.Panel1MinSize = 120; @@ -327,23 +332,27 @@ public sealed class SavedNestsForm : Form SelectionMode = DataGridViewSelectionMode.FullRowSelect, MultiSelect = false, AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None, - BackgroundColor = Color.White, - BorderStyle = BorderStyle.FixedSingle, - GridColor = Color.Silver, + BackgroundColor = SystemColors.Window, + BorderStyle = BorderStyle.None, + GridColor = Color.FromArgb(230, 230, 230), EnableHeadersVisualStyles = false, + ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single, + RowHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single, ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize, RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.DisableResizing, StandardTab = true, }; grid.RowTemplate.Height = 22; - grid.DefaultCellStyle.SelectionBackColor = Color.Black; - grid.DefaultCellStyle.SelectionForeColor = Color.White; - grid.ColumnHeadersDefaultCellStyle.BackColor = SystemColors.Control; - grid.ColumnHeadersDefaultCellStyle.SelectionBackColor = SystemColors.Control; + grid.DefaultCellStyle.SelectionBackColor = Color.FromArgb(204, 228, 247); + grid.DefaultCellStyle.SelectionForeColor = Color.Black; + grid.ColumnHeadersDefaultCellStyle.BackColor = Color.FromArgb(245, 245, 245); + grid.ColumnHeadersDefaultCellStyle.ForeColor = Color.FromArgb(90, 90, 90); + grid.ColumnHeadersDefaultCellStyle.SelectionBackColor = Color.FromArgb(245, 245, 245); grid.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; - grid.RowHeadersDefaultCellStyle.BackColor = SystemColors.Control; - grid.RowHeadersDefaultCellStyle.SelectionBackColor = Color.Black; - grid.RowHeadersDefaultCellStyle.SelectionForeColor = Color.White; + grid.RowHeadersDefaultCellStyle.BackColor = Color.FromArgb(245, 245, 245); + grid.RowHeadersDefaultCellStyle.ForeColor = Color.FromArgb(90, 90, 90); + grid.RowHeadersDefaultCellStyle.SelectionBackColor = Color.FromArgb(204, 228, 247); + grid.RowHeadersDefaultCellStyle.SelectionForeColor = Color.Black; grid.RowHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; return grid; } @@ -505,8 +514,20 @@ public sealed class SavedNestsForm : Form private static string FormatDate(DateTime value) => value == default ? "" : value.ToString("g", CultureInfo.CurrentCulture); - private static string FormatFileSize(long bytes) => - $"{System.Math.Ceiling(bytes / 1024.0).ToString("N0", CultureInfo.CurrentCulture)} KB"; + private static readonly string[] FileSizeUnits = { "B", "KB", "MB", "GB" }; + + private static string FormatFileSize(long bytes) + { + double size = bytes; + var unitIndex = 0; + while (size >= 1024 && unitIndex < FileSizeUnits.Length - 1) + { + size /= 1024; + unitIndex++; + } + + return unitIndex == 0 ? $"{size:0} {FileSizeUnits[unitIndex]}" : $"{size:0.#} {FileSizeUnits[unitIndex]}"; + } private Guid? SelectedNestId() => nestGrid.SelectedRows.Count > 0 && nestGrid.SelectedRows[0].Cells[IdColumn].Value is Guid id ? id : null; diff --git a/docs/nest-storage.md b/docs/nest-storage.md index eb95485d..e30b5885 100644 --- a/docs/nest-storage.md +++ b/docs/nest-storage.md @@ -26,6 +26,13 @@ The desktop app can save nests two ways: file to share or back up. Export does not change the document's file save path or database record association. +Dragging one or more `.nest` files onto the main window (or onto an open +plate) opens them from disk directly, in either mode — this is the only way +to open a local file while in Database mode, since Open there browses server +records instead of showing a file dialog. A document opened this way is not +bound to a server record, so a subsequent Save in Database mode creates a new +record on first save, same as any other unbound document. + The mode and server address are stored per-PC at `%APPDATA%\OpenNest\storage.json` (`OpenNest.Data.NestStorageSettings`), defaulting to File mode so existing installs are unaffected until an operator opts in via **File > Storage Mode...**.