diff --git a/OpenNest.Engine/Fill/FillExtents.cs b/OpenNest.Engine/Fill/FillExtents.cs index 6fae350..14ea616 100644 --- a/OpenNest.Engine/Fill/FillExtents.cs +++ b/OpenNest.Engine/Fill/FillExtents.cs @@ -47,13 +47,21 @@ namespace OpenNest.Engine.Fill var adjusted = AdjustColumn(pair.Value, column, token); + // The iterative pair adjustment can shift parts enough to cause + // genuine overlap. Fall back to the unadjusted column when this happens. + if (HasOverlappingParts(adjusted)) + { + Debug.WriteLine("[FillExtents] Adjusted column has overlaps, using unadjusted"); + adjusted = column; + } + NestEngineBase.ReportProgress(progress, new ProgressReport { Phase = NestPhase.Extents, PlateNumber = plateNumber, Parts = adjusted, WorkArea = workArea, - Description = $"Extents: adjusted column {adjusted.Count} parts", + Description = $"Extents: column {adjusted.Count} parts", }); var result = RepeatColumns(adjusted, token); @@ -386,5 +394,31 @@ namespace OpenNest.Engine.Fill part.BoundingBox.Left >= workArea.Left - Tolerance.Epsilon && part.BoundingBox.Bottom >= workArea.Bottom - Tolerance.Epsilon; } + + private static bool HasOverlappingParts(List parts) + { + for (var i = 0; i < parts.Count; i++) + { + var b1 = parts[i].BoundingBox; + + for (var j = i + 1; j < parts.Count; j++) + { + var b2 = parts[j].BoundingBox; + + var overlapX = System.Math.Min(b1.Right, b2.Right) + - System.Math.Max(b1.Left, b2.Left); + var overlapY = System.Math.Min(b1.Top, b2.Top) + - System.Math.Max(b1.Bottom, b2.Bottom); + + if (overlapX <= Tolerance.Epsilon || overlapY <= Tolerance.Epsilon) + continue; + + if (parts[i].Intersects(parts[j], out _)) + return true; + } + } + + return false; + } } } diff --git a/OpenNest.Engine/Fill/StripeFiller.cs b/OpenNest.Engine/Fill/StripeFiller.cs index 7fb60f3..9f322d2 100644 --- a/OpenNest.Engine/Fill/StripeFiller.cs +++ b/OpenNest.Engine/Fill/StripeFiller.cs @@ -158,6 +158,15 @@ public class StripeFiller if (gridParts.Count == 0) return null; + // Reject results where bounding boxes overlap — the angle convergence + // can produce slightly off-axis rotations where FillLinear's copy + // distance calculation doesn't fully account for the rotated geometry. + if (HasOverlappingParts(gridParts)) + { + Debug.WriteLine($"[StripeFiller] Rejected grid: overlapping bounding boxes detected"); + return null; + } + var allParts = new List(gridParts); var remnantParts = FillRemnant(gridParts, primaryAxis); @@ -470,4 +479,34 @@ public class StripeFiller { return axis == NestDirection.Horizontal ? box.Width : box.Length; } + + /// + /// Checks if any pair of parts geometrically overlap. Uses bounding box + /// pre-filtering for performance, then falls back to shape intersection. + /// + private static bool HasOverlappingParts(List parts) + { + for (var i = 0; i < parts.Count; i++) + { + var b1 = parts[i].BoundingBox; + + for (var j = i + 1; j < parts.Count; j++) + { + var b2 = parts[j].BoundingBox; + + var overlapX = System.Math.Min(b1.Right, b2.Right) + - System.Math.Max(b1.Left, b2.Left); + var overlapY = System.Math.Min(b1.Top, b2.Top) + - System.Math.Max(b1.Bottom, b2.Bottom); + + if (overlapX <= Tolerance.Epsilon || overlapY <= Tolerance.Epsilon) + continue; + + if (parts[i].Intersects(parts[j], out _)) + return true; + } + } + + return false; + } }