Skip to content

editor: level-scoped alignment, reference-floor symbols & registry slab tool#373

Merged
wass08 merged 3 commits into
pascalorg:mainfrom
sudhir9297:feat/level-scoped-alignment
Jun 4, 2026
Merged

editor: level-scoped alignment, reference-floor symbols & registry slab tool#373
wass08 merged 3 commits into
pascalorg:mainfrom
sudhir9297:feat/level-scoped-alignment

Conversation

@sudhir9297
Copy link
Copy Markdown
Contributor

What does this PR do?

Follow-up to #372 (merged). Tightens the floor-plan / 3D alignment system and brings a few registry-driven kinds to parity.

  • Level-scoped alignment candidates. Alignment now drops nodes that resolve to a different level, so a node directly below on another floor no longer snaps (alignment is XZ-only). Building-/site-scoped nodes with no level ancestor (e.g. an elevator shaft, which spans every floor) stay in the pool. The 2D floor plan deliberately keeps cross-floor alignment so you can still align to the reference floor below.
  • Elevator footprint = outer shaft, not cab. The alignment footprint is derived from the outer shaft (shaft + wall, what's actually drawn) instead of the inset cab. The cab corners sat ~9 cm inside the visible edge — past the 8 cm snap — so the elevator never surfaced a guide. Default elevator width/depth bumped to 1.84 m to match.
  • Shared stair footprint geometry. Extracted rotateXZ, computeSegmentTransforms, and stairFootprintAABB into stair-footprint.ts so slab-opening sync and alignment anchors derive the chain identically. Stairs now contribute plan-bbox anchors (straight = segment chain, curved/spiral = annular sector).
  • Reference-floor registry symbols. Stairs/roofs/elevators/shelves/spawns on the reference floor now render through their registry def.floorplan builders, so the plan symbol is pixel-identical to one on the active floor.
  • Registry-driven slab creation. Slab creation moved to the registry slab tool (parity with ceiling): the 2D handlers only maintain draft state and forward the closing double-click; the 3D tool commits the node, so a slab is no longer built twice.
  • 3D guide ribbon tracks the active level. The alignment-guide ribbon is lifted to the active level's building-local Y each frame, so it lies on the floor being edited when floors are stacked.

How to test

  1. bun dev, open a scene, switch to the 2D floor plan.
  2. Stack two floors. Place a stair/elevator on floor 1, then move a node on floor 0 directly above/below it → it should not snap across floors (3D), while the 2D reference floor still aligns to the floor below.
  3. Place an elevator → it now surfaces an alignment guide against nearby geometry (the guide derives from its outer shaft outline).
  4. Place/move a stair (straight, curved, and spiral) → it shows alignment guides off its plan footprint.
  5. On the reference floor, confirm stairs/roofs/elevators/shelves/spawns render the same plan symbol as on the active floor.
  6. Draw a slab in the floor plan → exactly one slab is created (no duplicate).
  7. With floors stacked, drag a node and confirm the 3D guide ribbon sits on the floor being edited, not the building base.
  8. bun check and bun check-types should pass.

Screenshots / screen recording

N/A in this description — change is visual/interactive; a screen recording of the cross-floor alignment + elevator/stair guides will be added on the PR. Reviewers can reproduce via the steps above.

Checklist

  • I've tested this locally with bun dev
  • My code follows the existing code style (run bun check to verify)
  • I've updated relevant documentation (if applicable)
  • This PR targets the main branch

sudhir9297 and others added 3 commits June 5, 2026 00:40
…s, registry slab tool

- Scope alignment candidates to the active level so a node directly below
  on another floor no longer snaps (alignment is XZ-only); building-scoped
  nodes like elevator shafts stay in the pool across floors.
- Derive the elevator alignment footprint from its outer shaft (not the
  inset cab), so its guide actually surfaces within the snap threshold.
- Extract shared stair footprint geometry (rotateXZ, segment transforms,
  stairFootprintAABB) so opening-sync and alignment anchors derive the
  chain identically; stairs now contribute plan bbox anchors.
- Render reference-floor stairs/roofs/elevators/shelves/spawns through
  their registry floorplan builders for pixel-identical symbols.
- Move slab creation to the registry-driven slab tool (parity with
  ceiling); 2D handlers only maintain draft state.
- Lift the 3D alignment guide ribbon to the active level's Y each frame.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address architecture review of the level-scoped alignment work: the core
anchor bridge (`alignment-anchors.ts`) hardcoded `if (node.type === 'elevator')`
and `if (node.type === 'stair')` branches to derive their plan footprints.
Move that onto the kinds themselves via a new `alignmentFootprint` capability
so the bridge dispatches generically — matching the registry composition model.

- Add `Capabilities.alignmentFootprint`: returns a `box` (rotatable rect
  centred on position, relocatable for movable kinds) or an `aabb` (already
  resolved, for non-rectangular plan shapes). Elevator uses `box` (its outer
  shaft, and it's movable); stair uses `aabb` (segment chain / annular sector,
  moves by origin).
- Drop both hardcoded branches; the bridge now consults the capability via
  `floorFootprint` (box) and a unified `alignmentAABB` (box ∪ aabb).
- Export `stairFootprintAABB` from core so the stair definition consumes it.
- Tests register synthetic defs carrying the capability (the bridge no longer
  knows elevator/stair by name), reproducing the production glue from the same
  core helpers.
- Document why `REFERENCE_REGISTRY_KINDS` is a deliberate editor-local
  curation, not an auto-derived set (most floorplan-builder kinds shouldn't
  appear as standalone reference symbols, and "reference floor" is an editor
  concept core must not know).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The arrow-handle rig lives on SCENE_LAYER (so its chevrons read as proper
3D plates), but the thumbnail camera only filters EDITOR_LAYER + GRID_LAYER —
so a node selected at capture time would leak its arrows into the snapshot.
Hide the rig on `thumbnail:before-capture` and restore it on
`thumbnail:after-capture`, the same emitter handshake SelectionManager uses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@wass08 wass08 merged commit d1b40aa into pascalorg:main Jun 4, 2026
1 check passed
@sudhir9297 sudhir9297 deleted the feat/level-scoped-alignment branch June 4, 2026 20:08
Aymericr added a commit that referenced this pull request Jun 5, 2026
…diting + interaction polish (#375)

- Live slab-stacking Y previews for all floor-placed kinds (item/shelf/spawn/column/stair) during placement + both move pathways, via a shared core resolver; canonical positions unchanged.
- Unified 3D handle system (one drag pipeline + one visual primitive) with forgiving invisible hit-areas on every handle, kept on EDITOR_LAYER so they don't poison the MRT scene pass.
- Hover + click-to-edit slab holes in 3D (manual hole -> hole editor; stair/elevator hole -> select owner); generic cross-arrow polygon-move grip; normalized handle interaction colors.
- NaN-safe node mutations + non-finite shadow-light bounds guard.
- Built on #373 (level-scoped alignment / registry slab tool); #373 owns X/Z alignment, this owns Y floor-stacking.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants