代码之家  ›  专栏  ›  技术社区  ›  Nick

图轴的记号算法

  •  23
  • Nick  · 技术社区  · 17 年前

    例如,考虑到我需要在1e-6和5e-6之间显示,以及以像素为单位显示的宽度,该算法将确定我应该在1e-6、2e-6、3e-6、4e-6和5e-6上打勾号(例如)。如果宽度较小,则可能决定最佳位置仅位于偶数位置,即2e-6和4e-6(因为放置更多记号会导致它们重叠)。

    5 回复  |  直到 17 年前
        1
  •  12
  •   Andrew    8 年前

    它基本上从可能的步骤列表中选择显示所有值的最小步骤, 不在边缘留下任何值 ,使您可以轻松选择要使用的可能步骤(无需编辑) if-else if 块),并支持任何范围的值。我用了C# Tuple 返回三个值只是为了快速简单的演示。

    private static Tuple<decimal, decimal, decimal> GetScaleDetails(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<decimal, decimal, decimal>(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;
    }
    
        2
  •  2
  •   mindvirus    17 年前

    计算出该段的大致长度,单位为刻度。这只是将长度除以刻度的宽度。假设这个方法说我们可以在-5到0之间放入11个刻度。这是我们的上限。对于较短的一端,我们将在较长的一端镜像结果。

    现在尝试输入尽可能多(最多11个)的记号,这样每个记号的标记形式为i*10*10^n,i*5*10^n,i*2*10^n,其中n是一个整数,i是记号的索引。现在这是一个优化问题——我们希望最大化我们可以放入的刻度数,同时最小化最后一个刻度和结果结束之间的距离。因此,为获得尽可能多的刻度分配一个分数,小于上限,并为获得接近n的最后一个刻度分配一个分数-你必须在这里进行实验。

    在上面的示例中,尝试n=1。我们得到1个刻度(i=0)。n=2给我们一个刻度,我们离下限更远,所以我们知道我们必须走另一条路。n=0表示在每个整数点上有6个刻度。n=-1给出12个刻度(0,-0.5,…,-5.0)。n=-2给出24个刻度,以此类推。评分算法会给他们每个人一个分数-越高意味着一个更好的方法。

    对i*5*10^n和i*2*10^n再做一次,然后取得分最高的一个。

    (作为一个评分算法示例,假设分数是到最后一个刻度的距离乘以最大刻度数减去所需的数字。这可能不好,但可以作为一个不错的起点)。

        3
  •  1
  •   Giorgio Barchiesi    9 年前

    这个简单的算法产生的间隔是10次幂的1、2或5倍。轴范围被划分为至少5个间隔。代码示例使用java语言编写:

    protected double calculateInterval(double range) {
        double x = Math.pow(10.0, Math.floor(Math.log10(range)));
        if (range / x >= 5)
            return x;
        else if (range / (x / 2.0) >= 5)
            return x / 2.0;
        else
            return x / 5.0;
    }
    

    protected double calculateInterval(double range) {
        double x = Math.pow(10.0, Math.floor(Math.log10(range)));
        if (range / (x / 2.0) >= 10)
            return x / 2.0;
        else if (range / (x / 5.0) >= 10)
            return x / 5.0;
        else
            return x / 10.0;
    }
    
        4
  •  1
  •   Auspex    7 年前

    我一直在使用jQuery flot 图形库。它是开源的,并且可以很好地生成axis/tick。我建议看一下它的代码,从中汲取一些想法。