editor: improve floorplan navigation performance#541
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>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 999baaa. Configure here.
| return | ||
| } | ||
| if (latestCameraPose) publishCameraNavigationPose(latestCameraPose) | ||
| }, |
There was a problem hiding this comment.
Align-north ignored in 3D-only
High Severity
When in 3D view, the FloorplanCameraSyncBridge is inactive and drops incoming 2D navigation poses. This prevents the 3D camera from updating for actions like 'align-to-north', causing a desync where the compass animates but the camera stays put. Any pending 2D poses are never applied, leading to stale camera state.
Reviewed by Cursor Bugbot for commit 999baaa. Configure here.
| bridgeRef.current = createFloorplanCameraSyncBridge({ | ||
| active, | ||
| applyCameraPose: (pose) => emitter.emit('camera-controls:apply-pose', pose), | ||
| publishNavigationPose: (pose) => liveCameraNavigation.publish(pose), |
There was a problem hiding this comment.
Export loses 3D camera azimuth
Medium Severity
Floorplan PDF exports show outdated camera rotation because live 3D navigation updates are published to liveCameraNavigation but don't update useEditor.navigationSyncPose, which the export relies on. This occurs after orbiting or panning in 3D or split view.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 999baaa. Configure here.
| layoutOverrides: annotationLayoutOverrides, | ||
| }) | ||
| setPreflightIssues(preflightIssues) | ||
| appliedLayoutInputsRef.current = layoutInputs |
There was a problem hiding this comment.
Zoom skips annotation collision layout
High Severity
The new annotation layout system in FloorplanAnnotationLayoutResolver doesn't react to viewport scale changes. After zooming, label plates, handle sizes, and collision offsets can become stale, causing incorrect collision resolution and overlapping labels.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 999baaa. Configure here.


What does this PR do?
How to test
bun run check,bun run check-types, andbun run build.bun run dev, open a heavy scene in 2D or split view, then pan, zoom, and rotate; verify navigation stays responsive and labels settle readable with a white background.Screenshots / screen recording
Not included yet — this is a performance-focused interaction change; a recording can be added during review.
Checklist
bun devbun checkto verify)mainbranchNote
Medium Risk
Touches 2D/3D camera synchronization and large floorplan interaction paths; behavior is heavily unit-tested but regressions would show as desynced views or label layout glitches.
Overview
Improves floorplan pan/zoom/rotate responsiveness by doing less work on every frame while keeping 2D/3D camera alignment.
2D/3D camera sync moves out of
custom-camera-controls: the 3D camera now publishes poses through a camera pose store instead ofcamera-controls:pose/ per-framenavigationSyncPoseecho logic. A newfloorplan-camera-syncbridge converts poses, suppresses tiny updates and sub-degree rotation noise, skips sync in 3D-only mode, and applies 2D poses viacamera-controls:apply-pose. The floorplan panel consumes navigation through a transient channel pluscreateFloorplanNavigationSyncScheduler, which drives imperative SVG during motion and commits React viewport state only after the stream settles.Annotation layout replaces the SVG MutationObserver with subscriptions to scene/transform/override changes (rAF-coalesced). Rotation-only scene changes update label transforms imperatively (new label
data-*+updateSvgFloorplanLabelOrientations) without rerunning collision layout; full collision passes use cached label node lists.FloorplanRenderProvidersplits context so zoom scale can update via a stablegetUnitsPerPixelreader without churning registry consumers.Navigation sync pose writes also go through a small navigation sync pose store for selector-based subscribers alongside
use-editorstate.Reviewed by Cursor Bugbot for commit 999baaa. Bugbot is set up for automated code reviews on this repo. Configure here.