From cb5cfc7e40acbb9cda024041a3cf766d92f2e7af Mon Sep 17 00:00:00 2001 From: Aymeric Rabot Date: Thu, 9 Jul 2026 13:09:31 +0200 Subject: [PATCH 1/2] fix(editor): compass sync + fast align-to-north MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove CSS transition on compass needle SVG so rotation tracks exactly without rubber-band lag (was transition-transform 150ms) - Drive compass needle imperatively via ref when floorplan panel is hidden (3D-only mode), avoiding per-frame React re-renders of the full SVG while keeping the needle live - Align-to-north in 3D-only mode derives from latestNavigationSyncPose instead of stale viewport, publishes directly to the 3D camera - Temporarily lower camera-controls smoothTime from 0.25s to 0.18s during 2D-origin transitions (SmoothDamp τ≈90ms matches the 2D exponential decay), restoring on completion or cancellation Co-Authored-By: Claude Fable 5 --- .../editor/custom-camera-controls.tsx | 16 ++++++++ .../src/components/editor/floorplan-panel.tsx | 41 ++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/packages/editor/src/components/editor/custom-camera-controls.tsx b/packages/editor/src/components/editor/custom-camera-controls.tsx index a930a75b4..d88cebcbc 100644 --- a/packages/editor/src/components/editor/custom-camera-controls.tsx +++ b/packages/editor/src/components/editor/custom-camera-controls.tsx @@ -369,10 +369,15 @@ export const CustomCameraControls = () => { const lastPublishedNavigationSync = useRef(null) const pendingFloorplanNavigationPose = useRef(null) const lastApplied2dNavigationRevision = useRef(0) + const savedSmoothTimeRef = useRef(null) const maxPolarAngle = !isPreviewMode && allowUndergroundCamera ? DEBUG_MAX_POLAR_ANGLE : DEFAULT_MAX_POLAR_ANGLE const clearPendingFloorplanNavigationPose = useCallback(() => { pendingFloorplanNavigationPose.current = null + if (savedSmoothTimeRef.current !== null && controls.current) { + controls.current.smoothTime = savedSmoothTimeRef.current + savedSmoothTimeRef.current = null + } }, []) const camera = useThree((state) => state.camera) @@ -478,6 +483,13 @@ export const CustomCameraControls = () => { Math.abs(viewWidthUpdate.viewWidth - pose.viewWidth) >= NAVIGATION_SYNC_VIEW_WIDTH_EPSILON, } + // Match 3D settle time to 2D exponential decay (τ=90ms). SmoothDamp's + // effective time constant is smoothTime/2, so smoothTime=0.18 gives + // τ≈90ms and visual convergence in ~350-400ms, matching the 2D panel. + if (savedSmoothTimeRef.current === null) { + savedSmoothTimeRef.current = control.smoothTime + } + control.smoothTime = 0.18 control.moveTo(pose.target[0], pose.target[1], pose.target[2], true) control.rotateTo(targetAzimuth, control.polarAngle, true) applyCameraViewWidth(control, viewWidthUpdate) @@ -500,6 +512,10 @@ export const CustomCameraControls = () => { ) { lastPublishedNavigationSync.current = pendingFloorplanPose pendingFloorplanNavigationPose.current = null + if (savedSmoothTimeRef.current !== null && controls.current) { + controls.current.smoothTime = savedSmoothTimeRef.current + savedSmoothTimeRef.current = null + } if (pendingFloorplanPose.publishOnComplete) { useEditor.getState().publishNavigationSyncPose({ source: '3d', diff --git a/packages/editor/src/components/editor/floorplan-panel.tsx b/packages/editor/src/components/editor/floorplan-panel.tsx index dd29ee8d4..8ddfaae2f 100644 --- a/packages/editor/src/components/editor/floorplan-panel.tsx +++ b/packages/editor/src/components/editor/floorplan-panel.tsx @@ -457,9 +457,11 @@ type GuideHandleHintAnchor = { function FloorplanCompassButton({ northRotationDeg, onAlignNorth, + needleRef, }: { northRotationDeg: number onAlignNorth: () => void + needleRef?: React.RefObject }) { return ( @@ -480,7 +482,8 @@ function FloorplanCompassButton({