diff --git a/OpenNest.Engine/Jobs/Placement/Fillers/DefaultPlateFiller.cs b/OpenNest.Engine/Jobs/Placement/Fillers/DefaultPlateFiller.cs index 24a2ef5..43f81bc 100644 --- a/OpenNest.Engine/Jobs/Placement/Fillers/DefaultPlateFiller.cs +++ b/OpenNest.Engine/Jobs/Placement/Fillers/DefaultPlateFiller.cs @@ -112,7 +112,7 @@ internal class DefaultPlateFiller : PlateFillerBase ); } - var best = RunFillPipeline(canonicalItem, effectiveWorkArea, progress, token); + var best = RunFillPipeline(canonicalItem, originalDrawing, effectiveWorkArea, progress, token); if ( canonicalItem.Quantity > 0 @@ -125,7 +125,7 @@ internal class DefaultPlateFiller : PlateFillerBase ); PhaseResults.Clear(); AngleResults.Clear(); - best = RunFillPipeline(canonicalItem, workArea, progress, token); + best = RunFillPipeline(canonicalItem, originalDrawing, workArea, progress, token); } if (canonicalItem.Quantity > 0 && best.Count > canonicalItem.Quantity) @@ -333,6 +333,7 @@ internal class DefaultPlateFiller : PlateFillerBase private List RunFillPipeline( NestItem item, + Drawing originalDrawing, Box workArea, IProgress progress, CancellationToken token @@ -341,6 +342,7 @@ internal class DefaultPlateFiller : PlateFillerBase var context = new FillContext { Item = item, + OriginalDrawing = originalDrawing, WorkArea = workArea, Plate = Plate, PlateNumber = PlateNumber, @@ -474,7 +476,7 @@ internal class DefaultPlateFiller : PlateFillerBase { Phase = context.WinnerPhase, PlateNumber = PlateNumber, - Parts = context.CurrentBest, + Parts = context.ToOriginalFrame(context.CurrentBest), WorkArea = context.WorkArea, Description = BuildProgressSummary(), IsOverallBest = true, diff --git a/OpenNest.Engine/Strategies/FillContext.cs b/OpenNest.Engine/Strategies/FillContext.cs index 3e4c0b2..c7df18f 100644 --- a/OpenNest.Engine/Strategies/FillContext.cs +++ b/OpenNest.Engine/Strategies/FillContext.cs @@ -20,6 +20,16 @@ namespace OpenNest.Engine.Strategies public int MaxQuantity { get; init; } public PartType PartType { get; set; } + /// + /// The pre-canonicalization drawing that 's canonical copy was built + /// from. When set, rebinds reported parts to this drawing + /// before they reach , so intermediate previews (e.g. the Nesting + /// Progress dialog, PlateView's active-parts overlay) show the drawing's real/visible + /// orientation instead of the transient canonical (MBR-axis-aligned) one. Null when the + /// caller isn't operating in canonical frame (e.g. tests driving a strategy directly). + /// + public Drawing OriginalDrawing { get; init; } + public List CurrentBest { get; set; } /// For progress reporting only; comparisons use Policy.Comparer. @@ -57,12 +67,32 @@ namespace OpenNest.Engine.Strategies { Phase = ActivePhase, PlateNumber = PlateNumber, - Parts = isNewBest ? parts : CurrentBest, + Parts = ToOriginalFrame(isNewBest ? parts : CurrentBest), WorkArea = WorkArea, Description = description, IsOverallBest = isNewBest, } ); } + + /// + /// Rebinds to for outward-facing + /// progress reports. Uses a shallow list copy (not per-part ) so + /// 's in-place slot replacement can't corrupt + /// the caller's list (e.g. ) — Part.Clone() re-derives its + /// target rotation from BaseDrawing.Program.Rotation + Rotation, which double-counts + /// the canonical drawing's baked source angle whenever it's non-zero. No-op when + /// isn't set. Internal so callers that build their own + /// outside (e.g. the fallback + /// report in DefaultPlateFiller.RunPipelineCore for strategies that don't self-report) + /// can apply the same rebind before reaching the UI. + /// + internal List ToOriginalFrame(List parts) + { + if (parts == null || parts.Count == 0 || OriginalDrawing == null) + return parts; + + return CanonicalFrame.RebindToOriginal(new List(parts), OriginalDrawing); + } } }