From 9411dd0fdd05bde0401355546ada5fc54f1f741a Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Fri, 20 Mar 2026 14:42:50 -0400 Subject: [PATCH] refactor: extract PlacedPart/SequenceEntry types, add IFP caching Move PlacedPart to its own file. Replace tuple-based sequences with SequenceEntry struct for clarity. Add IProgress parameter to INestOptimizer. Add IFP caching to NfpCache to avoid recomputing inner fit polygons for the same drawing/rotation/workArea. Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest.Engine/Nfp/INestOptimizer.cs | 6 ++++-- OpenNest.Engine/Nfp/NfpCache.cs | 22 ++++++++++++++++++++++ OpenNest.Engine/Nfp/PlacedPart.cs | 15 +++++++++++++++ OpenNest.Engine/Nfp/SequenceEntry.cs | 24 ++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 OpenNest.Engine/Nfp/PlacedPart.cs create mode 100644 OpenNest.Engine/Nfp/SequenceEntry.cs diff --git a/OpenNest.Engine/Nfp/INestOptimizer.cs b/OpenNest.Engine/Nfp/INestOptimizer.cs index cad4304..2664d98 100644 --- a/OpenNest.Engine/Nfp/INestOptimizer.cs +++ b/OpenNest.Engine/Nfp/INestOptimizer.cs @@ -1,5 +1,6 @@ using OpenNest.Engine.Fill; using OpenNest.Geometry; +using System; using System.Collections.Generic; using System.Threading; @@ -11,9 +12,9 @@ namespace OpenNest.Engine.Nfp public class OptimizationResult { /// - /// The best sequence found: (drawingId, rotation, drawing) tuples in placement order. + /// The best placement sequence found. /// - public List<(int drawingId, double rotation, Drawing drawing)> Sequence { get; set; } + public List Sequence { get; set; } /// /// The score achieved by the best sequence. @@ -34,6 +35,7 @@ namespace OpenNest.Engine.Nfp { OptimizationResult Optimize(List items, Box workArea, NfpCache cache, Dictionary> candidateRotations, + IProgress progress = null, CancellationToken cancellation = default); } } diff --git a/OpenNest.Engine/Nfp/NfpCache.cs b/OpenNest.Engine/Nfp/NfpCache.cs index 90076b2..8f6a553 100644 --- a/OpenNest.Engine/Nfp/NfpCache.cs +++ b/OpenNest.Engine/Nfp/NfpCache.cs @@ -14,6 +14,8 @@ namespace OpenNest.Engine.Nfp private readonly Dictionary cache = new Dictionary(); private readonly Dictionary> polygonCache = new Dictionary>(); + private readonly Dictionary<(int drawingId, double rotation), Polygon> ifpCache + = new Dictionary<(int drawingId, double rotation), Polygon>(); /// /// Registers a pre-computed polygon for a drawing at a specific rotation. @@ -28,6 +30,26 @@ namespace OpenNest.Engine.Nfp } rotations[rotation] = polygon; + + // Clear IFP cache if a polygon is updated (though usually they aren't). + ifpCache.Remove((drawingId, rotation)); + } + + /// + /// Gets or computes the IFP for a drawing at a specific rotation within a work area. + /// + public Polygon GetIfp(int drawingId, double rotation, Box workArea) + { + if (ifpCache.TryGetValue((drawingId, rotation), out var ifp)) + return ifp; + + var polygon = GetPolygon(drawingId, rotation); + if (polygon == null) + return new Polygon(); + + ifp = InnerFitPolygon.Compute(workArea, polygon); + ifpCache[(drawingId, rotation)] = ifp; + return ifp; } /// diff --git a/OpenNest.Engine/Nfp/PlacedPart.cs b/OpenNest.Engine/Nfp/PlacedPart.cs new file mode 100644 index 0000000..a32fc49 --- /dev/null +++ b/OpenNest.Engine/Nfp/PlacedPart.cs @@ -0,0 +1,15 @@ +using OpenNest.Geometry; + +namespace OpenNest.Engine.Nfp +{ + /// + /// Represents a part that has been placed by the BLF algorithm. + /// + public class PlacedPart + { + public int DrawingId { get; set; } + public double Rotation { get; set; } + public Vector Position { get; set; } + public Drawing Drawing { get; set; } + } +} diff --git a/OpenNest.Engine/Nfp/SequenceEntry.cs b/OpenNest.Engine/Nfp/SequenceEntry.cs new file mode 100644 index 0000000..1cc6091 --- /dev/null +++ b/OpenNest.Engine/Nfp/SequenceEntry.cs @@ -0,0 +1,24 @@ +namespace OpenNest.Engine.Nfp +{ + /// + /// An entry in a placement sequence — identifies which drawing to place and at what rotation. + /// + public readonly struct SequenceEntry + { + public int DrawingId { get; } + public double Rotation { get; } + public Drawing Drawing { get; } + + public SequenceEntry(int drawingId, double rotation, Drawing drawing) + { + DrawingId = drawingId; + Rotation = rotation; + Drawing = drawing; + } + + public SequenceEntry WithRotation(double rotation) + { + return new SequenceEntry(DrawingId, rotation, Drawing); + } + } +}