diff --git a/steps/index.html b/steps/index.html
index 26c3930..39e9e61 100644
--- a/steps/index.html
+++ b/steps/index.html
@@ -147,7 +147,7 @@
id="toggle-debug2-overlay" class="form-checkbox h-4 w-4 text-blue-600 rounded focus:ring-blue-500" /> Show
Advanced Debug (A)
+ class="form-checkbox h-4 w-4 text-blue-600 rounded focus:ring-blue-500" /> GOD MODE
diff --git a/steps/src/drawUtils.js b/steps/src/drawUtils.js
index 0551faf..e6fa1eb 100644
--- a/steps/src/drawUtils.js
+++ b/steps/src/drawUtils.js
@@ -532,13 +532,26 @@ export function handleCloseUpDisplay(p, plotScales) {
// ... (Step 1a: Find hovered points - no changes here) ...
if (frameData.pointCloud) {
- for (const pt of frameData.pointCloud) {
- if (pt.x === null || pt.y === null) continue;
- const screenX = pt.x * plotScales.plotScaleX + p.width / 2;
- const screenY = p.height * 0.95 - pt.y * plotScales.plotScaleY;
- const d = p.dist(p.mouseX, p.mouseY, screenX, screenY);
- if (d < radius) {
- hoveredItems.push({ type: "point", data: pt, screenX, screenY });
+ // In steps/src/drawUtils.js
+
+ // Find hovered points
+ if (frameData.pointCloud) {
+ for (let i = 0; i < frameData.pointCloud.length; i++) {
+ const pt = frameData.pointCloud[i];
+ if (pt.x === null || pt.y === null) continue;
+ const screenX = pt.x * plotScales.plotScaleX + p.width / 2;
+ const screenY = p.height * 0.95 - pt.y * plotScales.plotScaleY;
+ const d = p.dist(p.mouseX, p.mouseY, screenX, screenY);
+ if (d < radius) {
+ // Add the index 'i' to the object we push
+ hoveredItems.push({
+ type: "point",
+ data: pt,
+ screenX,
+ screenY,
+ index: i,
+ });
+ }
}
}
}
@@ -631,7 +644,7 @@ export function handleCloseUpDisplay(p, plotScales) {
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 | X:${data.x.toFixed(2)}, Y:${data.y.toFixed(
+ infoText = `Point ${item.index} | X:${data.x.toFixed(2)}, Y:${data.y.toFixed(
2
)} | V:${vel}, SNR:${snr}, Cluster: ${data.clusterNumber}`;
break;
@@ -843,8 +856,6 @@ export function drawEgoVehicle(p, plotScales) {
p.pop();
}
-
-
//OLD_Solid Fill Logic
/**
@@ -1225,8 +1236,7 @@ export function drawClusterCentroids(p, clustersInput, plotScales) {
// // p.pop(); // Restore the original global drawing state.
// // }
-
-// OLD HATCH FILL logic
+// OLD HATCH FILL logic
// /**
// * Draws a hatched pattern inside a rectangle defined by corner points.
// * This is a new helper function.
@@ -1348,4 +1358,4 @@ export function drawClusterCentroids(p, clustersInput, plotScales) {
// );
// b.pop();
-// }
\ No newline at end of file
+// }
diff --git a/steps/src/p5/radarSketch.js b/steps/src/p5/radarSketch.js
index caf925e..00b1bd6 100644
--- a/steps/src/p5/radarSketch.js
+++ b/steps/src/p5/radarSketch.js
@@ -221,11 +221,16 @@ export const radarSketch = function (p) {
// BUG FIX 1: Call the close-up handler if the mode is active
// --- Zoom and Tooltip Logic ---
+ const COOLING_PERIOD_MS = 2000;
const zoomPanel = document.getElementById("zoom-panel");
if (appState.isCloseUpMode) {
const hoveredItems = handleCloseUpDisplay(p, plotScales);
if (hoveredItems.length > 0) {
- zoomPanel.style.display = "block"; // show the panel
+ clearTimeout(appState.zoomHoverTimeout); // Cancel the timer
+ appState.zoomHoverTimeout = null;
+ if (zoomPanel.style.display !== "block") {
+ zoomPanel.style.display = "block";
+ }
if (
appState.zoomSketchInstance &&
appState.zoomSketchInstance.updateAndDraw
@@ -237,9 +242,30 @@ export const radarSketch = function (p) {
plotScales
);
}
- } else {
- zoomPanel.style.display = "none";
- }
+ } 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(
+ 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(() => {
+ console.log("Cooling period ended. Hiding zoom panel.");
+ zoomPanel.style.display = "none";
+ appState.zoomHoverTimeout = null;
+ }, COOLING_PERIOD_MS);
+ }
+}
} else {
zoomPanel.style.display = "none";
}
@@ -324,29 +350,31 @@ export const radarSketch = function (p) {
// In src/p5/radarSketch.js
-p.windowResized = function () {
- console.log("radarSketch: windowResized triggered!");
-
- // Immediately resize the elements that we know are stable.
- p.resizeCanvas(canvasContainer.offsetWidth, canvasContainer.offsetHeight);
- staticBackgroundBuffer = p.createGraphics(p.width, p.height);
- trackLegendBuffer = p.createGraphics(120, 120);
- p.drawTrackLegendToBuffer();
- calculatePlotScales();
- drawStaticRegionsToBuffer(p, staticBackgroundBuffer, plotScales);
-
- // Defer the call to destroy the zoom canvas.
- if (appState.zoomSketchInstance && appState.isCloseUpMode) {
- setTimeout(() => {
- console.log("radarSketch: Executing deferred call to zoomSketch.handleResize().");
- appState.zoomSketchInstance.handleResize();
- }, 10); // A 10ms delay is slightly more robust than 0.
- }
+ p.windowResized = function () {
+ console.log("radarSketch: windowResized triggered!");
- if (appState.vizData) {
- p.redraw();
- }
-};
+ // Immediately resize the elements that we know are stable.
+ p.resizeCanvas(canvasContainer.offsetWidth, canvasContainer.offsetHeight);
+ staticBackgroundBuffer = p.createGraphics(p.width, p.height);
+ trackLegendBuffer = p.createGraphics(120, 120);
+ p.drawTrackLegendToBuffer();
+ calculatePlotScales();
+ drawStaticRegionsToBuffer(p, staticBackgroundBuffer, plotScales);
+
+ // Defer the call to destroy the zoom canvas.
+ if (appState.zoomSketchInstance && appState.isCloseUpMode) {
+ setTimeout(() => {
+ console.log(
+ "radarSketch: Executing deferred call to zoomSketch.handleResize()."
+ );
+ appState.zoomSketchInstance.handleResize();
+ }, 10); // A 10ms delay is slightly more robust than 0.
+ }
+
+ if (appState.vizData) {
+ p.redraw();
+ }
+ };
// Function to draw the SNR legend to its buffer
p.drawSnrLegendToBuffer = function (minV, maxV) {
diff --git a/steps/src/p5/zoomSketch.js b/steps/src/p5/zoomSketch.js
index 19bd317..d2ff8ed 100644
--- a/steps/src/p5/zoomSketch.js
+++ b/steps/src/p5/zoomSketch.js
@@ -17,133 +17,7 @@ import {
toggleCovariance,
} from "../dom.js";
-/**
- * A dedicated tooltip function for the zoom sketch.
- * It draws the tooltip relative to the hovered items and compensates for the zoom factor.
- */
-/**
- * A dedicated tooltip function for the zoom sketch with full features.
- * It draws the tooltip in the least cluttered quadrant, has dynamic connectors,
- * highlights items, and compensates for the zoom factor.
- */
-/**
- * A dedicated tooltip function for the zoom sketch with smart quadrant positioning.
- */
-/**
- * A dedicated tooltip function for the zoom sketch that "pushes" the tooltip
- * 100 pixels away from the hovered items towards the least cluttered corner.
- */
-// function drawZoomTooltip(p, hoveredItems) {
-// if (!hoveredItems || hoveredItems.length === 0) return;
-// // 1. Generate text content (this is unchanged)
-// const infoStrings = [];
-// for (const item of hoveredItems) {
-// let infoText = '';
-// const data = item.data;
-// switch (item.type) {
-// case 'point': infoText = `Point | X:${data.x.toFixed(2)}, Y:${data.y.toFixed(2)} | V:${data.velocity?.toFixed(2)}, SNR:${data.snr?.toFixed(1)}`; break;
-// case 'cluster': infoText = `Cluster ${data.id} | X:${data.x.toFixed(2)}, Y:${data.y.toFixed(2)} | rSpeed:${data.radialSpeed?.toFixed(2)}`; break;
-// case 'track': infoText = `Track ${item.trackId} | X:${data.correctedPosition[0].toFixed(2)}, Y:${data.correctedPosition[1].toFixed(2)}`; break;
-// case 'prediction': infoText = `Pred. for ${item.trackId} | X:${data.predictedPosition[0].toFixed(2)}, Y:${data.predictedPosition[1].toFixed(2)}`; break;
-// }
-// if (infoText) infoStrings.push({ text: infoText, color: item.color || null });
-// }
-
-// // 2. Find the average screen position of hovered items. This is our anchor point.
-// const avgX = hoveredItems.reduce((acc, item) => acc + item.screenX, 0) / hoveredItems.length;
-// const avgY = hoveredItems.reduce((acc, item) => acc + item.screenY, 0) / hoveredItems.length;
-
-// p.push();
-
-// // 3. Compensate for zoom factor for all drawing operations (unchanged)
-// const zoomFactor = appState.zoomFactor || 6.0;
-// p.textSize(12 / zoomFactor);
-// p.strokeWeight(1 / zoomFactor);
-
-// const lineHeight = 15 / zoomFactor;
-// const boxPadding = 8 / zoomFactor;
-
-// let boxWidth = 0;
-// infoStrings.forEach(info => {
-// boxWidth = Math.max(boxWidth, p.textWidth(info.text));
-// });
-// const boxHeight = (infoStrings.length * lineHeight) + (boxPadding * 2);
-// boxWidth += (boxPadding * 2);
-
-// // --- START: New "Push" Positioning Logic ---
-
-// // Line 1: Define the push distance. We scale it by the zoomFactor so it's a consistent
-// // visual distance on the screen, regardless of zoom level.
-// const pushDistance = 100 / zoomFactor;
-
-// // Line 2: Determine the horizontal direction. If the items are on the right, our direction is left (-1).
-// // If they are on the left, our direction is right (1).
-// const dirX = (avgX > appState.p5_instance.width / 2) ? -1 : 1;
-
-// // Line 3: Determine the vertical direction. If the items are on the bottom, our direction is up (-1).
-// // If they are on the top, our direction is down (1).
-// const dirY = (avgY > appState.p5_instance.height / 2) ? -1 : 1;
-
-// // Line 4: Create a p5.Vector object. This is like an arrow representing our direction (e.g., up and to the right).
-// const pushVector = p.createVector(dirX, dirY);
-
-// // Line 5: Normalize the vector. This makes its length exactly 1, so it only represents a pure direction.
-// pushVector.normalize();
-
-// // Line 6: Scale the vector. Now it's an arrow that is exactly `pushDistance` pixels long.
-// pushVector.mult(pushDistance);
-
-// // Line 7: Calculate the tooltip's corner position by adding our push vector to the anchor point.
-// let boxX = avgX + pushVector.x;
-// let boxY = avgY + pushVector.y;
-
-// // Line 8: Define where the connector line should attach to the box.
-// // If we pushed right, the connector attaches to the left side of the box.
-// let connectorAnchorX = (dirX > 0) ? boxX : boxX + boxWidth;
-
-// // Line 9: If we pushed down, the connector attaches to the top side of the box.
-// let connectorAnchorY = (dirY > 0) ? boxY : boxY + boxHeight;
-
-// // Line 10: Adjust the box's final position to account for its own size, so the *corner*
-// // of the box is at our calculated position, not its top-left.
-// if (dirX < 0) boxX -= boxWidth; // If we pushed left, shift the box left by its own width.
-// if (dirY < 0) boxY -= boxHeight; // If we pushed up, shift the box up by its own height.
-
-// // --- END: New "Push" Positioning Logic ---
-
-// // 4. Draw highlights, box, text, and connectors (this logic is now restored and complete)
-// const highlightColor = p.color(46, 204, 113);
-// hoveredItems.forEach(item => {
-// p.noFill(); p.stroke(highlightColor); p.strokeWeight(2 / zoomFactor);
-// p.ellipse(item.screenX, item.screenY, 15 / zoomFactor, 15 / zoomFactor);
-// });
-
-// const bgColor = document.documentElement.classList.contains('dark') ? p.color(20, 20, 30, 220) : p.color(245, 245, 245, 220);
-// p.fill(bgColor); p.stroke(highlightColor); p.strokeWeight(1 / zoomFactor);
-// p.rect(boxX, boxY, boxWidth, boxHeight, 4 / zoomFactor);
-
-// const defaultTextColor = document.documentElement.classList.contains('dark') ? p.color(230) : p.color(20);
-// p.noStroke(); p.textAlign(p.LEFT, p.TOP);
-// infoStrings.forEach((info, i) => {
-// p.fill(info.color || defaultTextColor);
-// p.text(info.text, boxX + boxPadding, boxY + boxPadding + (i * lineHeight));
-// });
-
-// hoveredItems.forEach((item, i) => {
-// p.stroke(highlightColor); p.strokeWeight(1 / zoomFactor);
-// p.line(connectorAnchorX, connectorAnchorY, item.screenX, item.screenY);
-// });
-
-// p.pop();
-// }
-/**
- * A dedicated tooltip function for the zoom sketch with smart quadrant positioning,
- * individual dynamic connectors, and item highlighting.
- */
-/**
- * A dedicated tooltip function for the zoom sketch with full features and customizations.
- */
function drawZoomTooltip(p, hoveredItems, mainMouseX) {
if (!hoveredItems || hoveredItems.length === 0) return;
@@ -154,7 +28,7 @@ function drawZoomTooltip(p, hoveredItems, mainMouseX) {
const data = item.data;
switch (item.type) {
case "point":
- infoText = `Point | X:${data.x.toFixed(2)}, Y:${data.y.toFixed(
+ infoText = `Point${item.index} | X:${data.x.toFixed(2)}, Y:${data.y.toFixed(
2
)} | V:${data.velocity?.toFixed(2)}, SNR:${data.snr?.toFixed(1)}`;
break;
@@ -295,13 +169,12 @@ export const zoomSketch = function (p) {
if (container && container.offsetWidth > 0) {
canvas = p.createCanvas(container.offsetWidth, container.offsetHeight);
canvas.parent(containerId);
- console.log(`zoomSketch: Canvas CREATED with dimensions ${p.width}x${p.height}`); // debug
+ //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
return;
}
}
- console.log(`zoomSketch: updateAndDraw is running. Canvas dimensions are ${p.width}x${p.height}. Hovered items: ${hoveredItems.length}`); //debug
p.redraw();
};
@@ -316,8 +189,6 @@ export const zoomSketch = function (p) {
}
p.draw = function () {
if (!appState.vizData || !canvas) return;
- console.log("zoomSketch: Draw function is executing."); //debug
-
p.background(
document.documentElement.classList.contains("dark")
? p.color(55, 65, 81)
@@ -406,7 +277,7 @@ export const zoomSketch = function (p) {
p.fill(textColor);
p.noStroke();
p.textSize(16);
- p.textAlign(p.LEFT, p.TOP);
+ p.textAlign(p.LEFT -2, p.TOP);
p.textStyle(p.BOLD);
p.text(titleText, 10, 10);
p.pop();
diff --git a/steps/src/state.js b/steps/src/state.js
index 92016db..13c9801 100644
--- a/steps/src/state.js
+++ b/steps/src/state.js
@@ -1,4 +1,5 @@
export const appState = {
+ zoomHoverTimeout: null, // timeout for hovering over the GOD MODE
isRawOnlyMode: false, // <-- ADD THIS LINE
// Stores the parsed visualization data (radar frames, tracks, etc.)