diff --git a/OpenNest.Engine.Tests/NestingEngines/IrregularNestingEngineTests.cs b/OpenNest.Engine.Tests/NestingEngines/IrregularNestingEngineTests.cs index 664aa6f2..4555c0a2 100644 --- a/OpenNest.Engine.Tests/NestingEngines/IrregularNestingEngineTests.cs +++ b/OpenNest.Engine.Tests/NestingEngines/IrregularNestingEngineTests.cs @@ -1,13 +1,13 @@ +using OpenNest.Engine.NestingEngines.Irregular; +using static OpenNest.Engine.Tests.NestingEngines.JobBuilder; +using static OpenNest.Engine.Tests.NestingEngines.Shapes; using System; using System.Collections.Generic; using System.Linq; using OpenNest.CNC; using OpenNest.Engine.Jobs; using OpenNest.Engine.Jobs.Adapters; -using OpenNest.Engine.NestingEngines.Irregular; using OpenNest.Geometry; -using static OpenNest.Engine.Tests.NestingEngines.JobBuilder; -using static OpenNest.Engine.Tests.NestingEngines.Shapes; namespace OpenNest.Engine.Tests.NestingEngines; @@ -211,42 +211,6 @@ public class IrregularNestingEngineTests Assert.Equal(2, Assert.Single(result.Plates).Placements.Count); } - [Fact] - public void ReportsLiveSheetPreviewWithoutChangingTheLayout() - { - var job = Job(new[] { Part("disc", Disc(3), 10), Part("ell", LShape(12, 8, 4), 10) }, - new[] { Stock("sheet", 40, 60, spacing: 0.5) }); - var reports = new List(); - - var withPreview = new IrregularNestingEngine().Solve(job, new SyncProgress(reports.Add)); - var without = new IrregularNestingEngine().Solve(job); - - var previews = reports.Where(r => r.LegacyProgress?.BestParts is { Count: > 0 }).ToList(); - Assert.NotEmpty(previews); - var work = job.Plates[0].WorkArea; - foreach (var preview in previews.Select(r => r.LegacyProgress)) - { - Assert.True(preview.IsOverallBest); - Assert.Equal((work.Left, work.Bottom, work.Right, work.Top), (preview.ActiveWorkArea.Left, - preview.ActiveWorkArea.Bottom, preview.ActiveWorkArea.Right, preview.ActiveWorkArea.Top)); - foreach (var part in preview.BestParts) - { - Assert.InRange(part.BoundingBox.Left, work.Left - 1e-6, work.Right); - Assert.InRange(part.BoundingBox.Right, work.Left, work.Right + 1e-6); - Assert.InRange(part.BoundingBox.Bottom, work.Bottom - 1e-6, work.Top); - Assert.InRange(part.BoundingBox.Top, work.Bottom, work.Top + 1e-6); - } - } - Assert.Equal( - without.Plates.SelectMany(p => p.Placements), - withPreview.Plates.SelectMany(p => p.Placements)); - } - - private sealed class SyncProgress(Action report) : IProgress - { - public void Report(NestJobProgress value) => report(value); - } - [Fact] public void HasPublicParameterlessConstructorForPluginDiscovery() { diff --git a/OpenNest.Engine/NestingEngines/Irregular/FrontierPacker.cs b/OpenNest.Engine/NestingEngines/Irregular/FrontierPacker.cs index e8c109a1..deb3b394 100644 --- a/OpenNest.Engine/NestingEngines/Irregular/FrontierPacker.cs +++ b/OpenNest.Engine/NestingEngines/Irregular/FrontierPacker.cs @@ -64,9 +64,6 @@ internal sealed class FrontierPacker private readonly BlockCatalog? blocks; private readonly ClampObstacles? clamps; - /// Observer of the placements so far (live preview); read-only, it never steers packing. - private readonly Action>? placedChanged; - public FrontierPacker( IReadOnlyList types, NoFitCache nfps, @@ -76,11 +73,9 @@ internal sealed class FrontierPacker double beta, WorkCounter counter, BlockCatalog? blocks = null, - ClampObstacles? clamps = null, - Action>? placedChanged = null + ClampObstacles? clamps = null ) { - this.placedChanged = placedChanged; this.blocks = blocks; this.clamps = stock.ClampZones.Count == 0 ? null : clamps ?? new ClampObstacles(stock); this.counter = counter; @@ -187,7 +182,6 @@ internal sealed class FrontierPacker partArea += types[typeIndex].Area; front = System.Math.Max(front, axis == PackAxis.X ? part.Right : part.Top); } - placedChanged?.Invoke(placed); left[typeIndex] -= choice.Count; blockStates.RemoveAll(b => b.Members.Count > left[b.Members[0].Orientation.TypeIndex]); diff --git a/OpenNest.Engine/NestingEngines/Irregular/IrregularNestingEngine.cs b/OpenNest.Engine/NestingEngines/Irregular/IrregularNestingEngine.cs index 8dcaf3bf..fd1470ff 100644 --- a/OpenNest.Engine/NestingEngines/Irregular/IrregularNestingEngine.cs +++ b/OpenNest.Engine/NestingEngines/Irregular/IrregularNestingEngine.cs @@ -131,7 +131,6 @@ public sealed class IrregularNestingEngine : INestingEngine } private const int MaxTail = 3; - private readonly SheetPreview? preview = progress == null ? null : new SheetPreview(progress, types, drawingFor); private readonly Dictionary caches = new(); private readonly Dictionary clamps = new(); private readonly Dictionary>> pairs = new(); @@ -290,10 +289,8 @@ public sealed class IrregularNestingEngine : INestingEngine if (stock.Quantity is int available && used[stock.Id] >= available) continue; progress?.Report(new NestJobProgress(NestJobStage.EvaluatingCandidate, stock.Id, sheets.Count, 0, 0)); - var plateIndex = sheets.Count; var packer = new FrontierPacker(types, CacheFor(stock), PairsFor(stock), stock, axis, beta, Work, - BlocksFor(stock), ClampsFor(stock), - preview == null ? null : placed => preview.Report(stock, plateIndex, placed)); + BlocksFor(stock), ClampsFor(stock)); var fill = packer.Fill(remaining, token); sheetPacked?.Invoke(); if (fill.Parts.Count > 0) diff --git a/OpenNest.Engine/NestingEngines/Irregular/SheetPreview.cs b/OpenNest.Engine/NestingEngines/Irregular/SheetPreview.cs deleted file mode 100644 index 80f5fce6..00000000 --- a/OpenNest.Engine/NestingEngines/Irregular/SheetPreview.cs +++ /dev/null @@ -1,57 +0,0 @@ -#nullable enable -using System; -using System.Collections.Generic; -using System.Diagnostics; -using OpenNest.Engine.Jobs; -using OpenNest.Geometry; - -namespace OpenNest.Engine.NestingEngines.Irregular; - -/// -/// Live preview of the sheet a packer is filling, sent as job progress detail -/// () for front ends that draw it. Reports are -/// throttled to one per ; the clock only gates reporting and never feeds -/// back into packing, so the solve stays deterministic. -/// -internal sealed class SheetPreview( - IProgress progress, - IReadOnlyList types, - Func drawingFor -) -{ - internal static readonly TimeSpan Interval = TimeSpan.FromMilliseconds(250); - - private readonly Stopwatch clock = Stopwatch.StartNew(); - private TimeSpan? last; - - /// Called after each placement on a sheet trial; reports when the interval has passed. - public void Report(NestPlateStock stock, int plateIndex, IReadOnlyList placed) - { - var now = clock.Elapsed; - if (last is TimeSpan previous && now - previous < Interval) - return; - last = now; - - var parts = new List(placed.Count); - foreach (var pose in placed) - { - // Same pose mapping as the materialized result: rotate about the snapshot origin, then move. - var part = new Part(drawingFor(types[pose.Orientation.TypeIndex].Part)); - part.Rotate(pose.Orientation.Rotation); - part.Location = new Vector(pose.X, pose.Y); - part.UpdateBounds(); - parts.Add(part); - } - - progress.Report(new NestJobProgress(NestJobStage.EvaluatingCandidate, stock.Id, plateIndex, 0, 0, - new NestProgress - { - Phase = NestPhase.Custom, - PlateNumber = plateIndex + 1, - BestParts = parts, - ActiveWorkArea = stock.WorkArea, - IsOverallBest = true, - Description = $"Irregular: packing plate {plateIndex + 1} on stock {stock.Id} ({parts.Count} parts)", - })); - } -} diff --git a/docs/nesting-engines.md b/docs/nesting-engines.md index b1ad7b93..5fb40528 100644 --- a/docs/nesting-engines.md +++ b/docs/nesting-engines.md @@ -63,13 +63,6 @@ Curved outlines retain conservative flattening-error padding. This does not rela footprints, no-fit polygons or layout validation tolerances; a part extending beyond the accepted work-area bounds remains invalid. -While it runs with a progress sink, Irregular previews the sheet it is packing: at most four -times a second it reports the placements so far as `NestProgress.BestParts` with the stock's -work area, and desktop Auto Nest draws them on the plate view. The preview follows every trial -(each stock, sheet, strategy variant and tail re-plan), so it shows the search at work, not the -layout that will be kept. Reporting only reads finished placements, so layouts are identical -with or without a sink. - Irregular fills gaps and open notches using outer profiles; it does not yet place parts inside enclosed cutouts. For a part with two or more copies it also offers its best-fit pairs (two copies interlocked, as the Best Fit viewer shows them) alongside the single copies, and places a pair