perf: optimize fill hot path — bbox pre-check and geometry inner loop

- Add bounding box rejection in HasOverlaps to skip expensive
  Part.Intersects (CNC→geometry conversion) for non-adjacent parts.
  Eliminates ~35% CPU in IsBetterValidFill for grid layouts.
- Optimize RayEdgeDistance: access Line fields directly instead of
  property getters (avoids Vector struct copies), inline IsEqualTo
  with direct range comparison (avoids Math.Abs), and precompute
  deltas for reuse in interpolation.
- Cache line endpoints in DirectionalDistance outer loop to avoid
  repeated struct copies in the inner loop.
- Add fill timer to ActionClone.Fill, displayed in PlateView status
  bar as "Fill: N parts in M ms".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 22:10:25 -04:00
parent 35d7248da0
commit 91908c1732
5 changed files with 50 additions and 23 deletions

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;
using OpenNest.Controls;
@@ -170,6 +171,8 @@ namespace OpenNest.Actions
private void Fill()
{
var sw = Stopwatch.StartNew();
var plate = plateView.Plate;
var engine = new NestEngine(plate);
var groupParts = parts.Select(p => p.BasePart).ToList();
@@ -179,6 +182,8 @@ namespace OpenNest.Actions
if (plate.Parts.Count == 0)
{
engine.Fill(groupParts);
sw.Stop();
plateView.Status = $"Fill: {plate.Parts.Count} parts in {sw.ElapsedMilliseconds} ms";
return;
}
@@ -197,7 +202,10 @@ namespace OpenNest.Actions
if (bestArea == Box.Empty)
return;
var before = plate.Parts.Count;
engine.Fill(groupParts, bestArea);
sw.Stop();
plateView.Status = $"Fill: {plate.Parts.Count - before} parts in {sw.ElapsedMilliseconds} ms";
}
}
}

View File

@@ -139,7 +139,7 @@ namespace OpenNest.Controls
public string Status
{
get { return status; }
protected set
set
{
status = value;