diff --git a/OpenNest.Engine.Tests/NestingEngines/IrregularBlockTests.cs b/OpenNest.Engine.Tests/NestingEngines/IrregularBlockTests.cs index 92b79ef..c2327ef 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 78c0991..2df13e3 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 aeb106e..12c12ac 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; }