mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-11 13:14:13 -04:00
fix(cutting): anchor near-corner entries to their native edge
This commit is contained in:
1 parent
dc142cc88d
commit
08acb092a7
3 files changed
+41
-5
No files matched your search
@@ -254,12 +254,11 @@ public sealed class PreparedContours
|
||||
.Where(p => p.Kind == AutomaticEntryKind.ConvexCorner))
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
var cornerPoint = End(shape.Entities[corner.EntityOrdinal]);
|
||||
// Back INTO each incident edge from the corner: the edge ending at the
|
||||
// corner retreats against its own travel, the edge starting at the corner
|
||||
// advances along its own travel.
|
||||
AddInset(corner.EntityOrdinal, cornerPoint, inward: false);
|
||||
AddInset((corner.EntityOrdinal + 1) % count, cornerPoint, inward: true);
|
||||
AddInset(corner.EntityOrdinal, inward: false);
|
||||
AddInset((corner.EntityOrdinal + 1) % count, inward: true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +275,7 @@ public sealed class PreparedContours
|
||||
token.ThrowIfCancellationRequested();
|
||||
return fallbacks;
|
||||
|
||||
void AddInset(int entityOrdinal, Vector corner, bool inward)
|
||||
void AddInset(int entityOrdinal, bool inward)
|
||||
{
|
||||
// `inward` selects along the edge's own travel from its start; without it the
|
||||
// point retreats against travel. Both ways move BACK INTO the edge from the
|
||||
@@ -285,7 +284,9 @@ public sealed class PreparedContours
|
||||
return;
|
||||
var direction = (line.EndPoint - line.StartPoint).Normalize();
|
||||
var offset = direction * (2 * lead);
|
||||
var point = inward ? corner + offset : corner - offset;
|
||||
// Adjacent native endpoints may differ within contour-continuity tolerance.
|
||||
// Anchor to the selected edge itself, never the preceding edge's vertex.
|
||||
var point = inward ? line.StartPoint + offset : line.EndPoint - offset;
|
||||
// Strictly inside the edge by projection parameter (distance alone loses the
|
||||
// sign when a short edge is overshoot): never the corner, never the far
|
||||
// endpoint (where a reflex vertex might sit). Short edges omit the point.
|
||||
|
||||
@@ -310,6 +310,36 @@ public class ContourEntryCandidateTests
|
||||
Assert.All(near, c => Assert.True(c.Kind > AutomaticEntryKind.TangentJoint));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NearCornerOnRoundedJointStaysOnItsSelectedLine()
|
||||
{
|
||||
// Native contour joints can differ within the accepted closure tolerance. The
|
||||
// preceding edge's corner is off the following edge by one micron, while the
|
||||
// near-corner entry must lie on the following edge itself.
|
||||
var shape = new Shape();
|
||||
shape.Entities.Add(new Line(0, 0, 10, 0));
|
||||
shape.Entities.Add(new Line(10, 0, 10, 10));
|
||||
shape.Entities.Add(new Line(10, 10.000001, 0, 10.000001));
|
||||
shape.Entities.Add(new Line(0, 10.000001, 0, 0));
|
||||
shape.UpdateBounds();
|
||||
// Capture constructs this exact owned native shape after program conversion;
|
||||
// build it directly to characterize its legal, sub-tolerance joint discrepancy.
|
||||
var constructor = typeof(PreparedContours).GetConstructor(
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance,
|
||||
null, [typeof(Shape[]), typeof(List<Entity>), typeof(OpenNest.CNC.CuttingStrategy.CuttingParameters)], null);
|
||||
Assert.NotNull(constructor);
|
||||
var prepared = (PreparedContours)constructor.Invoke([
|
||||
new[] { shape }, new List<Entity>(), ExplicitContourTests.Parameters()]);
|
||||
var near = prepared.AutomaticEntryCandidatesWithFallbacks(0)
|
||||
.Where(c => c.Kind == AutomaticEntryKind.NearCorner).ToList();
|
||||
Assert.NotEmpty(near);
|
||||
Assert.All(near, c => Assert.Equal(c.Choice.Point,
|
||||
prepared.Entry(c.Choice.ContourOrdinal, c.Choice.EntityOrdinal, c.Choice.Point).Point));
|
||||
Assert.Contains(near, c => c.Choice.EntityOrdinal == 2 &&
|
||||
System.Math.Abs(c.Choice.Point.Y - 10.000001) < 1e-10);
|
||||
Assert.Throws<ArgumentException>(() => prepared.Entry(0, 2, new Vector(9.4, 10)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShortEdge_OmitsNearCornerInsteadOfExtrapolating()
|
||||
{
|
||||
|
||||
@@ -192,6 +192,11 @@ more of the catalogue; a later side cannot lose a usable point merely because
|
||||
an earlier side's scan passed it. Replacements preserve other covered sides and
|
||||
global rank, never exceed the cap, and stop evaluating the tail once coverage
|
||||
settles. Smaller caps retain rank priority rather than promising all-side coverage.
|
||||
An inset near a convex corner is measured along the selected native straight edge from
|
||||
that edge's own endpoint, not a neighboring entity's endpoint. This keeps the
|
||||
candidate on its selected entity when a joined contour has an accepted rounding gap;
|
||||
no entry-membership or lead-clearance tolerance is widened.
|
||||
|
||||
The next cut is the next unfinished part in a supplied order — recomputed
|
||||
after every learned-order replan — or, in the full fallback search, the nearest
|
||||
dependency-ready remaining part, stable-ordinal ties; the last part has no
|
||||
|
||||
Reference in new issue
Block a user