From 6d0535b8be861977ebd423c3709b22568bc9a517 Mon Sep 17 00:00:00 2001 From: rakadu1 Date: Wed, 26 Nov 2025 14:25:40 +0530 Subject: [PATCH] Speed Graph Sketch issue solved using CLI " Here is the detailed breakdown of why that solution worked. The Root Cause: Initialization Order The error ellipse() ... received NaN occurred because the speedGraphSketch was trying to plot a point based on time (x-axis), but the time value for the current frame was invalid (NaN or undefined). Here is the chain of events that caused this: 1. The Dependency Chain: * To draw the graph, the sketch needs frame.timestampMs. * To calculate frame.timestampMs, the JSON Parser (parseVisualizationJson) needs appState.videoStartDate. * To get appState.videoStartDate, the helper function calculateAndSetOffset() needs to know the Video Filename. 2. The Bug (Before Refactor): When you dragged both files in, the code executed sequentially like this: * Step A (JSON Block): The code detected the JSON file. It set appState.jsonFilename and immediately called calculateAndSetOffset(). * CRITICAL FAILURE: At this exact moment, the code had not yet processed the Video file. It didn't know the video filename. * Result: calculateAndSetOffset couldn't find a video timestamp, so appState.videoStartDate remained null. * Step B (Parsing): The JSON parser ran. Because videoStartDate was null, it skipped the math that calculates frame.timestampMs. All your data frames were created with timestampMs: undefined. * Step C (Video Block): The code moved to the Video file. It set appState.videoFilename and called calculateAndSetOffset() again. * Now it calculated the correct date, but it was too late. The JSON data had already been parsed and baked without timestamps. 3. The Crash: When the visualization started, the Speed Graph looked at the current frame, found timestampMs was missing, tried to divide it by 1000, got NaN, and crashed the ellipse() drawing function. How the Solution Fixed It By restructuring processFilePipeline, we enforced the correct dependency order: 1. Pre-Processing (The Fix): We moved the "registration" of both filenames to the very top of the function. 1 // 1. Register JSON file (if present) 2 // 2. Register Video file (if present) 2. Calculation: We call calculateAndSetOffset() once, immediately after registration. * Since both filenames are now known, it successfully extracts the timestamp from the video filename and sets appState.videoStartDate. 3. Processing: We then start the JSON parser. * It receives the valid appState.videoStartDate. * It successfully calculates frame.timestampMs for every frame. In short: We ensured that the "ingredients" (filenames and dates) were fully prepared before we started "cooking" (parsing) the data" --- steps/src/main.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/steps/src/main.js b/steps/src/main.js index 447aa58..2153726 100644 --- a/steps/src/main.js +++ b/steps/src/main.js @@ -182,13 +182,25 @@ async function processFilePipeline() { showLoadingModal("Starting file load..."); let _parsedJsonData = null; - // 2. Handle JSON Parsing FIRST (if a JSON file is present) + // --- PRE-PROCESSING: Setup Filenames and Cache --- if (jsonFileToLoad) { appState.jsonFilename = jsonFileToLoad.name; localStorage.setItem("jsonFilename", appState.jsonFilename); await saveFileWithMetadata("json", jsonFileToLoad); - calculateAndSetOffset(); + } + + if (videoFileToLoad) { + appState.videoFilename = videoFileToLoad.name; + localStorage.setItem("videoFilename", appState.videoFilename); + await saveFileWithMetadata("video", videoFileToLoad); + } + // --- CALCULATE OFFSET --- + // Calculate offset/dates once we have all potential filenames. + calculateAndSetOffset(); + + // 2. Handle JSON Parsing (if a JSON file is present) + if (jsonFileToLoad) { const worker = new Worker("./src/parser.worker.js"); const parsedData = await new Promise((resolve, reject) => { worker.onmessage = (e) => { @@ -255,8 +267,6 @@ async function processFilePipeline() { // STAGE 1: Fired when video duration is known. const onMetadataLoaded = () => { updateLoadingModal(95, "Finalizing visualization..."); - // This is the key fix: initialize data-dependent sketches immediately. - finalizeSetup(_parsedJsonData); }; // STAGE 2: Fired when video is buffered enough to play. @@ -283,10 +293,7 @@ async function processFilePipeline() { }); // Set up file metadata and start the simulated progress spinner - appState.videoFilename = videoFileToLoad.name; - localStorage.setItem("videoFilename", appState.videoFilename); - await saveFileWithMetadata("video", videoFileToLoad); - calculateAndSetOffset(); + // Note: Filename setup and caching moved to start of processFilePipeline const spinnerChars = ["|", "/", "-", "\\"]; let spinnerIndex = 0; @@ -302,6 +309,8 @@ async function processFilePipeline() { // Await the promise, which resolves only after 'canplaythrough' fires. await videoReadyPromise; + finalizeSetup(_parsedJsonData); + // 4. Finalize the UI by hiding the modal updateLoadingModal(100, "Complete!"); setTimeout(hideModal, 300);