From add637e11c244c8fa2dc1ec1aa6770750f167731 Mon Sep 17 00:00:00 2001 From: wass08 Date: Wed, 4 Mar 2026 11:13:20 +0100 Subject: [PATCH] pin for zones on hover --- .../renderers/zone/zone-renderer.tsx | 65 +++++++++++++++---- .../viewer/src/systems/zone/zone-system.tsx | 38 ++++++++++- 2 files changed, 89 insertions(+), 14 deletions(-) diff --git a/packages/viewer/src/components/renderers/zone/zone-renderer.tsx b/packages/viewer/src/components/renderers/zone/zone-renderer.tsx index 315d5f60..3654c255 100644 --- a/packages/viewer/src/components/renderers/zone/zone-renderer.tsx +++ b/packages/viewer/src/components/renderers/zone/zone-renderer.tsx @@ -19,9 +19,9 @@ const createWallGradientMaterial = (zoneColor: string) => { // Use UV y coordinate for vertical gradient (0 at bottom, 1 at top) const gradientT = uv().y - const opacity = uniform(0); + const opacity = uniform(0) // Fade opacity from 0.6 at bottom to 0 at top - const finalOpacity = float(0.6).mul(float(1).sub(gradientT)).mul(opacity); + const finalOpacity = float(0.6).mul(float(1).sub(gradientT)).mul(opacity) return new MeshBasicNodeMaterial({ transparent: true, @@ -32,7 +32,7 @@ const createWallGradientMaterial = (zoneColor: string) => { depthTest: false, userData: { uOpacity: opacity, - } + }, }) } @@ -49,7 +49,7 @@ const createFloorMaterial = (zoneColor: string) => { side: DoubleSide, depthWrite: false, depthTest: false, - userData: { uOpacity: opacity} + userData: { uOpacity: opacity }, }) } @@ -176,25 +176,68 @@ export const ZoneRenderer = ({ node }: { node: ZoneNode }) => { return null } + return ( -
- {node.name} +
+ {node.name} +
+
+
+
+
+ {/* Floor fill */} - + diff --git a/packages/viewer/src/systems/zone/zone-system.tsx b/packages/viewer/src/systems/zone/zone-system.tsx index 4062fc5b..ba7c122c 100644 --- a/packages/viewer/src/systems/zone/zone-system.tsx +++ b/packages/viewer/src/systems/zone/zone-system.tsx @@ -6,25 +6,56 @@ import type { MeshBasicNodeMaterial } from 'three/webgpu' import useViewer from '../../store/use-viewer' const TRANSITION_DURATION = 400 // ms +const EXIT_DEBOUNCE_MS = 50 // ignore rapid exit→re-enter within this window export const ZoneSystem = () => { const lastHighlightedZoneRef = useRef(null) const lastChangeTimeRef = useRef(0) const isTransitioningRef = useRef(false) + // Debounce exit-to-null: track the raw pending value and when it last changed + const pendingZoneRef = useRef(null) + const pendingZoneSinceRef = useRef(0) useFrame(({clock}, delta) => { const hoveredId = useViewer.getState().hoveredId - let highlightedZone: string | null = null + let rawZone: string | null = null if (hoveredId) { const hoveredNode = useScene.getState().nodes[hoveredId] if (hoveredNode?.type === 'zone') { - highlightedZone = hoveredId + rawZone = hoveredId } } - // Detect zone change + // Update pending zone when the raw value changes + if (rawZone !== pendingZoneRef.current) { + pendingZoneRef.current = rawZone + pendingZoneSinceRef.current = clock.elapsedTime * 1000 + } + + // Apply non-null immediately; debounce null to filter out brief exits + const age = clock.elapsedTime * 1000 - pendingZoneSinceRef.current + const highlightedZone = rawZone !== null + ? rawZone + : age >= EXIT_DEBOUNCE_MS ? null : lastHighlightedZoneRef.current + + // Detect stable zone change if (highlightedZone !== lastHighlightedZoneRef.current) { + // Fade out previous zone label-pin + if (lastHighlightedZoneRef.current) { + const prevLabel = document.getElementById(`${lastHighlightedZoneRef.current}-label`) + const pin = prevLabel?.querySelector('.label-pin') as HTMLElement | null + if (pin) pin.style.opacity = '0' + console.log('setting opacity to 0 for', lastHighlightedZoneRef.current) + } + // Fade in new zone label-pin + if (highlightedZone) { + const label = document.getElementById(`${highlightedZone}-label`) + const pin = label?.querySelector('.label-pin') as HTMLElement | null + if (pin) pin.style.opacity = '1' + console.log('setting opacity to 1 for', highlightedZone) + } + lastHighlightedZoneRef.current = highlightedZone lastChangeTimeRef.current = clock.elapsedTime * 1000 isTransitioningRef.current = true @@ -63,6 +94,7 @@ export const ZoneSystem = () => { const currentOpacity = material.userData.uOpacity.value material.userData.uOpacity.value = MathUtils.lerp(currentOpacity, targetOpacity, lerpSpeed) } + }) })