From ccd230568e4d7db6ba4ba33b6ee161ca0b6bf5d6 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Wed, 18 Mar 2026 00:51:51 -0400 Subject: [PATCH] feat(engine): integrate FillExtents phase into DefaultNestEngine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds FillExtents as a fourth nesting phase in both FindBestFill and Fill(groupParts, Box), running at bestRotation and bestRotation+90°. Updates Description to reflect the new phase. Co-Authored-By: Claude Sonnet 4.6 --- OpenNest.Engine/DefaultNestEngine.cs | 53 +++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/OpenNest.Engine/DefaultNestEngine.cs b/OpenNest.Engine/DefaultNestEngine.cs index df10036..118471d 100644 --- a/OpenNest.Engine/DefaultNestEngine.cs +++ b/OpenNest.Engine/DefaultNestEngine.cs @@ -17,7 +17,7 @@ namespace OpenNest public override string Name => "Default"; - public override string Description => "Multi-phase nesting (Linear, Pairs, RectBestFit)"; + public override string Description => "Multi-phase nesting (Linear, Pairs, RectBestFit, Extents)"; private readonly AngleCandidateBuilder angleBuilder = new(); @@ -146,6 +146,29 @@ namespace OpenNest best = pairResult; ReportProgress(progress, NestPhase.Pairs, PlateNumber, best, workArea, BuildProgressSummary()); } + + token.ThrowIfCancellationRequested(); + + var extentsFiller = new FillExtents(workArea, Plate.PartSpacing); + var extentsAngles2 = new[] { groupParts[0].Rotation, groupParts[0].Rotation + Angle.HalfPI }; + List bestExtents2 = null; + + foreach (var angle in extentsAngles2) + { + token.ThrowIfCancellationRequested(); + var result = extentsFiller.Fill(groupParts[0].BaseDrawing, angle, PlateNumber, token, progress); + if (result != null && result.Count > (bestExtents2?.Count ?? 0)) + bestExtents2 = result; + } + + PhaseResults.Add(new PhaseResult(NestPhase.Extents, bestExtents2?.Count ?? 0, 0)); + Debug.WriteLine($"[Fill(groupParts,Box)] Extents: {bestExtents2?.Count ?? 0} parts"); + + if (IsBetterFill(bestExtents2, best, workArea)) + { + best = bestExtents2; + ReportProgress(progress, NestPhase.Extents, PlateNumber, best, workArea, BuildProgressSummary()); + } } catch (OperationCanceledException) { @@ -263,6 +286,34 @@ namespace OpenNest WinnerPhase = NestPhase.RectBestFit; ReportProgress(progress, NestPhase.RectBestFit, PlateNumber, best, workArea, BuildProgressSummary()); } + + // Extents phase + token.ThrowIfCancellationRequested(); + var extentsSw = Stopwatch.StartNew(); + var extentsFiller = new FillExtents(workArea, Plate.PartSpacing); + List bestExtents = null; + var extentsAngles = new[] { bestRotation, bestRotation + Angle.HalfPI }; + + foreach (var angle in extentsAngles) + { + token.ThrowIfCancellationRequested(); + var extentsResult = extentsFiller.Fill(item.Drawing, angle, PlateNumber, token, progress); + if (bestExtents == null || (extentsResult != null && extentsResult.Count > (bestExtents?.Count ?? 0))) + bestExtents = extentsResult; + } + + extentsSw.Stop(); + var extentsScore = bestExtents != null ? FillScore.Compute(bestExtents, workArea) : default; + Debug.WriteLine($"[FindBestFill] Extents: {extentsScore.Count} parts"); + PhaseResults.Add(new PhaseResult(NestPhase.Extents, bestExtents?.Count ?? 0, extentsSw.ElapsedMilliseconds)); + + var bestScore2 = FillScore.Compute(best, workArea); + if (extentsScore > bestScore2) + { + best = bestExtents; + WinnerPhase = NestPhase.Extents; + ReportProgress(progress, NestPhase.Extents, PlateNumber, best, workArea, BuildProgressSummary()); + } } catch (OperationCanceledException) {