perf: optimize best fit computation and plate optimizer
- Try all valid best fit pairs instead of only the first when qty=2, picking the best via IsBetterFill comparer (fixes suboptimal plate selection during auto-nesting) - Pre-compute best fits across all plate sizes once via BestFitCache.ComputeForSizes instead of per-size GPU evaluation - Early exit plate optimizer when all items fit (salvage < 100%) - Trim slide offset sweep range to 50% overlap to reduce candidates - Use actual geometry (ray-arc/ray-circle intersection) instead of tessellated polygons for slide distance computation — eliminates the massive line count from circle/arc tessellation - Add RayArcDistance and RayCircleDistance to SpatialQuery - Add PartGeometry.GetOffsetPerimeterEntities for non-tessellated perimeter extraction - Disable GPU slide computer (slower than CPU currently) - Remove dead SelectBestFitPair virtual method and overrides Reduces best fit computation from 7+ minutes to ~4 seconds for a 73x25" part with 30+ holes on a 48x96 plate. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -139,24 +139,42 @@ namespace OpenNest
|
||||
var bestFits = BestFitCache.GetOrCompute(
|
||||
drawing, Plate.Size.Length, Plate.Size.Width, Plate.PartSpacing);
|
||||
|
||||
var best = SelectBestFitPair(bestFits);
|
||||
if (best == null)
|
||||
return null;
|
||||
List<Part> bestPlacement = null;
|
||||
|
||||
// BuildParts produces landscape orientation (Width >= Height).
|
||||
// Try both landscape and portrait (90° rotated) and let the
|
||||
// engine's comparer pick the better orientation.
|
||||
var landscape = best.BuildParts(drawing);
|
||||
var portrait = RotatePair90(landscape);
|
||||
foreach (var fit in bestFits)
|
||||
{
|
||||
if (!fit.Keep)
|
||||
continue;
|
||||
|
||||
var lFits = TryOffsetToWorkArea(landscape, workArea);
|
||||
var pFits = TryOffsetToWorkArea(portrait, workArea);
|
||||
// Skip pairs that can't possibly fit the work area in either orientation.
|
||||
if (fit.ShortestSide > System.Math.Min(workArea.Width, workArea.Length) + Tolerance.Epsilon)
|
||||
continue;
|
||||
if (fit.LongestSide > System.Math.Max(workArea.Width, workArea.Length) + Tolerance.Epsilon)
|
||||
continue;
|
||||
|
||||
if (!lFits && !pFits)
|
||||
return null;
|
||||
if (lFits && pFits)
|
||||
return IsBetterFill(portrait, landscape, workArea) ? portrait : landscape;
|
||||
return lFits ? landscape : portrait;
|
||||
var landscape = fit.BuildParts(drawing);
|
||||
var portrait = RotatePair90(landscape);
|
||||
|
||||
var lFits = TryOffsetToWorkArea(landscape, workArea);
|
||||
var pFits = TryOffsetToWorkArea(portrait, workArea);
|
||||
|
||||
// Pick the better orientation for this pair.
|
||||
List<Part> candidate = null;
|
||||
if (lFits && pFits)
|
||||
candidate = IsBetterFill(portrait, landscape, workArea) ? portrait : landscape;
|
||||
else if (lFits)
|
||||
candidate = landscape;
|
||||
else if (pFits)
|
||||
candidate = portrait;
|
||||
|
||||
if (candidate == null)
|
||||
continue;
|
||||
|
||||
if (bestPlacement == null || IsBetterFill(candidate, bestPlacement, workArea))
|
||||
bestPlacement = candidate;
|
||||
}
|
||||
|
||||
return bestPlacement;
|
||||
}
|
||||
|
||||
private static List<Part> RotatePair90(List<Part> parts)
|
||||
|
||||
Reference in New Issue
Block a user