我正在寻找一种在轴上放置刻度线的算法,给定要显示的范围,显示它的宽度,以及测量刻度线的字符串宽度的功能.
例如,假设我需要在1e-6和5e-6之间显示以及以像素显示的宽度,算法将确定我应该将标记(例如)放在1e-6,2e-6,3e-6处,4e-6和5e-6.给定较小的宽度,可以确定最佳放置仅在偶数位置,即2e-6和4e-6(因为放置更多的标记将导致它们重叠).
智能算法将优先考虑10,5和2的倍数的标记.此外,智能算法将在零附近对称.
因为我不喜欢到目前为止我找到的任何解决方案,所以我实现了自己的解决方案.它在C#中,但可以很容易地翻译成任何其他语言.
它基本上从可能的步骤列表中选择显示所有值的最小步骤,而不会在边缘中保留任何值,让您轻松选择要使用的步骤(无需编辑丑陋的if-else if
块),并支持任何范围价值观 我使用C#Tuple
返回三个值只是为了快速简单的演示.
private static TupleGetScaleDetails(decimal min, decimal max) { // Minimal increment to avoid round extreme values to be on the edge of the chart decimal epsilon = (max - min) / 1e6m; max += epsilon; min -= epsilon; decimal range = max - min; // Target number of values to be displayed on the Y axis (it may be less) int stepCount = 20; // First approximation decimal roughStep = range / (stepCount - 1); // Set best step for the range decimal[] goodNormalizedSteps = { 1, 1.5m, 2, 2.5m, 5, 7.5m, 10 }; // keep the 10 at the end // Or use these if you prefer: { 1, 2, 5, 10 }; // Normalize rough step to find the normalized one that fits best decimal stepPower = (decimal)Math.Pow(10, -Math.Floor(Math.Log10((double)Math.Abs(roughStep)))); var normalizedStep = roughStep * stepPower; var goodNormalizedStep = goodNormalizedSteps.First(n => n >= normalizedStep); decimal step = goodNormalizedStep / stepPower; // Determine the scale limits based on the chosen step. decimal scaleMax = Math.Ceiling(max / step) * step; decimal scaleMin = Math.Floor(min / step) * step; return new Tuple (scaleMin, scaleMax, step); } static void Main() { // Dummy code to show a usage example. var minimumValue = data.Min(); var maximumValue = data.Max(); var results = GetScaleDetails(minimumValue, maximumValue); chart.YAxis.MinValue = results.Item1; chart.YAxis.MaxValue = results.Item2; chart.YAxis.Step = results.Item3; }