merge: integrate refactor/menubar-reorg

Brings in the Plate menu reorganization, Database-mode drag-and-drop
open, the SavedNestsForm look fixes and the Irregular block-fill work
budget fix.

Conflicts:
- MainForm.Designer.cs: keep the branch's Plate menu order (Arrange
  submenu, View in CAD last) and add master's Plan Cutting item ahead
  of the lead-in commands.
- SavedNestsForm.cs: keep master's browser rewrite (details, preview,
  columns) and apply the branch's intent to it: open maximized with
  MaximizedBounds clear of the taskbar, the lighter grid palette on all
  three grids, and B/KB/MB/GB file sizes.
This commit is contained in:
aj committed 2026-10-06 06:39:41 -04:00
commit bc061488ee
8 files changed
+279 -176

No files matched your search

@@ -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<int, Drawing> drawings = new();
private readonly Dictionary<int, List<Orientation>> orientations = new();
private readonly Dictionary<int, int> attempts = new();
@@ -24,9 +26,11 @@ internal sealed class BlockCatalog : IDisposable
private readonly Dictionary<(int Type, int Quantity, double Length, double Width), IReadOnlyList<Placed>> cache = new();
public BlockCatalog(double spacing, IReadOnlyList<PartType> types,
IReadOnlyDictionary<int, IReadOnlyList<PairPose>> pairs)
IReadOnlyDictionary<int, IReadOnlyList<PairPose>> 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<Placed>();
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<Placed> 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
@@ -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<PartType> types,
IProgress<NestJobProgress>? 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;
}