const debugPanel = document.getElementById('debug-panel'); const infoPanel = document.getElementById('info-panel'); const infoButton = document.getElementById('info-button'); const moveButton = document.getElementById('move-button'); const toolPalette = document.getElementById('tool-palette'); let infoPanelPinned = false; let pinnedBuildingData = null; let debugOn = true; let interactableObjects = []; let INTERSECTED; // Add these variables at the top of your file (outside any function) let lastFpsUpdate = performance.now(); let frameCount = 0; let currentFps = 0; // Call this in your animation loop (e.g., in animate() in main.js) function updateFps() { frameCount++; const now = performance.now(); if (now - lastFpsUpdate > 500) { // Update every 0.5s currentFps = (frameCount * 1000) / (now - lastFpsUpdate); lastFpsUpdate = now; frameCount = 0; } } function initPanels(){ window.addEventListener('keydown', function(event) { if (event.key === 'h') { console.log('H'); hideDebugPanel() debugOn = !debugOn; } }); /* renderer.domElement.addEventListener('pointerdown', function(event) { // Calculate mouse position in normalized device coordinates 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); const intersects = raycaster.intersectObjects(interactableObjects); if (intersects.length > 0) { console.log(intersects[0].object); // Pin the info panel to this building infoPanelPinned = true; const selected = intersects[0].object.userData.parentGroup || intersects[0].object; pinnedBuildingData = selected.userData; displayInfo(pinnedBuildingData); } else { // Unpin if clicking empty space infoPanelPinned = false; pinnedBuildingData = null; hideInfo(); } }); */ document.addEventListener('mousemove', onMouseMove, false); toolPalette.addEventListener('mouseup', (event) => event.stopPropagation(), false); // Tool Palette Button Listeners infoButton.addEventListener('click', showInfoPanelForSelected); moveButton.addEventListener('click', () => { if (selectedObject) { console.log("Move tool clicked for:", selectedObject.userData.description); // Add move logic here later hideToolPalette(); // Hide palette after action (optional) } }); } function onMouseMove(event) { if (infoPanelPinned) return; // Calculate mouse position (Unchanged) mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; // --- Info Panel Positioning (Adjusted for bottom-left) --- const panelRect = infoPanel.getBoundingClientRect(); let panelX = event.clientX + 15; let panelY = event.clientY - panelRect.height - 15; // Position above cursor // Basic boundary checks (adjust as needed) if (panelX + panelRect.width > window.innerWidth - 10) { panelX = event.clientX - panelRect.width - 15; } if (panelY < 10) { // Check top boundary panelY = event.clientY + 15; // Move below cursor if too high } if (panelX < 10) panelX = 10; // Prevent going off left infoPanel.style.left = `${panelX}px`; infoPanel.style.top = `${panelY}px`; } // --- Debug Panel --- function displaySunDebug() { const pos = directionalLight.position; debugPanel.innerHTML = `
Position: (${pos.x.toFixed(2)}, ${pos.y.toFixed(2)}, ${pos.z.toFixed(2)})
`; debugPanel.style.display = 'block'; } function displayDebugPanel(object) { if (!object || !object.position) { debugPanel.style.display = 'none'; return; } const pos = object.position; debugPanel.innerHTML = `ID: ${object.userData?.id ?? 'N/A'}
Position: (${pos.x.toFixed(2)}, ${pos.y.toFixed(2)}, ${pos.z.toFixed(2)})
`; debugPanel.style.display = 'block'; } function hideDebugPanel() { debugPanel.style.display = 'none'; } function showToolPalette(targetObject) { if (!targetObject) return; // Calculate screen position of the object const screenPos = toScreenPosition(targetObject, camera); // Position palette slightly above and to the right of the object's center toolPalette.style.left = `${screenPos.x + 10}px`; toolPalette.style.top = `${screenPos.y - toolPalette.offsetHeight - 10}px`; // Offset by palette height // Basic boundary check (prevent going off screen) - needs improvement const paletteRect = toolPalette.getBoundingClientRect(); // Get actual size after potential content change if (parseInt(toolPalette.style.left) + paletteRect.width > window.innerWidth - 10) { toolPalette.style.left = `${window.innerWidth - paletteRect.width - 10}px`; } if (parseInt(toolPalette.style.top) < 10) { toolPalette.style.top = '10px'; } if (parseInt(toolPalette.style.left) < 10) { toolPalette.style.left = '10px'; } toolPalette.style.display = 'flex'; // Use flex to show it } function hideToolPalette() { toolPalette.style.display = 'none'; } function showInfoPanelForSelected() { if (selectedObject && selectedObject.userData) { displayInfo(selectedObject.userData); } else { hideInfoPanel(); } // Optionally hide palette after clicking info // hideToolPalette(); } function displayInfo(buildingData) { document.getElementById('info-title').textContent = buildingData.description; document.getElementById('info-consumption').textContent = buildingData.consumption; document.getElementById('info-generation').textContent = buildingData.generation; document.getElementById('info-iot').textContent = buildingData.iot; infoPanel.style.display = 'block'; // Show the panel } function hideInfoPanel() { infoPanel.style.display = 'none'; // Hide the panel } // --- Info Panel --- function displayInfo(buildingData) { if (infoPanelPinned && pinnedBuildingData && pinnedBuildingData !== buildingData) return; if (infoPanelPinned && !pinnedBuildingData) pinnedBuildingData = buildingData; document.getElementById('info-title').textContent = buildingData.id; document.getElementById('info-consumption').textContent = buildingData.consumption + ' kWh'; document.getElementById('info-generation').textContent = buildingData.generation + ' kWh'; // Dynamically show IoT/device consumption let iotHtml = ''; if (buildingData.devices) { for (const [key, value] of Object.entries(buildingData.devices)) { iotHtml += `${key}: ${value ?? 'N/A'}ID: ${object.userData?.id ?? 'N/A'}
Object Position: (${object.position.x.toFixed(2)}, ${object.position.y.toFixed(2)}, ${object.position.z.toFixed(2)})
`; } html += `FPS: ${currentFps.toFixed(1)}
`; html += `Camera Position: (${camera.position.x.toFixed(2)}, ${camera.position.y.toFixed(2)}, ${camera.position.z.toFixed(2)})
`; html += `Controls Target: (${controls.target.x.toFixed(2)}, ${controls.target.y.toFixed(2)}, ${controls.target.z.toFixed(2)})
`; html += `Sun Position: (${directionalLight.position.x.toFixed(2)}, ${directionalLight.position.y.toFixed(2)}, ${directionalLight.position.z.toFixed(2)})
`; debugPanel.innerHTML = html; debugPanel.style.display = 'block'; } function updatePanels(){ if (infoPanelPinned && pinnedBuildingData) { displayInfo(pinnedBuildingData); } // --- Raycasting Logic --- raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObjects(interactableObjects); let debugTarget = null; if (infoPanelPinned && pinnedBuildingData) { debugTarget = interactableObjects.find(obj => obj.userData === pinnedBuildingData); } else if (intersects.length > 0) { debugTarget = intersects[0].object.userData.parentGroup || intersects[0].object; } if (debugOn){ displayDebugPanelWithCamera(debugTarget); //displaySunDebug(); } if (intersects.length > 0) { if (INTERSECTED != intersects[0].object) { if (INTERSECTED) { /* Optional: Revert highlight */ } //INTERSECTED = intersects[0].object.userData.parentGroup || intersects[0].object; INTERSECTED = intersects[0].object; if (INTERSECTED.userData) { //console.log('Pinned building data:', INTERSECTED.userData); if (INTERSECTED.userData.id){ displayInfo(INTERSECTED.userData); } else{ displayInfo(INTERSECTED.userData.parentGroup.userData); } } else { hideInfo(); } } } else { if (INTERSECTED) { hideInfo(); } INTERSECTED = null; } }