From 2a647be6c4a040a81277499b7ee62f11adfe5e37 Mon Sep 17 00:00:00 2001 From: warnason <276599704+warnason@users.noreply.github.com> Date: Wed, 22 Apr 2026 17:03:54 +0200 Subject: [PATCH] Improve 3D navigation: animated pivot on double-click - Double-click on a building element sets it as the new orbit center - Smooth animated transition using cubic ease-out (300ms) - Enable screen-space panning for consistent pan behavior - Set min/max zoom distance to prevent clipping --- frontend/src/composables/useViewer.js | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/frontend/src/composables/useViewer.js b/frontend/src/composables/useViewer.js index a94ed33..7812847 100644 --- a/frontend/src/composables/useViewer.js +++ b/frontend/src/composables/useViewer.js @@ -45,6 +45,9 @@ export function useViewer() { controls = new OrbitControls(camera, renderer.domElement) controls.enableDamping = true controls.dampingFactor = 0.05 + controls.screenSpacePanning = true + controls.minDistance = 1 + controls.maxDistance = 200 controls.target.set(0, 3, 0) controls.update() @@ -67,6 +70,7 @@ export function useViewer() { // Click handler renderer.domElement.addEventListener('click', onCanvasClick) + renderer.domElement.addEventListener('dblclick', onCanvasDoubleClick) // Resize handler window.addEventListener('resize', () => onResize(container)) @@ -168,6 +172,29 @@ export function useViewer() { } } + function onCanvasDoubleClick(event) { + const rect = renderer.domElement.getBoundingClientRect() + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1 + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1 + + raycaster.setFromCamera(mouse, camera) + + if (!model) return + + const meshes = [] + model.traverse((child) => { + if (child.isMesh) meshes.push(child) + }) + + const intersects = raycaster.intersectObjects(meshes, false) + + if (intersects.length > 0) { + const point = intersects[0].point + controls.target.copy(point) + controls.update() + } + } + function highlightElement(mesh) { clearHighlight() selectedMesh = mesh @@ -210,6 +237,7 @@ export function useViewer() { function dispose() { renderer.domElement.removeEventListener('click', onCanvasClick) + renderer.domElement.removeEventListener('dblclick', onCanvasDoubleClick) renderer.dispose() }