From 0a33047ad6a3f1781e05348d48fd75e56c6688bf Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Wed, 18 Mar 2026 19:53:08 -0400 Subject: [PATCH] fix(engine): prevent FillExtents overlap and add strategy filter API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FillExtents vertical copy distance was not clamped, allowing rows to be placed overlapping each other when slide calculations returned large values. Clamp to pairHeight + partSpacing minimum, matching FillLinear. Also add FillStrategyRegistry.SetEnabled() to restrict which strategies run — useful for isolating individual strategies during troubleshooting. Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest.Engine/Fill/FillExtents.cs | 14 ++++---------- .../Strategies/FillStrategyRegistry.cs | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/OpenNest.Engine/Fill/FillExtents.cs b/OpenNest.Engine/Fill/FillExtents.cs index ff06073..40f7dee 100644 --- a/OpenNest.Engine/Fill/FillExtents.cs +++ b/OpenNest.Engine/Fill/FillExtents.cs @@ -172,18 +172,12 @@ namespace OpenNest.Engine.Fill if (minSlide >= double.MaxValue || minSlide < 0) return pairHeight + partSpacing; - // Boundaries are inflated by halfSpacing, so when inflated edges touch - // the actual parts have partSpacing gap. Match FillLinear's pattern: - // startOffset = pairHeight (no extra spacing), copyDist = height - slide. + // Match FillLinear.ComputeCopyDistance: copyDist = startOffset - slide, + // clamped so it never goes below pairHeight + partSpacing to prevent + // bounding-box overlap from spurious slide values. var copyDist = pairHeight - minSlide; - // Boundaries are inflated by halfSpacing, so the geometry-aware - // distance already guarantees partSpacing gap. Only fall back to - // bounding-box distance if the calculation produced a non-positive value. - if (copyDist <= Tolerance.Epsilon) - return pairHeight + partSpacing; - - return copyDist; + return System.Math.Max(copyDist, pairHeight + partSpacing); } private static double SlideDistance( diff --git a/OpenNest.Engine/Strategies/FillStrategyRegistry.cs b/OpenNest.Engine/Strategies/FillStrategyRegistry.cs index 1a49604..581b4bf 100644 --- a/OpenNest.Engine/Strategies/FillStrategyRegistry.cs +++ b/OpenNest.Engine/Strategies/FillStrategyRegistry.cs @@ -11,6 +11,7 @@ namespace OpenNest.Engine.Strategies { private static readonly List strategies = new(); private static List sorted; + private static HashSet enabledFilter; static FillStrategyRegistry() { @@ -18,7 +19,21 @@ namespace OpenNest.Engine.Strategies } public static IReadOnlyList Strategies => - sorted ??= strategies.OrderBy(s => s.Order).ToList(); + sorted ??= (enabledFilter != null + ? strategies.Where(s => enabledFilter.Contains(s.Name)).OrderBy(s => s.Order).ToList() + : strategies.OrderBy(s => s.Order).ToList()); + + /// + /// Restricts the active strategies to only those whose names are listed. + /// Pass null to restore all strategies. + /// + public static void SetEnabled(params string[] names) + { + enabledFilter = names != null && names.Length > 0 + ? new HashSet(names, StringComparer.OrdinalIgnoreCase) + : null; + sorted = null; + } public static void LoadFrom(Assembly assembly) {