diff --git a/steps/src/dom.js b/steps/src/dom.js index 0c7373b..0ca12da 100644 --- a/steps/src/dom.js +++ b/steps/src/dom.js @@ -1,10 +1,7 @@ // TODO(sync-refactor): move sync logic into src/sync.js import { appState } from "./state.js"; import { formatUTCTime } from "./utils.js"; -import { throttledUpdateExplorer } from "./dataExplorer.js"; -// Also import VIDEO_FPS from constants import { VIDEO_FPS } from "./constants.js"; - // --- DOM Element References --- // export const themeToggleBtn = document.getElementById("theme-toggle"); @@ -86,89 +83,6 @@ export const toggleConfirmedOnly = document.getElementById("toggle-confirmed-onl export const explorerBtn = document.getElementById("explorer-btn"); - -//----------------------UPDATE FRAME Function----------------------// -// Updates the UI to reflect the current radar frame and synchronizes video playback. -export function updateFrame(frame, forceVideoSeek) { - const startTime = performance.now(); //start emasuring timer of performance. - if ( - !appState.vizData || - frame < 0 || - frame >= appState.vizData.radarFrames.length - ) - // Exit if no visualization data or invalid frame. - return; // Exit if no visualization data or invalid frame - appState.currentFrame = frame; - timelineSlider.value = appState.currentFrame; - frameCounter.textContent = `Frame: ${appState.currentFrame + 1} / ${ - appState.vizData.radarFrames.length - }`; - const frameData = appState.vizData.radarFrames[appState.currentFrame]; - if (toggleEgoSpeed.checked && frameData) { - // Update ego speed display if enabled. - const egoVy_kmh = (frameData.egoVelocity[1] * 3.6).toFixed(1); // Convert m/s to km/h and format - egoSpeedDisplay.textContent = `Ego: ${egoVy_kmh} km/h`; - egoSpeedDisplay.classList.remove("hidden"); - } else { - egoSpeedDisplay.classList.add("hidden"); // Hide ego speed display. - } - - // --- ADD THIS NEW BLOCK --- - if ( - frameData && - frameData.canVehSpeed_kmph !== null && - !isNaN(frameData.canVehSpeed_kmph) - ) { - canSpeedDisplay.textContent = `CAN: ${frameData.canVehSpeed_kmph.toFixed( - 1 - )} km/h`; - canSpeedDisplay.classList.remove("hidden"); - } else { - canSpeedDisplay.classList.add("hidden"); - } - // --- END OF NEW BLOCK --- - - let timeForUpdates = videoPlayer.currentTime; // NEW: Default to the video's current time - - if ( - forceVideoSeek && - videoPlayer.src && - videoPlayer.readyState > 1 && - appState.videoStartDate && - frameData - ) { - const offsetMs = parseFloat(offsetInput.value) || 0; - const targetRadarTimeMs = frameData.timestampMs; - const targetVideoTimeSec = (targetRadarTimeMs - offsetMs) / 1000; - if (targetVideoTimeSec >= 0 && targetVideoTimeSec <= videoPlayer.duration) { - // Ensure target time is within video duration - if (Math.abs(videoPlayer.currentTime - targetVideoTimeSec) > 0.05) { - // Check for significant drift - videoPlayer.currentTime = targetVideoTimeSec; // Seek video if drift is significant - } - // MODIFIED: Use the calculated target time for our updates, not the stale videoPlayer.currentTime - timeForUpdates = targetVideoTimeSec; // Update time for subsequent UI updates - } - } // End of forceVideoSeek block - - if (!appState.isPlaying) { - // MODIFIED: Use our new synchronized time variable - updatePersistentOverlays(timeForUpdates); - } - // --- End of fix --- - // --- START: Conditional Redraw Logic --- - // Only force a redraw from here if the animation loop is NOT running. - // When playing, the animationLoop is responsible for redrawing. - if (!appState.isPlaying && appState.p5_instance) appState.p5_instance.redraw(); - if (!appState.isPlaying && appState.speedGraphInstance) appState.speedGraphInstance.redraw(); - // --- NEW: Centralized Explorer Update --- - throttledUpdateExplorer(); - // --- END: Centralized Explorer Update --- - const endTime = performance.now(); - appState.lastFrameRenderTime = endTime - startTime; // <-- End timer and update state - -} - //----------------------Reset UI for New file Load----------------------// // Resets the UI to make sure everything is clean before new files load. export function resetUIForNewLoad() { @@ -209,16 +123,6 @@ export function resetUIForNewLoad() { speedGraphPlaceholder.classList.remove('hidden'); } -//----------------------RESET VISUALIZATION Function----------------------// -// Resets the visualization to its initial state. -export function resetVisualization() { - appState.isPlaying = false; - playPauseBtn.textContent = "Play"; - const numFrames = appState.vizData.radarFrames.length; - timelineSlider.max = numFrames > 0 ? numFrames - 1 : 0; - updateFrame(0, true); // Update to the first frame and force video seek -} - //----------------------CAN DISPLAY UPDATE Function----------------------// // Updates the CAN speed display based on the current media time. diff --git a/steps/src/main.js b/steps/src/main.js index 99bf04a..eb767d4 100644 --- a/steps/src/main.js +++ b/steps/src/main.js @@ -34,6 +34,8 @@ import { stopPlayback, forceResyncWithOffset, initSyncUIHandlers, + updateFrame, + resetVisualization, handleTimelineInput, } from "./sync.js"; import { radarSketch } from "./p5/radarSketch.js"; @@ -94,8 +96,6 @@ import { speedGraphContainer, speedGraphPlaceholder, toggleCloseUp, - updateFrame, - resetVisualization, updateDebugOverlay, timelineTooltip, saveSessionBtn, @@ -915,14 +915,11 @@ document.addEventListener("keydown", (event) => { if (key === "a") { toggleDebugOverlay.click(); toggleDebug2Overlay.click(); - if (isDebug1Visible && isDebug2Visible) { - radarInfoOverlay.classList.add("hidden"); - videoInfoOverlay.classList.add("hidden"); - return; - } - // Otherwise, make sure they are visible. - radarInfoOverlay.classList.remove("hidden"); - videoInfoOverlay.classList.remove("hidden"); + updatePersistentOverlays(videoPlayer.currentTime); + // The 'a' key is a shortcut to toggle all debug overlays on/off. + // The `updateDebugOverlay` and `updatePersistentOverlays` functions, + // which are called by the toggle's 'change' event listener, + // already handle the logic for showing/hiding the other overlays. } if (key === "m") { if (collapsibleMenu.classList.contains("-translate-x-full")) { diff --git a/steps/src/sync.js b/steps/src/sync.js index a98b8ed..03e6034 100644 --- a/steps/src/sync.js +++ b/steps/src/sync.js @@ -1,18 +1,33 @@ import { appState } from "./state.js"; import { + timelineSlider, speedSlider, offsetInput, stopBtn, playPauseBtn, - updateFrame, updateDebugOverlay, updatePersistentOverlays, videoPlayer, - timelineSlider, + frameCounter, + toggleEgoSpeed, + egoSpeedDisplay, + canSpeedDisplay, } from "./dom.js"; import { findRadarFrameIndexForTime } from "./utils.js"; import { throttledUpdateExplorer } from "./dataExplorer.js"; +// --- [START] MOVED FROM DOM.JS --- + +//----------------------RESET VISUALIZATION Function----------------------// +// Resets the visualization to its initial state. +export function resetVisualization() { + appState.isPlaying = false; + playPauseBtn.textContent = "Play"; + const numFrames = appState.vizData.radarFrames.length; + timelineSlider.max = numFrames > 0 ? numFrames - 1 : 0; + updateFrame(0, true); // Update to the first frame and force video seek +} + // --- NEW Playback Control Functions --- let seekDebounceTimer = null; @@ -56,6 +71,88 @@ export function forceResyncWithOffset() { updateFrame(appState.currentFrame, true); } +//----------------------UPDATE FRAME Function----------------------// +// Updates the UI to reflect the current radar frame and synchronizes video playback. +export function updateFrame(frame, forceVideoSeek) { + const startTime = performance.now(); //start emasuring timer of performance. + if ( + !appState.vizData || + frame < 0 || + frame >= appState.vizData.radarFrames.length + ) + // Exit if no visualization data or invalid frame. + return; // Exit if no visualization data or invalid frame + appState.currentFrame = frame; + timelineSlider.value = appState.currentFrame; + frameCounter.textContent = `Frame: ${appState.currentFrame + 1} / ${ + appState.vizData.radarFrames.length + }`; + const frameData = appState.vizData.radarFrames[appState.currentFrame]; + if (toggleEgoSpeed.checked && frameData) { + // Update ego speed display if enabled. + const egoVy_kmh = (frameData.egoVelocity[1] * 3.6).toFixed(1); // Convert m/s to km/h and format + egoSpeedDisplay.textContent = `Ego: ${egoVy_kmh} km/h`; + egoSpeedDisplay.classList.remove("hidden"); + } else { + egoSpeedDisplay.classList.add("hidden"); // Hide ego speed display. + } + + // --- ADD THIS NEW BLOCK --- + if ( + frameData && + frameData.canVehSpeed_kmph !== null && + !isNaN(frameData.canVehSpeed_kmph) + ) { + canSpeedDisplay.textContent = `CAN: ${frameData.canVehSpeed_kmph.toFixed( + 1 + )} km/h`; + canSpeedDisplay.classList.remove("hidden"); + } else { + canSpeedDisplay.classList.add("hidden"); + } + // --- END OF NEW BLOCK --- + + let timeForUpdates = videoPlayer.currentTime; // NEW: Default to the video's current time + + if ( + forceVideoSeek && + videoPlayer.src && + videoPlayer.readyState > 1 && + appState.videoStartDate && + frameData + ) { + const offsetMs = parseFloat(offsetInput.value) || 0; + const targetRadarTimeMs = frameData.timestampMs; + const targetVideoTimeSec = (targetRadarTimeMs - offsetMs) / 1000; + if (targetVideoTimeSec >= 0 && targetVideoTimeSec <= videoPlayer.duration) { + // Ensure target time is within video duration + if (Math.abs(videoPlayer.currentTime - targetVideoTimeSec) > 0.05) { + // Check for significant drift + videoPlayer.currentTime = targetVideoTimeSec; // Seek video if drift is significant + } + // MODIFIED: Use the calculated target time for our updates, not the stale videoPlayer.currentTime + timeForUpdates = targetVideoTimeSec; // Update time for subsequent UI updates + } + } // End of forceVideoSeek block + + if (!appState.isPlaying) { + // MODIFIED: Use our new synchronized time variable + updatePersistentOverlays(timeForUpdates); + } + // --- End of fix --- + // --- START: Conditional Redraw Logic --- + // Only force a redraw from here if the animation loop is NOT running. + // When playing, the animationLoop is responsible for redrawing. + if (!appState.isPlaying && appState.p5_instance) appState.p5_instance.redraw(); + if (!appState.isPlaying && appState.speedGraphInstance) appState.speedGraphInstance.redraw(); + // --- NEW: Centralized Explorer Update --- + throttledUpdateExplorer(); + // --- END: Centralized Explorer Update --- + const endTime = performance.now(); + appState.lastFrameRenderTime = endTime - startTime; // <-- End timer and update state +} +// --- [END] MOVED FROM DOM.JS --- + export function stopPlayback() { videoPlayer.pause(); if (appState.vizData) {