From 5910e934a6405660fbdda1b4d57cb0e617a1a364 Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Thu, 30 Apr 2026 02:37:43 +0200 Subject: [PATCH] Ensure camera state is committed after smooth zoom completion --- src/viewport-three/viewport-host.ts | 34 +++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/viewport-three/viewport-host.ts b/src/viewport-three/viewport-host.ts index b7b253b9..bc5538b6 100644 --- a/src/viewport-three/viewport-host.ts +++ b/src/viewport-three/viewport-host.ts @@ -917,6 +917,7 @@ export class ViewportHost { private panelId: ViewportPanelId = "topLeft"; private targetPerspectiveCameraRadius: number | null = null; private targetOrthographicCameraZoom: number | null = null; + private pendingSmoothZoomCameraStateCommit = false; private creationPreview: CreationViewportToolPreview | null = null; private currentTerrainBrushState: ArmedTerrainBrushState | null = null; private terrainBrushHover: TerrainBrushHit | null = null; @@ -1806,6 +1807,7 @@ export class ViewportHost { this.lastCameraStateTraceSnapshot = cloneViewportPanelCameraState(nextCameraState); + this.pendingSmoothZoomCameraStateCommit = false; this.cameraStateChangeHandler?.(nextCameraState); } @@ -1823,9 +1825,12 @@ export class ViewportHost { private cancelSmoothZoom() { this.targetPerspectiveCameraRadius = null; this.targetOrthographicCameraZoom = null; + this.pendingSmoothZoomCameraStateCommit = false; } private finishSmoothZoom() { + const shouldCommitCameraState = this.pendingSmoothZoomCameraStateCommit; + if (this.targetPerspectiveCameraRadius !== null) { this.cameraSpherical.radius = this.targetPerspectiveCameraRadius; this.targetPerspectiveCameraRadius = null; @@ -1835,6 +1840,11 @@ export class ViewportHost { this.orthographicCamera.zoom = this.targetOrthographicCameraZoom; this.targetOrthographicCameraZoom = null; } + + if (shouldCommitCameraState) { + this.applyViewModePose(); + this.emitCameraStateChange(); + } } private stepSmoothZoomValue( @@ -1876,11 +1886,13 @@ export class ViewportHost { private updateSmoothZoom(dt: number) { const response = this.getSmoothZoomFrameResponse(dt); + let zoomWasActive = false; if ( this.viewMode === "perspective" && this.targetPerspectiveCameraRadius !== null ) { + zoomWasActive = true; const nextRadius = this.stepSmoothZoomValue( this.cameraSpherical.radius, this.targetPerspectiveCameraRadius, @@ -1897,6 +1909,7 @@ export class ViewportHost { isOrthographicViewportViewMode(this.viewMode) && this.targetOrthographicCameraZoom !== null ) { + zoomWasActive = true; const nextZoom = this.stepSmoothZoomValue( this.orthographicCamera.zoom, this.targetOrthographicCameraZoom, @@ -1908,6 +1921,15 @@ export class ViewportHost { } this.applyOrthographicCameraPose(); } + + if ( + zoomWasActive && + this.pendingSmoothZoomCameraStateCommit && + this.targetPerspectiveCameraRadius === null && + this.targetOrthographicCameraZoom === null + ) { + this.emitCameraStateChange(); + } } private updatePerspectiveCameraSphericalFromPose() { @@ -10347,6 +10369,7 @@ export class ViewportHost { private handleWheel = (event: WheelEvent) => { event.preventDefault(); + this.pendingSmoothZoomCameraStateCommit = true; if (this.viewMode === "perspective") { this.targetPerspectiveCameraRadius = this.clampPerspectiveCameraRadius( @@ -10363,7 +10386,11 @@ export class ViewportHost { this.targetPerspectiveCameraRadius = null; } this.applyPerspectiveCameraPose(); - this.emitCameraStateChange(); + + if (this.targetPerspectiveCameraRadius === null) { + this.emitCameraStateChange(); + } + return; } @@ -10381,7 +10408,10 @@ export class ViewportHost { this.targetOrthographicCameraZoom = null; } this.applyOrthographicCameraPose(); - this.emitCameraStateChange(); + + if (this.targetOrthographicCameraZoom === null) { + this.emitCameraStateChange(); + } }; private handleAuxClick = (event: MouseEvent) => {