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) {