From ee83f17afeb1e2c728088b9fa41e942a876b8b99 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sat, 21 Mar 2026 12:46:30 -0400 Subject: [PATCH] feat: wire FillPolicy into DefaultNestEngine and FillContext - FillContext gains a Policy property (init-only) carrying the IFillComparer - DefaultNestEngine.Fill sets Policy = BuildPolicy() on every context - RunPipeline now uses context.Policy.Comparer.IsBetter instead of IsBetterFill - RunPipeline promoted to protected virtual so subclasses can override - BuildAngles/RecordProductiveAngles overrides delegate to angleBuilder - RunPipeline calls virtual BuildAngles/RecordProductiveAngles instead of angleBuilder directly - TODO comment added in group-fill overload for Task 6 Comparer pass-through Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest.Engine/DefaultNestEngine.cs | 20 ++++++++++++++++---- OpenNest.Engine/Strategies/FillContext.cs | 2 ++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/OpenNest.Engine/DefaultNestEngine.cs b/OpenNest.Engine/DefaultNestEngine.cs index 283eac8..cf61be0 100644 --- a/OpenNest.Engine/DefaultNestEngine.cs +++ b/OpenNest.Engine/DefaultNestEngine.cs @@ -26,6 +26,16 @@ namespace OpenNest set => angleBuilder.ForceFullSweep = value; } + public override List BuildAngles(NestItem item, double bestRotation, Box workArea) + { + return angleBuilder.Build(item, bestRotation, workArea); + } + + protected override void RecordProductiveAngles(List angleResults) + { + angleBuilder.RecordProductive(angleResults); + } + // --- Public Fill API --- public override List Fill(NestItem item, Box workArea, @@ -42,6 +52,7 @@ namespace OpenNest PlateNumber = PlateNumber, Token = token, Progress = progress, + Policy = BuildPolicy(), }; RunPipeline(context); @@ -78,6 +89,7 @@ namespace OpenNest PhaseResults.Clear(); var engine = new FillLinear(workArea, Plate.PartSpacing); var angles = RotationAnalysis.FindHullEdgeAngles(groupParts); + // TODO: pass Comparer to FillPattern (Task 6) var best = FillHelpers.FillPattern(engine, groupParts, angles, workArea); PhaseResults.Add(new PhaseResult(NestPhase.Linear, best?.Count ?? 0, 0)); @@ -105,12 +117,12 @@ namespace OpenNest // --- RunPipeline: strategy-based orchestration --- - private void RunPipeline(FillContext context) + protected virtual void RunPipeline(FillContext context) { var bestRotation = RotationAnalysis.FindBestRotation(context.Item); context.SharedState["BestRotation"] = bestRotation; - var angles = angleBuilder.Build(context.Item, bestRotation, context.WorkArea); + var angles = BuildAngles(context.Item, bestRotation, context.WorkArea); context.SharedState["AngleCandidates"] = angles; try @@ -131,7 +143,7 @@ namespace OpenNest // during progress reporting. PhaseResults.Add(phaseResult); - if (IsBetterFill(result, context.CurrentBest, context.WorkArea)) + if (context.Policy.Comparer.IsBetter(result, context.CurrentBest, context.WorkArea)) { context.CurrentBest = result; context.CurrentBestScore = FillScore.Compute(result, context.WorkArea); @@ -151,7 +163,7 @@ namespace OpenNest Debug.WriteLine("[RunPipeline] Cancelled, returning current best"); } - angleBuilder.RecordProductive(context.AngleResults); + RecordProductiveAngles(context.AngleResults); } } diff --git a/OpenNest.Engine/Strategies/FillContext.cs b/OpenNest.Engine/Strategies/FillContext.cs index 26b3159..166894a 100644 --- a/OpenNest.Engine/Strategies/FillContext.cs +++ b/OpenNest.Engine/Strategies/FillContext.cs @@ -14,8 +14,10 @@ namespace OpenNest.Engine.Strategies public int PlateNumber { get; init; } public CancellationToken Token { get; init; } public IProgress Progress { get; init; } + public FillPolicy Policy { get; init; } public List CurrentBest { get; set; } + /// For progress reporting only; comparisons use Policy.Comparer. public FillScore CurrentBestScore { get; set; } public NestPhase WinnerPhase { get; set; } public List PhaseResults { get; } = new();