refactor: unify FillLinear tiling into recursive FillRecursive method

Extract MakeSeedPattern for shared part creation, and replace the
two-step primary/perpendicular tiling with a single FillRecursive
method that tiles along one axis then recurses perpendicular.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 15:00:09 -04:00
parent c28d5d8c12
commit 4196a30791

View File

@@ -259,9 +259,10 @@ namespace OpenNest
} }
/// <summary> /// <summary>
/// Fills a single row of identical parts along one axis using geometry-aware spacing. /// Creates a seed pattern containing a single part positioned at the work area origin.
/// Returns an empty pattern if the part does not fit.
/// </summary> /// </summary>
public Pattern FillRow(Drawing drawing, double rotationAngle, NestDirection direction) private Pattern MakeSeedPattern(Drawing drawing, double rotationAngle)
{ {
var pattern = new Pattern(); var pattern = new Pattern();
@@ -278,17 +279,55 @@ namespace OpenNest
template.BoundingBox.Height > WorkArea.Height + Tolerance.Epsilon) template.BoundingBox.Height > WorkArea.Height + Tolerance.Epsilon)
return pattern; return pattern;
var boundary = new PartBoundary(template, HalfSpacing);
pattern.Parts.Add(template); pattern.Parts.Add(template);
pattern.UpdateBounds();
return pattern;
}
/// <summary>
/// Recursively fills the work area. At depth 0, tiles the pattern along the
/// primary axis, then recurses perpendicular. At depth 1, tiles and returns.
/// </summary>
private List<Part> FillRecursive(Pattern pattern, NestDirection direction, int depth)
{
var boundaries = CreateBoundaries(pattern);
var result = new List<Part>(pattern.Parts);
result.AddRange(TilePattern(pattern, direction, boundaries));
if (depth == 0 && result.Count > pattern.Parts.Count)
{
var rowPattern = new Pattern();
rowPattern.Parts.AddRange(result);
rowPattern.UpdateBounds();
return FillRecursive(rowPattern, PerpendicularAxis(direction), depth + 1);
}
if (depth == 0)
{
// Single part didn't tile along primary — still try perpendicular.
return FillRecursive(pattern, PerpendicularAxis(direction), depth + 1);
}
return result;
}
/// <summary>
/// Fills a single row of identical parts along one axis using geometry-aware spacing.
/// </summary>
public Pattern FillRow(Drawing drawing, double rotationAngle, NestDirection direction)
{
var seed = MakeSeedPattern(drawing, rotationAngle);
if (seed.Parts.Count == 0)
return seed;
var template = seed.Parts[0];
var boundary = new PartBoundary(template, HalfSpacing);
var copyDistance = FindCopyDistance(template, direction, boundary); var copyDistance = FindCopyDistance(template, direction, boundary);
if (copyDistance <= 0) if (copyDistance <= 0)
{ return seed;
pattern.UpdateBounds();
return pattern;
}
var dim = GetDimension(template.BoundingBox, direction); var dim = GetDimension(template.BoundingBox, direction);
var start = GetStart(template.BoundingBox, direction); var start = GetStart(template.BoundingBox, direction);
@@ -305,12 +344,12 @@ namespace OpenNest
var clone = (Part)template.Clone(); var clone = (Part)template.Clone();
clone.Offset(MakeOffset(direction, copyDistance * count)); clone.Offset(MakeOffset(direction, copyDistance * count));
pattern.Parts.Add(clone); seed.Parts.Add(clone);
count++; count++;
} }
pattern.UpdateBounds(); seed.UpdateBounds();
return pattern; return seed;
} }
/// <summary> /// <summary>
@@ -318,58 +357,31 @@ namespace OpenNest
/// </summary> /// </summary>
public List<Part> Fill(Pattern pattern, NestDirection primaryAxis) public List<Part> Fill(Pattern pattern, NestDirection primaryAxis)
{ {
var result = new List<Part>();
if (pattern.Parts.Count == 0) if (pattern.Parts.Count == 0)
return result; return new List<Part>();
var offset = WorkArea.Location - pattern.BoundingBox.Location; var offset = WorkArea.Location - pattern.BoundingBox.Location;
var basePattern = pattern.Clone(offset); var basePattern = pattern.Clone(offset);
if (basePattern.BoundingBox.Width > WorkArea.Width + Tolerance.Epsilon || if (basePattern.BoundingBox.Width > WorkArea.Width + Tolerance.Epsilon ||
basePattern.BoundingBox.Height > WorkArea.Height + Tolerance.Epsilon) basePattern.BoundingBox.Height > WorkArea.Height + Tolerance.Epsilon)
return result; return new List<Part>();
var boundaries = CreateBoundaries(basePattern); return FillRecursive(basePattern, primaryAxis, depth: 0);
result.AddRange(basePattern.Parts);
// Tile along the primary axis.
var primaryTiles = TilePattern(basePattern, primaryAxis, boundaries);
result.AddRange(primaryTiles);
// Build a full-row pattern for perpendicular tiling.
if (primaryTiles.Count > 0)
{
var rowPattern = new Pattern();
rowPattern.Parts.AddRange(result);
rowPattern.UpdateBounds();
basePattern = rowPattern;
boundaries = CreateBoundaries(basePattern);
}
// Tile along the perpendicular axis.
result.AddRange(TilePattern(basePattern, PerpendicularAxis(primaryAxis), boundaries));
return result;
} }
/// <summary> /// <summary>
/// Fills the work area by creating a row along the primary axis, /// Fills the work area by creating a seed part, then recursively tiling
/// then tiling that row pattern along the perpendicular axis. /// along the primary axis and then the perpendicular axis.
/// </summary> /// </summary>
public List<Part> Fill(Drawing drawing, double rotationAngle, NestDirection primaryAxis) public List<Part> Fill(Drawing drawing, double rotationAngle, NestDirection primaryAxis)
{ {
var rowPattern = FillRow(drawing, rotationAngle, primaryAxis); var seed = MakeSeedPattern(drawing, rotationAngle);
if (rowPattern.Parts.Count == 0) if (seed.Parts.Count == 0)
return new List<Part>(); return new List<Part>();
var boundaries = CreateBoundaries(rowPattern); return FillRecursive(seed, primaryAxis, depth: 0);
var result = new List<Part>(rowPattern.Parts);
result.AddRange(TilePattern(rowPattern, PerpendicularAxis(primaryAxis), boundaries));
return result;
} }
} }
} }