You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
9.4 KiB
247 lines
9.4 KiB
import { appState } from "./state.js";
|
|
import { findLastCanIndexBefore } from "./utils.js";
|
|
import { VIDEO_FPS } from "./constants.js";
|
|
|
|
// --- DOM Element References --- //
|
|
|
|
export const canvasContainer = document.getElementById("canvas-container");
|
|
export const canvasPlaceholder = document.getElementById("canvas-placeholder");
|
|
export const videoPlayer = document.getElementById("video-player");
|
|
export const videoPlaceholder = document.getElementById("video-placeholder");
|
|
export const loadJsonBtn = document.getElementById("load-json-btn");
|
|
export const loadVideoBtn = document.getElementById("load-video-btn");
|
|
export const loadCanBtn = document.getElementById("load-can-btn");
|
|
export const jsonFileInput = document.getElementById("json-file-input");
|
|
export const videoFileInput = document.getElementById("video-file-input");
|
|
export const canFileInput = document.getElementById("can-file-input");
|
|
export const playPauseBtn = document.getElementById("play-pause-btn");
|
|
export const stopBtn = document.getElementById("stop-btn");
|
|
export const timelineSlider = document.getElementById("timeline-slider");
|
|
export const frameCounter = document.getElementById("frame-counter");
|
|
export const offsetInput = document.getElementById("offset-input");
|
|
export const speedSlider = document.getElementById("speed-slider");
|
|
export const speedDisplay = document.getElementById("speed-display");
|
|
export const featureToggles = document.getElementById("feature-toggles");
|
|
export const toggleSnrColor = document.getElementById("toggle-snr-color");
|
|
export const toggleClusterColor = document.getElementById(
|
|
"toggle-cluster-color"
|
|
);
|
|
export const toggleInlierColor = document.getElementById("toggle-inlier-color");
|
|
export const toggleStationaryColor = document.getElementById(
|
|
"toggle-stationary-color"
|
|
);
|
|
export const toggleVelocity = document.getElementById("toggle-velocity");
|
|
export const toggleTracks = document.getElementById("toggle-tracks");
|
|
export const toggleEgoSpeed = document.getElementById("toggle-ego-speed");
|
|
export const toggleFrameNorm = document.getElementById("toggle-frame-norm");
|
|
export const toggleDebugOverlay = document.getElementById(
|
|
"toggle-debug-overlay"
|
|
);
|
|
export const egoSpeedDisplay = document.getElementById("ego-speed-display");
|
|
export const canSpeedDisplay = document.getElementById("can-speed-display");
|
|
export const debugOverlay = document.getElementById("debug-overlay");
|
|
export const toggleDebug2Overlay = document.getElementById(
|
|
"toggle-debug2-overlay"
|
|
);
|
|
export const snrMinInput = document.getElementById("snr-min-input");
|
|
export const snrMaxInput = document.getElementById("snr-max-input");
|
|
export const applySnrBtn = document.getElementById("apply-snr-btn");
|
|
export const autoOffsetIndicator = document.getElementById(
|
|
"auto-offset-indicator"
|
|
);
|
|
export const clearCacheBtn = document.getElementById("clear-cache-btn");
|
|
export const speedGraphContainer = document.getElementById(
|
|
"speed-graph-container"
|
|
);
|
|
export const speedGraphPlaceholder = document.getElementById(
|
|
"speed-graph-placeholder"
|
|
);
|
|
export const modalContainer = document.getElementById("modal-container");
|
|
export const modalOverlay = document.getElementById("modal-overlay");
|
|
export const modalContent = document.getElementById("modal-content");
|
|
export const modalText = document.getElementById("modal-text");
|
|
export const modalOkBtn = document.getElementById("modal-ok-btn");
|
|
export const modalCancelBtn = document.getElementById("modal-cancel-btn");
|
|
export const toggleCloseUp = document.getElementById("toggle-close-up");
|
|
|
|
//----------------------UPDATE FRAME Function----------------------//
|
|
|
|
// Located in: src/dom.js
|
|
|
|
export function updateFrame(frame, forceVideoSeek) {
|
|
if (
|
|
!appState.vizData ||
|
|
frame < 0 ||
|
|
frame >= appState.vizData.radarFrames.length
|
|
)
|
|
return;
|
|
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) {
|
|
const egoVy_kmh = (frameData.egoVelocity[1] * 3.6).toFixed(1);
|
|
egoSpeedDisplay.textContent = `Ego: ${egoVy_kmh} km/h`;
|
|
egoSpeedDisplay.classList.remove("hidden");
|
|
} else {
|
|
egoSpeedDisplay.classList.add("hidden");
|
|
}
|
|
|
|
// --- Start of fix ---
|
|
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) {
|
|
if (Math.abs(videoPlayer.currentTime - targetVideoTimeSec) > 0.05) {
|
|
videoPlayer.currentTime = targetVideoTimeSec;
|
|
}
|
|
// MODIFIED: Use the calculated target time for our updates, not the stale videoPlayer.currentTime
|
|
timeForUpdates = targetVideoTimeSec;
|
|
}
|
|
}
|
|
|
|
if (!appState.isPlaying) {
|
|
// MODIFIED: Use our new synchronized time variable
|
|
updateCanDisplay(timeForUpdates);
|
|
updateDebugOverlay(timeForUpdates);
|
|
}
|
|
// --- End of fix ---
|
|
|
|
if (appState.p5_instance) appState.p5_instance.redraw();
|
|
if (appState.speedGraphInstance && !appState.isPlaying)
|
|
appState.speedGraphInstance.redraw();
|
|
}
|
|
|
|
//----------------------RESET VISUALIZATION Function----------------------//
|
|
|
|
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);
|
|
}
|
|
|
|
//----------------------CAN DISPLAY UPDATE Function----------------------//
|
|
|
|
export function updateCanDisplay(currentMediaTime) {
|
|
if (
|
|
appState.canData.length > 0 &&
|
|
videoPlayer.src &&
|
|
appState.videoStartDate
|
|
) {
|
|
const videoAbsoluteTimeMs =
|
|
appState.videoStartDate.getTime() + currentMediaTime * 1000;
|
|
const canIndex = findLastCanIndexBefore(
|
|
videoAbsoluteTimeMs,
|
|
appState.canData
|
|
);
|
|
if (canIndex !== -1) {
|
|
const currentCanMessage = appState.canData[canIndex];
|
|
canSpeedDisplay.textContent = `CAN: ${currentCanMessage.speed} km/h`;
|
|
canSpeedDisplay.classList.remove("hidden");
|
|
} else {
|
|
canSpeedDisplay.classList.add("hidden");
|
|
}
|
|
} else {
|
|
canSpeedDisplay.classList.add("hidden");
|
|
}
|
|
}
|
|
|
|
//----------------------DEBUG OVERLAY UPDATE Function----------------------//
|
|
|
|
export function updateDebugOverlay(currentMediaTime) {
|
|
// Check the state of both debug toggles
|
|
const isDebug1Visible = toggleDebugOverlay.checked;
|
|
const isDebug2Visible = toggleDebug2Overlay.checked;
|
|
|
|
// If neither is checked, hide the overlay and stop
|
|
if (!isDebug1Visible && !isDebug2Visible) {
|
|
debugOverlay.classList.add("hidden");
|
|
return;
|
|
}
|
|
|
|
debugOverlay.classList.remove("hidden");
|
|
let content = [];
|
|
|
|
// --- Logic for the original debug overlay ---
|
|
if (isDebug1Visible) {
|
|
content.push(`--- Basic Info ---`);
|
|
if (appState.videoStartDate) {
|
|
const videoAbsoluteTimeMs =
|
|
appState.videoStartDate.getTime() + currentMediaTime * 1000;
|
|
content.push(`Media Time (s): ${currentMediaTime.toFixed(3)}`);
|
|
content.push(`Video Frame: ${Math.floor(currentMediaTime * VIDEO_FPS)}`);
|
|
content.push(
|
|
`Vid Abs Time: ${new Date(videoAbsoluteTimeMs)
|
|
.toISOString()
|
|
.split("T")[1]
|
|
.replace("Z", "")}`
|
|
);
|
|
} else {
|
|
content.push("Video not loaded...");
|
|
}
|
|
if (
|
|
appState.vizData &&
|
|
appState.vizData.radarFrames[appState.currentFrame]
|
|
) {
|
|
content.push(`Radar Frame: ${appState.currentFrame + 1}`);
|
|
const frameTime =
|
|
appState.vizData.radarFrames[appState.currentFrame].timestampMs;
|
|
content.push(
|
|
`Radar Abs Time: ${new Date(
|
|
appState.videoStartDate.getTime() + frameTime
|
|
)
|
|
.toISOString()
|
|
.split("T")[1]
|
|
.replace("Z", "")}`
|
|
);
|
|
}
|
|
}
|
|
|
|
// --- Logic for the new advanced debug overlay ---
|
|
if (isDebug2Visible) {
|
|
content.push(`--- Sync Diagnostics ---`);
|
|
if (
|
|
appState.videoStartDate &&
|
|
appState.vizData &&
|
|
appState.vizData.radarFrames[appState.currentFrame]
|
|
) {
|
|
const currentRadarFrame =
|
|
appState.vizData.radarFrames[appState.currentFrame];
|
|
const targetRadarTimeMs = currentRadarFrame.timestampMs;
|
|
const driftMs = currentMediaTime * 1000 - targetRadarTimeMs;
|
|
|
|
// 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
|
|
|
|
content.push(`Video Time (s): ${currentMediaTime.toFixed(3)}`);
|
|
content.push(`Target Radar Time (ms): ${targetRadarTimeMs.toFixed(0)}`);
|
|
content.push(
|
|
`Drift (ms): <b style="color: ${driftColor};">${driftMs.toFixed(0)}</b>`
|
|
);
|
|
content.push(
|
|
`Video Start Time: ${appState.videoStartDate.toISOString()}`
|
|
);
|
|
content.push(
|
|
`Radar Start Time: ${new Date(appState.radarStartTimeMs).toISOString()}`
|
|
);
|
|
content.push(`Calculated Offset (ms): ${offsetInput.value}`);
|
|
} else {
|
|
content.push("Load video and radar data to see sync info.");
|
|
}
|
|
}
|
|
|
|
debugOverlay.innerHTML = content.join("<br>");
|
|
}
|