diff --git a/OpenNest.Engine/Fill/FillExtents.cs b/OpenNest.Engine/Fill/FillExtents.cs index e5ccd6f..40f7dee 100644 --- a/OpenNest.Engine/Fill/FillExtents.cs +++ b/OpenNest.Engine/Fill/FillExtents.cs @@ -1,10 +1,8 @@ -using OpenNest.Engine.Strategies; using OpenNest.Geometry; using OpenNest.Math; using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Threading; namespace OpenNest.Engine.Fill @@ -107,7 +105,7 @@ namespace OpenNest.Engine.Fill private List BuildColumn(Part part1, Part part2, Box pairBbox) { - var pairParts = new List { (Part)part1.Clone(), (Part)part2.Clone() }; + var column = new List { (Part)part1.Clone(), (Part)part2.Clone() }; // Find geometry-aware copy distance for the pair vertically. var boundary1 = new PartBoundary(part1, halfSpacing); @@ -128,11 +126,22 @@ namespace OpenNest.Engine.Fill boundary1, boundary2, pairHeight); if (copyDistance <= 0) - return pairParts; + return column; - var result = new List(pairParts); - result.AddRange(FillHelpers.Tile(pairParts, workArea, copyDistance, NestDirection.Vertical, allowPartial: false)); - return result; + var count = 1; + while (true) + { + var nextBottom = pairBbox.Bottom + copyDistance * count; + if (nextBottom + pairHeight > workArea.Top + Tolerance.Epsilon) + break; + + var offset = new Vector(0, copyDistance * count); + column.Add(part1.CloneAtOffset(offset)); + column.Add(part2.CloneAtOffset(offset)); + count++; + } + + return column; } private double FindVerticalCopyDistance( @@ -315,10 +324,48 @@ namespace OpenNest.Engine.Fill Debug.WriteLine($"[FillExtents] Column copy distance: {copyDistance:F2} (bbox width: {columnWidth:F2}, spacing: {partSpacing:F2})"); + // Build all columns. var result = new List(column); - result.AddRange(FillHelpers.Tile(column, workArea, copyDistance, NestDirection.Horizontal, allowPartial: true)); + + // Add the test column we already computed as column 2. + foreach (var part in testColumn) + { + if (IsWithinWorkArea(part)) + result.Add(part); + } + + // Tile additional columns at the copy distance. + var colIndex = 2; + while (!token.IsCancellationRequested) + { + var offset = new Vector(copyDistance * colIndex, 0); + var anyFit = false; + + foreach (var part in column) + { + var clone = part.CloneAtOffset(offset); + if (IsWithinWorkArea(clone)) + { + result.Add(clone); + anyFit = true; + } + } + + if (!anyFit) + break; + + colIndex++; + } return result; } + + private bool IsWithinWorkArea(Part part) + { + return part.BoundingBox.Right <= workArea.Right + Tolerance.Epsilon && + part.BoundingBox.Top <= workArea.Top + Tolerance.Epsilon && + part.BoundingBox.Left >= workArea.Left - Tolerance.Epsilon && + part.BoundingBox.Bottom >= workArea.Bottom - Tolerance.Epsilon; + } } } diff --git a/OpenNest.Engine/Fill/FillLinear.cs b/OpenNest.Engine/Fill/FillLinear.cs index 98e88ed..674bff3 100644 --- a/OpenNest.Engine/Fill/FillLinear.cs +++ b/OpenNest.Engine/Fill/FillLinear.cs @@ -1,4 +1,3 @@ -using OpenNest.Engine.Strategies; using OpenNest.Geometry; using OpenNest.Math; using System.Collections.Generic; @@ -250,7 +249,57 @@ namespace OpenNest.Engine.Fill private List TilePattern(Pattern basePattern, NestDirection direction, PartBoundary[] boundaries) { var copyDistance = FindPatternCopyDistance(basePattern, direction, boundaries); - return FillHelpers.Tile(basePattern.Parts, WorkArea, copyDistance, direction, allowPartial: true); + + if (copyDistance <= 0) + return new List(); + + var dim = GetDimension(basePattern.BoundingBox, direction); + var start = GetStart(basePattern.BoundingBox, direction); + var limit = GetLimit(direction); + + var estimatedCopies = (int)((limit - start - dim) / copyDistance); + var result = new List(estimatedCopies * basePattern.Parts.Count); + + var count = 1; + + while (true) + { + var nextPos = start + copyDistance * count; + + if (nextPos + dim > limit + Tolerance.Epsilon) + break; + + var offset = MakeOffset(direction, copyDistance * count); + + foreach (var part in basePattern.Parts) + result.Add(part.CloneAtOffset(offset)); + + count++; + } + + // For multi-part patterns, try to place individual parts from the + // next copy that didn't fit as a whole. This handles cases where + // e.g. a 2-part pair only partially fits — one part may still be + // within the work area even though the full pattern exceeds it. + if (basePattern.Parts.Count > 1) + { + var offset = MakeOffset(direction, copyDistance * count); + + foreach (var basePart in basePattern.Parts) + { + var part = basePart.CloneAtOffset(offset); + + if (part.BoundingBox.Right <= WorkArea.Right + Tolerance.Epsilon && + part.BoundingBox.Top <= WorkArea.Top + Tolerance.Epsilon && + part.BoundingBox.Left >= WorkArea.Left - Tolerance.Epsilon && + part.BoundingBox.Bottom >= WorkArea.Bottom - Tolerance.Epsilon) + { + result.Add(part); + } + } + } + + return result; } ///