diff --git a/OpenNest.Core/Geometry/Polygon.cs b/OpenNest.Core/Geometry/Polygon.cs
index 662ade4..4c5d3f2 100644
--- a/OpenNest.Core/Geometry/Polygon.cs
+++ b/OpenNest.Core/Geometry/Polygon.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using OpenNest.Math;
namespace OpenNest.Geometry
{
@@ -473,6 +474,118 @@ namespace OpenNest.Geometry
get { return EntityType.Polygon; }
}
+ ///
+ /// Removes self-intersecting loops from the polygon by finding non-adjacent
+ /// edge crossings and keeping the larger contour at each crossing.
+ ///
+ public void RemoveSelfIntersections()
+ {
+ if (!IsClosed() || Vertices.Count < 5)
+ return;
+
+ bool found = true;
+
+ while (found)
+ {
+ found = false;
+ int n = Vertices.Count - 1; // exclude closing vertex
+
+ for (int i = 0; i < n && !found; i++)
+ {
+ var a1 = Vertices[i];
+ var a2 = Vertices[i + 1];
+
+ for (int j = i + 2; j < n && !found; j++)
+ {
+ // Skip edges that share a vertex (first and last edge)
+ if (i == 0 && j == n - 1)
+ continue;
+
+ var b1 = Vertices[j];
+ var b2 = Vertices[j + 1];
+
+ Vector pt;
+
+ if (SegmentsIntersect(a1, a2, b1, b2, out pt))
+ {
+ // Two loops formed by the crossing:
+ // Loop A: vertices[0..i], pt, vertices[j+1..n-1], close
+ // Loop B: pt, vertices[i+1..j], close
+ var loopA = new List();
+
+ for (int k = 0; k <= i; k++)
+ loopA.Add(Vertices[k]);
+
+ loopA.Add(pt);
+
+ for (int k = j + 1; k < n; k++)
+ loopA.Add(Vertices[k]);
+
+ loopA.Add(loopA[0]);
+
+ var loopB = new List();
+ loopB.Add(pt);
+
+ for (int k = i + 1; k <= j; k++)
+ loopB.Add(Vertices[k]);
+
+ loopB.Add(pt);
+
+ var areaA = System.Math.Abs(CalculateArea(loopA));
+ var areaB = System.Math.Abs(CalculateArea(loopB));
+
+ Vertices = areaA >= areaB ? loopA : loopB;
+ found = true;
+ }
+ }
+ }
+ }
+ }
+
+ private static bool SegmentsIntersect(Vector a1, Vector a2, Vector b1, Vector b2, out Vector pt)
+ {
+ var da = a2 - a1;
+ var db = b2 - b1;
+ var cross = da.X * db.Y - da.Y * db.X;
+
+ if (cross.IsEqualTo(0.0))
+ {
+ pt = Vector.Zero;
+ return false;
+ }
+
+ var dc = b1 - a1;
+ var t = (dc.X * db.Y - dc.Y * db.X) / cross;
+ var u = (dc.X * da.Y - dc.Y * da.X) / cross;
+
+ if (t > Tolerance.Epsilon && t < 1.0 - Tolerance.Epsilon &&
+ u > Tolerance.Epsilon && u < 1.0 - Tolerance.Epsilon)
+ {
+ pt = new Vector(a1.X + t * da.X, a1.Y + t * da.Y);
+ return true;
+ }
+
+ pt = Vector.Zero;
+ return false;
+ }
+
+ private static double CalculateArea(List vertices)
+ {
+ double xsum = 0;
+ double ysum = 0;
+
+ for (int i = 0; i < vertices.Count - 1; i++)
+ {
+ var current = vertices[i];
+ var next = vertices[i + 1];
+
+ xsum += current.X * next.Y;
+ ysum += current.Y * next.X;
+ }
+
+ return (xsum - ysum) * 0.5;
+ }
+
internal void Cleanup()
{
for (int i = Vertices.Count - 1; i > 0; i--)
diff --git a/OpenNest.Core/Helper.cs b/OpenNest.Core/Helper.cs
index c694c29..2b18cb8 100644
--- a/OpenNest.Core/Helper.cs
+++ b/OpenNest.Core/Helper.cs
@@ -789,6 +789,7 @@ namespace OpenNest
continue;
var polygon = offsetEntity.ToPolygonWithTolerance(PushChordTolerance);
+ polygon.RemoveSelfIntersections();
polygon.Offset(part.Location);
lines.AddRange(polygon.ToLines());
}
@@ -810,6 +811,7 @@ namespace OpenNest
continue;
var polygon = offsetEntity.ToPolygonWithTolerance(PushChordTolerance);
+ polygon.RemoveSelfIntersections();
polygon.Offset(part.Location);
lines.AddRange(GetDirectionalLines(polygon, facingDirection));
}
diff --git a/OpenNest/Controls/PlateView.cs b/OpenNest/Controls/PlateView.cs
index d5938a4..edaec13 100644
--- a/OpenNest/Controls/PlateView.cs
+++ b/OpenNest/Controls/PlateView.cs
@@ -459,9 +459,20 @@ namespace OpenNest.Controls
if (offsetEntity == null)
continue;
- offsetEntity.Offset(part.Location);
+ var polygon = offsetEntity.ToPolygonWithTolerance(0.01);
+ polygon.RemoveSelfIntersections();
+ polygon.Offset(part.Location);
- var path = GraphicsHelper.GetGraphicsPath(offsetEntity);
+ if (polygon.Vertices.Count < 2)
+ continue;
+
+ var pts = new PointF[polygon.Vertices.Count];
+
+ for (int j = 0; j < pts.Length; j++)
+ pts[j] = new PointF((float)polygon.Vertices[j].X, (float)polygon.Vertices[j].Y);
+
+ var path = new GraphicsPath();
+ path.AddLines(pts);
path.Transform(Matrix);
g.DrawPath(offsetPen, path);
path.Dispose();