feat: integrate GPU slide computation into best-fit pipeline
Thread ISlideComputer through BestFitCache → BestFitFinder → RotationSlideStrategy. RotationSlideStrategy now collects all offsets across 4 push directions and dispatches them in a single batch (GPU or CPU fallback). Also improves rotation angle extraction: uses raw geometry (line endpoints + arc cardinal extremes) instead of tessellation to avoid flooding the hull with near-duplicate edge angles, and adds a 5-degree deduplication threshold. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,11 +12,14 @@ namespace OpenNest.Engine.BestFit
|
||||
public class BestFitFinder
|
||||
{
|
||||
private readonly IPairEvaluator _evaluator;
|
||||
private readonly ISlideComputer _slideComputer;
|
||||
private readonly BestFitFilter _filter;
|
||||
|
||||
public BestFitFinder(double maxPlateWidth, double maxPlateHeight, IPairEvaluator evaluator = null)
|
||||
public BestFitFinder(double maxPlateWidth, double maxPlateHeight,
|
||||
IPairEvaluator evaluator = null, ISlideComputer slideComputer = null)
|
||||
{
|
||||
_evaluator = evaluator ?? new PairEvaluator();
|
||||
_slideComputer = slideComputer;
|
||||
_filter = new BestFitFilter
|
||||
{
|
||||
MaxPlateWidth = maxPlateWidth,
|
||||
@@ -78,7 +81,7 @@ namespace OpenNest.Engine.BestFit
|
||||
foreach (var angle in angles)
|
||||
{
|
||||
var desc = string.Format("{0:F1} deg rotated, offset slide", Angle.ToDegrees(angle));
|
||||
strategies.Add(new RotationSlideStrategy(angle, type++, desc));
|
||||
strategies.Add(new RotationSlideStrategy(angle, type++, desc, _slideComputer));
|
||||
}
|
||||
|
||||
return strategies;
|
||||
@@ -102,6 +105,7 @@ namespace OpenNest.Engine.BestFit
|
||||
AddUniqueAngle(angles, Angle.NormalizeRad(hullAngle + System.Math.PI));
|
||||
}
|
||||
|
||||
angles.Sort();
|
||||
return angles;
|
||||
}
|
||||
|
||||
@@ -115,8 +119,24 @@ namespace OpenNest.Engine.BestFit
|
||||
|
||||
foreach (var shape in shapes)
|
||||
{
|
||||
var polygon = shape.ToPolygonWithTolerance(0.01);
|
||||
points.AddRange(polygon.Vertices);
|
||||
// Extract key points from original geometry — line endpoints
|
||||
// plus arc endpoints and cardinal extreme points. This avoids
|
||||
// tessellating arcs into many chords that flood the hull with
|
||||
// near-duplicate edge angles.
|
||||
foreach (var entity in shape.Entities)
|
||||
{
|
||||
if (entity is Line line)
|
||||
{
|
||||
points.Add(line.StartPoint);
|
||||
points.Add(line.EndPoint);
|
||||
}
|
||||
else if (entity is Arc arc)
|
||||
{
|
||||
points.Add(arc.StartPoint());
|
||||
points.Add(arc.EndPoint());
|
||||
AddArcExtremes(points, arc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (points.Count < 3)
|
||||
@@ -143,13 +163,49 @@ namespace OpenNest.Engine.BestFit
|
||||
return hullAngles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the cardinal extreme points of an arc (0°, 90°, 180°, 270°)
|
||||
/// if they fall within the arc's angular span.
|
||||
/// </summary>
|
||||
private static void AddArcExtremes(List<Vector> points, Arc arc)
|
||||
{
|
||||
var a1 = arc.StartAngle;
|
||||
var a2 = arc.EndAngle;
|
||||
|
||||
if (arc.IsReversed)
|
||||
Generic.Swap(ref a1, ref a2);
|
||||
|
||||
// Right (0°)
|
||||
if (Angle.IsBetweenRad(Angle.TwoPI, a1, a2))
|
||||
points.Add(new Vector(arc.Center.X + arc.Radius, arc.Center.Y));
|
||||
|
||||
// Top (90°)
|
||||
if (Angle.IsBetweenRad(Angle.HalfPI, a1, a2))
|
||||
points.Add(new Vector(arc.Center.X, arc.Center.Y + arc.Radius));
|
||||
|
||||
// Left (180°)
|
||||
if (Angle.IsBetweenRad(System.Math.PI, a1, a2))
|
||||
points.Add(new Vector(arc.Center.X - arc.Radius, arc.Center.Y));
|
||||
|
||||
// Bottom (270°)
|
||||
if (Angle.IsBetweenRad(System.Math.PI * 1.5, a1, a2))
|
||||
points.Add(new Vector(arc.Center.X, arc.Center.Y - arc.Radius));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimum angular separation (radians) between hull-derived rotation candidates.
|
||||
/// Tessellated arcs produce many hull edges with nearly identical angles;
|
||||
/// a 1° threshold collapses those into a single representative.
|
||||
/// </summary>
|
||||
private const double AngleTolerance = System.Math.PI / 36; // 5 degrees
|
||||
|
||||
private static void AddUniqueAngle(List<double> angles, double angle)
|
||||
{
|
||||
angle = Angle.NormalizeRad(angle);
|
||||
|
||||
foreach (var existing in angles)
|
||||
{
|
||||
if (existing.IsEqualTo(angle))
|
||||
if (existing.IsEqualTo(angle, AngleTolerance))
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user