From 09fc8b889eb76e25a8826926ac4ee5959eec3b5d Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Fri, 13 Mar 2026 07:37:39 -0400 Subject: [PATCH] fix: close polygon offset shape and handle nest template load failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shape.OffsetEntity computed joins between consecutive offset segments but never joined the last segment back to the first, leaving the closing corner with a straight line instead of a proper miter/arc. Track the first entity and apply the same join logic after the loop. Also wrap nest template loading in try-catch so a corrupt template file doesn't crash the app on startup — falls back to default nest. Co-Authored-By: Claude Opus 4.6 --- OpenNest.Core/Geometry/Shape.cs | 75 ++++++++++++++++++++++----------- OpenNest/Forms/MainForm.cs | 34 +++++++++++---- 2 files changed, 76 insertions(+), 33 deletions(-) diff --git a/OpenNest.Core/Geometry/Shape.cs b/OpenNest.Core/Geometry/Shape.cs index d439f7a..d65d393 100644 --- a/OpenNest.Core/Geometry/Shape.cs +++ b/OpenNest.Core/Geometry/Shape.cs @@ -463,6 +463,8 @@ namespace OpenNest.Geometry var offsetShape = new Shape(); var definedShape = new ShapeProfile(this); + Entity firstEntity = null; + Entity firstOffsetEntity = null; Entity lastEntity = null; Entity lastOffsetEntity = null; @@ -473,6 +475,12 @@ namespace OpenNest.Geometry if (offsetEntity == null) continue; + if (firstEntity == null) + { + firstEntity = entity; + firstOffsetEntity = offsetEntity; + } + switch (entity.Type) { case EntityType.Line: @@ -482,31 +490,10 @@ namespace OpenNest.Geometry if (lastOffsetEntity != null && lastOffsetEntity.Type == EntityType.Line) { - var lastLine = lastEntity as Line; - var lastOffsetLine = lastOffsetEntity as Line; - - if (lastLine == null || lastOffsetLine == null) - continue; - - Vector intersection; - - if (Helper.Intersects(offsetLine, lastOffsetLine, out intersection)) - { - offsetLine.StartPoint = intersection; - lastOffsetLine.EndPoint = intersection; - } - else - { - var arc = new Arc( - line.StartPoint, - distance, - line.StartPoint.AngleTo(lastOffsetLine.EndPoint), - line.StartPoint.AngleTo(offsetLine.StartPoint), - side == OffsetSide.Left - ); - - offsetShape.Entities.Add(arc); - } + JoinOffsetLines( + (Line)lastEntity, (Line)lastOffsetEntity, + line, offsetLine, + distance, side, offsetShape); } offsetShape.Entities.Add(offsetLine); @@ -522,12 +509,50 @@ namespace OpenNest.Geometry lastEntity = entity; } + // Close the shape: join last offset entity back to first + if (lastOffsetEntity != null && firstOffsetEntity != null + && lastOffsetEntity != firstOffsetEntity + && lastOffsetEntity.Type == EntityType.Line + && firstOffsetEntity.Type == EntityType.Line) + { + JoinOffsetLines( + (Line)lastEntity, (Line)lastOffsetEntity, + (Line)firstEntity, (Line)firstOffsetEntity, + distance, side, offsetShape); + } + foreach (var cutout in definedShape.Cutouts) offsetShape.Entities.AddRange(((Shape)cutout.OffsetEntity(distance, side)).Entities); return offsetShape; } + private static void JoinOffsetLines( + Line lastLine, Line lastOffsetLine, + Line line, Line offsetLine, + double distance, OffsetSide side, Shape offsetShape) + { + Vector intersection; + + if (Helper.Intersects(offsetLine, lastOffsetLine, out intersection)) + { + offsetLine.StartPoint = intersection; + lastOffsetLine.EndPoint = intersection; + } + else + { + var arc = new Arc( + line.StartPoint, + distance, + line.StartPoint.AngleTo(lastOffsetLine.EndPoint), + line.StartPoint.AngleTo(offsetLine.StartPoint), + side == OffsetSide.Left + ); + + offsetShape.Entities.Add(arc); + } + } + public override Entity OffsetEntity(double distance, Vector pt) { throw new NotImplementedException(); diff --git a/OpenNest/Forms/MainForm.cs b/OpenNest/Forms/MainForm.cs index 78b48fb..3debd8a 100644 --- a/OpenNest/Forms/MainForm.cs +++ b/OpenNest/Forms/MainForm.cs @@ -47,6 +47,17 @@ namespace OpenNest.Forms // BestFitCache.CreateEvaluator = (drawing, spacing) => GpuEvaluatorFactory.Create(drawing, spacing); } + private Nest CreateDefaultNest() + { + var nest = new Nest(); + nest.Units = Properties.Settings.Default.DefaultUnit; + nest.PlateDefaults.EdgeSpacing = new Spacing(1, 1, 1, 1); + nest.PlateDefaults.PartSpacing = 1; + nest.PlateDefaults.Size = new OpenNest.Geometry.Size(100, 100); + nest.PlateDefaults.Quadrant = 1; + return nest; + } + private string GetNestName(DateTime date, int id) { var month = date.Month.ToString().PadLeft(2, '0'); @@ -326,17 +337,24 @@ namespace OpenNest.Forms if (File.Exists(Properties.Settings.Default.NestTemplatePath)) { - var reader = new NestReader(Properties.Settings.Default.NestTemplatePath); - nest = reader.Read(); + try + { + var reader = new NestReader(Properties.Settings.Default.NestTemplatePath); + nest = reader.Read(); + } + catch (Exception ex) + { + MessageBox.Show( + $"Failed to load nest template:\n{ex.Message}\n\nA default nest will be created instead.", + "Template Error", + MessageBoxButtons.OK, + MessageBoxIcon.Warning); + nest = CreateDefaultNest(); + } } else { - nest = new Nest(); - nest.Units = Properties.Settings.Default.DefaultUnit; - nest.PlateDefaults.EdgeSpacing = new Spacing(0, 0, 0, 0); - nest.PlateDefaults.PartSpacing = 0; - nest.PlateDefaults.Size = new OpenNest.Geometry.Size(100, 100); - nest.PlateDefaults.Quadrant = 1; + nest = CreateDefaultNest(); } nest.DateCreated = DateTime.Now;