From a399c89f58247b765e9a5f006e65011cc58f0348 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Mon, 30 Mar 2026 19:35:29 -0400 Subject: [PATCH] fix: resolve rendering issues when applying lead-ins to parts Three issues caused incorrect rendering after lead-in application: - Rapid move entities from ToGeometry() were included in ShapeProfile contour detection, turning traversal paths into cutting moves - Program created with Mode.Incremental default made the absolute-to- incremental conversion a no-op, leaving coordinates unconverted - AddProgramSplit didn't call StartFigure() at rapid moves, causing GraphicsPath to draw implicit connecting lines between contours - Part.Rotation returned 0 from the new program instead of the actual rotation, displacing the sequence label on rotated parts Co-Authored-By: Claude Opus 4.6 (1M context) --- .../CuttingStrategy/ContourCuttingStrategy.cs | 3 +- OpenNest.Core/Part.cs | 2 +- OpenNest/GraphicsHelper.cs | 70 +++++++++++++++---- 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/OpenNest.Core/CNC/CuttingStrategy/ContourCuttingStrategy.cs b/OpenNest.Core/CNC/CuttingStrategy/ContourCuttingStrategy.cs index 5d70ee5..a67cac0 100644 --- a/OpenNest.Core/CNC/CuttingStrategy/ContourCuttingStrategy.cs +++ b/OpenNest.Core/CNC/CuttingStrategy/ContourCuttingStrategy.cs @@ -11,6 +11,7 @@ namespace OpenNest.CNC.CuttingStrategy { var exitPoint = approachPoint; var entities = partProgram.ToGeometry(); + entities.RemoveAll(e => e.Layer == SpecialLayers.Rapid); var profile = new ShapeProfile(entities); // Find closest point on perimeter from exit point @@ -22,7 +23,7 @@ namespace OpenNest.CNC.CuttingStrategy orderedCutouts.Reverse(); // Build output program: cutouts first (farthest to nearest), perimeter last - var result = new Program(); + var result = new Program(Mode.Absolute); var currentPoint = exitPoint; foreach (var cutout in orderedCutouts) diff --git a/OpenNest.Core/Part.cs b/OpenNest.Core/Part.cs index 075115e..dd18df4 100644 --- a/OpenNest.Core/Part.cs +++ b/OpenNest.Core/Part.cs @@ -94,7 +94,7 @@ namespace OpenNest /// public double Rotation { - get { return Program.Rotation; } + get { return HasManualLeadIns ? preLeadInRotation : Program.Rotation; } } /// diff --git a/OpenNest/GraphicsHelper.cs b/OpenNest/GraphicsHelper.cs index de20e7e..0d499d9 100644 --- a/OpenNest/GraphicsHelper.cs +++ b/OpenNest/GraphicsHelper.cs @@ -135,6 +135,8 @@ namespace OpenNest break; case CodeType.RapidMove: + cutPath.StartFigure(); + leadPath.StartFigure(); AddLine(cutPath, (RapidMove)code, mode, ref curpos); break; @@ -203,12 +205,6 @@ namespace OpenNest size, size, (float)startAngle, (float)sweepAngle); - - if (arc.Layer == LayerType.Leadin || arc.Layer == LayerType.Leadout) - { - path.AddArc(pt.X, pt.Y, size, size, (float)(-startAngle + sweepAngle), (float)-sweepAngle); - path.CloseFigure(); - } } curpos = endpt; @@ -226,9 +222,6 @@ namespace OpenNest path.AddLine(pt1, pt2); - if (line.Layer == LayerType.Leadin || line.Layer == LayerType.Leadout) - path.CloseFigure(); - curpos = pt; } @@ -239,14 +232,23 @@ namespace OpenNest if (mode == Mode.Incremental) pt += curpos; - path.CloseFigure(); - curpos = pt; } private static void AddProgram(GraphicsPath path, Program pgm, Mode mode, ref Vector curpos) { mode = pgm.Mode; + GraphicsPath currentFigure = null; + + void Flush() + { + if (currentFigure != null) + { + path.AddPath(currentFigure, false); + currentFigure.Dispose(); + currentFigure = null; + } + } for (int i = 0; i < pgm.Length; ++i) { @@ -255,25 +257,54 @@ namespace OpenNest switch (code.Type) { case CodeType.ArcMove: - AddArc(path, (ArcMove)code, mode, ref curpos); + { + var arc = (ArcMove)code; + if (arc.Layer != LayerType.Leadin && arc.Layer != LayerType.Leadout) + { + if (currentFigure == null) currentFigure = new GraphicsPath(); + AddArc(currentFigure, arc, mode, ref curpos); + } + else + { + Flush(); + var endpt = arc.EndPoint; + if (mode == Mode.Incremental) endpt += curpos; + curpos = endpt; + } + } break; case CodeType.LinearMove: - AddLine(path, (LinearMove)code, mode, ref curpos); + { + var line = (LinearMove)code; + if (line.Layer != LayerType.Leadin && line.Layer != LayerType.Leadout) + { + if (currentFigure == null) currentFigure = new GraphicsPath(); + AddLine(currentFigure, line, mode, ref curpos); + } + else + { + Flush(); + var endpt = line.EndPoint; + if (mode == Mode.Incremental) endpt += curpos; + curpos = endpt; + } + } break; case CodeType.RapidMove: + Flush(); AddLine(path, (RapidMove)code, mode, ref curpos); break; case CodeType.SubProgramCall: { + Flush(); var tmpmode = mode; var subpgm = (SubProgramCall)code; if (subpgm.Program != null) { - path.StartFigure(); AddProgram(path, subpgm.Program, mode, ref curpos); } @@ -282,6 +313,8 @@ namespace OpenNest } } } + + Flush(); } private static void AddArc(GraphicsPath path, Arc arc) @@ -331,6 +364,15 @@ namespace OpenNest { foreach (var entity in shape.Entities) { + if (entity.Layer != null) + { + if (string.Equals(entity.Layer.Name, SpecialLayers.Leadin.Name, System.StringComparison.OrdinalIgnoreCase) || + string.Equals(entity.Layer.Name, SpecialLayers.Leadout.Name, System.StringComparison.OrdinalIgnoreCase)) + { + continue; + } + } + switch (entity.Type) { case EntityType.Arc: