diff --git a/OpenNest.Engine/CuttingPlanning/PreferredContourEntries.cs b/OpenNest.Engine/CuttingPlanning/PreferredContourEntries.cs index a2137b2..fcb5818 100644 --- a/OpenNest.Engine/CuttingPlanning/PreferredContourEntries.cs +++ b/OpenNest.Engine/CuttingPlanning/PreferredContourEntries.cs @@ -89,11 +89,13 @@ internal static class PreferredContourEntries /// first non-rapid motion (lead-in when present — that is where the next rapid must /// arrive), which reflects native rounding and clamping of a nominal entry. /// - private static Vector Pierce(PreparedContours prepared, ContourChoice choice, CancellationToken token) + internal static Vector Pierce(PreparedContours prepared, ContourChoice choice, CancellationToken token) { var program = prepared.EmitCandidateForValidation(choice); var execution = ExecutionMotionReader.Read(program, Vector.Zero, null, token); - var first = execution.Motions.First(m => !m.Rapid); + // The diagnostic emission includes the part's scribes before the selected contour. + // Those marks are not the downstream cut's pierce. + var first = execution.Motions.First(m => !m.Rapid && m.Layer != LayerType.Scribe); return first.Start ?? first.End; } } diff --git a/OpenNest.Tests/CuttingPlanning/PreferredContourEntriesTests.cs b/OpenNest.Tests/CuttingPlanning/PreferredContourEntriesTests.cs index 119b875..f8476ae 100644 --- a/OpenNest.Tests/CuttingPlanning/PreferredContourEntriesTests.cs +++ b/OpenNest.Tests/CuttingPlanning/PreferredContourEntriesTests.cs @@ -201,6 +201,24 @@ public class PreferredContourEntriesTests Assert.All(proposal.HoleChoices, c => Assert.NotEqual(3, c.ContourOrdinal)); // never the perimeter } + [Fact] + public void ScribesDoNotBecomeTheDownstreamPierceTarget() + { + var clean = RectHolesProgram((3, 3, 5, 5), (7, 3, 9, 5)); + var centres = new[] { new Vector(4, 4), new Vector(8, 4), new Vector(5, 5) }; + var prepared = PreparedContours.Capture(clean, ExplicitContourTests.Parameters()); + var entry = prepared.Entry(2, 2, new Vector(10, 4)); + var baseline = PreferredContourEntries.TryPlan(prepared, entry, new[] { 0, 1 }, + centres, Arrival, _ => new(ContourFeasibilityStatus.Clear, null)); + clean.MoveTo(-20, -20); + clean.Codes.Add(new LinearMove(-19, -19) { Layer = LayerType.Scribe }); + var marked = PreparedContours.Capture(clean, ExplicitContourTests.Parameters()); + var actual = PreferredContourEntries.TryPlan(marked, marked.Entry(2, 2, new Vector(10, 4)), + new[] { 0, 1 }, centres, Arrival, _ => new(ContourFeasibilityStatus.Clear, null)); + Assert.True(actual.IsPreferred, actual.Reason); + Assert.Equal(baseline.HoleChoices.Select(c => c.Point), actual.HoleChoices.Select(c => c.Point)); + } + [Fact] public void ChainingIsDeterministic() {