Skip to content

Door/window placement-feel polish + placement-state refactor#411

Merged
Aymericr merged 1 commit into
mainfrom
feat/editor-opening-placement-polish
Jun 16, 2026
Merged

Door/window placement-feel polish + placement-state refactor#411
Aymericr merged 1 commit into
mainfrom
feat/editor-opening-placement-polish

Conversation

@Aymericr

Copy link
Copy Markdown
Contributor

What

Round 8 of the opening-placement UX work — makes door/window placement feel physical and predictable, and unifies the validity/placement logic behind one shared decision. Follows the merged #407 (always-visible ghosts) and #122.

UX

  • Window sill 0.5 m by default — fresh windows float slightly off the floor; existing windows keep their sill.
  • Floor "shadow" projection (dev, windows only): footprint + dashed drop-line during placement so an elevated window's plan position is legible.
  • Move SFX: one soft grid-snap click per grid step — identical free-following over floor and sliding on a wall (no separate snap cue that read as a "double"). Mirrored into 2D so it matches 3D.
  • Shift = force-place over a collision (commit allowed; ghost stays red as a warning) + free-place (raw-cursor, no snap, but keeps the alignment guides visible). Tint flips green/red live on Shift press/release.
  • On-wall preview is the tinted ghost (green placeable / red colliding), matching the free-follow ghost.
  • R-flip fixes: always toggles (no first-press no-op), e.repeat filtered, ghost rebuilds with the live side, and the on-wall ghost yaw uses itemRotation - wallAngle so the ghost faces exactly what commit places (the old cursorRotation was π off for the asymmetric ghost). R ownership follows the current pointer pane (capture-phase + stopImmediatePropagation) so 2D/3D never double-flip or go dead.

Refactor / quality

  • New resolveOpeningPlacement({collides,forcePlace}) -> {placeable,tint} — the single source of truth the ghost tint AND the commit gates both consume (they could previously disagree under Shift).
  • Consolidated the byte-identical hasWallChildOverlap into one shared impl (door-math/window-math re-export).
  • applyGhost gained a green "valid" tint; removed dead cursorRotation from the move targets.
  • Docs: "2D ↔ 3D behavioral parity" principle in wiki/architecture/tools.md (+ README + AGENTS.md).

Safety / verification

  • Core stays Three-free; nodes→editor import allowed; no schema/migrations.
  • tsc --build + repo bun run build clean; bun typecheck green; biome clean.
  • Numerically verified the ghost yaw equals the committed wall-child world yaw in all front/back × flip cases.
  • Adversarial/quality review performed (hand review of record; Codex dispatched).

🤖 Generated with Claude Code

…actor

Round 8 of the opening-placement UX work. Make door/window placement feel
physical and predictable, and unify the validity/placement logic behind one
shared decision.

UX:
- Window default sill 0.5 m (DEFAULT_WINDOW_SILL_M) so fresh windows float
  slightly above the floor; existing windows keep their own sill.
- Dev-only floor "shadow" projection for windows during placement/move
  (footprint + dashed drop-line) so an elevated window's plan spot is legible.
- Move SFX: one soft grid-snap click per grid step — identical free-following
  over floor or sliding on a wall (keyed on the raw cursor; per-frame + step
  dedup), no separate snap cue (that was a "double"). Mirrored into the 2D
  floorplan-move so 2D and 3D match.
- Shift = force-place over a collision (commit allowed; ghost stays a red
  warning) + free-place (lands at the raw cursor but keeps the alignment guides
  visible). Tint flips green/red live when Shift is pressed/released stationary.
- On-wall preview is now the tinted ghost (green placeable / red colliding),
  matching the free-follow ghost, instead of a pale solid mesh + thin wireframe.
- R-flip fixes: always toggles (no initial no-op needing a second press),
  e.repeat filtered, ghost rebuilds with the live `side`, and the ghost's
  on-wall world yaw uses `itemRotation - wallAngle` so it faces exactly what
  commit places (cursorRotation was π off for the asymmetric ghost). R ownership
  follows the current pointer pane (capture-phase + stopImmediatePropagation in
  the 2D overlay) so 3D and 2D never double-flip or go dead.

Refactor / quality:
- New `resolveOpeningPlacement({collides,forcePlace}) -> {placeable,tint}` in
  shared/wall-attach-target.ts — the single source of truth the ghost tint AND
  the commit gates both consume, so they can't disagree under Shift.
- Consolidated the byte-identical `hasWallChildOverlap` into one shared impl
  (door-math/window-math re-export it).
- applyGhost gained a green "valid" tint.
- Removed dead `cursorRotation` from the move-tool targets after the yaw fix.

Docs: "2D <-> 3D behavioral parity" principle in wiki/architecture/tools.md
(+ README + AGENTS.md) — applicable behaviors must exist in both views.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mintlify

mintlify Bot commented Jun 16, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
pascal 🔴 Failed Jun 16, 2026, 2:15 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@Aymericr Aymericr merged commit a0d3d9c into main Jun 16, 2026
1 of 2 checks passed
@Aymericr Aymericr deleted the feat/editor-opening-placement-polish branch June 16, 2026 14:20
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.

1 participant