mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-12 11:04:13 -04:00
Fix false self-intersection warnings at rounded contour joints
This commit is contained in:
1 parent
72776886cb
commit
90ae3b6a84
3 files changed
+97
-4
No files matched your search
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Geometry;
|
||||
using OpenNest.Math;
|
||||
|
||||
namespace OpenNest.CNC.CuttingPlanning;
|
||||
|
||||
@@ -66,11 +67,14 @@ public sealed class LeadMaterialSnapshot
|
||||
var b = rings[s][j];
|
||||
var contacts = a.Contacts(b, out var overlap);
|
||||
var adjacent = r == s && (j == i + 1 || (i == 0 && j == rings[r].Length - 1));
|
||||
// Native contacts at rounded arc/line joints can drift from the
|
||||
// authored endpoint. Use the shared geometry tolerance only to
|
||||
// recognize that joint; overlaps and other contacts still refuse.
|
||||
if (overlap || contacts.Any(p => !adjacent
|
||||
|| !(p.DistanceTo(a.End) <= PostVerificationGeometry.Epsilon
|
||||
&& p.DistanceTo(b.Start) <= PostVerificationGeometry.Epsilon)
|
||||
&& !(p.DistanceTo(a.Start) <= PostVerificationGeometry.Epsilon
|
||||
&& p.DistanceTo(b.End) <= PostVerificationGeometry.Epsilon)))
|
||||
|| !(p.DistanceTo(a.End) <= Tolerance.Epsilon
|
||||
&& p.DistanceTo(b.Start) <= Tolerance.Epsilon)
|
||||
&& !(p.DistanceTo(a.Start) <= Tolerance.Epsilon
|
||||
&& p.DistanceTo(b.End) <= Tolerance.Epsilon)))
|
||||
throw new ArgumentException($"Material boundaries overlap, touch or self-intersect ({r}:{i}, {s}:{j}, overlap={overlap}).");
|
||||
}
|
||||
var outer = -1;
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.CNC.CuttingPlanning;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Tests.CuttingPlanning;
|
||||
|
||||
public class LeadMaterialSnapshotTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public void RoundedCornerContact_RoundingAtAdjacentEndpointIsAccepted(bool atSeam)
|
||||
{
|
||||
// Rounded imported corner: its native tangent contact falls 1.52e-8
|
||||
// from the authored arc/line joint. Exercise both consecutive and seam adjacency.
|
||||
var program = new Program();
|
||||
var start = new Vector(120.509177, 14.879559);
|
||||
program.MoveTo(start);
|
||||
program.Codes.Add(new ArcMove(new Vector(120.5, 14.9375),
|
||||
new Vector(120.6875000024, 14.9374999848), RotationType.CW));
|
||||
program.LineTo(120.5, 28.5625);
|
||||
program.Codes.Add(new ArcMove(new Vector(120.6875, 28.75),
|
||||
new Vector(120.6875, 28.5625), RotationType.CW));
|
||||
program.LineTo(127.6875, 28.75);
|
||||
program.Codes.Add(new ArcMove(new Vector(127.875, 28.5625),
|
||||
new Vector(127.6875, 28.5625), RotationType.CW));
|
||||
program.LineTo(127.875, 14.9375);
|
||||
program.Codes.Add(new ArcMove(new Vector(127.6875, 14.75),
|
||||
new Vector(127.6875, 14.9375), RotationType.CW));
|
||||
program.LineTo(120.6875, 14.75);
|
||||
program.Codes.Add(new ArcMove(start,
|
||||
new Vector(120.6874999967, 14.9375000024), RotationType.CW));
|
||||
if (atSeam)
|
||||
{
|
||||
var first = program.Codes[1];
|
||||
program.Codes.RemoveAt(1);
|
||||
program.Codes.Add(first);
|
||||
program.Codes[0] = new RapidMove(120.5, 14.9375);
|
||||
}
|
||||
|
||||
var material = LeadMaterialSnapshot.Capture(program, Vector.Zero);
|
||||
|
||||
Assert.True(material.IsComplete, material.Reason);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public void CrossingOrRetracedBoundary_RemainsRejected(bool retraced)
|
||||
{
|
||||
var program = new Program();
|
||||
program.MoveTo(0, 0);
|
||||
program.LineTo(8, 8);
|
||||
if (retraced)
|
||||
{
|
||||
program.LineTo(4, 4);
|
||||
program.LineTo(0, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
program.LineTo(0, 8);
|
||||
program.LineTo(8, 0);
|
||||
}
|
||||
program.LineTo(0, 0);
|
||||
|
||||
var material = LeadMaterialSnapshot.Capture(program, Vector.Zero);
|
||||
|
||||
Assert.False(material.IsComplete);
|
||||
Assert.Contains("Material boundaries", material.Reason);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SeparateTouchingHoles_RemainRejected()
|
||||
{
|
||||
var part = BestEffortCuttingPlanTests.TouchingContours();
|
||||
|
||||
var material = LeadMaterialSnapshot.Capture(part.BaseDrawing.Program, Vector.Zero);
|
||||
|
||||
Assert.False(material.IsComplete);
|
||||
Assert.Contains("Material boundaries", material.Reason);
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,13 @@ does not visit a globally shared descendant twice; it never changes the fixed pa
|
||||
|
||||
## Cutting dependencies
|
||||
|
||||
Nominal material validation recognizes contacts at shared endpoints of adjacent
|
||||
contour segments using `OpenNest.Math.Tolerance.Epsilon` (0.00001 model units).
|
||||
This avoids false self-intersection warnings from rounding at arc/line joints,
|
||||
including the contour seam. Overlapping segments and contacts away from those
|
||||
joints or between separate contours still fail validation. Contour closure and
|
||||
the underlying native intersection queries retain their existing tolerances.
|
||||
|
||||
Capture builds whole-part prerequisites from owned values, and both the search and
|
||||
the final replay enforce them:
|
||||
|
||||
|
||||
Reference in new issue
Block a user