fix(geometry): treat near-joint contacts as the joint when classifying slides

Drawing data can fix a fillet's center slightly off the tangent normal, so
the arc's end sits ~1e-7 past its neighbouring line's end. The slide contact
classifier only joined entities within 1e-7, so a contact at that tangent
point read as two boundaries, was classified ambiguous and blocked the push.
In N26-243 this stopped a part pushed left along another once the parts
aligned. Contacts within 1e-6 of a joint now take the joint's tangents;
the 1e-7 incidence tolerance is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ajandClaude Opus 5.5 committed 2026-10-24 21:52:48 -04:00
1 parent ba55d5bff0
commit fa70713bfc
2 files changed
+29 -2

No files matched your search

+7 -2
View File
@@ -36,6 +36,11 @@ namespace OpenNest.Geometry
// tangential classification can admit, so keep it far below spacing tolerances.
internal const double IncidenceTolerance = 1e-7;
// Drawing data fixes arc centers to finite precision, so a tangent joint's arc end
// can sit a little past its neighbour's end. A contact this close to a joint takes
// the joint's tangents; otherwise the neighbour reads as a second boundary there.
internal const double JointTolerance = 1e-6;
private readonly List<Entity> entities;
private readonly int[] loopOf;
private readonly int[] previous;
@@ -254,11 +259,11 @@ namespace OpenNest.Geometry
private bool StartsNear(int index, Vector point) =>
TryEndpoints(entities[index], out var start, out _)
&& start.DistanceTo(point) <= IncidenceTolerance;
&& start.DistanceTo(point) <= JointTolerance;
private bool EndsNear(int index, Vector point) =>
TryEndpoints(entities[index], out _, out var end)
&& end.DistanceTo(point) <= IncidenceTolerance;
&& end.DistanceTo(point) <= JointTolerance;
private bool InRun(int index, int first, int last)
{
@@ -147,6 +147,28 @@ public class SlideContactTests
Assert.Equal(double.MaxValue, EntityDistance(cpu, moving, stationary, new Vector(1, 0)));
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void FilletWithImpreciseCenter_DoesNotBlockSlidingAlongItsTangentLine(bool cpu)
{
// N26-243: the G-code fixes the fillet center 1.04e-7 off the tangent normal, so
// the arc's top lies just past the flat edge's end rather than on the joint.
var offset = 1.043e-7;
var fillet = new Arc(new Vector(2 - offset, 0.5), System.Math.Sqrt(1 + offset * offset),
0, System.Math.Atan2(1, offset), false);
var stationary = new List<Entity>
{
new Line(0, 0, 3, 0),
new Line(new Vector(3, 0), fillet.StartPoint()),
fillet,
new Line(fillet.EndPoint(), new Vector(0, 1.5)),
new Line(0, 1.5, 0, 0),
};
var moving = Rect(1, 1.5, 2, 1).Cast<Entity>().ToList();
Assert.Equal(double.MaxValue, EntityDistance(cpu, moving, stationary, new Vector(-1, 0)));
}
[Theory]
[InlineData(false)]
[InlineData(true)]