diff --git a/OpenNest.Core/Geometry/Arc.cs b/OpenNest.Core/Geometry/Arc.cs index faaa91e..dfd805f 100644 --- a/OpenNest.Core/Geometry/Arc.cs +++ b/OpenNest.Core/Geometry/Arc.cs @@ -185,6 +185,19 @@ namespace OpenNest.Geometry return center == circle.Center; } + /// + /// Returns the minimum number of segments needed so that the chord-to-arc + /// deviation (sagitta) does not exceed the given tolerance. + /// + public int SegmentsForTolerance(double tolerance) + { + if (tolerance >= Radius) + return 1; + + var maxAngle = 2.0 * System.Math.Acos(1.0 - tolerance / Radius); + return System.Math.Max(1, (int)System.Math.Ceiling(System.Math.Abs(SweepAngle()) / maxAngle)); + } + /// /// Converts the arc to a group of points. /// diff --git a/OpenNest.Core/Geometry/Circle.cs b/OpenNest.Core/Geometry/Circle.cs index fdada85..5d57e9e 100644 --- a/OpenNest.Core/Geometry/Circle.cs +++ b/OpenNest.Core/Geometry/Circle.cs @@ -122,6 +122,19 @@ namespace OpenNest.Geometry return Center.DistanceTo(pt) <= Radius; } + /// + /// Returns the minimum number of segments needed so that the chord-to-arc + /// deviation (sagitta) does not exceed the given tolerance. + /// + public int SegmentsForTolerance(double tolerance) + { + if (tolerance >= Radius) + return 3; + + var maxAngle = 2.0 * System.Math.Acos(1.0 - tolerance / Radius); + return System.Math.Max(3, (int)System.Math.Ceiling(Angle.TwoPI / maxAngle)); + } + public List ToPoints(int segments = 1000) { var points = new List(); diff --git a/OpenNest.Core/Geometry/Shape.cs b/OpenNest.Core/Geometry/Shape.cs index f910a06..843485d 100644 --- a/OpenNest.Core/Geometry/Shape.cs +++ b/OpenNest.Core/Geometry/Shape.cs @@ -243,6 +243,49 @@ namespace OpenNest.Geometry return polygon; } + /// + /// Converts the shape to a polygon using a chord tolerance to determine + /// the number of segments per arc/circle. + /// + public Polygon ToPolygonWithTolerance(double tolerance) + { + var polygon = new Polygon(); + + foreach (var entity in Entities) + { + switch (entity.Type) + { + case EntityType.Arc: + var arc = (Arc)entity; + polygon.Vertices.AddRange(arc.ToPoints(arc.SegmentsForTolerance(tolerance))); + break; + + case EntityType.Line: + var line = (Line)entity; + polygon.Vertices.AddRange(new[] + { + line.StartPoint, + line.EndPoint + }); + break; + + case EntityType.Circle: + var circle = (Circle)entity; + polygon.Vertices.AddRange(circle.ToPoints(circle.SegmentsForTolerance(tolerance))); + break; + + default: + Debug.Fail("Unhandled geometry type"); + break; + } + } + + polygon.Close(); + polygon.Cleanup(); + + return polygon; + } + /// /// Reverses the rotation direction of the shape. /// diff --git a/OpenNest.Core/Helper.cs b/OpenNest.Core/Helper.cs index a6b31f5..8a1a0b8 100644 --- a/OpenNest.Core/Helper.cs +++ b/OpenNest.Core/Helper.cs @@ -739,7 +739,7 @@ namespace OpenNest return pts.Count > 0; } - private const int PushArcSegments = 36; + private const double PushChordTolerance = 0.08; public static List GetPartLines(Part part) { @@ -749,7 +749,7 @@ namespace OpenNest foreach (var shape in shapes) { - var polygon = shape.ToPolygon(PushArcSegments); + var polygon = shape.ToPolygonWithTolerance(PushChordTolerance); polygon.Offset(part.Location); lines.AddRange(polygon.ToLines()); } @@ -770,7 +770,7 @@ namespace OpenNest if (offsetEntity == null) continue; - var polygon = offsetEntity.ToPolygon(PushArcSegments); + var polygon = offsetEntity.ToPolygonWithTolerance(PushChordTolerance); polygon.Offset(part.Location); lines.AddRange(polygon.ToLines()); }