feat(engine): generalize Compactor.Push to support arbitrary angles and BB-only mode

Add Vector-based overloads to SpatialQuery (ray casting, edge distance,
directional gap, perpendicular overlap) and PartGeometry (directional
line filtering) to support pushing parts along any angle, not just
cardinal directions.

Add Compactor.PushBoundingBox for fast coarse positioning using only
bounding box gaps. ActionClone shift+click now uses a two-phase strategy:
BB push first to skip past irregular geometry snags, then geometry push
to settle against actual contours.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-18 09:41:09 -04:00
parent 285e7082fb
commit 3d6be3900e
4 changed files with 396 additions and 19 deletions
+19 -19
View File
@@ -141,28 +141,28 @@ namespace OpenNest.Actions
{
if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift)
{
var movingParts = parts.Select(p => p.BasePart).ToList();
PushDirection hDir, vDir;
switch (plateView.Plate.Quadrant)
{
case 1:
plateView.PushSelected(PushDirection.Left);
plateView.PushSelected(PushDirection.Down);
break;
case 2:
plateView.PushSelected(PushDirection.Right);
plateView.PushSelected(PushDirection.Down);
break;
case 3:
plateView.PushSelected(PushDirection.Right);
plateView.PushSelected(PushDirection.Up);
break;
case 4:
plateView.PushSelected(PushDirection.Left);
plateView.PushSelected(PushDirection.Up);
break;
case 1: hDir = PushDirection.Left; vDir = PushDirection.Down; break;
case 2: hDir = PushDirection.Right; vDir = PushDirection.Down; break;
case 3: hDir = PushDirection.Right; vDir = PushDirection.Up; break;
case 4: hDir = PushDirection.Left; vDir = PushDirection.Up; break;
default: hDir = PushDirection.Left; vDir = PushDirection.Down; break;
}
// Phase 1: BB-only push to get past irregular geometry quickly.
Compactor.PushBoundingBox(movingParts, plateView.Plate, hDir);
Compactor.PushBoundingBox(movingParts, plateView.Plate, vDir);
// Phase 2: Geometry push to settle against actual contours.
Compactor.Push(movingParts, plateView.Plate, hDir);
Compactor.Push(movingParts, plateView.Plate, vDir);
parts.ForEach(p => p.IsDirty = true);
plateView.Invalidate();
}
parts.ForEach(p => plateView.Plate.Parts.Add(p.BasePart.Clone() as Part));