From 418f75916b0a59686591b8175c41373bb182ffca Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sun, 4 Oct 2026 19:40:49 -0400 Subject: [PATCH] refactor(engine): share the private-plate Fill run between blocks and future callers Move BlockCatalog's private-drawing Fill invocation (BestFitCache stabilization plus the Default FillItem call) into PrivatePlateFill so the cutout fill can reuse it without duplicating the cache handling. Behavior unchanged; Irregular filter 96/96. --- .../Jobs/Placement/PrivatePlateFill.cs | 41 +++++++++++++++++++ .../NestingEngines/Irregular/BlockCatalog.cs | 18 +------- 2 files changed, 43 insertions(+), 16 deletions(-) create mode 100644 OpenNest.Engine/Jobs/Placement/PrivatePlateFill.cs diff --git a/OpenNest.Engine/Jobs/Placement/PrivatePlateFill.cs b/OpenNest.Engine/Jobs/Placement/PrivatePlateFill.cs new file mode 100644 index 0000000..71ca769 --- /dev/null +++ b/OpenNest.Engine/Jobs/Placement/PrivatePlateFill.cs @@ -0,0 +1,41 @@ +#nullable enable +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using OpenNest.Engine.BestFit; +using OpenNest.Engine.Jobs.Adapters; +using OpenNest.Geometry; + +namespace OpenNest.Engine.Jobs.Placement; + +/// +/// Runs the Default Fill for one private drawing on a private plate, for callers that turn the +/// result into job poses. The caller owns the drawing and must call +/// for it when finished. +/// +internal static class PrivatePlateFill +{ + /// X extent of the plate and work area. + /// Y extent of the plate and work area. + /// Copies wanted; zero fills the whole area. + internal static List Run(Drawing drawing, RotationPolicy rotation, double spacing, + double length, double width, int quantity, CancellationToken token) + { + var plate = new Plate(new Size(width, length)) { PartSpacing = spacing }; + // The drawing is private: stabilize this cache entry before Fill's candidate pruning. + var fits = BestFitCache.GetOrCompute(drawing, plate.Size.Length, plate.Size.Width, spacing); + var sorted = fits.OrderBy(f => f.RotatedArea).ThenBy(f => f.Candidate.StrategyIndex) + .ThenBy(f => f.Candidate.Part2Rotation).ThenBy(f => f.Candidate.Part2Offset.X) + .ThenBy(f => f.Candidate.Part2Offset.Y).ThenBy(f => f.OptimalRotation).ToArray(); + fits.Clear(); + fits.AddRange(sorted); + return PlateFillService.FillItem("Default", plate, new NestItem + { + Drawing = drawing, + Quantity = quantity, + RotationStart = rotation.Start, + RotationEnd = rotation.End, + StepAngle = DrawingJobMapper.LegacyStep(rotation), + }, new Box(0, 0, length, width), null!, token); + } +} diff --git a/OpenNest.Engine/NestingEngines/Irregular/BlockCatalog.cs b/OpenNest.Engine/NestingEngines/Irregular/BlockCatalog.cs index c00b04d..78c0991 100644 --- a/OpenNest.Engine/NestingEngines/Irregular/BlockCatalog.cs +++ b/OpenNest.Engine/NestingEngines/Irregular/BlockCatalog.cs @@ -77,24 +77,10 @@ internal sealed class BlockCatalog : IDisposable { if (!drawings.TryGetValue(type.Index, out var drawing)) drawings[type.Index] = drawing = DrawingJobMapper.CreateDrawing(type.Part); - var plate = new Plate(new Size(rectangle.Width, rectangle.Length)) { PartSpacing = spacing }; try { - // The drawing is private: stabilize this cache entry before Fill's candidate pruning. - var fits = BestFitCache.GetOrCompute(drawing, plate.Size.Length, plate.Size.Width, spacing); - var sorted = fits.OrderBy(f => f.RotatedArea).ThenBy(f => f.Candidate.StrategyIndex) - .ThenBy(f => f.Candidate.Part2Rotation).ThenBy(f => f.Candidate.Part2Offset.X) - .ThenBy(f => f.Candidate.Part2Offset.Y).ThenBy(f => f.OptimalRotation).ToArray(); - fits.Clear(); - fits.AddRange(sorted); - var members = PlateFillService.FillItem("Default", plate, new NestItem - { - Drawing = drawing, - Quantity = quantity, - RotationStart = type.Part.Rotation.Start, - RotationEnd = type.Part.Rotation.End, - StepAngle = DrawingJobMapper.LegacyStep(type.Part.Rotation), - }, new Box(0, 0, rectangle.Length, rectangle.Width), null!, token); + var members = PrivatePlateFill.Run(drawing, type.Part.Rotation, spacing, + rectangle.Length, rectangle.Width, quantity, token); token.ThrowIfCancellationRequested(); return Resolve(type, members.Take(quantity).ToArray(), orientations[type.Index], spacing, token); }