fix(core): use chain tolerance for entity gap check to prevent spurious rapids

Ellipse-to-arc conversion creates tiny floating-point gaps (~0.00002")
between consecutive arc segments. ShapeBuilder chains these with
ChainTolerance (0.0001"), but ConvertGeometry checked gaps with Epsilon
(0.00001"). Gaps between these thresholds generated spurious rapid moves
that broke GraphicsPath figures, causing diagonal fill artifacts from
GDI+'s implicit figure closing.

Root cause fix: align ConvertGeometry's gap check with ShapeBuilder's
ChainTolerance so precision gaps are absorbed instead of generating rapids.

Defense-in-depth: GraphicsHelper no longer breaks figures at near-zero
rapids, protecting against any programs with residual tiny rapids.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-23 21:32:08 -04:00
parent e41f335c63
commit 0b322817d7
2 changed files with 28 additions and 8 deletions
+3 -3
View File
@@ -82,7 +82,7 @@ namespace OpenNest.Converters
var startpt = arc.StartPoint(); var startpt = arc.StartPoint();
var endpt = arc.EndPoint(); var endpt = arc.EndPoint();
if (startpt != lastpt) if (startpt.DistanceTo(lastpt) > Tolerance.ChainTolerance)
pgm.MoveTo(startpt); pgm.MoveTo(startpt);
lastpt = endpt; lastpt = endpt;
@@ -104,7 +104,7 @@ namespace OpenNest.Converters
{ {
var startpt = new Vector(circle.Center.X + circle.Radius, circle.Center.Y); var startpt = new Vector(circle.Center.X + circle.Radius, circle.Center.Y);
if (startpt != lastpt) if (startpt.DistanceTo(lastpt) > Tolerance.ChainTolerance)
pgm.MoveTo(startpt); pgm.MoveTo(startpt);
pgm.ArcTo(startpt, circle.Center, circle.Rotation); pgm.ArcTo(startpt, circle.Center, circle.Rotation);
@@ -115,7 +115,7 @@ namespace OpenNest.Converters
private static Vector AddLine(Program pgm, Vector lastpt, Line line) private static Vector AddLine(Program pgm, Vector lastpt, Line line)
{ {
if (line.StartPoint != lastpt) if (line.StartPoint.DistanceTo(lastpt) > Tolerance.ChainTolerance)
pgm.MoveTo(line.StartPoint); pgm.MoveTo(line.StartPoint);
var move = new LinearMove(line.EndPoint); var move = new LinearMove(line.EndPoint);
+22 -2
View File
@@ -138,9 +138,20 @@ namespace OpenNest
break; break;
case CodeType.RapidMove: case CodeType.RapidMove:
{
var rapid = (RapidMove)code;
var endpt = rapid.EndPoint;
if (mode == Mode.Incremental)
endpt += curpos;
var dx = endpt.X - curpos.X;
var dy = endpt.Y - curpos.Y;
if (dx * dx + dy * dy > 0.001 * 0.001)
{
cutPath.StartFigure(); cutPath.StartFigure();
leadPath.StartFigure(); leadPath.StartFigure();
AddLine(cutPath, (RapidMove)code, mode, ref curpos); }
curpos = endpt;
}
break; break;
case CodeType.SubProgramCall: case CodeType.SubProgramCall:
@@ -300,8 +311,17 @@ namespace OpenNest
break; break;
case CodeType.RapidMove: case CodeType.RapidMove:
{
var rapid = (RapidMove)code;
var endpt = rapid.EndPoint;
if (mode == Mode.Incremental)
endpt += curpos;
var dx = endpt.X - curpos.X;
var dy = endpt.Y - curpos.Y;
if (dx * dx + dy * dy > 0.001 * 0.001)
Flush(); Flush();
AddLine(path, (RapidMove)code, mode, ref curpos); curpos = endpt;
}
break; break;
case CodeType.SubProgramCall: case CodeType.SubProgramCall: