Files

58 lines
2.9 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace DeepDrftPublic.Client.Controls;
/// <summary>
/// Pure mapping between the waveform visualizer's zoom slider position [0, 1] and the visible time-span in
/// seconds. The span range is wide (0.333 s … 30 s, ~90×), so the mapping is logarithmic — equal
/// slider travel changes the span by an equal *ratio*, which feels even to the hand. Slider
/// orientation: fraction 0 = most zoomed-out (longest span), fraction 1 = most zoomed-in (the
/// 0.333 s quarter-note-@-180-BPM anchor). Extracted from the component so the math is unit-testable.
/// </summary>
public static class WaveformZoomMapping
{
/// <summary>Shortest span (max zoom): one quarter note at 180 BPM = 60/180 s. Hard anchor.</summary>
public const double MinVisibleSeconds = 60.0 / 180.0;
/// <summary>Longest span (min zoom). Tunable.</summary>
public const double MaxVisibleSeconds = 30.0;
/// <summary>
/// Lower edge of the useful zoom band on the underlying fraction axis. Phase 10 retune: the bottom
/// 60% of the old knob travel (fraction 0…0.6) was a useless slow/wide window, so the scroll knob's
/// full [0,1] travel now maps onto the upper 0.6…1.0 band where every position reads as a useful
/// zoom (Daniel: "range below 60% is useless; optimize for the current 60%110% zoom values" — 110%
/// caps at the hard 0.333 s max-zoom anchor, fraction 1.0).
/// </summary>
public const double ScrollKnobZoomFloor = 0.60;
/// <summary>
/// Maps the scroll-speed knob [0,1] onto the useful zoom band [<see cref="ScrollKnobZoomFloor"/>, 1.0]
/// of the underlying fraction axis, then to visible seconds. So knob 0 sits at the slow edge of the
/// *useful* range (not the dead slow end), and knob 1 reaches max zoom. Phase 10 scroll retune.
/// </summary>
public static double ScrollKnobToSeconds(double knob)
{
knob = Math.Clamp(knob, 0, 1);
var fraction = ScrollKnobZoomFloor + (1.0 - ScrollKnobZoomFloor) * knob;
return FractionToSeconds(fraction);
}
/// <summary>Slider position [0, 1] -> visible seconds. 0 = zoomed out, 1 = zoomed in.</summary>
public static double FractionToSeconds(double fraction)
{
fraction = Math.Clamp(fraction, 0, 1);
var logMax = Math.Log(MaxVisibleSeconds);
var logMin = Math.Log(MinVisibleSeconds);
// Interpolate in log space from out (frac 0 -> logMax) to in (frac 1 -> logMin).
return Math.Exp(logMax + (logMin - logMax) * fraction);
}
/// <summary>Visible seconds -> slider position [0, 1]. Inverse of <see cref="FractionToSeconds"/>.</summary>
public static double SecondsToFraction(double seconds)
{
seconds = Math.Clamp(seconds, MinVisibleSeconds, MaxVisibleSeconds);
var logMax = Math.Log(MaxVisibleSeconds);
var logMin = Math.Log(MinVisibleSeconds);
return (logMax - Math.Log(seconds)) / (logMax - logMin);
}
}