Browse Source

Keyboard shortcuts added.

R= Reset Visualization.
T= Toggle display tracks.
P= Toggle show predicted position.
A= Show advanced Debugs.
S= Toggle color by SNR mode.
D= Toggle Details of Objects.
C= Toggle close up mode.
refactor/modularize
RUSHIL AMBARISH KADU 9 months ago
parent
commit
f06a93a4f8
  1. 135
      steps/src/main.js

135
steps/src/main.js

@ -199,7 +199,7 @@ clearCacheBtn.addEventListener("click", async () => {
} }
}); });
// Event listener for saving the session // Event listener for saving the session
saveSessionBtn.addEventListener('click', () => {
saveSessionBtn.addEventListener("click", () => {
// We can only save a session if at least one data file has been loaded. // We can only save a session if at least one data file has been loaded.
if (!appState.jsonFilename && !appState.videoFilename) { if (!appState.jsonFilename && !appState.videoFilename) {
showModal("Nothing to save. Please load data files first."); showModal("Nothing to save. Please load data files first.");
@ -229,23 +229,27 @@ saveSessionBtn.addEventListener('click', () => {
closeUp: toggleCloseUp.checked, closeUp: toggleCloseUp.checked,
predictedPos: togglePredictedPos.checked, predictedPos: togglePredictedPos.checked,
covariance: toggleCovariance.checked, covariance: toggleCovariance.checked,
}
},
}; };
const sessionString = JSON.stringify(sessionState, null, 2); const sessionString = JSON.stringify(sessionState, null, 2);
const blob = new Blob([sessionString], { type: 'application/json' });
const blob = new Blob([sessionString], { type: "application/json" });
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
// --- Dynamic Filename Logic --- // --- Dynamic Filename Logic ---
const now = new Date(); const now = new Date();
const pad = (num) => String(num).padStart(2, '0');
const date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
const time = `${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`;
const pad = (num) => String(num).padStart(2, "0");
const date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(
now.getDate()
)}`;
const time = `${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(
now.getSeconds()
)}`;
const timestamp = `${date}_${time}`; const timestamp = `${date}_${time}`;
const defaultFilename = `visualizer-session_${timestamp}.json`; const defaultFilename = `visualizer-session_${timestamp}.json`;
// --- Trigger "Save As" Dialog --- // --- Trigger "Save As" Dialog ---
const a = document.createElement('a');
const a = document.createElement("a");
a.href = url; a.href = url;
// This is the key instruction for the browser. It suggests a filename // This is the key instruction for the browser. It suggests a filename
@ -266,12 +270,13 @@ loadSessionBtn.addEventListener("click", () => {
// This listener handles the selected session file. // This listener handles the selected session file.
sessionFileInput.addEventListener('change', (event) => {
sessionFileInput.addEventListener("change", (event) => {
const file = event.target.files[0]; const file = event.target.files[0];
if (!file) return; if (!file) return;
const reader = new FileReader(); const reader = new FileReader();
reader.onload = async (e) => { // Make the function async to use 'await'
reader.onload = async (e) => {
// Make the function async to use 'await'
try { try {
const sessionState = JSON.parse(e.target.result); const sessionState = JSON.parse(e.target.result);
@ -285,8 +290,14 @@ sessionFileInput.addEventListener('change', (event) => {
// 1. Before doing anything else, check if the required files exist in the cache. // 1. Before doing anything else, check if the required files exist in the cache.
// We use the same 'loadFreshFileFromDB' function that the startup process uses. // We use the same 'loadFreshFileFromDB' function that the startup process uses.
const videoBlob = await loadFreshFileFromDB("video", sessionState.videoFilename);
const jsonBlob = await loadFreshFileFromDB("json", sessionState.jsonFilename);
const videoBlob = await loadFreshFileFromDB(
"video",
sessionState.videoFilename
);
const jsonBlob = await loadFreshFileFromDB(
"json",
sessionState.jsonFilename
);
// 2. If either file is missing from the cache, show an informative error and stop. // 2. If either file is missing from the cache, show an informative error and stop.
if (!jsonBlob || !videoBlob) { if (!jsonBlob || !videoBlob) {
@ -294,36 +305,40 @@ sessionFileInput.addEventListener('change', (event) => {
Please manually load '${sessionState.jsonFilename}' and '${sessionState.videoFilename}' before loading this session.`); Please manually load '${sessionState.jsonFilename}' and '${sessionState.videoFilename}' before loading this session.`);
event.target.value = ''; // Reset file input
event.target.value = ""; // Reset file input
return; return;
} }
// 3. If we get here, it means the files ARE in the cache and match the session! // 3. If we get here, it means the files ARE in the cache and match the session!
// It is now safe to set localStorage and reload the page. // It is now safe to set localStorage and reload the page.
localStorage.setItem('jsonFilename', sessionState.jsonFilename || '');
localStorage.setItem('videoFilename', sessionState.videoFilename || '');
localStorage.setItem('visualizerOffset', sessionState.offset || '0');
localStorage.setItem('playbackSpeed', sessionState.playbackSpeed || '1');
localStorage.setItem('snrMin', sessionState.snrMin || '');
localStorage.setItem('snrMax', sessionState.snrMax || '');
localStorage.setItem("jsonFilename", sessionState.jsonFilename || "");
localStorage.setItem("videoFilename", sessionState.videoFilename || "");
localStorage.setItem("visualizerOffset", sessionState.offset || "0");
localStorage.setItem("playbackSpeed", sessionState.playbackSpeed || "1");
localStorage.setItem("snrMin", sessionState.snrMin || "");
localStorage.setItem("snrMax", sessionState.snrMax || "");
if (sessionState.toggles) { if (sessionState.toggles) {
localStorage.setItem('togglesState', JSON.stringify(sessionState.toggles));
localStorage.setItem(
"togglesState",
JSON.stringify(sessionState.toggles)
);
} }
// Inform the user and then reload the page to apply the session. // Inform the user and then reload the page to apply the session.
showModal("Session files found in cache. The application will now reload.").then(() => {
showModal(
"Session files found in cache. The application will now reload."
).then(() => {
window.location.reload(); window.location.reload();
}); });
// --- END: New Robust Session Check --- // --- END: New Robust Session Check ---
} catch (error) { } catch (error) {
showModal("Error: Could not parse the session file. It may be invalid."); showModal("Error: Could not parse the session file. It may be invalid.");
console.error("Session load error:", error); console.error("Session load error:", error);
} }
}; };
reader.readAsText(file); reader.readAsText(file);
event.target.value = ''; // Clear the input for future loads.
event.target.value = ""; // Clear the input for future loads.
}); });
// --- END: Add Session Management Logic --- // --- END: Add Session Management Logic ---
@ -685,29 +700,83 @@ videoPlayer.addEventListener("ended", () => {
}); });
document.addEventListener("keydown", (event) => { document.addEventListener("keydown", (event) => {
if (
!appState.vizData ||
["ArrowRight", "ArrowLeft"].indexOf(event.key) === -1
)
// --- FIX APPLIED HERE ---
// We only want to block shortcuts if the user is actively typing in a text or number input.
// This allows shortcuts to work even when other elements, like the timeline slider, are focused.
const isTextInputFocused = event.target.tagName === 'INPUT' && (event.target.type === 'text' || event.target.type === 'number');
if (isTextInputFocused) {
return; return;
}
// --- END OF FIX ---
const key = event.key;
// We can add any new shortcut keys to this array.
const recognizedKeys = ["ArrowRight", "ArrowLeft", " ", "1", "2", "3", "4", "t", "d", "c", "r", "p", "a", "s"];
if (!appState.vizData || !recognizedKeys.includes(key)) {
return;
}
event.preventDefault(); event.preventDefault();
// --- Spacebar for Play/Pause ---
if (key === " ") {
playPauseBtn.click();
}
// --- Arrow keys for frame-by-frame seeking ---
if (key === "ArrowRight" || key === "ArrowLeft") {
if (appState.isPlaying) { if (appState.isPlaying) {
appState.isPlaying = false;
playPauseBtn.textContent = "Play";
videoPlayer.pause();
playPauseBtn.click();
} }
let newFrame = appState.currentFrame; let newFrame = appState.currentFrame;
if (event.key === "ArrowRight")
if (key === "ArrowRight") {
newFrame = Math.min( newFrame = Math.min(
appState.vizData.radarFrames.length - 1, appState.vizData.radarFrames.length - 1,
appState.currentFrame + 1 appState.currentFrame + 1
); );
else if (event.key === "ArrowLeft")
} else if (key === "ArrowLeft") {
newFrame = Math.max(0, appState.currentFrame - 1); newFrame = Math.max(0, appState.currentFrame - 1);
}
if (newFrame !== appState.currentFrame) { if (newFrame !== appState.currentFrame) {
updateFrame(newFrame, true); updateFrame(newFrame, true);
appState.mediaTimeStart = videoPlayer.currentTime;
appState.masterClockStart = performance.now();
}
}
// --- Number keys for color modes ---
if (key >= '1' && key <= '4') {
const colorToggles = [
toggleSnrColor,
toggleClusterColor,
toggleInlierColor,
toggleStationaryColor,
];
const toggleIndex = parseInt(key) - 1;
if (colorToggles[toggleIndex]) {
colorToggles[toggleIndex].click();
}
}
if (key === "t") {
toggleTracks.click();
}
if (key === "d") {
toggleVelocity.click();
}
if (key === "c") {
toggleCloseUp.click();
}
if (key === "r") {
resetVisualization();
}
if (key === "p") {
togglePredictedPos.click();
}
if (key === "s") {
toggleSnrColor.click();
}
if (key === "a") {
toggleDebugOverlay.click();
toggleDebug2Overlay.click();
} }
}); });

Loading…
Cancel
Save