From 0f9ad03fc54af5884136ecd94c184f188f32d8fb Mon Sep 17 00:00:00 2001 From: Tyler Date: Wed, 12 Aug 2026 00:19:14 -0700 Subject: [PATCH] fix: map scope grid and cursor through the visible time window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DrawScopeView's waveform envelope already drew only viewStart..viewEnd, but TimeToX ignored both and mapped 0-1 across the full widget width. The grid lines and the playback cursor were therefore laid out against the whole signal while the trace beneath them showed a zoomed sub-range, so the two drifted out of register the moment the view was zoomed or panned — and out of register with the spectrogram directly above, which shares the same time axis. Map through the visible window in TimeToX, space the ten divisions across that window rather than the whole signal, and keep the existing bounds check so a cursor outside the current view is dropped instead of clamped to an edge. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01V8ZWfr5XZyyDttvkhJUgHN --- src/primitives.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/primitives.c b/src/primitives.c index 86ae68a..a273277 100644 --- a/src/primitives.c +++ b/src/primitives.c @@ -33,9 +33,16 @@ static int AmplitudeToY(ScopeView* view, float amp) return view->y + view->height - (int)((amp - view->ampMin) / (view->ampMax - view->ampMin) * view->height); } +// Map a signal-space time (0-1 over the WHOLE signal) to a screen X, honoring +// the visible window. The waveform envelope below already draws only +// viewStart..viewEnd, so grid lines and the cursor have to use the same mapping +// or they drift out of register with the trace (and with the spectrogram above) +// as soon as the user zooms or pans. static int TimeToX(ScopeView* view, float t) { - return view->x + (int)(t * view->width); + float span = view->viewEnd - view->viewStart; + if (span <= 0.0f) span = 1.0f; + return view->x + (int)((t - view->viewStart) / span * view->width); } void DrawScopeView(ScopeView* view, float cursorT) @@ -56,9 +63,11 @@ void DrawScopeView(ScopeView* view, float cursorT) if (view->showGrid) { Color gridColor = (Color){ view->gridR, view->gridG, view->gridB, (int)(view->gridAlpha * 255) }; - // Vertical time divisions + // Vertical time divisions — ten evenly spaced lines across the VISIBLE + // window, so the grid stays put under zoom instead of sliding off. for (int i = 0; i <= 10; i++) { - int x = TimeToX(view, (float)i / 10.0f); + float t = view->viewStart + (float)i / 10.0f * (view->viewEnd - view->viewStart); + int x = TimeToX(view, t); DrawLineV((Vector2){ x, view->y }, (Vector2){ x, view->y + view->height }, gridColor); } @@ -119,7 +128,8 @@ void DrawScopeView(ScopeView* view, float cursorT) DrawLine(px + view->x, yTop, px + view->x, yBot, waveColor); } - // Cursor + // Cursor. cursorT is signal-space; TimeToX maps it into the visible window, + // and the bounds check below drops it when it falls outside the current view. if (cursorT >= 0.0f && cursorT <= 1.0f) { int cursorX = TimeToX(view, cursorT); if (cursorX >= view->x && cursorX <= view->x + view->width) {