feat: improve remnant fill with rotation sweep, smart pair selection, and partial pattern fill

Narrow remnant strips now get more parts by:
- Sweeping rotations every 5° when the strip is narrower than the part
- Including all pairs that fit the strip width (not just top 50 by area)
- Placing individual parts from incomplete pattern copies that still fit
- Using finer polygon tolerance (0.01) for hull edge angle detection

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 18:33:06 -04:00
parent c5a9c27160
commit 435a08074b
3 changed files with 100 additions and 18 deletions

View File

@@ -15,7 +15,7 @@ namespace OpenNest
public Box WorkArea { get; }
public double PartSpacing { get; }
public double HalfSpacing => PartSpacing / 2;
private static Vector MakeOffset(NestDirection direction, double distance)
@@ -227,7 +227,9 @@ namespace OpenNest
/// <summary>
/// Tiles a pattern along the given axis, returning the cloned parts
/// (does not include the original pattern's parts).
/// (does not include the original pattern's parts). For multi-part
/// patterns, also adds individual parts from the next incomplete copy
/// that still fit within the work area.
/// </summary>
private List<Part> TilePattern(Pattern basePattern, NestDirection direction, PartBoundary[] boundaries)
{
@@ -255,6 +257,26 @@ namespace OpenNest
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 partialClone = basePattern.Clone(MakeOffset(direction, copyDistance * count));
foreach (var part in partialClone.Parts)
{
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;
}