mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-29 01:21:09 -04:00
fix(planning): rank hole entries by a clear way out to the next pierce
With no lead-out the tool leaves a hole where it entered it. A tiny corner arc's midpoint lies on both facing sides of its hole, so the ranker scored it as an ideal two-side corner, but the tool leaves it along its 45-degree tangent: a shallow rapid to a nearby next hole re-entered the corner and crossed the hole just cut. Every outside endpoint's preferred hole chain then failed the same way, and the reorder search spent its budget on full backtracking. A 31-part plate of such parts needed 31,959 expansions against a default budget of 20,000. Hole entries whose straight rapid to the next pierce clears the hole itself now rank first, in the preferred chain and in the full search. This is a stable reorder only: no entry is dropped, and every emitted rapid is still checked. That plate now plans Ready on its first preferred pass, using 7,781 expansions; the budget is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b9c5240a43
commit
40e4e559dd
5 files changed
+177
-1
No files matched your search
@@ -739,6 +739,10 @@ internal static class JointCuttingPlanSearch
|
||||
ordered = ordered.OrderBy(c => sightTargets.Count(sight => PostVerificationGeometry.Crosses(
|
||||
c.Choice.Point + source.Location, sight, perimeter, token))).ToList();
|
||||
}
|
||||
// A hole is left where it was entered too: entries whose rapid to the next
|
||||
// pierce clears the hole itself lead.
|
||||
if (target is { } next && contour != source.Prepared.PerimeterOrdinal)
|
||||
ordered = ordered.DepartingClear(source.Prepared, next, token);
|
||||
var selection = ContourEntrySelection.Select(ordered,
|
||||
candidate => Evaluate(source, candidate), snapshot.MaxEntries, token);
|
||||
// No configured lead-in fits the perimeter: with a pierce diameter, try open scrap.
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using OpenNest.CNC;
|
||||
using OpenNest.CNC.CuttingPlanning;
|
||||
using OpenNest.Diagnostics;
|
||||
using OpenNest.Geometry;
|
||||
|
||||
namespace OpenNest.Engine.CuttingPlanning;
|
||||
@@ -70,7 +71,7 @@ internal static class PreferredContourEntries
|
||||
// The S04 merged catalogue: preferred kinds when present, fallback kinds
|
||||
// (compass points, arc midpoints, ...) otherwise — pure circles included.
|
||||
var catalogue = prepared.AutomaticEntryCandidatesWithFallbacks(contour, target, token);
|
||||
var ranked = catalogue.RankTowardNextCut(target, arrivalProxy);
|
||||
var ranked = catalogue.RankTowardNextCut(target, arrivalProxy).DepartingClear(prepared, target, token);
|
||||
var selection = ContourEntrySelection.Select(ranked, evaluate, 1, token);
|
||||
if (selection.Choices.Count == 0)
|
||||
return new(Array.Empty<ContourChoice>(), selection.Shortfall, contour,
|
||||
@@ -84,6 +85,36 @@ internal static class PreferredContourEntries
|
||||
return new(choices, ContourSelectionShortfall.None, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stable reordering of one hole's ranked entries: those whose straight rapid to
|
||||
/// <paramref name="target"/> (the next cut's pierce, local) clears the hole itself come
|
||||
/// first. With no lead-out the tool leaves a hole where it entered it, so from an entry
|
||||
/// whose way out turns back into the hole (a tiny corner arc's midpoint, whose tangent
|
||||
/// leaves at 45 degrees, ranks as a two-side corner) the rapid onward re-enters the hole
|
||||
/// just cut and is refused. Order only: every entry stays and every rapid is still checked.
|
||||
/// </summary>
|
||||
internal static IReadOnlyList<ContourEntryCandidate> DepartingClear(this IReadOnlyList<ContourEntryCandidate> ranked,
|
||||
PreparedContours prepared, Vector target, CancellationToken token)
|
||||
{
|
||||
if (ranked.Count < 2)
|
||||
return ranked;
|
||||
IReadOnlyList<PostVerificationGeometry.Curve> contour;
|
||||
try
|
||||
{
|
||||
// Any entry's probe cuts the same contour (a tab gap aside); scribes and leads are not part of it.
|
||||
var execution = ExecutionMotionReader.Read(prepared.EmitCandidateForValidation(ranked[0].Choice),
|
||||
Vector.Zero, null, token);
|
||||
contour = execution.Motions
|
||||
.Where(m => !m.Rapid && m.Layer is LayerType.Cut or LayerType.Display && m.Curve != null)
|
||||
.Select(m => m.Curve).ToArray();
|
||||
}
|
||||
catch (Exception ex) when (ex is ArgumentException or InvalidOperationException or ArithmeticException or NotSupportedException)
|
||||
{
|
||||
return ranked; // An unemittable probe is the lead checks' finding, not the ranker's.
|
||||
}
|
||||
return ranked.OrderBy(c => PostVerificationGeometry.Crosses(c.Choice.Point, target, contour, token) ? 1 : 0).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The actual emitted pierce of one owned choice in local coordinates: the start of the
|
||||
/// first non-rapid motion (lead-in when present — that is where the next rapid must
|
||||
|
||||
Reference in new issue
Block a user