fix: dynamic frequency labels and fix frequency clamping crash
- Frequency tick/label intervals now adapt to zoom level instead of always showing 0–Nyquist (5Hz → 10kHz depending on zoom) - Labels use coarser spacing than ticks to avoid clutter - Fix segfault when zooming out past signal bounds by properly clamping freqViewStart/freqViewEnd to [0,1] with correct width preservation in zoom, pan, and scrollbar drag - Fix firstTick calculation that was off-by-one
This commit is contained in:
+62
-20
@@ -1123,27 +1123,50 @@ static void DrawLabels(Rectangle bounds)
|
|||||||
DrawTextScaled(label, x, bounds.y + bounds.height + 5, baseFontSize, textColor);
|
DrawTextScaled(label, x, bounds.y + bounds.height + 5, baseFontSize, textColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frequency labels at 1kHz intervals with 200Hz ticks
|
// Frequency labels adapted to current zoom level
|
||||||
float maxFreq = (float)app.signal.sampleRate / 2.0f;
|
float maxFreq = (float)app.signal.sampleRate / 2.0f;
|
||||||
int maxKhz = (int)(maxFreq / 1000.0f);
|
float freqMin = app.freqViewStart * maxFreq;
|
||||||
|
float freqMax = app.freqViewEnd * maxFreq;
|
||||||
|
|
||||||
for (int hz = 0; hz <= maxFreq; hz += 200) {
|
// Choose tick spacing based on zoom range
|
||||||
float t = hz / maxFreq;
|
float freqRange = freqMax - freqMin;
|
||||||
|
int tickSpacing;
|
||||||
|
if (freqRange < 20) tickSpacing = 5;
|
||||||
|
else if (freqRange < 50) tickSpacing = 10;
|
||||||
|
else if (freqRange < 200) tickSpacing = 50;
|
||||||
|
else if (freqRange < 1000) tickSpacing = 100;
|
||||||
|
else if (freqRange < 5000) tickSpacing = 200;
|
||||||
|
else if (freqRange < 20000) tickSpacing = 1000;
|
||||||
|
else if (freqRange < 50000) tickSpacing = 5000;
|
||||||
|
else tickSpacing = 10000;
|
||||||
|
|
||||||
|
// Labels use next coarser spacing so they stay readable
|
||||||
|
int labelSpacing = tickSpacing;
|
||||||
|
if (labelSpacing <= 10) labelSpacing = 10;
|
||||||
|
else if (labelSpacing <= 50) labelSpacing = 50;
|
||||||
|
else if (labelSpacing <= 200) labelSpacing = 200;
|
||||||
|
else if (labelSpacing <= 1000) labelSpacing = 1000;
|
||||||
|
else if (labelSpacing <= 5000) labelSpacing = 5000;
|
||||||
|
else labelSpacing = 10000;
|
||||||
|
|
||||||
|
// Round freqMin up to nearest tick spacing (smallest multiple >= freqMin)
|
||||||
|
int firstTick = ((int)(freqMin / tickSpacing)) * tickSpacing;
|
||||||
|
if (firstTick < freqMin) firstTick += tickSpacing;
|
||||||
|
for (int hz = firstTick; hz <= freqMax; hz += tickSpacing) {
|
||||||
|
float t = (hz - freqMin) / freqRange;
|
||||||
float y = bounds.y + bounds.height - t * bounds.height;
|
float y = bounds.y + bounds.height - t * bounds.height;
|
||||||
Color tickColor = (hz % 1000 == 0) ? GRAY : Fade(GRAY, 0.4f);
|
Color tickColor = (hz % 1000 == 0) ? GRAY : Fade(GRAY, 0.4f);
|
||||||
DrawLineV((Vector2){ bounds.x - 5, y }, (Vector2){ bounds.x, y }, tickColor);
|
DrawLineV((Vector2){ bounds.x - 5, y }, (Vector2){ bounds.x, y }, tickColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int khz = 0; khz <= maxKhz; khz++) {
|
// Draw labels at the coarser spacing
|
||||||
float freq = khz * 1000.0f;
|
for (int hz = firstTick; hz <= freqMax; hz += labelSpacing) {
|
||||||
if (freq > maxFreq) break;
|
float t = (hz - freqMin) / freqRange;
|
||||||
float t = freq / maxFreq;
|
|
||||||
float y = bounds.y + bounds.height - t * bounds.height;
|
float y = bounds.y + bounds.height - t * bounds.height;
|
||||||
char label[32];
|
char label[32];
|
||||||
if (khz == 0) sprintf(label, "0");
|
if (hz < 10000) sprintf(label, "%.0fHz", (float)hz);
|
||||||
else if (khz < 10) sprintf(label, "%dk", khz);
|
else sprintf(label, "%.0fkHz", (float)hz / 1000.0f);
|
||||||
else sprintf(label, "%d", khz);
|
DrawTextScaled(label, bounds.x - 70, y - 5, baseFontSize, textColor);
|
||||||
DrawTextScaled(label, bounds.x - 15, y - 5, baseFontSize, textColor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1693,14 +1716,14 @@ int main(int argc, char* argv[])
|
|||||||
mouseF = app.freqViewStart + mouseF * (app.freqViewEnd - app.freqViewStart);
|
mouseF = app.freqViewStart + mouseF * (app.freqViewEnd - app.freqViewStart);
|
||||||
float freqWidth = app.freqViewEnd - app.freqViewStart;
|
float freqWidth = app.freqViewEnd - app.freqViewStart;
|
||||||
float newFreqWidth = freqWidth * zoomFactor;
|
float newFreqWidth = freqWidth * zoomFactor;
|
||||||
if (newFreqWidth < 0.02f) newFreqWidth = 0.02f;
|
if (newFreqWidth < 0.001f) newFreqWidth = 0.001f;
|
||||||
if (newFreqWidth > 1.0f) newFreqWidth = 1.0f;
|
|
||||||
float belowMouse = mouseF - app.freqViewStart;
|
float belowMouse = mouseF - app.freqViewStart;
|
||||||
float aboveMouse = app.freqViewEnd - mouseF;
|
float aboveMouse = app.freqViewEnd - mouseF;
|
||||||
app.freqViewStart = mouseF - belowMouse * (newFreqWidth / freqWidth);
|
app.freqViewStart = mouseF - belowMouse * (newFreqWidth / freqWidth);
|
||||||
app.freqViewEnd = mouseF + aboveMouse * (newFreqWidth / freqWidth);
|
app.freqViewEnd = mouseF + aboveMouse * (newFreqWidth / freqWidth);
|
||||||
if (app.freqViewStart < 0) { app.freqViewStart = 0; app.freqViewEnd = newFreqWidth; }
|
// Clamp to physical frequency limits [0, 1] — can't see beyond Nyquist or below 0 Hz
|
||||||
if (app.freqViewEnd > 1) { app.freqViewEnd = 1; app.freqViewStart = 1 - newFreqWidth; }
|
if (app.freqViewStart < 0) { app.freqViewStart = 0; app.freqViewEnd = fminf(app.freqViewEnd, 1.0f); }
|
||||||
|
if (app.freqViewEnd > 1) { app.freqViewEnd = 1; app.freqViewStart = fmaxf(app.freqViewStart, 0.0f); }
|
||||||
|
|
||||||
// Invalidate texture cache
|
// Invalidate texture cache
|
||||||
app.visibleTextureValid = false;
|
app.visibleTextureValid = false;
|
||||||
@@ -1728,8 +1751,19 @@ int main(int argc, char* argv[])
|
|||||||
if (app.viewEnd > 1) { app.viewEnd = 1; app.viewStart = 1 - viewWidth; }
|
if (app.viewEnd > 1) { app.viewEnd = 1; app.viewStart = 1 - viewWidth; }
|
||||||
app.freqViewStart = app.panStartFreqViewStart + dy * freqWidth;
|
app.freqViewStart = app.panStartFreqViewStart + dy * freqWidth;
|
||||||
app.freqViewEnd = app.panStartFreqViewEnd + dy * freqWidth;
|
app.freqViewEnd = app.panStartFreqViewEnd + dy * freqWidth;
|
||||||
if (app.freqViewStart < 0) { app.freqViewStart = 0; app.freqViewEnd = freqWidth; }
|
// Clamp to physical limits [0, 1]
|
||||||
if (app.freqViewEnd > 1) { app.freqViewEnd = 1; app.freqViewStart = 1 - freqWidth; }
|
if (app.freqViewStart < 0) {
|
||||||
|
float actualWidth = app.freqViewEnd - app.freqViewStart;
|
||||||
|
app.freqViewStart = 0;
|
||||||
|
app.freqViewEnd = fminf(actualWidth, 1.0f);
|
||||||
|
}
|
||||||
|
if (app.freqViewEnd > 1) {
|
||||||
|
float actualWidth = app.freqViewEnd - app.freqViewStart;
|
||||||
|
app.freqViewEnd = 1;
|
||||||
|
app.freqViewStart = fmaxf(1.0f - actualWidth, 0.0f);
|
||||||
|
}
|
||||||
|
if (app.freqViewStart < 0) app.freqViewStart = 0;
|
||||||
|
if (app.freqViewEnd > 1) app.freqViewEnd = 1;
|
||||||
app.visibleTextureValid = false;
|
app.visibleTextureValid = false;
|
||||||
}
|
}
|
||||||
if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) app.isPanning = false;
|
if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) app.isPanning = false;
|
||||||
@@ -2182,8 +2216,16 @@ int main(int argc, char* argv[])
|
|||||||
float freqWidth = app.freqViewEnd - app.freqViewStart;
|
float freqWidth = app.freqViewEnd - app.freqViewStart;
|
||||||
app.freqViewStart = dragStartFreqViewStart - dy;
|
app.freqViewStart = dragStartFreqViewStart - dy;
|
||||||
app.freqViewEnd = app.freqViewStart + freqWidth;
|
app.freqViewEnd = app.freqViewStart + freqWidth;
|
||||||
if (app.freqViewStart < 0) { app.freqViewStart = 0; app.freqViewEnd = freqWidth; }
|
if (app.freqViewStart < 0) {
|
||||||
if (app.freqViewEnd > 1) { app.freqViewEnd = 1; app.freqViewStart = 1 - freqWidth; }
|
app.freqViewStart = 0;
|
||||||
|
app.freqViewEnd = fminf(freqWidth, 1.0f);
|
||||||
|
}
|
||||||
|
if (app.freqViewEnd > 1) {
|
||||||
|
app.freqViewEnd = 1;
|
||||||
|
app.freqViewStart = fmaxf(1.0f - freqWidth, 0.0f);
|
||||||
|
}
|
||||||
|
if (app.freqViewStart < 0) app.freqViewStart = 0;
|
||||||
|
if (app.freqViewEnd > 1) app.freqViewEnd = 1;
|
||||||
app.visibleTextureValid = false;
|
app.visibleTextureValid = false;
|
||||||
}
|
}
|
||||||
if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { draggingH = false; draggingV = false; }
|
if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { draggingH = false; draggingV = false; }
|
||||||
|
|||||||
Reference in New Issue
Block a user