From b24f6f460ee1ca25e3a42739a24291976db42062 Mon Sep 17 00:00:00 2001 From: rakadu1 Date: Mon, 23 Feb 2026 14:54:36 +0530 Subject: [PATCH] fix(viz): add stability guards for smoothing and data mapping - Clamp delta time to minimum of 0 to prevent negative interpolation. - Add protection against division by zero in SNR color mapping. - Ensure trajectory points exist before fading alpha calculation. - Standardize p.deltaTime usage for cross-browser stability. --- steps/src/drawUtils.js | 33 ++++++++++++++++++++------------- steps/src/p5/radarSketch.js | 6 ++++-- steps/src/p5/zoomSketch.js | 3 ++- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/steps/src/drawUtils.js b/steps/src/drawUtils.js index f2fffea..6edee7e 100644 --- a/steps/src/drawUtils.js +++ b/steps/src/drawUtils.js @@ -246,6 +246,11 @@ export function drawPointCloud(p, points, plotScales, pointSize = 4) { if (snrVals.length > 1) { minSnr = Math.min(...snrVals); maxSnr = Math.max(...snrVals); + // If all SNR values are the same, add a small range to avoid division by zero + if (minSnr === maxSnr) { + minSnr -= 1; + maxSnr += 1; + } } else if (snrVals.length === 1) { minSnr = snrVals[0] - 1; maxSnr = snrVals[0] + 1; @@ -450,19 +455,21 @@ export function drawTrajectories(p, plotScales, scaleFactor = 1) { p.drawingContext.setLineDash([]); // 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); - p.stroke(trajectoryColor); - - const prevPt = trajPts[i - 1]; - const currPt = trajPts[i]; - p.line( - prevPt[0] * plotScales.plotScaleX, - prevPt[1] * plotScales.plotScaleY, - currPt[0] * plotScales.plotScaleX, - currPt[1] * plotScales.plotScaleY - ); + if (trajPts.length > 0) { + for (let i = 1; i < trajPts.length; i++) { + const alpha = p.map(i, 0, trajPts.length, 50, 255); + trajectoryColor.setAlpha(alpha); + p.stroke(trajectoryColor); + + const prevPt = trajPts[i - 1]; + const currPt = trajPts[i]; + p.line( + prevPt[0] * plotScales.plotScaleX, + prevPt[1] * plotScales.plotScaleY, + currPt[0] * plotScales.plotScaleX, + currPt[1] * plotScales.plotScaleY + ); + } } // --- END: New Dynamic Coloring Logic --- } diff --git a/steps/src/p5/radarSketch.js b/steps/src/p5/radarSketch.js index 0fbf9b2..51cbaac 100644 --- a/steps/src/p5/radarSketch.js +++ b/steps/src/p5/radarSketch.js @@ -211,7 +211,8 @@ export const radarSketch = function (p) { } else { // --- START: Frame-Rate Independent FPS Smoothing --- const baseFactor = 0.05; // Smoothing factor at 60 FPS - const adjustedFactor = 1 - Math.pow(1 - baseFactor, delta / (1000 / 60)); + const dt = Math.max(0, delta); + const adjustedFactor = 1 - Math.pow(1 - baseFactor, dt / (1000 / 60)); appState.fps = p.lerp(appState.fps, currentFps, adjustedFactor); // --- END: Frame-Rate Independent FPS Smoothing --- } @@ -382,7 +383,8 @@ export const radarSketch = function (p) { // We use p.deltaTime to adjust the smoothing factor so that the animation // speed remains consistent across different monitor refresh rates. const baseSmoothing = 0.5; // Target smoothing at 60 FPS - const adjustedSmoothing = 1 - Math.pow(1 - baseSmoothing, p.deltaTime / (1000 / 60)); + const dt = Math.max(0, p.deltaTime); + const adjustedSmoothing = 1 - Math.pow(1 - baseSmoothing, dt / (1000 / 60)); // Linearly interpolate the smoothed position towards the actual mouse position. smoothedMouseX = p.lerp(smoothedMouseX, p.mouseX, adjustedSmoothing); diff --git a/steps/src/p5/zoomSketch.js b/steps/src/p5/zoomSketch.js index f4142f3..10531d7 100644 --- a/steps/src/p5/zoomSketch.js +++ b/steps/src/p5/zoomSketch.js @@ -275,7 +275,8 @@ export const zoomSketch = function (p) { // We use p.deltaTime to adjust the smoothing factor so that the animation // speed remains consistent across different monitor refresh rates. const baseSmoothing = 0.05; // Target smoothing at 60 FPS - const adjustedSmoothing = 1 - Math.pow(1 - baseSmoothing, p.deltaTime / (1000 / 60)); + const dt = Math.max(0, p.deltaTime); + const adjustedSmoothing = 1 - Math.pow(1 - baseSmoothing, dt / (1000 / 60)); smoothedAvgX = p.lerp(smoothedAvgX, targetAvgX, adjustedSmoothing); smoothedAvgY = p.lerp(smoothedAvgY, targetAvgY, adjustedSmoothing); // --- END: Frame-Rate Independent Smoothing ---