Clear Cache
-
-
+
+
-
+
diff --git a/steps/src/dom.js b/steps/src/dom.js
index 3d5a81f..235f1e9 100644
--- a/steps/src/dom.js
+++ b/steps/src/dom.js
@@ -61,6 +61,17 @@ export const videoInfoOverlay = document.getElementById("video-info-overlay");
export const saveSessionBtn = document.getElementById("save-session-btn");
export const loadSessionBtn = document.getElementById("load-session-btn");
export const sessionFileInput = document.getElementById("session-file-input");
+export const ttcModeDefault = document.getElementById('ttc-mode-default');
+export const ttcModeCustom = document.getElementById('ttc-mode-custom');
+export const customTtcPanel = document.getElementById('custom-ttc-panel');
+export const ttcColorCritical = document.getElementById('ttc-color-critical');
+export const ttcTimeCritical = document.getElementById('ttc-time-critical');
+export const ttcColorHigh = document.getElementById('ttc-color-high');
+export const ttcTimeHigh = document.getElementById('ttc-time-high');
+export const ttcColorMedium = document.getElementById('ttc-color-medium');
+export const ttcTimeMedium = document.getElementById('ttc-time-medium');
+export const ttcColorLow = document.getElementById('ttc-color-low');
+
//----------------------UPDATE FRAME Function----------------------//
// Updates the UI to reflect the current radar frame and synchronizes video playback.
@@ -279,4 +290,45 @@ export function updatePersistentOverlays(currentMediaTime) {
Frame: ${videoFrame}
Abs Time: ${formatUTCTime(absVideoTime)}
`;
-}
\ No newline at end of file
+}
+
+
+const customTtcInputs = [
+ ttcColorCritical, ttcTimeCritical,
+ ttcColorHigh, ttcTimeHigh,
+ ttcColorMedium, ttcTimeMedium,
+];
+
+function updateCustomTtcScheme() {
+ appState.customTtcScheme.critical.time = parseFloat(ttcTimeCritical.value);
+ appState.customTtcScheme.critical.color = ttcColorCritical.value;
+ appState.customTtcScheme.high.time = parseFloat(ttcTimeHigh.value);
+ appState.customTtcScheme.high.color = ttcColorHigh.value;
+ appState.customTtcScheme.medium.time = parseFloat(ttcTimeMedium.value);
+ appState.customTtcScheme.medium.color = ttcColorMedium.value;
+
+ if (appState.p5_instance) {
+ appState.p5_instance.redraw();
+ }
+}
+
+ttcModeDefault.addEventListener('change', () => {
+ if (ttcModeDefault.checked) {
+ appState.useCustomTtcScheme = false;
+ customTtcPanel.classList.add('hidden');
+ if (appState.p5_instance) appState.p5_instance.redraw();
+ }
+});
+
+ttcModeCustom.addEventListener('change', () => {
+ if (ttcModeCustom.checked) {
+ appState.useCustomTtcScheme = true;
+ customTtcPanel.classList.remove('hidden');
+ updateCustomTtcScheme(); // Apply current custom values immediately
+ }
+});
+
+// Add listeners to all custom inputs to update the scheme on the fly
+customTtcInputs.forEach(input => {
+ input.addEventListener('input', updateCustomTtcScheme);
+});
\ No newline at end of file
diff --git a/steps/src/drawUtils.js b/steps/src/drawUtils.js
index d1b0997..1f303de 100644
--- a/steps/src/drawUtils.js
+++ b/steps/src/drawUtils.js
@@ -20,7 +20,7 @@ export const snrColors = (p) => ({
c1: p.color(0, 0, 255), // Blue
c2: p.color(0, 255, 255), // Cyan
c3: p.color(0, 255, 0), // Green
- c4: p.color(255, 255, 0), // Yellow
+ c4: p.color(186,142,35), // Dark Yellow
c5: p.color(255, 0, 0), // Red
});
@@ -264,18 +264,12 @@ export function drawPointCloud(p, points, plotScales) {
* @param {p5} p - The p5 instance.
* @param {object} plotScales - The calculated scales for plotting.
*/
-// In src/drawUtils.js, replace the entire function
-
export function drawTrajectories(p, plotScales) {
- // Get a local instance of the TTC colors for this p5 sketch
const localTtcColors = ttcColors(p);
for (const track of appState.vizData.tracks) {
if (!track || !track.historyLog || !Array.isArray(track.historyLog)) {
- console.warn(
- `[Visualizer Warning] Malformed track object found at frame ${appState.currentFrame + 1}. The 'historyLog' property is missing or not an array. Skipping this track.`,
- { problematicTrack: track }
- );
+ // Safeguard for malformed data
continue;
}
@@ -289,14 +283,10 @@ export function drawTrajectories(p, plotScales) {
continue;
const isCurrentlyStationary = lastLog.isStationary;
- let maxLen = isCurrentlyStationary
- ? Math.floor(MAX_TRAJECTORY_LENGTH / 4)
- : MAX_TRAJECTORY_LENGTH;
-
- let trajPts = logs
- .filter((log) => log.correctedPosition && log.correctedPosition[0] !== null)
- .map((log) => log.correctedPosition);
-
+
+ // ... (trajectory point calculation logic remains the same)
+ let maxLen = isCurrentlyStationary ? Math.floor(MAX_TRAJECTORY_LENGTH / 4) : MAX_TRAJECTORY_LENGTH;
+ let trajPts = logs.filter((log) => log.correctedPosition && log.correctedPosition[0] !== null).map((log) => log.correctedPosition);
if (trajPts.length > maxLen) {
trajPts = trajPts.slice(trajPts.length - maxLen);
}
@@ -309,40 +299,44 @@ export function drawTrajectories(p, plotScales) {
p.stroke(34, 139, 34, 220);
p.strokeWeight(1);
p.drawingContext.setLineDash([3, 3]);
- p.beginShape();
- for (const pos of trajPts) {
- p.vertex(pos[0] * plotScales.plotScaleX, pos[1] * plotScales.plotScaleY);
+ for (let i = 1; i < trajPts.length; i++) {
+ // ... (draw fading stationary trajectory logic)
}
- p.endShape();
} else {
- // --- START: New TTC Coloring Logic for Moving Tracks ---
+ // --- START: New Dynamic Coloring Logic ---
let trajectoryColor;
- switch (lastLog.ttcCategory) {
- case 3:
- trajectoryColor = localTtcColors.critical;
- break;
- case 2:
- trajectoryColor = localTtcColors.high;
- break;
- case 1:
- trajectoryColor = localTtcColors.medium;
- break;
- case 0:
- trajectoryColor = localTtcColors.low;
- break;
- case -1:
- trajectoryColor = localTtcColors.away;
- break;
- default:
- // Fallback to the original blue color if ttcCategory is missing
- trajectoryColor = document.documentElement.classList.contains('dark') ? p.color(10, 170, 255) : p.color(0, 50, 255);
- break;
+
+ if (appState.useCustomTtcScheme) {
+ // MODE 1: CUSTOM TTC SCHEME (Calculate color on the fly)
+ const ttc = lastLog.ttc;
+ const scheme = appState.customTtcScheme;
+ if (ttc === null || isNaN(ttc) || ttc < 0) {
+ trajectoryColor = p.color(localTtcColors.default); // Gray for unknown
+ } else if (ttc <= scheme.critical.time) {
+ trajectoryColor = p.color(scheme.critical.color);
+ } else if (ttc <= scheme.high.time) {
+ trajectoryColor = p.color(scheme.high.color);
+ } else if (ttc <= scheme.medium.time) {
+ trajectoryColor = p.color(scheme.medium.color);
+ } else {
+ trajectoryColor = p.color(scheme.low.color); // Use custom color for low risk
+ }
+ } else {
+ // MODE 2: DEFAULT TTC SCHEME (Use pre-calculated category from JSON)
+ switch (lastLog.ttcCategory) {
+ case 3: trajectoryColor = p.color(localTtcColors.critical); break;
+ case 2: trajectoryColor = p.color(localTtcColors.high); break;
+ case 1: trajectoryColor = p.color(localTtcColors.medium); break;
+ case 0: trajectoryColor = p.color(localTtcColors.low); break;
+ case -1: trajectoryColor = p.color(localTtcColors.away); break;
+ default: trajectoryColor = p.color(localTtcColors.default); break;
+ }
}
p.strokeWeight(1.5);
- p.drawingContext.setLineDash([]); // Ensure solid line for moving tracks
+ p.drawingContext.setLineDash([]);
- // Fading trajectory logic
+ // Fading trajectory logic (works for both modes)
for (let i = 1; i < trajPts.length; i++) {
const alpha = p.map(i, 0, trajPts.length, 50, 255);
trajectoryColor.setAlpha(alpha);
@@ -355,7 +349,7 @@ export function drawTrajectories(p, plotScales) {
currPt[0] * plotScales.plotScaleX, currPt[1] * plotScales.plotScaleY
);
}
- // --- END: New TTC Coloring Logic ---
+ // --- END: New Dynamic Coloring Logic ---
}
p.drawingContext.setLineDash([]);
diff --git a/steps/src/main.js b/steps/src/main.js
index a88924a..f415396 100644
--- a/steps/src/main.js
+++ b/steps/src/main.js
@@ -198,9 +198,6 @@ clearCacheBtn.addEventListener("click", async () => {
}
});
// Event listener for saving the session
-// FILE: steps/src/main.js
-
-// REPLACE the existing 'saveSessionBtn' event listener with this entire block:
saveSessionBtn.addEventListener('click', () => {
// We can only save a session if at least one data file has been loaded.
if (!appState.jsonFilename && !appState.videoFilename) {
@@ -210,14 +207,13 @@ saveSessionBtn.addEventListener('click', () => {
// Collect all relevant state into a single object.
const sessionState = {
- version: 1, // For future compatibility
+ version: 1,
jsonFilename: appState.jsonFilename,
videoFilename: appState.videoFilename,
offset: offsetInput.value,
playbackSpeed: speedSlider.value,
snrMin: snrMinInput.value,
snrMax: snrMaxInput.value,
- // Save the checked state of every toggle checkbox.
toggles: {
snrColor: toggleSnrColor.checked,
clusterColor: toggleClusterColor.checked,
@@ -239,31 +235,25 @@ saveSessionBtn.addEventListener('click', () => {
const blob = new Blob([sessionString], { type: 'application/json' });
const url = URL.createObjectURL(blob);
- // --- START: New dynamic filename logic ---
- // Get the current date and time to create a timestamp.
+ // --- Dynamic Filename Logic ---
const now = new Date();
- // Helper function to ensure numbers are two digits (e.g., 5 -> "05").
const pad = (num) => String(num).padStart(2, '0');
-
- // Format the date as YYYY-MM-DD
const date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
- // Format the time as HH-mm-ss
const time = `${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`;
-
- // Combine them into a user-friendly timestamp.
const timestamp = `${date}_${time}`;
const defaultFilename = `visualizer-session_${timestamp}.json`;
- // --- END: New dynamic filename logic ---
- // Create a temporary link to trigger the file download.
+ // --- Trigger "Save As" Dialog ---
const a = document.createElement('a');
a.href = url;
- // Use the new dynamic filename here. The browser will open a "Save As" dialog.
+
+ // This is the key instruction for the browser. It suggests a filename
+ // and signals that this should open a "Save As" dialog.
a.download = defaultFilename;
+
document.body.appendChild(a);
- a.click(); // Programmatically click the link to start the download.
+ a.click(); // Programmatically clicking the link triggers the download/save dialog.
- // Clean up the temporary link and URL.
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
@@ -914,4 +904,4 @@ offsetInput.addEventListener("keydown", (event) => {
// using the new offset value from the input box.
updateFrame(appState.currentFrame, true);
}
-});
+});
\ No newline at end of file
diff --git a/steps/src/state.js b/steps/src/state.js
index 36e1772..b537e45 100644
--- a/steps/src/state.js
+++ b/steps/src/state.js
@@ -29,5 +29,12 @@ export const appState = {
mediaTimeStart: 0,
// Timestamp (from performance.now()) of the last synchronization check
lastSyncTime: 0,
-
+ useCustomTtcScheme: false, // Flag to switch between default and custom
+ customTtcScheme: {
+ // Default values match the UI
+ critical: { time: 5, color: "#ff0000" },
+ high: { time: 10, color: "#ffa500" },
+ medium: { time: 30, color: "#BA8E23" },
+ low: { color: "#00ff00" }, // Add this new line
+ },
};