diff --git a/OpenNest.Core/CutOff.cs b/OpenNest.Core/CutOff.cs index 9f9cfea..99a7996 100644 --- a/OpenNest.Core/CutOff.cs +++ b/OpenNest.Core/CutOff.cs @@ -129,9 +129,11 @@ namespace OpenNest private List<(double Start, double End)> IntersectPerimeter( Entity perimeter, double cutPosition, double lineStart, double lineEnd, double clearance) { + var target = OffsetOutward(perimeter, clearance) ?? perimeter; + var usedOffset = target != perimeter; var cutLine = new Line(MakePoint(cutPosition, lineStart), MakePoint(cutPosition, lineEnd)); - if (!perimeter.Intersects(cutLine, out var pts) || pts.Count < 2) + if (!target.Intersects(cutLine, out var pts) || pts.Count < 2) return null; var coords = pts @@ -142,13 +144,31 @@ namespace OpenNest if (coords.Count % 2 != 0) return null; + var padding = usedOffset ? 0 : clearance; var result = new List<(double Start, double End)>(); for (var i = 0; i < coords.Count; i += 2) - result.Add((coords[i] - clearance, coords[i + 1] + clearance)); + result.Add((coords[i] - padding, coords[i + 1] + padding)); return result; } + private static Entity OffsetOutward(Entity perimeter, double clearance) + { + if (clearance <= 0) + return null; + + try + { + var offset = perimeter.OffsetEntity(clearance, OffsetSide.Left); + offset?.UpdateBounds(); + return offset; + } + catch + { + return null; + } + } + private Vector MakePoint(double cutCoord, double lineCoord) => Axis == CutOffAxis.Vertical ? new Vector(cutCoord, lineCoord) diff --git a/OpenNest.Core/Geometry/Polygon.cs b/OpenNest.Core/Geometry/Polygon.cs index 4ff2a37..449831e 100644 --- a/OpenNest.Core/Geometry/Polygon.cs +++ b/OpenNest.Core/Geometry/Polygon.cs @@ -317,12 +317,68 @@ namespace OpenNest.Geometry public override Entity OffsetEntity(double distance, OffsetSide side) { - throw new NotImplementedException(); + if (Vertices.Count < 3) + return null; + + var isClosed = IsClosed(); + var count = isClosed ? Vertices.Count - 1 : Vertices.Count; + if (count < 3) + return null; + + var ccw = CalculateArea() > 0; + var outward = ccw ? OffsetSide.Left : OffsetSide.Right; + var sign = side == outward ? 1.0 : -1.0; + var d = distance * sign; + + var normals = new Vector[count]; + for (var i = 0; i < count; i++) + { + var next = (i + 1) % count; + var dx = Vertices[next].X - Vertices[i].X; + var dy = Vertices[next].Y - Vertices[i].Y; + var len = System.Math.Sqrt(dx * dx + dy * dy); + if (len < Tolerance.Epsilon) + return null; + normals[i] = new Vector(-dy / len * d, dx / len * d); + } + + var result = new Polygon(); + for (var i = 0; i < count; i++) + { + var prev = (i - 1 + count) % count; + + var a1 = new Vector(Vertices[prev].X + normals[prev].X, Vertices[prev].Y + normals[prev].Y); + var a2 = new Vector(Vertices[i].X + normals[prev].X, Vertices[i].Y + normals[prev].Y); + var b1 = new Vector(Vertices[i].X + normals[i].X, Vertices[i].Y + normals[i].Y); + var b2 = new Vector(Vertices[(i + 1) % count].X + normals[i].X, Vertices[(i + 1) % count].Y + normals[i].Y); + + var edgeA = new Line(a1, a2); + var edgeB = new Line(b1, b2); + + if (edgeA.Intersects(edgeB, out var pt) && pt.IsValid()) + result.Vertices.Add(pt); + else + result.Vertices.Add(new Vector(Vertices[i].X + normals[i].X, Vertices[i].Y + normals[i].Y)); + } + + result.Close(); + result.RemoveSelfIntersections(); + result.UpdateBounds(); + return result; } public override Entity OffsetEntity(double distance, Vector pt) { - throw new NotImplementedException(); + var left = OffsetEntity(distance, OffsetSide.Left); + var right = OffsetEntity(distance, OffsetSide.Right); + + if (left == null) return right; + if (right == null) return left; + + var distLeft = left.ClosestPointTo(pt).DistanceTo(pt); + var distRight = right.ClosestPointTo(pt).DistanceTo(pt); + + return distLeft > distRight ? left : right; } ///