feat: unify ActionAddPart into ActionClone and add group fill support

Merge ActionAddPart into ActionClone by adding a Drawing constructor,
eliminating the redundant class. ActionClone now handles both adding
new parts from a drawing and cloning selected part groups. Added
Ctrl+F fill support for groups using FillLinear pattern tiling, and
adopted quadrant-aware push directions from ActionAddPart. Refactored
FillLinear to extract shared helpers and add a Fill(Pattern) overload
for tiling arbitrary part groups across the work area.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 09:56:48 -05:00
parent 5807255931
commit 40b40ca4ba
7 changed files with 237 additions and 297 deletions

View File

@@ -55,6 +55,46 @@ namespace OpenNest
return true;
}
public bool Fill(List<Part> groupParts)
{
if (groupParts == null || groupParts.Count == 0)
return false;
var workArea = Plate.WorkArea();
var engine = new FillLinear(workArea, Plate.PartSpacing);
// Build a pattern from the group of parts.
var pattern = new Pattern();
foreach (var part in groupParts)
{
var clone = (Part)part.Clone();
clone.UpdateBounds();
pattern.Parts.Add(clone);
}
pattern.UpdateBounds();
// Try 4 configurations: 2 axes x 2 orientations (horizontal/vertical).
var configs = new[]
{
engine.Fill(pattern, NestDirection.Horizontal),
engine.Fill(pattern, NestDirection.Vertical)
};
List<Part> best = null;
foreach (var config in configs)
{
if (best == null || config.Count > best.Count)
best = config;
}
if (best == null || best.Count == 0)
return false;
Plate.Parts.AddRange(best);
return true;
}
public bool Fill(NestItem item, int maxCount)
{
if (maxCount <= 0)
@@ -169,12 +209,17 @@ namespace OpenNest
}
}
// Convert to polygon so arcs are properly represented as line segments.
// Shape.FindBestRotation() uses Entity cardinal points which are incorrect
// for arcs that don't sweep through all 4 cardinal directions.
var polygon = largest.ToPolygonWithTolerance(0.1);
BoundingRectangleResult result;
if (item.RotationStart.IsEqualTo(0) && item.RotationEnd.IsEqualTo(0))
result = largest.FindBestRotation();
result = polygon.FindBestRotation();
else
result = largest.FindBestRotation(item.RotationStart, item.RotationEnd);
result = polygon.FindBestRotation(item.RotationStart, item.RotationEnd);
// Negate the angle to align the minimum bounding rectangle with the axes.
return -result.Angle;