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;