feat: scale time zoom by duration, decouple the two axes

The minimum visible time window was a flat 2% of the file, but view.start
and view.end are normalized to the whole signal — so the achievable time
resolution scaled with file length. A 30-minute recording could never show
less than a 36-second span, while a 30-second one reached 0.6 s. Long
captures were effectively unreadable at the sample level no matter how far
you scrolled.

Derive the floor from the STFT hop instead (MinTimeViewWidth): segments sit
fftSize/HOP_RATIO samples apart, so the real limit is the point where only
a handful of segments span the viewport and further zoom would interpolate
rather than reveal. The floor is now a constant ~43 ms at 48 kHz/1024
regardless of duration — an 844x improvement on a 30-minute file, and it
tightens further with a smaller FFT. Guards cover the unloaded (sampleRate
0) and shorter-than-the-floor cases.

Zooming is also no longer forced to move both axes together. The bare wheel
keeps the existing coupled behaviour; Shift+wheel is time-only and
Ctrl+wheel frequency-only, so a long capture can be stretched along time
without collapsing the frequency range to match.

Time-axis labels now pick their precision from the span between adjacent
ticks (1 to 4 decimals, and m:ss.sss past a minute). At the spans this
change makes reachable the old fixed "%.1fs" printed the same value in
every slot, which read as a frozen axis.

Adds a `wheel X Y N [mod]` action to shot_input.sh for exercising zoom
headlessly, and known_bugs.md for behaviour that is unspecified rather
than broken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V8ZWfr5XZyyDttvkhJUgHN
This commit is contained in:
2026-08-12 00:19:36 -07:00
parent 0f9ad03fc5
commit 5c3c88dc22
7 changed files with 173 additions and 9 deletions
+4 -1
View File
@@ -765,7 +765,10 @@ void DrawAboutDialog(void)
DrawTextScaled(TextFormat(" %-5s %s", km[i].label, km[i].help), px, py, 13, LIGHTGRAY);
py += 16 * scale;
}
DrawTextScaled(" Mouse wheel = zoom, Alt+drag = pan, drag = select box",
DrawTextScaled(" Mouse wheel = zoom (Shift = time only, Ctrl = freq only)",
px, py, 13, LIGHTGRAY);
py += 16 * scale;
DrawTextScaled(" Alt+drag = pan, drag = select box",
px, py, 13, LIGHTGRAY);
DrawTextScaled("F1 / Esc / click to close", panel.x + pw - 196 * scale,