Browse Source

Fixed speed graph and Full Screen Button.

refactor/modularize
RUSHIL AMBARISH KADU 6 months ago
parent
commit
f05314a6d4
  1. 6
      steps/index.html
  2. 14
      steps/src/main.js
  3. 163
      steps/src/p5/speedGraphSketch.js

6
steps/index.html

@ -191,10 +191,8 @@
<div class="absolute top-4 right-4 flex items-center gap-2"> <div class="absolute top-4 right-4 flex items-center gap-2">
<button id="fullscreen-btn" type="button" <button id="fullscreen-btn" type="button"
class="bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 shadow-sm hover:bg-gray-200 dark:hover:bg-gray-600 active:scale-95 active:shadow-inner font-medium rounded-lg text-xs px-4 py-2 transition-all"> class="bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 shadow-sm hover:bg-gray-200 dark:hover:bg-gray-600 active:scale-95 active:shadow-inner font-medium rounded-lg text-xs px-4 py-2 transition-all">
FULLSCREEN
<span
class="text-xs font-mono bg-gray-200 dark:bg-gray-600 text-gray-700 dark:text-gray-200 px-1.5 py-0.5 rounded">
<br>(F11)
FULLSCREEN<br>
<span class="text-xs font-mono bg-gray-200 dark:bg-gray-600 text-gray-700 dark:text-gray-200 px-1.5 py-0.5 rounded">(F11)
</span> </span>
</button> </button>
<button id="explorer-btn" type="button" <button id="explorer-btn" type="button"

14
steps/src/main.js

