namespace DeepDrftPublic.Client.Controls; /// /// Pure mapping between the Mix 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. /// public static class MixZoomMapping { /// Shortest span (max zoom): one quarter note at 180 BPM = 60/180 s. Hard anchor. public const double MinVisibleSeconds = 60.0 / 180.0; /// Longest span (min zoom). Tunable. public const double MaxVisibleSeconds = 30.0; /// Slider position [0, 1] -> visible seconds. 0 = zoomed out, 1 = zoomed in. 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); } /// Visible seconds -> slider position [0, 1]. Inverse of . 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); } }