Wire IDistanceComputer into RotationSlideStrategy, replacing inline
CPU/GPU branching. BestFitFinder constructs the appropriate implementation.
Replace PushDirection enum with direction vectors in BuildOffsets.
Rename IBestFitStrategy.Type and PairCandidate.StrategyType to StrategyIndex
for clarity (JSON field name unchanged for backward compatibility).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three bugs fixed in NfpSlideStrategy pipeline:
1. NoFitPolygon.Reflect() incorrectly reversed vertex order. Point
reflection (negating both axes) is a 180° rotation that preserves
winding — the Reverse() call was converting CCW to CW, producing
self-intersecting bowtie NFPs.
2. PolygonHelper inflation used OffsetSide.Left which is inward for
CCW perimeters. Changed to OffsetSide.Right for outward inflation
so NFP boundary positions give properly-spaced part placements.
3. Removed incorrect correction vector — same-drawing pairs have
identical polygon-to-part offsets that cancel out in the NFP
displacement.
Also refactored NfpSlideStrategy to be immutable (removed mutable
cache fields, single constructor with required data, added Create
factory method). BestFitFinder remains on RotationSlideStrategy
as default.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Calling ConvexMinkowskiSum directly with manual reflection produced
wrong winding/reference-point handling, causing all pairs to overlap.
Route through Compute which handles reflection correctly. Hull inputs
keep it fast — few triangles means trivial Clipper union.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ConvexMinkowskiSum is O(n+m) with no boolean geometry ops.
The concave Minkowski path was doing triangulation + pairwise
sums + Clipper2 Union, which hung at 100% CPU for complex parts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>