fix(engine): make canonical-frame fills orientation-invariant
Part.Rotation is cumulative, so rebinding canonical parts with CreateAtOrigin(original, p.Rotation) double-counted the drawing's own rotation, and FromCanonical rotated each part about its Location, which moved it off its slot and out of the work area. Add CanonicalFrame.RebindToOriginal (rotation = part - original program rotation, footprint aligned to the canonical part) and use it in the three places that duplicated the old logic. The MBR only fixes the frame modulo 90 degrees and nest results are not 90-degree symmetric (an L gave 56/43/42/42 parts by orientation). CanonicalAngle.Compute now picks one of the four orientations from the centroid offset; symmetric shapes keep the MBR orientation. Fixes the three NestInvarianceTests. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -393,7 +393,6 @@ namespace OpenNest
|
||||
// from a canonical drawing copy so geometry and coords share a frame; rebind
|
||||
// + un-rotate winning pair to the original drawing's frame before returning.
|
||||
var canonicalDrawing = CanonicalFrame.AsCanonicalCopy(item.Drawing);
|
||||
var sourceAngle = item.Drawing?.Source?.Angle ?? 0.0;
|
||||
|
||||
List<Part> bestPlacement = null;
|
||||
Box bestTarget = null;
|
||||
@@ -438,9 +437,9 @@ namespace OpenNest
|
||||
if (bestPlacement == null)
|
||||
continue;
|
||||
|
||||
// Rebind to the original drawing and compose sourceAngle onto rotation so the
|
||||
// final placed parts sit in the user's visible frame.
|
||||
bestPlacement = RebindPairToOriginal(bestPlacement, item.Drawing, sourceAngle);
|
||||
// Rebind to the original drawing and compose the canonical angle onto rotation so
|
||||
// the final placed parts sit in the user's visible frame.
|
||||
bestPlacement = RebindPairToOriginal(bestPlacement, item.Drawing);
|
||||
|
||||
result.AddRange(bestPlacement);
|
||||
item.Quantity = 0;
|
||||
@@ -460,31 +459,12 @@ namespace OpenNest
|
||||
|
||||
/// <summary>
|
||||
/// Rebinds each canonical-frame Part in the pair to the original Drawing at its current
|
||||
/// world pose, then composes sourceAngle onto each via CanonicalFrame.FromCanonical so
|
||||
/// the returned list is in the original drawing's visible frame. Mirrors
|
||||
/// DefaultNestEngine.RebindAndUnCanonicalize.
|
||||
/// world pose, then composes the canonical angle onto each via
|
||||
/// CanonicalFrame.RebindToOriginal so the returned list is in the original drawing's
|
||||
/// visible frame. Mirrors DefaultNestEngine.RebindAndUnCanonicalize.
|
||||
/// </summary>
|
||||
private static List<Part> RebindPairToOriginal(
|
||||
List<Part> parts,
|
||||
Drawing original,
|
||||
double sourceAngle
|
||||
)
|
||||
{
|
||||
if (parts == null || parts.Count == 0)
|
||||
return parts;
|
||||
|
||||
for (var i = 0; i < parts.Count; i++)
|
||||
{
|
||||
var p = parts[i];
|
||||
var rebound = Part.CreateAtOrigin(original, p.Rotation);
|
||||
var delta = p.BoundingBox.Location - rebound.BoundingBox.Location;
|
||||
rebound.Offset(delta);
|
||||
rebound.UpdateBounds();
|
||||
parts[i] = rebound;
|
||||
}
|
||||
|
||||
return CanonicalFrame.FromCanonical(parts, sourceAngle);
|
||||
}
|
||||
private static List<Part> RebindPairToOriginal(List<Part> parts, Drawing original) =>
|
||||
CanonicalFrame.RebindToOriginal(parts, original);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a drawing should use grid-fill (true) or bin-pack (false).
|
||||
|
||||
Reference in New Issue
Block a user