feat: wire IFillComparer through PairFiller and StripeFiller
PairFiller now accepts an optional IFillComparer (defaulting to DefaultFillComparer) and uses it in EvaluateCandidates and EvaluateCandidate/FillPattern instead of raw FillScore comparisons. PairsFillStrategy passes context.Policy?.Comparer through. StripeFiller derives _comparer from FillContext.Policy in its constructor and uses it in Fill() instead of FillScore comparisons. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using OpenNest.Engine;
|
||||||
|
|
||||||
namespace OpenNest.Engine.Fill
|
namespace OpenNest.Engine.Fill
|
||||||
{
|
{
|
||||||
@@ -29,11 +30,13 @@ namespace OpenNest.Engine.Fill
|
|||||||
|
|
||||||
private readonly Size plateSize;
|
private readonly Size plateSize;
|
||||||
private readonly double partSpacing;
|
private readonly double partSpacing;
|
||||||
|
private readonly IFillComparer comparer;
|
||||||
|
|
||||||
public PairFiller(Size plateSize, double partSpacing)
|
public PairFiller(Size plateSize, double partSpacing, IFillComparer comparer = null)
|
||||||
{
|
{
|
||||||
this.plateSize = plateSize;
|
this.plateSize = plateSize;
|
||||||
this.partSpacing = partSpacing;
|
this.partSpacing = partSpacing;
|
||||||
|
this.comparer = comparer ?? new DefaultFillComparer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public PairFillResult Fill(NestItem item, Box workArea,
|
public PairFillResult Fill(NestItem item, Box workArea,
|
||||||
@@ -61,7 +64,6 @@ namespace OpenNest.Engine.Fill
|
|||||||
int plateNumber, CancellationToken token, IProgress<NestProgress> progress)
|
int plateNumber, CancellationToken token, IProgress<NestProgress> progress)
|
||||||
{
|
{
|
||||||
List<Part> best = null;
|
List<Part> best = null;
|
||||||
var bestScore = default(FillScore);
|
|
||||||
var sinceImproved = 0;
|
var sinceImproved = 0;
|
||||||
var effectiveWorkArea = workArea;
|
var effectiveWorkArea = workArea;
|
||||||
|
|
||||||
@@ -72,12 +74,10 @@ namespace OpenNest.Engine.Fill
|
|||||||
token.ThrowIfCancellationRequested();
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
var filled = EvaluateCandidate(candidates[i], drawing, effectiveWorkArea);
|
var filled = EvaluateCandidate(candidates[i], drawing, effectiveWorkArea);
|
||||||
var score = FillScore.Compute(filled, effectiveWorkArea);
|
|
||||||
|
|
||||||
if (score > bestScore)
|
if (comparer.IsBetter(filled, best, effectiveWorkArea))
|
||||||
{
|
{
|
||||||
best = filled;
|
best = filled;
|
||||||
bestScore = score;
|
|
||||||
sinceImproved = 0;
|
sinceImproved = 0;
|
||||||
effectiveWorkArea = TryReduceWorkArea(filled, targetCount, workArea, effectiveWorkArea);
|
effectiveWorkArea = TryReduceWorkArea(filled, targetCount, workArea, effectiveWorkArea);
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ namespace OpenNest.Engine.Fill
|
|||||||
}
|
}
|
||||||
|
|
||||||
NestEngineBase.ReportProgress(progress, NestPhase.Pairs, plateNumber, best, workArea,
|
NestEngineBase.ReportProgress(progress, NestPhase.Pairs, plateNumber, best, workArea,
|
||||||
$"Pairs: {i + 1}/{candidates.Count} candidates, best = {bestScore.Count} parts");
|
$"Pairs: {i + 1}/{candidates.Count} candidates, best = {best?.Count ?? 0} parts");
|
||||||
|
|
||||||
if (i + 1 >= EarlyExitMinTried && sinceImproved >= EarlyExitStaleLimit)
|
if (i + 1 >= EarlyExitMinTried && sinceImproved >= EarlyExitStaleLimit)
|
||||||
{
|
{
|
||||||
@@ -101,7 +101,7 @@ namespace OpenNest.Engine.Fill
|
|||||||
Debug.WriteLine("[PairFiller] Cancelled mid-phase, using results so far");
|
Debug.WriteLine("[PairFiller] Cancelled mid-phase, using results so far");
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.WriteLine($"[PairFiller] Best pair result: {bestScore.Count} parts, density={bestScore.Density:P1}");
|
Debug.WriteLine($"[PairFiller] Best pair result: {best?.Count ?? 0} parts");
|
||||||
return best ?? new List<Part>();
|
return best ?? new List<Part>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ namespace OpenNest.Engine.Fill
|
|||||||
var pairParts = candidate.BuildParts(drawing);
|
var pairParts = candidate.BuildParts(drawing);
|
||||||
var engine = new FillLinear(workArea, partSpacing);
|
var engine = new FillLinear(workArea, partSpacing);
|
||||||
var angles = BuildTilingAngles(candidate);
|
var angles = BuildTilingAngles(candidate);
|
||||||
return FillHelpers.FillPattern(engine, pairParts, angles, workArea);
|
return FillHelpers.FillPattern(engine, pairParts, angles, workArea, comparer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<double> BuildTilingAngles(BestFitResult candidate)
|
private static List<double> BuildTilingAngles(BestFitResult candidate)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using OpenNest.Engine;
|
||||||
using OpenNest.Engine.BestFit;
|
using OpenNest.Engine.BestFit;
|
||||||
using OpenNest.Engine.Strategies;
|
using OpenNest.Engine.Strategies;
|
||||||
using OpenNest.Geometry;
|
using OpenNest.Geometry;
|
||||||
@@ -18,6 +19,7 @@ public class StripeFiller
|
|||||||
|
|
||||||
private readonly FillContext _context;
|
private readonly FillContext _context;
|
||||||
private readonly NestDirection _primaryAxis;
|
private readonly NestDirection _primaryAxis;
|
||||||
|
private readonly IFillComparer _comparer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When true, only complete stripes are placed — no partial rows/columns.
|
/// When true, only complete stripes are placed — no partial rows/columns.
|
||||||
@@ -35,6 +37,7 @@ public class StripeFiller
|
|||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_primaryAxis = primaryAxis;
|
_primaryAxis = primaryAxis;
|
||||||
|
_comparer = context.Policy?.Comparer ?? new DefaultFillComparer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Part> Fill()
|
public List<Part> Fill()
|
||||||
@@ -49,7 +52,6 @@ public class StripeFiller
|
|||||||
var strategyName = _primaryAxis == NestDirection.Horizontal ? "Row" : "Column";
|
var strategyName = _primaryAxis == NestDirection.Horizontal ? "Row" : "Column";
|
||||||
|
|
||||||
List<Part> bestParts = null;
|
List<Part> bestParts = null;
|
||||||
var bestScore = default(FillScore);
|
|
||||||
|
|
||||||
for (var i = 0; i < bestFits.Count; i++)
|
for (var i = 0; i < bestFits.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -82,22 +84,20 @@ public class StripeFiller
|
|||||||
if (result == null || result.Count == 0)
|
if (result == null || result.Count == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var score = FillScore.Compute(result, workArea);
|
|
||||||
Debug.WriteLine($"[StripeFiller] {strategyName} candidate {i} {dirLabel}: " +
|
Debug.WriteLine($"[StripeFiller] {strategyName} candidate {i} {dirLabel}: " +
|
||||||
$"angle={Angle.ToDegrees(angle):F1}°, N={count}, waste={waste:F2}, " +
|
$"angle={Angle.ToDegrees(angle):F1}°, N={count}, waste={waste:F2}, " +
|
||||||
$"grid={result.Count} parts");
|
$"grid={result.Count} parts");
|
||||||
|
|
||||||
if (bestParts == null || score > bestScore)
|
if (_comparer.IsBetter(result, bestParts, workArea))
|
||||||
{
|
{
|
||||||
bestParts = result;
|
bestParts = result;
|
||||||
bestScore = score;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NestEngineBase.ReportProgress(_context.Progress, NestPhase.Custom,
|
NestEngineBase.ReportProgress(_context.Progress, NestPhase.Custom,
|
||||||
_context.PlateNumber, bestParts, workArea,
|
_context.PlateNumber, bestParts, workArea,
|
||||||
$"{strategyName}: {i + 1}/{bestFits.Count} pairs, best = {bestScore.Count} parts");
|
$"{strategyName}: {i + 1}/{bestFits.Count} pairs, best = {bestParts?.Count ?? 0} parts");
|
||||||
}
|
}
|
||||||
|
|
||||||
return bestParts ?? new List<Part>();
|
return bestParts ?? new List<Part>();
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ namespace OpenNest.Engine.Strategies
|
|||||||
|
|
||||||
public List<Part> Fill(FillContext context)
|
public List<Part> Fill(FillContext context)
|
||||||
{
|
{
|
||||||
var filler = new PairFiller(context.Plate.Size, context.Plate.PartSpacing);
|
var comparer = context.Policy?.Comparer;
|
||||||
|
var filler = new PairFiller(context.Plate.Size, context.Plate.PartSpacing, comparer);
|
||||||
var result = filler.Fill(context.Item, context.WorkArea,
|
var result = filler.Fill(context.Item, context.WorkArea,
|
||||||
context.PlateNumber, context.Token, context.Progress);
|
context.PlateNumber, context.Token, context.Progress);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user