Wall lifecycle: rectangle walls, loop-cut split and merge, with 2D navigation and selection fixes - #902
Merged
Conversation
|
I hit an error while handling your request (Model unavailable on AI Gateway free tier: Free tier users do not have access to this model. Upgrade to paid credits at https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%3Fmodal%3Dtop-up for unrestricted…). Please try again, rephrase, or reach out if it keeps failing. Error id: 7c700410-3ae4-4d68-b42a-b59f7c189810 |
AxiomeCG
force-pushed
the
feat/walls
branch
from
September 21, 2026 22:11
8ef96ff to
e6275b5
Compare
AxiomeCG
force-pushed
the
feat/walls
branch
from
September 21, 2026 22:25
e6275b5 to
41d84c7
Compare
AxiomeCG
force-pushed
the
feat/walls
branch
from
September 21, 2026 22:34
41d84c7 to
acf8990
Compare
AxiomeCG
force-pushed
the
feat/walls
branch
2 times, most recently
from
September 21, 2026 22:47
ee60958 to
2cd04ba
Compare
The 2D view had no navigation keys of its own: WASD and the orbit buttons drove the 3D camera, which the plan followed. Since the 3D canvas pauses while hidden (pascalorg#675), both did nothing in 2D-only view, and a session opened straight into 2D never mounted the camera at all. The plan now owns them in 2D-only view with the camera's own key state, guards and speed (lib/keyboard-pan), pans through its existing viewport pipeline and publishes the pose to 3D when the move ends. The camera stands down there, so each view mode has one owner. Split view is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017sG15rKXusC8rbBg6gjSRm
…tead of selecting A registry entry selected itself on pointer-down and stopped the click, so with the wall tool armed, clicking an existing wall to start or end a T junction selected that wall and the tool never saw the click. Only door and window placement passed through. Entries now yield to the active tool in build mode, matching 3D where the selection manager only runs in select mode; select and delete keep selecting. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017sG15rKXusC8rbBg6gjSRm
…en moved
The window tool only heard wall raycasts, so with walls hidden (cutaway,
2D-driven placement) it could not place: grid events now carry the surface
hit under the pointer and the tool resolves the wall from it
(wallEventFromGrid). Moving a door or window rewrote its metadata to {},
dropping persistent ownership (arrays, assets); commitOpeningMove strips
only the draft flags. A transient preview child never hosts a wall
attachment.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017sG15rKXusC8rbBg6gjSRm
…ping panel-wrapper clamps floating panels to the closest [data-viewer-bounds]; the mobile layout never set it, so panels could slide under its chrome. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017sG15rKXusC8rbBg6gjSRm
AxiomeCG
force-pushed
the
feat/walls
branch
from
September 21, 2026 22:57
2cd04ba to
dcd1cb9
Compare
AxiomeCG
force-pushed
the
feat/walls
branch
2 times, most recently
from
September 21, 2026 23:12
9356c8b to
94cff0e
Compare
AxiomeCG
force-pushed
the
feat/walls
branch
3 times, most recently
from
September 21, 2026 23:34
c11834d to
aa71a1b
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit aa71a1b. Configure here.
- Rectangle walls: R toggles line/rectangle inside the wall tool, shown as a Shape chip in the HUD; in 2D the rectangle drafts through the panel's own cursor, snapping, mitered footprints and measurement plates, and in 3D it keeps the wall icon and the line draft's look. A side drawn along an existing wall adds only the uncovered remainder (uncoveredWallSegments). - Split: a HUD-driven loop cut. Scrolling sets 1-32 cuts, one cut follows the pointer with the snapping modes (Shift/Ctrl/Alt as elsewhere), several divide the wall evenly, and a click commits them as one undo step (core planWallDivision/planWallDivisions). - Merge: selecting walls that continue each other shows Merge next to Split. core planWallMerge keeps the wall with the most attachments and never turns it around; an absorbed wall read backwards mirrors its children's local frame (side, depth, yaw); every child is re-hosted and rooms follow. - Openings keep their metadata when moved, transient previews never host walls, and windows place through grid events on hidden walls. Behaviour changes on main that come with the shared planners: - planWallInsertion (the line tool too) rewrites a room's boundaryWallIds when it splits one of its walls; the room used to keep the removed id. - The delete heal's joining rule and attachment re-hosting moved into systems/wall/wall-merge.ts; at a start-to-start joint the heal no longer reverses the kept wall. Split and merge live in packages/nodes/src/wall/ (session, store, preview, pointer, 3D tool, plan layer, actions). The editor only gains kind-agnostic seams: a reshape name without a dedicated ToolManager arm mounts def.affordanceTools[reshape]; the plan mounts the floorplan extension's reshapeLayers[reshape] during that scope; HelperManager renders def.affordanceHints[reshape]; NodeActionMenu renders the floorplan extension's actionMenu.actions for the selected kinds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017sG15rKXusC8rbBg6gjSRm
The multi-selection box measured the meshes in world space and mapped two corners of that box into the level frame; under a rotated building those corners land beside the meshes, so the 2D dashed box sat away from the walls and group move/rotate pivoted on the wrong point. In 2D-only view it was worse still: the 3D scene is paused there, so meshes are unbuilt placeholders or carry stale cached bounds, and a selection holding a room's floor and ceiling boxed 13 m of nothing. It also grew the selection to every wall connected through junctions, so two walls of a room boxed (and moved) the whole connected structure. Group transforms now act on the selection: connected walls outside it stretch at their shared ends to stay joined (the existing neighbour links). groupPlanBounds measures from node data wherever the plan draws from data — walls by their mitered outline, slabs, ceilings and zones by their polygon, fences by their run — and only placed objects by their meshes, with fresh bounds, placeholders skipped and the anchor as fallback. The 2D box, group move and duplicate bounds, keyboard R/T and the 3D rotate gizmo share its centre as the pivot, and the 3D dashed box turns with the building like the 2D one. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017sG15rKXusC8rbBg6gjSRm
A room's slab and ceiling overlap in the plan and only the slab takes a click, so after a marquee picked both, Shift-clicking the slab off left a ceiling nobody could remove from the plan, and the group box kept covering the room. Removing either surface now removes its counterpart (same level, same outline); adding stays single. 3D keeps single toggles, where each surface is clickable on its own. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017sG15rKXusC8rbBg6gjSRm
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017sG15rKXusC8rbBg6gjSRm
AxiomeCG
force-pushed
the
feat/walls
branch
from
September 21, 2026 23:51
aa71a1b to
2878f1b
Compare
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What does this PR do?
Completes the wall lifecycle — draw, split, merge — and fixes four
mainbugs found while testing it in the floor plan. Balconies and the plan workspace (calibrate / trace SVG plans) follow in separate PRs stacked on this one.Walls
Rtoggles line / rectangle inside the wall tool (HUD "Shape" chip cycles the same store). In 2D the rectangle drafts through the plan's own cursor, snapping, mitered footprints and measurement plates; in 3D it keeps the wall cursor and the line draft's look.planWallDivisions). Openings re-host on their segment. The old floating panel is gone.planWallMergekeeps the wall with the most attachments and never turns it around; an absorbed wall read backwards mirrors its children's local frame (side, depth, yaw — hinges and swing hang off the yaw and stay as stored); every child is re-hosted at its world position, stale child ids are dropped, rooms follow; it refuses T/cross joints, walls out of line, different thickness / visible height / finish, or curved walls — the reason shows on hover.uncoveredWallSegments); a side that T-joins or crosses a room wall keeps the room on the replacement walls.Behaviour changes on
mainthat come with the shared planners (worth knowing when reviewing):planWallInsertion— the line tool's planner too — now rewrites a room'sboundaryWallIdswhen it splits one of that room's walls; before, the zone kept the removed wall's id.resolveMergedWallEndpoints,buildMergedWallAttachmentUpdates) moved fromnode-actions.tsintosystems/wall/wall-merge.ts; at a start-to-start joint the heal no longer reverses the kept wall (which flipped itsfrontSide/backSideand hosted faces).Where it lives. Split and merge are wall code, so they live in
packages/nodes/src/wall/(split-session.ts,split-store.ts,split-preview.ts,split-pointer.ts,split-tool.tsx,split-floorplan-layer.tsx,actions.tsx). The editor only gains kind-agnostic seams, so the next kind-owned reshape needs no editor change:ToolManagermountsdef.affordanceTools[reshape]for anyreshapingscope without a dedicated arm (the split runs asreshape: 'split', which also gives it thepolygonsnapping chip).FloorplanRegisteredToolLayermounts the floorplan extension'sreshapeLayers[reshape]while that scope runs.HelperManagerrendersdef.affordanceHints[reshape](the cut-count chip) liketoolHints.NodeActionMenurenders the floorplan extension'sactionMenu.actionsfor the selected kinds (Split / Merge come from the wall).selectionCounterpartson the same extension for the paired deselect below.Known gap, left for a follow-up: the 3D rectangle draft has no alignment guides yet (the 2D one shares the line draft's).
Fixes on
main(each its own commit)lib/keyboard-pan.ts) and publishes the pose to 3D when the move ends.groupPlanBounds); the 2D box, 3D rotate gizmo and keyboard R/T share its centre. This is a behaviour change: two walls of a room no longer move the whole room.How to test
bun dev, open a scene with a few rooms, switch to the 2D view.R— the HUD shows Shape: Rectangle; click two opposite corners → four joined walls, mitered, with plates during the draft. PressRagain → line mode; end a wall on the side of an existing wall → a T-junction, not a selection.Shiftto cycle snapping, click to commit; undo once removes all cuts. Select the two halves → Merge (two arrows) → one wall again; hover a greyed Merge to read why.bun testinpackages/{core,editor,nodes}(all green),bun check.Screenshots / screen recording
Screen recording to be added by the author (2D rectangle draft, loop-cut split, merge).
Checklist
bun devbun checkto verify)mainbranch🤖 Generated with Claude Code
https://claude.ai/code/session_017sG15rKXusC8rbBg6gjSRm
Note
Medium Risk
Changes wall topology, opening re-hosting, and zone boundary references; group-move behavior no longer drags entire connected wall components, which can surprise users but is localized to editor geometry.
Overview
Adds core wall planners for rectangle drawing (
planWallRectangle+uncoveredWallSegmentsto reuse collinear walls), explicit split/division (planWallDivision/planWallDivisions), and merge (planWallMergeinwall-merge.ts, shared with delete-heal). Merge/split paths re-host openings (including frame mirroring when a wall is read backwards), update zoneboundaryWallIds, and validate collinearity, joints, and style.Editor integration: registry seams for kind-owned reshapes (
reshapeLayers,affordanceHints,actionMenu.actions), floorplan rectangle wall draft preview, and build-mode clicks that yield to tools instead of selecting walls underneath. Group transforms no longer auto-expand to every connected wall; the dashed box / gizmo pivot uses level-frame plan bounds (groupPlanBounds/computeGroupPlanBox). 2D-only view drives WASD/orbit via sharedkeyboard-panwhile the 3D camera is paused; slab/ceiling deselect together in the plan viaselectionCounterparts.Reviewed by Cursor Bugbot for commit 2878f1b. Bugbot is set up for automated code reviews on this repo. Configure here.