mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-14 08:05:07 -05:00
The labels said left and down, but every push follows the plate quadrant: on quadrant 4 "Push left, then down" pushed left, then up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
42 lines
7.6 KiB
Markdown
42 lines
7.6 KiB
Markdown
# Directional slides and repeated pushes
|
|
|
|
## Behavior contract
|
|
|
|
For initially non-overlapping closed material boundaries, a directional slide stops at the first contact that blocks forward motion. Leaving an existing contact or sliding along a straight shared edge is legal. Skipping a contact must not skip the rest of that obstacle: a later hook or the opposite wall of a hole still stops the slide. Distances retain the existing `Tolerance.Epsilon` snapping and `double.MaxValue` no-hit convention; supplied vector directions are unit vectors.
|
|
|
|
`SpatialQuery.DirectionalDistance` overloads and CPU best-fit batches use the same event sources and contact resolver. Events carry both contact points in their initial world frames, rather than only a snapped distance. The classifier uses closed-loop material sectors, native-curve containment for hole depth, and the curvature of the supporting boundary at a tangential contact. A full-circle arc has no physical corner at its seam. Raw ray helpers remain first-touch primitives, not material-aware slide queries.
|
|
|
|
Open/incomplete chains and ambiguous contacts conservatively block. Loops are recovered with ShapeBuilder's `Tolerance.ChainTolerance`, so a source contour that closes within it (PEP-rounded drawings can miss by 1e-6) stays a closed loop; a stricter closure test would leave the whole part unresolved and freeze any part resting against it. Rounded arc centers also tilt a joint's tangents (about gap / radius), so two parts touching along a shared edge can give a contact cone a hair wider than a half-plane; the classifier reads a cone up to `SlideContact.TangentSlack` (1e-4 rad) past a half-plane as a noisy half-plane and treats directions within that excess of its boundary as tangential, so a part can still slide along its neighbor. A wider cone is real overlap and blocks. This is not an overlap-repair operation or a general replacement for layout validation. Caller-provided contact topology must represent the same boundaries and offsets as the query. Prepared geometry must not be mutated; prepare a classifier before sharing it between parallel queries. Edge-array queries still sort their arrays, and recover loop order from private copies before classifying contacts.
|
|
|
|
## Callers
|
|
|
|
- PlateView uses `SelectionManager.PushSelected` → `Compactor.Push`. The zero-spacing nudge-and-discard workaround is removed. Cutout contours on stationary obstacles are retained, and the plate entry's existing-overlap filter accounts for holes rather than relying solely on `Part.Intersects` (which compares outer perimeters).
|
|
- With **Calculated distance** checked in Tools > Set Offset Increment, PlateView's arrow keys use `SelectionManager.StepSelected` → `Compactor.Step`. The step is the selection's linear-fill copy distance (`FillLinear.CopyDistance`) at the plate's part spacing, so a shape that nests into its own copy steps less than its size. That copy distance is the same in both directions along an axis. The move is not a slide: like a fixed nudge it always moves the full step, ignoring the work-area edge, clamps and other parts, because interlocked rows (staggered triangles, say) cannot slide straight past their neighbors to the next row. It can land off the plate or on a part; the layout check reports that as it does for any nudge.
|
|
- Linear fill inherits the shared native-entity query unchanged. Extents fill passes complete boundary loops instead of direction-filtered fragments.
|
|
- CPU best-fit batches prepare contact topology once and use all vertices plus curve/line interior and curve/curve tangency events. The old leading-half vertex filter cannot establish the next blocker after a skipped touch.
|
|
- GPU kernels retain nearest-hit reduction and return unsnapped contact witnesses. The shared CPU classifier accepts a blocking witness or replays the full query after a nonblocking witness, preserving tied/later blockers. Both batch APIs honor active buffer lengths and refresh mutated/reused segment arrays. The GPU distance adapter sends only exact cardinal directions to the axis-only slide interface; arbitrary directions and native curves use the shared CPU path.
|
|
|
|
- Shift-click while cloning parts settles the copied group toward the plate quadrant. If its starting bounds overlap any placed part's bounds, it skips the coarse bounding-box pass and uses geometry directly; otherwise each axis order starts with a coarse box pass. Each candidate then alternates geometry pushes until movement is negligible (at most 20 iterations). The coarse pass remains useful for avoiding sawtooth/rung traps when the starting boxes do not overlap.
|
|
- Ctrl-click settles the same way on bounding boxes alone (`SettlePlacement(boundingBoxesOnly: true)`): every push stops at the part spacing from obstacles' boxes, so the copy never nests into a neighbor's shape. A group whose box already overlaps an obstacle's box does not move.
|
|
- Options > Clone push placement (`ClonePlacementOrder`, a `SettleOrder` name) picks the axis order for both: *Closest to origin* (default) tries horizontal-first and vertical-first and keeps the group nearest the quadrant's work-area corner; *Push horizontally first* and *Push vertically first* run only that order. Every push goes toward the origin's side for the plate quadrant (quadrant 4 pushes left and up).
|
|
|
|
## Regression coverage
|
|
`SlideContactTests` exercises cardinal line, translated line, reused edge-array, arbitrary-vector, native-entity, and both CPU batch paths. Cases include winding reversal, nonzero origins, rotated hooks, holes, separating circles, positive-distance grazing followed by a blocker, full-circle arc seams, concave/straight junctions, thin rings, and circle/line interior contact.
|
|
|
|
`CompactorTests` covers the reported sequence (push left with spacing, then right/up/down), genuine zero-distance blocking, zero/nonzero-spacing later hooks, and inside-hole pushes through both direct and plate entry points. Physical spacing is measured from raw outlines rather than the inflated contours used by the solver.
|
|
|
|
Verification commands:
|
|
|
|
```sh
|
|
dotnet test OpenNest.Tests/OpenNest.Tests.csproj --filter 'FullyQualifiedName~SlideContactTests|FullyQualifiedName~CurveContactDistanceTests|FullyQualifiedName~CompactorTests'
|
|
dotnet test OpenNest.Tests/OpenNest.Tests.csproj
|
|
dotnet test OpenNest.Engine.Tests/OpenNest.Engine.Tests.csproj
|
|
dotnet test OpenNest.IO.Tests/OpenNest.IO.Tests.csproj
|
|
```
|
|
|
|
The isolated repair tree (excluding other sessions' fill-performance and spacing-expander work) passed 130 targeted cases and the full Debug suites: main 1758 passed / 21 skipped, engine 300 passed, IO 41 passed. The main Release suite passed 1728 / 21 skipped. Skips are not counted as passes. The 142 `GpuSlideContactTests` also pass in a net8 harness linking the production GPU class and test source, using ILGPU 1.5.1's CPU accelerator (not a mocked distance solver). The Windows desktop/test project cross-build passes in Release. Neither physical GPU execution nor Windows UI interaction was runtime-verified on Linux.
|
|
|
|
## Remaining hardening
|
|
|
|
This repair does not change saved best-fit cache versioning, add a clearance acceptance gate to every fill entry, repair existing overlapping layouts, or claim the earlier real-DXF/grid-validator discrepancies in [pair-spacing checks](pair-spacing.md) are resolved. That document's measured candidate counts describe its earlier tree; removing CPU projection filtering and adding interior curve/line contacts does not substitute for rerunning its corpus. General `Part.Intersects` hole semantics remain unchanged outside Compactor. Profile the new classification path before attempting optimizations; retain the first-blocking-contact regressions.
|