@ -99,8 +99,6 @@ import {
fullscreenBtn, fullscreenBtn,
mainContent, mainContent,
closeMenuBtn, closeMenuBtn,
fullscreenEnterIcon,
fullscreenExitIcon,
menuScrim, menuScrim,
toggleConfirmedOnly, toggleConfirmedOnly,
resetUIForNewLoad, resetUIForNewLoad,
@ -650,18 +648,6 @@ fullscreenBtn.addEventListener("click", () => {
} }
}); });
// This listener updates the icon whenever fullscreen state changes,
// whether it's triggered by our button or the F11 key.
document.addEventListener("fullscreenchange", () => {
if (document.fullscreenElement) {
fullscreenEnterIcon.classList.add("hidden");
fullscreenExitIcon.classList.remove("hidden");
} else {
fullscreenEnterIcon.classList.remove("hidden");
fullscreenExitIcon.classList.add("hidden");
}
});
// Event listener for offset input change. // Event listener for offset input change.
offsetInput.addEventListener("input", () => { offsetInput.addEventListener("input", () => {
autoOffsetIndicator.classList.add("hidden"); autoOffsetIndicator.classList.add("hidden");

163
steps/src/p5/speedGraphSketch.js

@ -1,9 +1,11 @@
// File: src/speedGraphSketch.js
import { appState } from "../state.js"; import { appState } from "../state.js";
import { videoPlayer, speedGraphContainer } from "../dom.js"; import { videoPlayer, speedGraphContainer } from "../dom.js";
export const speedGraphSketch = function (p) { export const speedGraphSketch = function (p) {
let staticBuffer, minSpeed, maxSpeed, videoDuration; let staticBuffer, minSpeed, maxSpeed, videoDuration;
const pad = { top: 20, right: 130, bottom: 30, left: 50 };
// Reserve more top space for legend and reduce the right padding so the plot can use more width.
const pad = { top: 48, right: 20, bottom: 30, left: 50 };
p.drawStaticGraphToBuffer = function (radarData) { p.drawStaticGraphToBuffer = function (radarData) {
const b = staticBuffer; const b = staticBuffer;
@ -16,17 +18,16 @@ export const speedGraphSketch = function (p) {
b.push(); b.push();
b.stroke(gridColor); b.stroke(gridColor);
b.strokeWeight(1); b.strokeWeight(1);
// Y axis
b.line(pad.left, pad.top, pad.left, b.height - pad.bottom); b.line(pad.left, pad.top, pad.left, b.height - pad.bottom);
b.line(
pad.left,
b.height - pad.bottom,
b.width - pad.right,
b.height - pad.bottom
);
// X axis across the new plotting width
b.line(pad.left, b.height - pad.bottom, b.width - pad.right, b.height - pad.bottom);
b.textAlign(b.RIGHT, b.CENTER); b.textAlign(b.RIGHT, b.CENTER);
b.noStroke(); b.noStroke();
b.fill(textColor); b.fill(textColor);
b.textSize(10); b.textSize(10);
for (let s = minSpeed; s <= maxSpeed; s += 10) { for (let s = minSpeed; s <= maxSpeed; s += 10) {
const y = b.map(s, minSpeed, maxSpeed, b.height - pad.bottom, pad.top); const y = b.map(s, minSpeed, maxSpeed, b.height - pad.bottom, pad.top);
b.text(s, pad.left - 8, y); b.text(s, pad.left - 8, y);
@ -40,51 +41,41 @@ export const speedGraphSketch = function (p) {
b.line(pad.left + 1, y, b.width - pad.right, y); b.line(pad.left + 1, y, b.width - pad.right, y);
b.noStroke(); b.noStroke();
} }
b.fill(textColor); b.fill(textColor);
b.text("km/h", pad.left - 8, pad.top - 8); b.text("km/h", pad.left - 8, pad.top - 8);
b.textAlign(b.CENTER, b.TOP); b.textAlign(b.CENTER, b.TOP);
b.noStroke(); b.noStroke();
b.fill(isDark ? 180 : 150); b.fill(isDark ? 180 : 150);
const tInt = Math.max(1, Math.floor(videoDuration / 10)); const tInt = Math.max(1, Math.floor(videoDuration / 10));
for (let t = 0; t <= videoDuration; t += tInt) { for (let t = 0; t <= videoDuration; t += tInt) {
const x = b.map(t, 0, videoDuration, pad.left, b.width - pad.right); const x = b.map(t, 0, videoDuration, pad.left, b.width - pad.right);
b.text(Math.round(t), x, b.height - pad.bottom + 5); b.text(Math.round(t), x, b.height - pad.bottom + 5);
} }
b.fill(textColor); b.fill(textColor);
b.text("Time (s)", b.width / 2, b.height - pad.bottom + 18);
b.text("Time (s)", (pad.left + (b.width - pad.right)) / 2, b.height - pad.bottom + 18);
b.pop(); b.pop();
// Draw CAN speed (solid blue)
if (radarData && radarData.radarFrames) { if (radarData && radarData.radarFrames) {
b.noFill(); b.noFill();
b.stroke(0, 150, 255); // Blue for CAN speed
b.stroke(0, 150, 255);
b.strokeWeight(1.5); b.strokeWeight(1.5);
b.beginShape(); b.beginShape();
for (const frame of radarData.radarFrames) { for (const frame of radarData.radarFrames) {
if (frame.canVehSpeed_kmph === null || isNaN(frame.canVehSpeed_kmph)) {
continue;
}
if (frame.canVehSpeed_kmph === null || isNaN(frame.canVehSpeed_kmph)) continue;
const relTime = frame.timestampMs / 1000; const relTime = frame.timestampMs / 1000;
if (relTime >= 0 && relTime <= videoDuration) { if (relTime >= 0 && relTime <= videoDuration) {
const x = b.map(
relTime,
0,
videoDuration,
pad.left,
b.width - pad.right
);
const y = b.map(
frame.canVehSpeed_kmph,
minSpeed,
maxSpeed,
b.height - pad.bottom,
pad.top
);
const x = b.map(relTime, 0, videoDuration, pad.left, b.width - pad.right);
const y = b.map(frame.canVehSpeed_kmph, minSpeed, maxSpeed, b.height - pad.bottom, pad.top);
b.vertex(x, y); b.vertex(x, y);
} }
} }
b.endShape(); b.endShape();
} }
// Draw Ego speed (dashed green)
if (radarData && radarData.radarFrames) { if (radarData && radarData.radarFrames) {
b.stroke(0, 200, 100); b.stroke(0, 200, 100);
b.drawingContext.setLineDash([5, 5]); b.drawingContext.setLineDash([5, 5]);
@ -92,21 +83,9 @@ export const speedGraphSketch = function (p) {
for (const frame of radarData.radarFrames) { for (const frame of radarData.radarFrames) {
const relTime = frame.timestampMs / 1000; const relTime = frame.timestampMs / 1000;
if (relTime >= 0 && relTime <= videoDuration) { if (relTime >= 0 && relTime <= videoDuration) {
const x = b.map(
relTime,
0,
videoDuration,
pad.left,
b.width - pad.right
);
const x = b.map(relTime, 0, videoDuration, pad.left, b.width - pad.right);
const egoSpeedKmh = frame.egoVelocity[1] * 3.6; const egoSpeedKmh = frame.egoVelocity[1] * 3.6;
const y = b.map(
egoSpeedKmh,
minSpeed,
maxSpeed,
b.height - pad.bottom,
pad.top
);
const y = b.map(egoSpeedKmh, minSpeed, maxSpeed, b.height - pad.bottom, pad.top);
b.vertex(x, y); b.vertex(x, y);
} }
} }
@ -114,44 +93,72 @@ export const speedGraphSketch = function (p) {
b.drawingContext.setLineDash([]); b.drawingContext.setLineDash([]);
} }
// --- Legend: centered in the top padding area (above the plotting area) ---
b.push(); b.push();
b.strokeWeight(2);
b.noStroke(); b.noStroke();
b.fill(textColor); b.fill(textColor);
b.textSize(12);
b.textAlign(b.LEFT, b.CENTER); b.textAlign(b.LEFT, b.CENTER);
const canLabel = "CAN Speed";
const egoLabel = "Ego Speed";
const segLen = 18;
const gapBetweenSegAndLabel = 8;
const betweenItemsGap = 24;
// compute widths of each legend item (segment + gap + label)
const canItemWidth = segLen + gapBetweenSegAndLabel + b.textWidth(canLabel);
const egoItemWidth = segLen + gapBetweenSegAndLabel + b.textWidth(egoLabel);
const totalLegendWidth = canItemWidth + betweenItemsGap + egoItemWidth;
// center the legend across the plotting region (pad.left .. b.width - pad.right)
const plottingLeft = pad.left;
const plottingRight = b.width - pad.right;
const centerX = (plottingLeft + plottingRight) / 2;
const legendStartX = centerX - totalLegendWidth / 2;
const legendY = pad.top / 2; // vertically centered inside the top padding
// Draw CAN legend item
b.push();
b.stroke(0, 150, 255); b.stroke(0, 150, 255);
b.line(b.width - 120, pad.top + 10, b.width - 100, pad.top + 10);
b.strokeWeight(2);
b.line(legendStartX, legendY + 6, legendStartX + segLen, legendY + 6);
b.noStroke(); b.noStroke();
b.text("CAN Speed", b.width - 95, pad.top + 10);
b.fill(textColor);
b.text(canLabel, legendStartX + segLen + gapBetweenSegAndLabel, legendY + 6);
b.pop();
// Draw Ego legend item
const egoX = legendStartX + canItemWidth + betweenItemsGap;
b.push();
b.stroke(0, 200, 100); b.stroke(0, 200, 100);
b.strokeWeight(2);
b.drawingContext.setLineDash([3, 3]); b.drawingContext.setLineDash([3, 3]);
b.line(b.width - 120, pad.top + 30, b.width - 100, pad.top + 30);
b.line(egoX, legendY + 6, egoX + segLen, legendY + 6);
b.drawingContext.setLineDash([]); b.drawingContext.setLineDash([]);
b.noStroke(); b.noStroke();
b.text("Ego Speed", b.width - 95, pad.top + 30);
b.fill(textColor);
b.text(egoLabel, egoX + segLen + gapBetweenSegAndLabel, legendY + 6);
b.pop();
b.pop(); b.pop();
}; };
p.setup = function () { p.setup = function () {
let canvas = p.createCanvas(
speedGraphContainer.offsetWidth,
speedGraphContainer.offsetHeight
);
let canvas = p.createCanvas(speedGraphContainer.offsetWidth, speedGraphContainer.offsetHeight);
canvas.parent("speed-graph-container"); canvas.parent("speed-graph-container");
staticBuffer = p.createGraphics(p.width, p.height); staticBuffer = p.createGraphics(p.width, p.height);
p.noLoop(); p.noLoop();
}; };
p.setData = function (radarData, duration) { p.setData = function (radarData, duration) {
if (!radarData || !radarData.radarFrames) return; if (!radarData || !radarData.radarFrames) return;
videoDuration = duration; // Accept duration, even if it's 0 or NaN initially
videoDuration = duration;
let speeds = []; let speeds = [];
if (radarData && radarData.radarFrames) { if (radarData && radarData.radarFrames) {
const egoSpeeds = radarData.radarFrames.map(
(frame) => frame.egoVelocity[1] * 3.6
);
const egoSpeeds = radarData.radarFrames.map((frame) => frame.egoVelocity[1] * 3.6);
speeds.push(...egoSpeeds); speeds.push(...egoSpeeds);
const canSpeeds = radarData.radarFrames const canSpeeds = radarData.radarFrames
@ -160,24 +167,17 @@ export const speedGraphSketch = function (p) {
speeds.push(...canSpeeds); speeds.push(...canSpeeds);
} }
minSpeed =
speeds.length > 0 ? Math.floor(Math.min(...speeds) / 10) * 10 : 0;
maxSpeed =
speeds.length > 0 ? Math.ceil(Math.max(...speeds) / 10) * 10 : 10;
minSpeed = speeds.length > 0 ? Math.floor(Math.min(...speeds) / 10) * 10 : 0;
maxSpeed = speeds.length > 0 ? Math.ceil(Math.max(...speeds) / 10) * 10 : 10;
if (maxSpeed <= 0) maxSpeed = 10; if (maxSpeed <= 0) maxSpeed = 10;
if (minSpeed >= 0) minSpeed = 0; if (minSpeed >= 0) minSpeed = 0;
// *** KEY CHANGE ***
// Only try to draw the static graph if the duration is valid.
if (videoDuration > 0) { if (videoDuration > 0) {
p.drawStaticGraphToBuffer(radarData); p.drawStaticGraphToBuffer(radarData);
} }
//p.redraw();
}; };
p.draw = function () { p.draw = function () {
// *** KEY CHANGE ***
// If duration is not ready, show a waiting message and stop
if (!videoDuration || videoDuration <= 0) { if (!videoDuration || videoDuration <= 0) {
const isDark = document.documentElement.classList.contains("dark"); const isDark = document.documentElement.classList.contains("dark");
p.background(isDark ? [55, 65, 81] : 255); p.background(isDark ? [55, 65, 81] : 255);
@ -191,51 +191,36 @@ export const speedGraphSketch = function (p) {
}; };
function drawTimeIndicator() { function drawTimeIndicator() {
// This new, more robust check is the fix. It ensures that the video duration is valid AND
// the main application has initialized the currentFrame before attempting to draw.
if ( if (
!videoDuration || !videoDuration ||
videoDuration <= 0 || videoDuration <= 0 ||
appState.currentFrame === null || appState.currentFrame === null ||
appState.currentFrame === undefined appState.currentFrame === undefined
) { ) {
return; // Stop here if the state is not ready
return;
} }
// Get the current frame's data as the single source of truth
const frameData = appState.vizData.radarFrames[appState.currentFrame]; const frameData = appState.vizData.radarFrames[appState.currentFrame];
if (!frameData) return; // Exit if data for the specific frame isn't ready
if (!frameData) return;
// Calculate the X position from the current frame's precise timestamp
const currentTimeSec = frameData.timestampMs / 1000.0; const currentTimeSec = frameData.timestampMs / 1000.0;
const x = p.map(
currentTimeSec,
0,
videoDuration,
pad.left,
p.width - pad.right
);
// Draw the red time indicator line at the accurate X position
const x = p.map(currentTimeSec, 0, videoDuration, pad.left, p.width - pad.right);
p.stroke(255, 0, 0, 150); p.stroke(255, 0, 0, 150);
p.strokeWeight(1.5); p.strokeWeight(1.5);
p.line(x, pad.top, x, p.height - pad.bottom); p.line(x, pad.top, x, p.height - pad.bottom);
// Now, draw the circle using the same frame data
if (frameData.canVehSpeed_kmph !== null && !isNaN(frameData.canVehSpeed_kmph)) { if (frameData.canVehSpeed_kmph !== null && !isNaN(frameData.canVehSpeed_kmph)) {
const canSpeed = frameData.canVehSpeed_kmph;
const y = p.map(canSpeed, minSpeed, maxSpeed, p.height - pad.bottom, pad.top);
p.fill(255, 0, 0);
p.noStroke();
p.ellipse(x, y, 8, 8);
const canSpeed = frameData.canVehSpeed_kmph;
const y = p.map(canSpeed, minSpeed, maxSpeed, p.height - pad.bottom, pad.top);
p.fill(255, 0, 0);
p.noStroke();
p.ellipse(x, y, 8, 8);
} }
}
}
p.windowResized = function () { p.windowResized = function () {
p.resizeCanvas(
speedGraphContainer.offsetWidth,
speedGraphContainer.offsetHeight
);
p.resizeCanvas(speedGraphContainer.offsetWidth, speedGraphContainer.offsetHeight);
staticBuffer = p.createGraphics(p.width, p.height); staticBuffer = p.createGraphics(p.width, p.height);
if (appState.vizData && videoDuration > 0) { if (appState.vizData && videoDuration > 0) {
p.drawStaticGraphToBuffer(appState.vizData); p.drawStaticGraphToBuffer(appState.vizData);

Loading…
Cancel
Save