editor: fix roof and placement previews - #718
Conversation
Items (e.g. solar panels) can now be placed on sloped roof surfaces. The placement system computes euler rotation from the roof surface normal so items sit flush on the slope instead of going inside. - Add roofStrategy to placement-strategies with enter/move/click/leave - Wire roof:enter/move/click/leave events in the placement coordinator - Add calculateRoofRotation in placement-math using surface normals - Support full 3D cursor rotation for sloped surfaces - Items on roofs are parented to the level with world-space rotation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… github.com:pascalorg/editor
… github.com:pascalorg/editor
| useViewer.setState({ hoveredId: null }) | ||
| } | ||
| return | ||
| } |
There was a problem hiding this comment.
Delete mode blocked by move
High Severity
Delete handling was moved into emitCanvasNodeSelection, but the existing click-to-move early return still runs first. In delete mode, clicking an already-selected movable node picks it up for move and returns before delete can run, so that click never removes the node.
Reviewed by Cursor Bugbot for commit 1f790d3. Configure here.
| editor.tool === 'item' || | ||
| editor.tool === 'lean-to-extension') | ||
| ) | ||
| } |
There was a problem hiding this comment.
Lean-to move double rotation
Medium Severity
isToolOwnedRotation now stands down for lean-to placement, and for moving doors, windows, and items, but not for a moving lean-to-extension. The registry move tool still handles R/T, so a lean-to move with an active selection can apply both the global and move-tool rotations.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 1f790d3. Configure here.
| depth: quarterTurn ? target.width : target.depth, | ||
| position: [0, 0, 0], | ||
| rotation: quarterTurn ? Math.PI / 2 : 0, | ||
| }) |
There was a problem hiding this comment.
Room roof commit ignores zero curb
High Severity
Room footprint commit spreads defaults after wallHeight: 0, so a preset or seeded wallHeight overrides the flush seating the ghost uses. The roof is already elevated with resolveRoofFootprintElevation, so the committed roof can sit higher than the green room preview with an extra curb wall.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit a2a4c85. Configure here.
| setPreviewedConicalWallId(null) | ||
| setPreviewSelectedIds([]) | ||
| setSelection({ selectedIds: [wall.id] }) | ||
| }, |
There was a problem hiding this comment.
Wall conical placement never commits
High Severity
In conical walls mode, RoofTool only selects the curved wall on click and never creates a roof. Creation lives in a BuildTab selection useEffect, so walls placement fails without that app shell, and entering conical+walls with a wall already selected can create a roof with no new click.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit a2a4c85. Configure here.
| function pointIsInsideSegment(segment: RoofSegmentNode, point: readonly [number, number]): boolean { | ||
| if (segment.roofType === 'conical') { | ||
| return Math.hypot(point[0], point[1]) <= segment.width / 2 + FOOTPRINT_EPSILON | ||
| } |
There was a problem hiding this comment.
Conical host ignores sector sweep
Medium Severity
pointIsInsideSegment treats conical hosts as full disks and never checks conicalStartAngle / conicalSweepAngle. A drawn cone can be accepted as roof-supported when its circle only overlaps the missing part of a clipped sector, so auto/roof placement can mount on empty air.
Reviewed by Cursor Bugbot for commit a2a4c85. Configure here.
| ) | ||
| patchedNodes[windowId] = window | ||
| patchedNodes[id] = { ...dormer, children: [...children, window.id] } | ||
| } |
There was a problem hiding this comment.
Dormer windows resurrect on load
Medium Severity
The dormer load migration always inserts a default hosted window whenever no window child is present. That conflates legacy inline-window promotion with intentional empty dormers, so deleting every dormer window and reloading the scene (or applying a snapshot) quietly recreates one.
Reviewed by Cursor Bugbot for commit a15ca02. Configure here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
There are 8 total unresolved issues (including 6 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 7fb4340. Configure here.
| draftPreview.setRoofDraftQuarterTurn(false) | ||
| } | ||
| }, [currentLevelId, setSelection]) | ||
| }, [currentLevelId, footprintSource, nodes, roofType, setPreviewSelectedIds, setSelection]) |
There was a problem hiding this comment.
Roof draft reset on scene updates
High Severity
The roof tool’s placement useEffect lists the whole scene nodes map as a dependency, and its cleanup clears corner1Ref plus the floorplan roof draft. Any scene mutation while a two-click draw is in progress tears down and remounts the listeners, so the first corner is lost and the ghost resets mid-placement.
Reviewed by Cursor Bugbot for commit 7fb4340. Configure here.
| mode, | ||
| selectedCurvedWall, | ||
| setSelection, | ||
| ]) |
There was a problem hiding this comment.
Conical wall create tied to Build tab
High Severity
Curved-wall conical roofs are only created in the Build tab selection effect, while the roof tool’s wall handler only selects the wall. The sidebar mounts a single active panel, so leaving Build unmounts that effect: wall clicks no longer place a roof, and returning to Build with the same wall selected can create a duplicate because conicalSourceRef resets.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 7fb4340. Configure here.


What does this PR do?
Improves roof, dormer, lean-to, and window placement so cursor rays, ghosts, and committed items resolve to the exact pointer location, including centered freestanding lean-to placement. Transient placement previews now use live overrides during pointer movement, snapping follows the active mode, and shared helpers stay within the correct architecture boundaries.
How to test
bun run checkandbun run check-types.bun test packages/nodes/src/lean-to-extension packages/nodes/src/dormer packages/nodes/src/window packages/editor/src/components/tools/shared/pointer-support-cap.test.ts.bun dev, place and move a freestanding lean-to, dormer window, wall window, and roof-face window; confirm the ghost and final item stay centered under the cursor and follow the same raycast position.The package builds for core, nodes, and viewer pass. The full app production build is currently blocked by the unchanged
react-scandependency's webpack export mismatch; Turbopack also hits an environment process-permission error.Screenshots / screen recording
Not included — visual verification should be performed with the interactive placement steps above.
Checklist
bun devbun checkto verify)mainbranchNote
Medium Risk
Touches scene schema, load-time dormer migration, roof overlap/clipping, and placement raycasting—bugs could misplace roofs or alter existing scenes on open; no auth or payment paths.
Overview
Adds conical as a roof type with sector geometry, surface height, and coverage controls, plus
RoofSupportso roofs can sit on a level or mount to another roof segment.resolveConicalRoofPlacementdrives commit/preview height and support; overlap logic treats mounted conicals differently from ground-placed ones.The Build tab roof panel now picks roof type and footprint source (room vs curved wall vs draw), persists them in
toolDefaults.roof, and can auto-create a conical sector when a curved wall is selected under wall footprint mode.The roof tool gains room hover/click placement, conical wall pick + ghost, P to cycle placement mode (auto/ground/roof), and draw-mode behavior gated by footprint source. Shared
roof-footprinthelpers cover room fitting and multi-level wall tops.Dormers promote legacy inline windows to hosted
WindowNodechildren on dormer faces (scene load migration). Lean-to schema expands for canopy forms and slab/conical hosts (nodes package changes implied by PR description).Editor fixes: orthographic pointer rays for top-surface support, live overrides during item placement rotation, connection snap on resize (Alt bypass), handle portalTarget / visibility filtering, read-only gating for action menu and panels, and a polygon union fix for point-touching branches. Root
checksscript runs biome + types.Reviewed by Cursor Bugbot for commit 7fb4340. Bugbot is set up for automated code reviews on this repo. Configure here.