5.3 KiB
Contour Re-Indexing Design
Overview
Add entity-splitting primitives and a Shape.ReindexAt method so that a closed contour can be reordered to start (and end) at an arbitrary point. Then wire this into ContourCuttingStrategy.Apply() to replace the NotImplementedException stubs.
All geometry additions live on existing classes in OpenNest.Geometry. The strategy wiring is a change to the existing ContourCuttingStrategy in OpenNest.CNC.CuttingStrategy.
Entity Splitting Primitives
Line.SplitAt(Vector point)
public (Line first, Line second)? SplitAt(Vector point)
- Returns two lines:
StartPoint → pointandpoint → EndPoint. - If the point is at
StartPoint(withinTolerance.Epsilon),firstis null. - If the point is at
EndPoint(withinTolerance.Epsilon),secondis null. - If both halves would be degenerate, returns null.
- The point is assumed to lie on the line (caller is responsible — it comes from
ClosestPointTo).
Arc.SplitAt(Vector point)
public (Arc first, Arc second)? SplitAt(Vector point)
- Computes
splitAngle = Center.AngleTo(point), normalized viaAngle.NormalizeRad. - First arc: same center, radius, direction —
StartAngle → splitAngle. - Second arc: same center, radius, direction —
splitAngle → EndAngle. - If
splitAngleequalsStartAngle(within tolerance),firstis null. - If
splitAngleequalsEndAngle(within tolerance),secondis null. - If both halves would be degenerate, returns null.
Circle.ToArcFrom(Vector point)
public Arc ToArcFrom(Vector point)
- Computes angle:
Center.AngleTo(point). - Returns an
Arcwith same center, radius,StartAngle = EndAngle = angle, andIsReversedmatching the circle'sRotation(CW → reversed, CCW → not reversed). - This produces a full-sweep arc that starts and ends at
point.
Shape.ReindexAt
public Shape ReindexAt(Vector point, Entity entity)
point: the start/end point for the reindexed contour (fromClosestPointTo).entity: the entity containingpoint(fromClosestPointTo'soutparameter).- Returns a new Shape (does not modify the original).
Algorithm
-
If
entityis aCircle:- Return a new Shape with a single entity:
circle.ToArcFrom(point).
- Return a new Shape with a single entity:
-
Find the index
iofentityinEntities. -
Split the entity at
point:Line→line.SplitAt(point)→(firstHalf, secondHalf)Arc→arc.SplitAt(point)→(firstHalf, secondHalf)
-
Build the new entity list (all non-null):
secondHalf(if not null)Entities[i+1],Entities[i+2], ...,Entities[count-1](after the split)Entities[0],Entities[1], ...,Entities[i-1](before the split, wrapping around)firstHalf(if not null)
-
Return a new Shape with this entity list.
Edge Cases
- Point lands on entity boundary (start/end of an entity): one half of the split is null. The reordering still works — it just starts from the next full entity.
- Single-entity shape that is an Arc: split produces two arcs, reorder is just
[secondHalf, firstHalf]. - Single-entity Circle: handled by step 1 (converts to full-sweep arc).
Wiring into ContourCuttingStrategy
Entity-to-ICode Conversion
Add a private method to ContourCuttingStrategy:
private List<ICode> ConvertShapeToMoves(Shape shape)
Iterates shape.Entities and converts each to cutting moves:
Line→LinearMove(line.EndPoint)Arc→ArcMove(arc.EndPoint(), arc.Center, arc.IsReversed ? RotationType.CW : RotationType.CCW)Circle→ should not appear (circles are converted to arcs byReindexAt)
No RapidMove between entities — they are contiguous in a reindexed shape. The lead-in already positions the head at the shape's start point.
Replace NotImplementedException
In ContourCuttingStrategy.Apply(), replace both throw new NotImplementedException(...) blocks with:
var reindexed = shape.ReindexAt(closestPt, entity);
result.Codes.AddRange(ConvertShapeToMoves(reindexed));
The full sequence for each contour becomes:
- Lead-in codes (rapid to pierce point, cutting moves to contour start)
- Contour body (reindexed entity moves from
ConvertShapeToMoves) - Lead-out codes (overcut moves away from contour)
MicrotabLeadOut Handling
When the lead-out is MicrotabLeadOut, the last cutting move must be trimmed by GapSize. This is a separate concern from re-indexing — stub it with a TODO comment for now. The trimming logic will shorten the last LinearMove or ArcMove in the contour body.
Files Modified
| File | Change |
|---|---|
OpenNest.Core/Geometry/Line.cs |
Add SplitAt(Vector) method |
OpenNest.Core/Geometry/Arc.cs |
Add SplitAt(Vector) method |
OpenNest.Core/Geometry/Circle.cs |
Add ToArcFrom(Vector) method |
OpenNest.Core/Geometry/Shape.cs |
Add ReindexAt(Vector, Entity) method |
OpenNest.Core/CNC/CuttingStrategy/ContourCuttingStrategy.cs |
Add ConvertShapeToMoves, replace NotImplementedException blocks |
Out of Scope
- MicrotabLeadOut trimming (trim last move by gap size — stubbed with TODO)
- Tab insertion (inserting tab codes mid-contour — already stubbed)
- Lead-in editor UI (interactive start point selection — separate feature)
- Contour re-indexing for open shapes (only closed contours supported)