Stabilize split view navigation sync
This commit is contained in:
@@ -43,6 +43,11 @@ type CameraPoseSnapshot = {
|
|||||||
position: [number, number, number]
|
position: [number, number, number]
|
||||||
target: [number, number, number]
|
target: [number, number, number]
|
||||||
}
|
}
|
||||||
|
type NavigationCameraPoseSnapshot = {
|
||||||
|
target: [number, number, number]
|
||||||
|
azimuth: number
|
||||||
|
viewWidth: number
|
||||||
|
}
|
||||||
|
|
||||||
function writeVectorTuple(tuple: [number, number, number], vector: Vector3) {
|
function writeVectorTuple(tuple: [number, number, number], vector: Vector3) {
|
||||||
tuple[0] = vector.x
|
tuple[0] = vector.x
|
||||||
@@ -115,6 +120,25 @@ function getCameraViewWidth(camera: Camera, distance: number, size: CameraViewpo
|
|||||||
return Math.max(0.001, distance)
|
return Math.max(0.001, distance)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAngleDeltaRadians(a: number, b: number) {
|
||||||
|
return Math.atan2(Math.sin(a - b), Math.cos(a - b))
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCameraAtNavigationPose(
|
||||||
|
pose: NavigationCameraPoseSnapshot,
|
||||||
|
target: Vector3,
|
||||||
|
azimuth: number,
|
||||||
|
viewWidth: number,
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
Math.abs(pose.target[0] - target.x) < NAVIGATION_SYNC_POSITION_EPSILON &&
|
||||||
|
Math.abs(pose.target[1] - target.y) < NAVIGATION_SYNC_POSITION_EPSILON &&
|
||||||
|
Math.abs(pose.target[2] - target.z) < NAVIGATION_SYNC_POSITION_EPSILON &&
|
||||||
|
Math.abs(getAngleDeltaRadians(pose.azimuth, azimuth)) < NAVIGATION_SYNC_AZIMUTH_EPSILON &&
|
||||||
|
Math.abs(pose.viewWidth - viewWidth) < NAVIGATION_SYNC_VIEW_WIDTH_EPSILON
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function getCameraDistanceForViewWidth(
|
function getCameraDistanceForViewWidth(
|
||||||
camera: Camera,
|
camera: Camera,
|
||||||
viewWidth: number,
|
viewWidth: number,
|
||||||
@@ -233,11 +257,8 @@ export const CustomCameraControls = () => {
|
|||||||
)
|
)
|
||||||
const currentLevelId = selection.levelId
|
const currentLevelId = selection.levelId
|
||||||
const firstLoad = useRef(true)
|
const firstLoad = useRef(true)
|
||||||
const lastPublishedNavigationSync = useRef<{
|
const lastPublishedNavigationSync = useRef<NavigationCameraPoseSnapshot | null>(null)
|
||||||
target: [number, number, number]
|
const pendingFloorplanNavigationPose = useRef<NavigationCameraPoseSnapshot | null>(null)
|
||||||
azimuth: number
|
|
||||||
viewWidth: number
|
|
||||||
} | null>(null)
|
|
||||||
const lastApplied2dNavigationRevision = useRef(0)
|
const lastApplied2dNavigationRevision = useRef(0)
|
||||||
const maxPolarAngle =
|
const maxPolarAngle =
|
||||||
!isPreviewMode && allowUndergroundCamera ? DEBUG_MAX_POLAR_ANGLE : DEFAULT_MAX_POLAR_ANGLE
|
!isPreviewMode && allowUndergroundCamera ? DEBUG_MAX_POLAR_ANGLE : DEFAULT_MAX_POLAR_ANGLE
|
||||||
@@ -326,6 +347,11 @@ export const CustomCameraControls = () => {
|
|||||||
if (!control) return
|
if (!control) return
|
||||||
|
|
||||||
lastApplied2dNavigationRevision.current = pose.revision
|
lastApplied2dNavigationRevision.current = pose.revision
|
||||||
|
pendingFloorplanNavigationPose.current = {
|
||||||
|
target: [...pose.target],
|
||||||
|
azimuth: pose.azimuth,
|
||||||
|
viewWidth: pose.viewWidth,
|
||||||
|
}
|
||||||
control.moveTo(pose.target[0], pose.target[1], pose.target[2], true)
|
control.moveTo(pose.target[0], pose.target[1], pose.target[2], true)
|
||||||
control.rotateTo(pose.azimuth, control.polarAngle, true)
|
control.rotateTo(pose.azimuth, control.polarAngle, true)
|
||||||
applyCameraViewWidth(control, camera, pose.viewWidth, viewportSize)
|
applyCameraViewWidth(control, camera, pose.viewWidth, viewportSize)
|
||||||
@@ -339,13 +365,27 @@ export const CustomCameraControls = () => {
|
|||||||
controls.current.getSpherical(syncSpherical, false)
|
controls.current.getSpherical(syncSpherical, false)
|
||||||
const viewWidth = getCameraViewWidth(camera, syncSpherical.radius, viewportSize)
|
const viewWidth = getCameraViewWidth(camera, syncSpherical.radius, viewportSize)
|
||||||
|
|
||||||
|
const pendingFloorplanPose = pendingFloorplanNavigationPose.current
|
||||||
|
if (pendingFloorplanPose) {
|
||||||
|
// The camera is still damping toward a 2D-originated pose; do not echo
|
||||||
|
// intermediate 3D poses back into the floorplan.
|
||||||
|
if (
|
||||||
|
isCameraAtNavigationPose(pendingFloorplanPose, syncTarget, syncSpherical.theta, viewWidth)
|
||||||
|
) {
|
||||||
|
lastPublishedNavigationSync.current = pendingFloorplanPose
|
||||||
|
pendingFloorplanNavigationPose.current = null
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const previous = lastPublishedNavigationSync.current
|
const previous = lastPublishedNavigationSync.current
|
||||||
if (
|
if (
|
||||||
previous &&
|
previous &&
|
||||||
Math.abs(previous.target[0] - syncTarget.x) < NAVIGATION_SYNC_POSITION_EPSILON &&
|
Math.abs(previous.target[0] - syncTarget.x) < NAVIGATION_SYNC_POSITION_EPSILON &&
|
||||||
Math.abs(previous.target[1] - syncTarget.y) < NAVIGATION_SYNC_POSITION_EPSILON &&
|
Math.abs(previous.target[1] - syncTarget.y) < NAVIGATION_SYNC_POSITION_EPSILON &&
|
||||||
Math.abs(previous.target[2] - syncTarget.z) < NAVIGATION_SYNC_POSITION_EPSILON &&
|
Math.abs(previous.target[2] - syncTarget.z) < NAVIGATION_SYNC_POSITION_EPSILON &&
|
||||||
Math.abs(previous.azimuth - syncSpherical.theta) < NAVIGATION_SYNC_AZIMUTH_EPSILON &&
|
Math.abs(getAngleDeltaRadians(previous.azimuth, syncSpherical.theta)) <
|
||||||
|
NAVIGATION_SYNC_AZIMUTH_EPSILON &&
|
||||||
Math.abs(previous.viewWidth - viewWidth) < NAVIGATION_SYNC_VIEW_WIDTH_EPSILON
|
Math.abs(previous.viewWidth - viewWidth) < NAVIGATION_SYNC_VIEW_WIDTH_EPSILON
|
||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
@@ -564,6 +604,7 @@ export const CustomCameraControls = () => {
|
|||||||
|
|
||||||
const onPointerDown = (event: PointerEvent) => {
|
const onPointerDown = (event: PointerEvent) => {
|
||||||
if (!(event.target instanceof Node) || !gl.domElement.contains(event.target)) return
|
if (!(event.target instanceof Node) || !gl.domElement.contains(event.target)) return
|
||||||
|
pendingFloorplanNavigationPose.current = null
|
||||||
if (event.button !== 1 && !(event.button === 0 && keyState.space)) return
|
if (event.button !== 1 && !(event.button === 0 && keyState.space)) return
|
||||||
|
|
||||||
panPointerId = event.pointerId
|
panPointerId = event.pointerId
|
||||||
@@ -571,6 +612,10 @@ export const CustomCameraControls = () => {
|
|||||||
updateNavigationCursor()
|
updateNavigationCursor()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onWheel = () => {
|
||||||
|
pendingFloorplanNavigationPose.current = null
|
||||||
|
}
|
||||||
|
|
||||||
const onPointerUp = (event: PointerEvent) => {
|
const onPointerUp = (event: PointerEvent) => {
|
||||||
if (panPointerId === null) return
|
if (panPointerId === null) return
|
||||||
if (event.type !== 'pointercancel' && event.pointerId !== panPointerId) return
|
if (event.type !== 'pointercancel' && event.pointerId !== panPointerId) return
|
||||||
@@ -595,6 +640,7 @@ export const CustomCameraControls = () => {
|
|||||||
window.addEventListener('pointerup', onPointerUp, true)
|
window.addEventListener('pointerup', onPointerUp, true)
|
||||||
window.addEventListener('pointercancel', onPointerUp, true)
|
window.addEventListener('pointercancel', onPointerUp, true)
|
||||||
window.addEventListener('blur', onBlur)
|
window.addEventListener('blur', onBlur)
|
||||||
|
gl.domElement.addEventListener('wheel', onWheel, { passive: true })
|
||||||
updateConfig()
|
updateConfig()
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -604,6 +650,7 @@ export const CustomCameraControls = () => {
|
|||||||
window.removeEventListener('pointerup', onPointerUp, true)
|
window.removeEventListener('pointerup', onPointerUp, true)
|
||||||
window.removeEventListener('pointercancel', onPointerUp, true)
|
window.removeEventListener('pointercancel', onPointerUp, true)
|
||||||
window.removeEventListener('blur', onBlur)
|
window.removeEventListener('blur', onBlur)
|
||||||
|
gl.domElement.removeEventListener('wheel', onWheel)
|
||||||
clearNavigationCursor()
|
clearNavigationCursor()
|
||||||
}
|
}
|
||||||
}, [cameraMode, gl, isPreviewMode, isFirstPersonMode])
|
}, [cameraMode, gl, isPreviewMode, isFirstPersonMode])
|
||||||
|
|||||||
@@ -217,18 +217,43 @@ const FLOORPLAN_GUIDE_ROTATION_FINE_SNAP_DEGREES = 1
|
|||||||
const FLOORPLAN_SITE_COLOR = '#10b981'
|
const FLOORPLAN_SITE_COLOR = '#10b981'
|
||||||
const FLOORPLAN_VIEW_ROTATION_DEG = 90
|
const FLOORPLAN_VIEW_ROTATION_DEG = 90
|
||||||
const FLOORPLAN_ROTATION_DEGREES_PER_PIXEL = 0.35
|
const FLOORPLAN_ROTATION_DEGREES_PER_PIXEL = 0.35
|
||||||
|
const FLOORPLAN_VIEW_ANIMATION_TIME_CONSTANT_MS = 90
|
||||||
|
const FLOORPLAN_VIEW_ANIMATION_EPSILON = 0.0005
|
||||||
|
const FLOORPLAN_ROTATION_ANIMATION_EPSILON_DEG = 0.01
|
||||||
type FloorplanViewport = {
|
type FloorplanViewport = {
|
||||||
centerX: number
|
centerX: number
|
||||||
centerY: number
|
centerY: number
|
||||||
width: number
|
width: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FloorplanNavigationViewOptions = {
|
||||||
|
smooth?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type FloorplanViewAnimationTarget = {
|
||||||
|
viewport: FloorplanViewport
|
||||||
|
userRotationDeg: number
|
||||||
|
lastTimestamp: number | null
|
||||||
|
}
|
||||||
|
|
||||||
function floorplanViewportEquals(a: FloorplanViewport | null, b: FloorplanViewport | null) {
|
function floorplanViewportEquals(a: FloorplanViewport | null, b: FloorplanViewport | null) {
|
||||||
if (a === b) return true
|
if (a === b) return true
|
||||||
if (!(a && b)) return false
|
if (!(a && b)) return false
|
||||||
return a.centerX === b.centerX && a.centerY === b.centerY && a.width === b.width
|
return a.centerX === b.centerX && a.centerY === b.centerY && a.width === b.width
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function floorplanViewportWithinEpsilon(
|
||||||
|
a: FloorplanViewport,
|
||||||
|
b: FloorplanViewport,
|
||||||
|
epsilon: number,
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
Math.abs(a.centerX - b.centerX) < epsilon &&
|
||||||
|
Math.abs(a.centerY - b.centerY) < epsilon &&
|
||||||
|
Math.abs(a.width - b.width) < epsilon
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
type SvgPoint = {
|
type SvgPoint = {
|
||||||
x: number
|
x: number
|
||||||
y: number
|
y: number
|
||||||
@@ -1684,13 +1709,13 @@ function nearestEquivalentDegrees(angle: number, reference: number) {
|
|||||||
|
|
||||||
function floorplanRotationFromCameraAzimuth(azimuth: number, reference: number) {
|
function floorplanRotationFromCameraAzimuth(azimuth: number, reference: number) {
|
||||||
return nearestEquivalentDegrees(
|
return nearestEquivalentDegrees(
|
||||||
FLOORPLAN_VIEW_ROTATION_DEG - radiansToDegrees(azimuth),
|
radiansToDegrees(azimuth) - FLOORPLAN_VIEW_ROTATION_DEG,
|
||||||
reference,
|
reference,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function cameraAzimuthFromFloorplanRotation(rotationDeg: number) {
|
function cameraAzimuthFromFloorplanRotation(rotationDeg: number) {
|
||||||
return degreesToRadians(FLOORPLAN_VIEW_ROTATION_DEG - rotationDeg)
|
return degreesToRadians(rotationDeg + FLOORPLAN_VIEW_ROTATION_DEG)
|
||||||
}
|
}
|
||||||
|
|
||||||
function floorplanLocalToWorldPoint(
|
function floorplanLocalToWorldPoint(
|
||||||
@@ -4272,6 +4297,8 @@ export function FloorplanPanel() {
|
|||||||
const latestFloorplanUserRotationDegRef = useRef(0)
|
const latestFloorplanUserRotationDegRef = useRef(0)
|
||||||
const latestViewportRef = useRef<FloorplanViewport | null>(null)
|
const latestViewportRef = useRef<FloorplanViewport | null>(null)
|
||||||
const latestFittedViewportRef = useRef<FloorplanViewport | null>(null)
|
const latestFittedViewportRef = useRef<FloorplanViewport | null>(null)
|
||||||
|
const floorplanViewAnimationFrameRef = useRef<number | null>(null)
|
||||||
|
const floorplanViewAnimationTargetRef = useRef<FloorplanViewAnimationTarget | null>(null)
|
||||||
const latestNavigationSyncPoseRef = useRef<NavigationSyncPose | null>(
|
const latestNavigationSyncPoseRef = useRef<NavigationSyncPose | null>(
|
||||||
useEditor.getState().navigationSyncPose,
|
useEditor.getState().navigationSyncPose,
|
||||||
)
|
)
|
||||||
@@ -5626,8 +5653,98 @@ export function FloorplanPanel() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const applyFloorplanNavigationState = useCallback(
|
||||||
|
(nextViewport: FloorplanViewport, userRotationDeg: number) => {
|
||||||
|
hasUserAdjustedViewportRef.current = true
|
||||||
|
latestFloorplanUserRotationDegRef.current = userRotationDeg
|
||||||
|
latestViewportRef.current = nextViewport
|
||||||
|
setFloorplanUserRotationDeg((current) =>
|
||||||
|
current === userRotationDeg ? current : userRotationDeg,
|
||||||
|
)
|
||||||
|
setViewport((current) =>
|
||||||
|
floorplanViewportEquals(current, nextViewport) ? current : nextViewport,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
|
||||||
|
const stopFloorplanViewAnimation = useCallback(() => {
|
||||||
|
if (floorplanViewAnimationFrameRef.current !== null) {
|
||||||
|
window.cancelAnimationFrame(floorplanViewAnimationFrameRef.current)
|
||||||
|
floorplanViewAnimationFrameRef.current = null
|
||||||
|
}
|
||||||
|
floorplanViewAnimationTargetRef.current = null
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const animateFloorplanNavigationState = useCallback(
|
||||||
|
(nextViewport: FloorplanViewport, userRotationDeg: number) => {
|
||||||
|
floorplanViewAnimationTargetRef.current = {
|
||||||
|
viewport: nextViewport,
|
||||||
|
userRotationDeg,
|
||||||
|
lastTimestamp: floorplanViewAnimationTargetRef.current?.lastTimestamp ?? null,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (floorplanViewAnimationFrameRef.current !== null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const step = (timestamp: number) => {
|
||||||
|
const target = floorplanViewAnimationTargetRef.current
|
||||||
|
if (!target) {
|
||||||
|
floorplanViewAnimationFrameRef.current = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentViewport = latestViewportRef.current ?? target.viewport
|
||||||
|
const currentRotationDeg = latestFloorplanUserRotationDegRef.current
|
||||||
|
const deltaMs = target.lastTimestamp === null ? 16.67 : timestamp - target.lastTimestamp
|
||||||
|
target.lastTimestamp = timestamp
|
||||||
|
const alpha = 1 - Math.exp(-deltaMs / FLOORPLAN_VIEW_ANIMATION_TIME_CONSTANT_MS)
|
||||||
|
const targetRotationDeg = nearestEquivalentDegrees(
|
||||||
|
target.userRotationDeg,
|
||||||
|
currentRotationDeg,
|
||||||
|
)
|
||||||
|
const nextRotationDeg =
|
||||||
|
currentRotationDeg + (targetRotationDeg - currentRotationDeg) * alpha
|
||||||
|
const animatedViewport = {
|
||||||
|
centerX:
|
||||||
|
currentViewport.centerX + (target.viewport.centerX - currentViewport.centerX) * alpha,
|
||||||
|
centerY:
|
||||||
|
currentViewport.centerY + (target.viewport.centerY - currentViewport.centerY) * alpha,
|
||||||
|
width: currentViewport.width + (target.viewport.width - currentViewport.width) * alpha,
|
||||||
|
}
|
||||||
|
|
||||||
|
const isComplete =
|
||||||
|
floorplanViewportWithinEpsilon(
|
||||||
|
animatedViewport,
|
||||||
|
target.viewport,
|
||||||
|
FLOORPLAN_VIEW_ANIMATION_EPSILON,
|
||||||
|
) &&
|
||||||
|
Math.abs(targetRotationDeg - nextRotationDeg) < FLOORPLAN_ROTATION_ANIMATION_EPSILON_DEG
|
||||||
|
|
||||||
|
if (isComplete) {
|
||||||
|
applyFloorplanNavigationState(target.viewport, targetRotationDeg)
|
||||||
|
floorplanViewAnimationTargetRef.current = null
|
||||||
|
floorplanViewAnimationFrameRef.current = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
applyFloorplanNavigationState(animatedViewport, nextRotationDeg)
|
||||||
|
floorplanViewAnimationFrameRef.current = window.requestAnimationFrame(step)
|
||||||
|
}
|
||||||
|
|
||||||
|
floorplanViewAnimationFrameRef.current = window.requestAnimationFrame(step)
|
||||||
|
},
|
||||||
|
[applyFloorplanNavigationState],
|
||||||
|
)
|
||||||
|
|
||||||
const applyFloorplanNavigationView = useCallback(
|
const applyFloorplanNavigationView = useCallback(
|
||||||
(localCenter: SvgPoint, userRotationDeg: number, viewWidth?: number) => {
|
(
|
||||||
|
localCenter: SvgPoint,
|
||||||
|
userRotationDeg: number,
|
||||||
|
viewWidth?: number,
|
||||||
|
options?: FloorplanNavigationViewOptions,
|
||||||
|
) => {
|
||||||
const currentViewport = latestViewportRef.current ?? latestFittedViewportRef.current
|
const currentViewport = latestViewportRef.current ?? latestFittedViewportRef.current
|
||||||
if (!currentViewport) {
|
if (!currentViewport) {
|
||||||
return
|
return
|
||||||
@@ -5647,19 +5764,34 @@ export function FloorplanPanel() {
|
|||||||
width: nextWidth,
|
width: nextWidth,
|
||||||
}
|
}
|
||||||
|
|
||||||
hasUserAdjustedViewportRef.current = true
|
if (options?.smooth) {
|
||||||
latestFloorplanUserRotationDegRef.current = userRotationDeg
|
animateFloorplanNavigationState(nextViewport, userRotationDeg)
|
||||||
latestViewportRef.current = nextViewport
|
} else {
|
||||||
setFloorplanUserRotationDeg((current) =>
|
stopFloorplanViewAnimation()
|
||||||
current === userRotationDeg ? current : userRotationDeg,
|
applyFloorplanNavigationState(nextViewport, userRotationDeg)
|
||||||
)
|
}
|
||||||
setViewport((current) =>
|
|
||||||
floorplanViewportEquals(current, nextViewport) ? current : nextViewport,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
[buildingRotationDeg],
|
[
|
||||||
|
animateFloorplanNavigationState,
|
||||||
|
applyFloorplanNavigationState,
|
||||||
|
buildingRotationDeg,
|
||||||
|
stopFloorplanViewAnimation,
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const smoothFloorplanNavigationView = useCallback(
|
||||||
|
(localCenter: SvgPoint, userRotationDeg: number, viewWidth?: number) => {
|
||||||
|
applyFloorplanNavigationView(localCenter, userRotationDeg, viewWidth, { smooth: true })
|
||||||
|
},
|
||||||
|
[applyFloorplanNavigationView],
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
stopFloorplanViewAnimation()
|
||||||
|
}
|
||||||
|
}, [stopFloorplanViewAnimation])
|
||||||
|
|
||||||
const syncFloorplanViewportToNavigationPose = useCallback(
|
const syncFloorplanViewportToNavigationPose = useCallback(
|
||||||
(pose: NavigationSyncPose) => {
|
(pose: NavigationSyncPose) => {
|
||||||
if (floorplanRotationStateRef.current) {
|
if (floorplanRotationStateRef.current) {
|
||||||
@@ -5764,6 +5896,7 @@ export function FloorplanPanel() {
|
|||||||
// to the current scene.
|
// to the current scene.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isFloorplanOpen) {
|
if (!isFloorplanOpen) {
|
||||||
|
stopFloorplanViewAnimation()
|
||||||
floorplanSpacePanPressedRef.current = false
|
floorplanSpacePanPressedRef.current = false
|
||||||
panStateRef.current = null
|
panStateRef.current = null
|
||||||
floorplanRotationStateRef.current = null
|
floorplanRotationStateRef.current = null
|
||||||
@@ -5775,13 +5908,14 @@ export function FloorplanPanel() {
|
|||||||
setMeasuredSceneBBox(null)
|
setMeasuredSceneBBox(null)
|
||||||
|
|
||||||
if (!latestNavigationSyncPoseRef.current) {
|
if (!latestNavigationSyncPoseRef.current) {
|
||||||
|
stopFloorplanViewAnimation()
|
||||||
hasUserAdjustedViewportRef.current = false
|
hasUserAdjustedViewportRef.current = false
|
||||||
latestFloorplanUserRotationDegRef.current = 0
|
latestFloorplanUserRotationDegRef.current = 0
|
||||||
latestViewportRef.current = null
|
latestViewportRef.current = null
|
||||||
setFloorplanUserRotationDeg(0)
|
setFloorplanUserRotationDeg(0)
|
||||||
setViewport(null)
|
setViewport(null)
|
||||||
}
|
}
|
||||||
}, [isFloorplanOpen])
|
}, [isFloorplanOpen, stopFloorplanViewAnimation])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const levelChanged = previousLevelIdRef.current !== (levelId ?? null)
|
const levelChanged = previousLevelIdRef.current !== (levelId ?? null)
|
||||||
@@ -5789,6 +5923,7 @@ export function FloorplanPanel() {
|
|||||||
if (levelChanged) {
|
if (levelChanged) {
|
||||||
previousLevelIdRef.current = levelId ?? null
|
previousLevelIdRef.current = levelId ?? null
|
||||||
if (!latestNavigationSyncPoseRef.current) {
|
if (!latestNavigationSyncPoseRef.current) {
|
||||||
|
stopFloorplanViewAnimation()
|
||||||
hasUserAdjustedViewportRef.current = false
|
hasUserAdjustedViewportRef.current = false
|
||||||
latestFloorplanUserRotationDegRef.current = 0
|
latestFloorplanUserRotationDegRef.current = 0
|
||||||
latestViewportRef.current = null
|
latestViewportRef.current = null
|
||||||
@@ -5824,6 +5959,7 @@ export function FloorplanPanel() {
|
|||||||
movingFenceEndpoint,
|
movingFenceEndpoint,
|
||||||
movingNode,
|
movingNode,
|
||||||
siteVertexDragState,
|
siteVertexDragState,
|
||||||
|
stopFloorplanViewAnimation,
|
||||||
])
|
])
|
||||||
|
|
||||||
const viewBox = useMemo(() => {
|
const viewBox = useMemo(() => {
|
||||||
@@ -6441,11 +6577,6 @@ export function FloorplanPanel() {
|
|||||||
guideTransformDraftRef.current = guideTransformDraft
|
guideTransformDraftRef.current = guideTransformDraft
|
||||||
}, [guideTransformDraft])
|
}, [guideTransformDraft])
|
||||||
|
|
||||||
const updateViewport = useCallback((nextViewport: FloorplanViewport) => {
|
|
||||||
hasUserAdjustedViewportRef.current = true
|
|
||||||
setViewport(nextViewport)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const floorplanGridLocalY = useMemo(() => {
|
const floorplanGridLocalY = useMemo(() => {
|
||||||
if (movingNode?.type === 'item' || movingNode?.type === 'spawn') {
|
if (movingNode?.type === 'item' || movingNode?.type === 'spawn') {
|
||||||
return movingNode.position[1]
|
return movingNode.position[1]
|
||||||
@@ -6661,6 +6792,11 @@ export function FloorplanPanel() {
|
|||||||
}
|
}
|
||||||
const localCenter = rotateSvgPoint(nextCenterSvg, -floorplanSceneRotationDeg)
|
const localCenter = rotateSvgPoint(nextCenterSvg, -floorplanSceneRotationDeg)
|
||||||
|
|
||||||
|
smoothFloorplanNavigationView(
|
||||||
|
localCenter,
|
||||||
|
latestFloorplanUserRotationDegRef.current,
|
||||||
|
nextWidth,
|
||||||
|
)
|
||||||
publishFloorplanNavigationPose(
|
publishFloorplanNavigationPose(
|
||||||
localCenter,
|
localCenter,
|
||||||
latestFloorplanUserRotationDegRef.current,
|
latestFloorplanUserRotationDegRef.current,
|
||||||
@@ -6674,6 +6810,7 @@ export function FloorplanPanel() {
|
|||||||
maxViewportWidth,
|
maxViewportWidth,
|
||||||
minViewportWidth,
|
minViewportWidth,
|
||||||
publishFloorplanNavigationPose,
|
publishFloorplanNavigationPose,
|
||||||
|
smoothFloorplanNavigationView,
|
||||||
svgAspectRatio,
|
svgAspectRatio,
|
||||||
viewBox,
|
viewBox,
|
||||||
viewport,
|
viewport,
|
||||||
@@ -7833,10 +7970,10 @@ export function FloorplanPanel() {
|
|||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
|
||||||
const angleDeltaDeg =
|
const angleDeltaDeg =
|
||||||
(event.clientX - rotationState.startClientX) * FLOORPLAN_ROTATION_DEGREES_PER_PIXEL
|
(rotationState.startClientX - event.clientX) * FLOORPLAN_ROTATION_DEGREES_PER_PIXEL
|
||||||
const nextUserRotationDeg = rotationState.initialUserRotationDeg + angleDeltaDeg
|
const nextUserRotationDeg = rotationState.initialUserRotationDeg + angleDeltaDeg
|
||||||
|
|
||||||
applyFloorplanNavigationView(rotationState.viewportCenterLocal, nextUserRotationDeg)
|
smoothFloorplanNavigationView(rotationState.viewportCenterLocal, nextUserRotationDeg)
|
||||||
publishFloorplanNavigationPose(rotationState.viewportCenterLocal, nextUserRotationDeg)
|
publishFloorplanNavigationPose(rotationState.viewportCenterLocal, nextUserRotationDeg)
|
||||||
setCursorPoint(null)
|
setCursorPoint(null)
|
||||||
return
|
return
|
||||||
@@ -7860,6 +7997,7 @@ export function FloorplanPanel() {
|
|||||||
FLOORPLAN_VIEW_ROTATION_DEG + currentUserRotationDeg - buildingRotationDeg
|
FLOORPLAN_VIEW_ROTATION_DEG + currentUserRotationDeg - buildingRotationDeg
|
||||||
const localCenter = rotateSvgPoint(nextCenterSvg, -currentSceneRotationDeg)
|
const localCenter = rotateSvgPoint(nextCenterSvg, -currentSceneRotationDeg)
|
||||||
|
|
||||||
|
smoothFloorplanNavigationView(localCenter, currentUserRotationDeg)
|
||||||
publishFloorplanNavigationPose(localCenter, currentUserRotationDeg)
|
publishFloorplanNavigationPose(localCenter, currentUserRotationDeg)
|
||||||
|
|
||||||
panStateRef.current = {
|
panStateRef.current = {
|
||||||
@@ -8158,8 +8296,8 @@ export function FloorplanPanel() {
|
|||||||
isPolygonBuildActive,
|
isPolygonBuildActive,
|
||||||
isRoofBuildActive,
|
isRoofBuildActive,
|
||||||
isWallBuildActive,
|
isWallBuildActive,
|
||||||
applyFloorplanNavigationView,
|
|
||||||
publishFloorplanNavigationPose,
|
publishFloorplanNavigationPose,
|
||||||
|
smoothFloorplanNavigationView,
|
||||||
referenceScaleDraft,
|
referenceScaleDraft,
|
||||||
roofDraftStart,
|
roofDraftStart,
|
||||||
elevatorResizeDragState,
|
elevatorResizeDragState,
|
||||||
|
|||||||
Reference in New Issue
Block a user