diff --git a/steps/src/drawUtils.js b/steps/src/drawUtils.js index e6fa1eb..10d189f 100644 --- a/steps/src/drawUtils.js +++ b/steps/src/drawUtils.js @@ -449,7 +449,7 @@ export function drawTrackMarkers(p, plotScales) { // --- END: Add the Same Safeguard Here --- const log = track.historyLog.find( - (log) => log.frameIdx === appState.currentFrame + 1 + (log) => log.frameIdx === appState.currentFrame ); if (log) { @@ -639,30 +639,49 @@ export function handleCloseUpDisplay(p, plotScales) { const infoStrings = []; for (const item of hoveredItems) { let infoText = ""; + let itemColor = item.color || null; // Initialize with existing item color or null const data = item.data; switch (item.type) { case "point": const vel = data.velocity !== null ? data.velocity.toFixed(2) : "N/A"; const snr = data.snr !== null ? data.snr.toFixed(1) : "N/A"; - infoText = `Point ${item.index} | X:${data.x.toFixed(2)}, Y:${data.y.toFixed( + infoText = `Point ${item.index} | X:${data.x.toFixed( 2 - )} | V:${vel}, SNR:${snr}, Cluster: ${data.clusterNumber}`; + )}, Y:${data.y.toFixed(2)} | V:${vel}, SNR:${snr}, Cluster: ${ + data.clusterNumber + }`; break; case "cluster": const rs = data.radialSpeed !== null ? data.radialSpeed.toFixed(2) : "N/A"; const vx = data.vx !== null ? data.vx.toFixed(2) : "N/A"; const vy = data.vy !== null ? data.vy.toFixed(2) : "N/A"; - infoText = `Cluster ${data.id} | X:${data.x.toFixed( + infoText = `Cluster ${data.id} | X:${data.x.toFixed(2)}, Y:${data.y.toFixed( 2 - )}, Y:${data.y.toFixed(2)} | rSpeed:${rs}, vX:${vx}, vY:${vy}`; + )} | rSpeed:${rs}, vX:${vx}, vY:${vy}`; + // itemColor is already set for clusters when pushed to hoveredItems break; case "track": - infoText = `Track ${ - item.trackId - } | X:${data.correctedPosition[0].toFixed( + const trackX = data.correctedPosition[0]; + const trackY = data.correctedPosition[1]; + let trackSpeed = "N/A"; + if ( + data.predictedVelocity && + data.predictedVelocity[0] !== null && + data.predictedVelocity[1] !== null + ) { + const [vx, vy] = data.predictedVelocity; + // Calculate speed in km/h, similar to drawTrackMarkers + trackSpeed = (p.sqrt(vx * vx + vy * vy) * 3.6).toFixed(1) + " km/h"; + } + infoText = `Track ${item.trackId} | X:${trackX.toFixed( 2 - )}, Y:${data.correctedPosition[1].toFixed(2)}`; + )}, Y:${trackY.toFixed(2)} | Speed: ${trackSpeed}`; + // Check for dark mode to ensure visibility + const isDark = document.documentElement.classList.contains("dark"); + itemColor = isDark + ? p.color(100, 149, 237) // A lighter "Cornflower Blue" for dark mode + : p.color(0, 0, 255); // Original blue for light mode break; case "prediction": const p_vx = @@ -673,15 +692,14 @@ export function handleCloseUpDisplay(p, plotScales) { data.predictedVelocity[1] !== null ? data.predictedVelocity[1].toFixed(2) : "N/A"; - infoText = `Pred. for ${ - item.trackId - } | X:${data.predictedPosition[0].toFixed( + infoText = `Pred. for ${item.trackId} | X:${data.predictedPosition[0].toFixed( 2 )}, Y:${data.predictedPosition[1].toFixed(2)} | vX:${p_vx}, vY:${p_vy}`; + itemColor = p.color(255, 0, 0); // Red color for prediction info break; } if (infoText) { - infoStrings.push({ text: infoText, color: item.color || null }); + infoStrings.push({ text: infoText, color: itemColor }); } } diff --git a/steps/src/p5/radarSketch.js b/steps/src/p5/radarSketch.js index 00b1bd6..0114e34 100644 --- a/steps/src/p5/radarSketch.js +++ b/steps/src/p5/radarSketch.js @@ -151,7 +151,7 @@ export const radarSketch = function (p) { if (togglePredictedPos.checked) { for (const track of appState.vizData.tracks) { const log = track.historyLog.find( - (log) => log.frameIdx === appState.currentFrame + 1 + (log) => log.frameIdx === appState.currentFrame ); if ( log && @@ -243,29 +243,32 @@ export const radarSketch = function (p) { ); } } else if (zoomPanel.style.display === "block") { - // --- THIS BLOCK IS THE FIX --- - // If NOT hovering, but the panel is still visible: - - // 1. Continue to update the zoom sketch's position to follow the mouse. - // We pass an empty array for hoveredItems, so no tooltip is drawn. - if (appState.zoomSketchInstance && appState.zoomSketchInstance.updateAndDraw) { - appState.zoomSketchInstance.updateAndDraw( + // --- THIS BLOCK IS THE FIX --- + // If NOT hovering, but the panel is still visible: + + // 1. Continue to update the zoom sketch's position to follow the mouse. + // We pass an empty array for hoveredItems, so no tooltip is drawn. + if ( + appState.zoomSketchInstance && + appState.zoomSketchInstance.updateAndDraw + ) { + appState.zoomSketchInstance.updateAndDraw( p.mouseX, p.mouseY, [], // Pass empty array plotScales - ); - } + ); + } - // 2. If a "hide" timer isn't already running, start one. - if (!appState.zoomHoverTimeout) { - appState.zoomHoverTimeout = setTimeout(() => { + // 2. If a "hide" timer isn't already running, start one. + if (!appState.zoomHoverTimeout) { + appState.zoomHoverTimeout = setTimeout(() => { console.log("Cooling period ended. Hiding zoom panel."); zoomPanel.style.display = "none"; appState.zoomHoverTimeout = null; - }, COOLING_PERIOD_MS); - } -} + }, COOLING_PERIOD_MS); + } + } } else { zoomPanel.style.display = "none"; } diff --git a/steps/src/p5/zoomSketch.js b/steps/src/p5/zoomSketch.js index d2ff8ed..8fbe899 100644 --- a/steps/src/p5/zoomSketch.js +++ b/steps/src/p5/zoomSketch.js @@ -17,7 +17,6 @@ import { toggleCovariance, } from "../dom.js"; - function drawZoomTooltip(p, hoveredItems, mainMouseX) { if (!hoveredItems || hoveredItems.length === 0) return; @@ -25,35 +24,71 @@ function drawZoomTooltip(p, hoveredItems, mainMouseX) { const infoStrings = []; for (const item of hoveredItems) { let infoText = ""; + let itemColor = item.color || null; // Initialize with existing item color or null const data = item.data; switch (item.type) { case "point": - infoText = `Point${item.index} | X:${data.x.toFixed(2)}, Y:${data.y.toFixed( + const vel = data.velocity !== null ? data.velocity.toFixed(2) : "N/A"; + const snr = data.snr !== null ? data.snr.toFixed(1) : "N/A"; + infoText = `Point ${item.index} | X:${data.x.toFixed( 2 - )} | V:${data.velocity?.toFixed(2)}, SNR:${data.snr?.toFixed(1)}`; + )}, Y:${data.y.toFixed(2)} | V:${vel}, SNR:${snr}, Cluster: ${ + data.clusterNumber + }`; break; case "cluster": + const rs = + data.radialSpeed !== null ? data.radialSpeed.toFixed(2) : "N/A"; + const vx = data.vx !== null ? data.vx.toFixed(2) : "N/A"; + const vy = data.vy !== null ? data.vy.toFixed(2) : "N/A"; infoText = `Cluster ${data.id} | X:${data.x.toFixed( 2 - )}, Y:${data.y.toFixed(2)} | rSpeed:${data.radialSpeed?.toFixed(2)}`; + )}, Y:${data.y.toFixed(2)} | rSpeed:${rs}, vX:${vx}, vY:${vy}`; break; case "track": - infoText = `Track ${ - item.trackId - } | X:${data.correctedPosition[0].toFixed( + const trackX = data.correctedPosition[0]; + const trackY = data.correctedPosition[1]; + let trackSpeed = "N/A", + trackVx = "N/A", + trackVy = "N/A"; + if ( + data.predictedVelocity && + data.predictedVelocity[0] !== null && + data.predictedVelocity[1] !== null + ) { + const [vx, vy] = data.predictedVelocity; + trackVx = vx.toFixed(2); + trackVy = vy.toFixed(2); + trackSpeed = (p.sqrt(vx * vx + vy * vy) * 3.6).toFixed(1) + " km/h"; + } + infoText = `Track ${item.trackId} | X:${trackX.toFixed( 2 - )}, Y:${data.correctedPosition[1].toFixed(2)}`; + )}, Y:${trackY.toFixed(2)} | Speed: ${trackSpeed}`; + const isDark = document.documentElement.classList.contains("dark"); + itemColor = isDark + ? p.color(100, 149, 237) // Lighter blue for dark mode + : p.color(0, 0, 255); // Original blue for light mode break; case "prediction": - infoText = `Pred. for ${ + const p_vx = + data.predictedVelocity[0] !== null + ? data.predictedVelocity[0].toFixed(2) + : "N/A"; + const p_vy = + data.predictedVelocity[1] !== null + ? data.predictedVelocity[1].toFixed(2) + : "N/A"; + infoText = `Pred. ${ item.trackId } | X:${data.predictedPosition[0].toFixed( 2 - )}, Y:${data.predictedPosition[1].toFixed(2)}`; + )}, Y:${data.predictedPosition[1].toFixed(2)} | Vx:${p_vx}, Vy:${p_vy}`; + itemColor = p.color(255, 0, 0); // Red color for prediction info break; } - if (infoText) - infoStrings.push({ text: infoText, color: item.color || null }); + if (infoText) { + infoStrings.push({ text: infoText, color: itemColor }); + } } // 2. Find the average screen position of hovered items @@ -171,22 +206,24 @@ export const zoomSketch = function (p) { canvas.parent(containerId); //console.log(`zoomSketch: Canvas CREATED with dimensions ${p.width}x${p.height}`); // debug } else { - console.warn("zoomSketch: updateAndDraw called, but container is not ready. Aborting draw."); //debug + console.warn( + "zoomSketch: updateAndDraw called, but container is not ready. Aborting draw." + ); //debug return; } } p.redraw(); }; - p.handleResize = function() { + p.handleResize = function () { console.log("zoomSketch: handleResize triggered. Destroying old canvas."); if (canvas) { canvas.remove(); // p5.js function to properly remove the canvas from the DOM - canvas = null; // Set the internal reference to null + canvas = null; // Set the internal reference to null } // The canvas will be recreated automatically the next time updateAndDraw() is called, // at which point the container will have its correct, final dimensions. - } + }; p.draw = function () { if (!appState.vizData || !canvas) return; p.background( @@ -277,7 +314,7 @@ export const zoomSketch = function (p) { p.fill(textColor); p.noStroke(); p.textSize(16); - p.textAlign(p.LEFT -2, p.TOP); + p.textAlign(p.LEFT - 2, p.TOP); p.textStyle(p.BOLD); p.text(titleText, 10, 10); p.pop(); @@ -289,5 +326,4 @@ export const zoomSketch = function (p) { p.line(p.width / 2 - 15, p.height / 2, p.width / 2 + 15, p.height / 2); p.line(p.width / 2, p.height / 2 - 15, p.width / 2, p.height / 2 + 15); }; - };