From 1d3131d7d2261d17592b30606935ec249ed485f5 Mon Sep 17 00:00:00 2001 From: rakadu1 Date: Wed, 3 Sep 2025 17:08:45 +0530 Subject: [PATCH] feat: Refactor visualizer and implement robust sync logic Main: This major update refactors the entire application from a single monolithic HTML file into a modern, modular JavaScript architecture for improved maintainability, performance, and future extensibility. Alongside the refactoring, this commit introduces a completely overhauled synchronization engine and several quality-of-life improvements. Key changes and new features include: - **Modular Architecture**: The application is now split into distinct, decoupled modules for state management (`state.js`), DOM manipulation (`dom.js`), synchronization (`sync.js`), file parsing (`fileParsers.js`), and UI components (`p5/radarSketch.js`, `modal.js`, etc.). - **Robust Synchronization Engine**: - The core playback loop in `sync.js` now correctly applies the manual time offset, ensuring accurate synchronization between the video and radar data during playback. - Fixed a bug where fast scrubbing with the timeline slider could leave a persistent drift while paused. The fix uses the video's `seeked` event for a reliable, event-driven UI update. - **Enhanced User Experience**: - Added a new feature allowing users to press 'Enter' in the offset input box to instantly resync the video to the current radar frame, which significantly streamlines the manual calibration process. - **Improved Debugging Tools**: - The advanced debug overlay's drift calculation is now "offset-aware," providing an accurate representation of the true synchronization status during both playback and seeking. --- steps/Visualization_Start.bat | 25 +++++++++++++ steps/python_check.bat | 35 +++++++++++++++++++ steps/src/dom.js | 9 +++-- steps/src/main.js | 66 +++++++++++++++++++++++++++++++++++ steps/src/state.js | 2 ++ steps/src/sync.js | 11 +++--- 6 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 steps/Visualization_Start.bat create mode 100644 steps/python_check.bat diff --git a/steps/Visualization_Start.bat b/steps/Visualization_Start.bat new file mode 100644 index 0000000..c65d72a --- /dev/null +++ b/steps/Visualization_Start.bat @@ -0,0 +1,25 @@ +@echo off +title Radar and Video Visualizer - Server +color 0B + +cls +echo. +echo ====================================================== +echo Radar and Video Visualizer - Local Server +echo ====================================================== +echo. +echo - This window is your local web server. +echo - Please KEEP THIS WINDOW OPEN while using the app. +echo - To stop the server, simply close this window. +echo. +echo ====================================================== +echo. + +echo Launching the application in your default browser... +start http://127.0.0.1:8000/index.html + +echo Server is now running on http://127.0.0.1:8000 +echo Press CTRL+C at any time to stop the server. + +:: Run the server command directly. We know 'python' works from our test. +python -m http.server 8000 \ No newline at end of file diff --git a/steps/python_check.bat b/steps/python_check.bat new file mode 100644 index 0000000..cc84684 --- /dev/null +++ b/steps/python_check.bat @@ -0,0 +1,35 @@ +@echo off +title Python Installation Diagnostic + +echo ================================================================= +echo This script will test your Python installation. +echo The window will PAUSE at the end. Please copy the output. +echo ================================================================= +echo. + +echo --- 1. Testing for the 'python' command... --- +python --version +echo. + +echo --- 2. Testing for the 'python3' command... --- +python3 --version +echo. + +echo --- 3. Testing for the 'py' command... --- +py --version +echo. + +echo ================================================================= +echo DIAGNOSTIC FINISHED. Please analyze the results above. +echo ================================================================= +echo. +echo If you see a version number (e.g., Python 3.12.4) next to +echo one or more of the commands, that is the command we need to use. +echo. +echo If you see "'python' is not recognized..." for ALL of them, +echo it means Python was installed without being added to the system PATH. +echo. +echo Please copy ALL the text from this window and send it to me. +echo. + +pause \ No newline at end of file diff --git a/steps/src/dom.js b/steps/src/dom.js index e867ddb..8161e02 100644 --- a/steps/src/dom.js +++ b/steps/src/dom.js @@ -107,7 +107,7 @@ export function updateFrame(frame, forceVideoSeek) { } // --- END OF NEW BLOCK --- - let timeForUpdates = videoPlayer.currentTime; // NEW: Default to the video's current time + let timeForUpdates = videoPlayer.currentTime; // NEW: Default to the video's current time if ( forceVideoSeek && @@ -214,10 +214,15 @@ export function updateDebugOverlay(currentMediaTime) { appState.vizData && appState.vizData.radarFrames[appState.currentFrame] ) { + // --- START: Corrected Debug Logic --- const currentRadarFrame = appState.vizData.radarFrames[appState.currentFrame]; const targetRadarTimeMs = currentRadarFrame.timestampMs; - const driftMs = currentMediaTime * 1000 - targetRadarTimeMs; + const offsetMs = parseFloat(offsetInput.value) || 0; // Read the current offset + + // Make the drift calculation "offset-aware" + const driftMs = currentMediaTime * 1000 + offsetMs - targetRadarTimeMs; + // --- END: Corrected Debug Logic --- // Style the drift value to be green if sync is good, and red if it's off. const driftColor = Math.abs(driftMs) > 40 ? "#FF6347" : "#98FB98"; // Tomato red or Pale green diff --git a/steps/src/main.js b/steps/src/main.js index 8440e2e..943904e 100644 --- a/steps/src/main.js +++ b/steps/src/main.js @@ -402,6 +402,22 @@ document.addEventListener("keydown", (event) => { } }); +// In src/main.js, add this new event listener +videoPlayer.addEventListener("seeked", () => { + // This event fires every time a seek operation completes. + // We only act if our flag has been set. + if (appState.needsPostSeekUpdate) { + console.log( + "Video has finished seeking. Performing final debug overlay update." + ); + // Now we can be sure videoPlayer.currentTime is accurate. + updateDebugOverlay(videoPlayer.currentTime); + + // Reset the flag so this logic doesn't run on every seek + appState.needsPostSeekUpdate = false; + } +}); + function calculateAndSetOffset() { const jsonTimestampInfo = extractTimestampInfo(appState.jsonFilename); const videoTimestampInfo = extractTimestampInfo(appState.videoFilename); @@ -541,3 +557,53 @@ document.addEventListener("DOMContentLoaded", () => { }); }); }); + +// In src/main.js, add this new event listener +offsetInput.addEventListener("keydown", (event) => { + // Check if the key pressed was 'Enter' + if (event.key === "Enter") { + // Prevent the default browser action for the Enter key (like submitting a form) + event.preventDefault(); + + // Make sure visualization data is loaded before proceeding + if (!appState.vizData) return; + + console.log( + `Enter pressed. Forcing resync with new offset: ${offsetInput.value}` + ); + + // If the video is playing, pause it to allow for precise frame tuning. + if (appState.isPlaying) { + playPauseBtn.click(); + } + + // Call updateFrame, forcing it to resync the video to the current radar frame + // using the new offset value from the input box. + updateFrame(appState.currentFrame, true); + } +}); + +// In src/main.js, REPLACE the 'change' event listener with this: +timelineSlider.addEventListener("change", () => { + if (!appState.vizData || appState.isPlaying) return; + + const currentRadarFrame = appState.vizData.radarFrames[appState.currentFrame]; + if (!currentRadarFrame) return; + + const targetRadarTimeMs = currentRadarFrame.timestampMs; + const offsetMs = parseFloat(offsetInput.value) || 0; + const currentVideoTimeMs = videoPlayer.currentTime * 1000; + const driftMs = currentVideoTimeMs + offsetMs - targetRadarTimeMs; + + if (Math.abs(driftMs) > 50) { + console.log( + `Setting flag for post-seek update. Initial drift: ${driftMs.toFixed( + 0 + )}ms` + ); + // 1. Set the flag to true + appState.needsPostSeekUpdate = true; + // 2. Initiate the final seek operation + updateFrame(appState.currentFrame, true); + } +}); diff --git a/steps/src/state.js b/steps/src/state.js index f50a142..ade8694 100644 --- a/steps/src/state.js +++ b/steps/src/state.js @@ -29,4 +29,6 @@ export const appState = { mediaTimeStart: 0, // Timestamp (from performance.now()) of the last synchronization check lastSyncTime: 0, + // new flag for seek finished + needsPostSeekUpdate: false, }; diff --git a/steps/src/sync.js b/steps/src/sync.js index a06a936..4c9a568 100644 --- a/steps/src/sync.js +++ b/steps/src/sync.js @@ -29,14 +29,17 @@ export function animationLoop() { // Check if visualization data and video start date are available if (appState.vizData && appState.videoStartDate) { // Get the offset from the input field, default to 0 if not a valid number + // --- START: Corrected Logic --- const offsetMs = parseFloat(offsetInput.value) || 0; - // Calculate the target radar time in milliseconds - const targetRadarTimeMs = currentMediaTime * 1000; - // Find the index of the radar frame that corresponds to the target time + // The master clock represents the VIDEO's timeline. + // To find the corresponding RADAR time, we must add the offset. + const targetRadarTimeMs = currentMediaTime * 1000 + offsetMs; + const targetFrame = findRadarFrameIndexForTime( targetRadarTimeMs, appState.vizData ); + // --- END: Corrected Logic --- if (targetFrame !== appState.currentFrame) { // Update the displayed frame if it's different from the current one updateFrame(targetFrame, false); @@ -45,7 +48,7 @@ export function animationLoop() { // Periodically check for drift between master clock and video element const now = performance.now(); - if (now - appState.lastSyncTime > 500) { + if (now - appState.lastSyncTime > 150) { const videoTime = videoPlayer.currentTime; const drift = Math.abs(currentMediaTime - videoTime); // Resync if drift is > 150ms