From eedff85904e1555a67c73b57c7da17291322f28f Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Tue, 27 Oct 2026 12:57:45 -0400 Subject: [PATCH] fix(ui): place bend lines in the program frame Drawing.Bends stay in the DXF's source frame while CadImporter moves the program so its first pierce sits at the origin (Source.Offset). The plate renderer added part.Location to raw bend points, so every bend and etch mark was shifted by its drawing's pierce offset, visible on the drawing library set preview and on plates alike. Remove Source.Offset before rotating and placing. Co-Authored-By: Claude Opus 5.5 --- .../Controls/BendLinePlacementTests.cs | 40 ++++++++++++++++++ OpenNest/Controls/PlateRenderer.cs | 41 ++++++++----------- 2 files changed, 56 insertions(+), 25 deletions(-) create mode 100644 OpenNest.WinForms.Tests/Controls/BendLinePlacementTests.cs diff --git a/OpenNest.WinForms.Tests/Controls/BendLinePlacementTests.cs b/OpenNest.WinForms.Tests/Controls/BendLinePlacementTests.cs new file mode 100644 index 00000000..a1f088cc --- /dev/null +++ b/OpenNest.WinForms.Tests/Controls/BendLinePlacementTests.cs @@ -0,0 +1,40 @@ +using OpenNest.Bending; +using OpenNest.Controls; +using OpenNest.Geometry; +using OpenNest.Math; + +namespace OpenNest.WinForms.Tests.Controls; + +/// +/// Bends are kept in the drawing's source frame while its program starts at the origin, so a +/// placed part draws them with Source.Offset removed. +/// +public class BendLinePlacementTests +{ + [Fact] + public void BendToWorld_RemovesSourceOffsetBeforePlacing() + { + var drawing = new Drawing("bent", new CNC.Program()); + drawing.Source.Offset = new Vector(100, 50); + // In the source frame the bend runs across the part at local x = 5. + var bend = new Bend { StartPoint = new Vector(105, 50), EndPoint = new Vector(105, 60) }; + var part = new Part(drawing, new Vector(10, 20)); + + Assert.Equal(new Vector(15, 20), PlateRenderer.BendToWorld(part, bend.StartPoint)); + Assert.Equal(new Vector(15, 30), PlateRenderer.BendToWorld(part, bend.EndPoint)); + } + + [Fact] + public void BendToWorld_RotatesAboutTheProgramOrigin() + { + var drawing = new Drawing("bent", new CNC.Program()); + drawing.Source.Offset = new Vector(100, 50); + var part = new Part(drawing, new Vector(10, 20)); + part.Rotate(Angle.HalfPI, part.Location); + + var world = PlateRenderer.BendToWorld(part, new Vector(105, 50)); + + Assert.Equal(10, world.X, 9); + Assert.Equal(25, world.Y, 9); + } +} diff --git a/OpenNest/Controls/PlateRenderer.cs b/OpenNest/Controls/PlateRenderer.cs index bed86480..e80781c7 100644 --- a/OpenNest/Controls/PlateRenderer.cs +++ b/OpenNest/Controls/PlateRenderer.cs @@ -465,25 +465,25 @@ namespace OpenNest.Controls foreach (var bend in part.BaseDrawing.Bends) { - var start = bend.StartPoint; - var end = bend.EndPoint; - - if (part.Rotation != 0) - { - start = start.Rotate(part.Rotation); - end = end.Rotate(part.Rotation); - } - - start = start + part.Location; - end = end + part.Location; - - var pt1 = view.PointWorldToGraph(start); - var pt2 = view.PointWorldToGraph(end); + var pt1 = view.PointWorldToGraph(BendToWorld(part, bend.StartPoint)); + var pt2 = view.PointWorldToGraph(BendToWorld(part, bend.EndPoint)); g.DrawLine(bendPen, pt1, pt2); } } + /// + /// Maps a bend point to world space. Bends stay in the drawing's source frame while the + /// program is moved by -Source.Offset, so the offset comes off before placing. + /// + internal static Vector BendToWorld(Part part, Vector point) + { + var local = point - part.BaseDrawing.Source.Offset; + if (part.Rotation != 0) + local = local.Rotate(part.Rotation); + return local + part.Location; + } + private void DrawEtchMarks(Graphics g, Part part, bool muted) { if ( @@ -501,17 +501,8 @@ namespace OpenNest.Controls if (bend.Direction != BendDirection.Up) continue; - var start = bend.StartPoint; - var end = bend.EndPoint; - - if (part.Rotation != 0) - { - start = start.Rotate(part.Rotation); - end = end.Rotate(part.Rotation); - } - - start = start + part.Location; - end = end + part.Location; + var start = BendToWorld(part, bend.StartPoint); + var end = BendToWorld(part, bend.EndPoint); var length = bend.Length; var angle = bend.StartPoint.AngleTo(bend.EndPoint) + part.Rotation;