Nice Label Algorithm for Charts with minimum ticks

后端 未结 16 1903
清酒与你
清酒与你 2020-11-29 17:07

I need to calculate the Ticklabels and the Tickrange for charts manually.

I know the \"standard\" algorithm for nice ticks (see http://books.google.de/books?id=fvA7

16条回答
  •  有刺的猬
    2020-11-29 17:36

    Since everybody and his dog is posting a translation to other popular languages, here is my version for the Nimrod programming language. I also added handling of cases where the amount of ticks is less than two:

    import math, strutils
    
    const
      defaultMaxTicks = 10
    
    type NiceScale = object
      minPoint: float
      maxPoint: float
      maxTicks: int
      tickSpacing: float
      niceMin: float
      niceMax: float
    
    proc ff(x: float): string =
      result = x.formatFloat(ffDecimal, 3)
    
    proc `$`*(x: NiceScale): string =
      result = "Input minPoint: " & x.minPoint.ff &
        "\nInput maxPoint: " & x.maxPoint.ff &
        "\nInput maxTicks: " & $x.maxTicks &
        "\nOutput niceMin: " & x.niceMin.ff &
        "\nOutput niceMax: " & x.niceMax.ff &
        "\nOutput tickSpacing: " & x.tickSpacing.ff &
        "\n"
    
    proc calculate*(x: var NiceScale)
    
    proc init*(x: var NiceScale; minPoint, maxPoint: float;
        maxTicks = defaultMaxTicks) =
      x.minPoint = minPoint
      x.maxPoint = maxPoint
      x.maxTicks = maxTicks
      x.calculate
    
    proc initScale*(minPoint, maxPoint: float;
        maxTicks = defaultMaxTicks): NiceScale =
      result.init(minPoint, maxPoint, maxTicks)
    
    proc niceNum(scaleRange: float; doRound: bool): float =
      var
        exponent: float ## Exponent of scaleRange.
        fraction: float ## Fractional part of scaleRange.
        niceFraction: float ## Nice, rounded fraction.
    
      exponent = floor(log10(scaleRange));
      fraction = scaleRange / pow(10, exponent);
    
      if doRound:
        if fraction < 1.5:
          niceFraction = 1
        elif fraction < 3:
          niceFraction = 2
        elif fraction < 7:
          niceFraction = 5
        else:
          niceFraction = 10
      else:
        if fraction <= 1:
          niceFraction = 1
        elif fraction <= 2:
          niceFraction = 2
        elif fraction <= 5:
          niceFraction = 5
        else:
          niceFraction = 10
    
      return niceFraction * pow(10, exponent)
    
    proc calculate*(x: var NiceScale) =
      assert x.maxPoint > x.minPoint, "Wrong input range!"
      assert x.maxTicks >= 0, "Sorry, can't have imaginary ticks!"
      let scaleRange = niceNum(x.maxPoint - x.minPoint, false)
      if x.maxTicks < 2:
        x.niceMin = floor(x.minPoint)
        x.niceMax = ceil(x.maxPoint)
        x.tickSpacing = (x.niceMax - x.niceMin) /
          (if x.maxTicks == 1: 2.0 else: 1.0)
      else:
        x.tickSpacing = niceNum(scaleRange / (float(x.maxTicks - 1)), true)
        x.niceMin = floor(x.minPoint / x.tickSpacing) * x.tickSpacing
        x.niceMax = ceil(x.maxPoint / x.tickSpacing) * x.tickSpacing
    
    when isMainModule:
      var s = initScale(57.2, 103.3)
      echo s
    

    This is the comment stripped version. Full one can be read at GitHub integrated into my project.

提交回复
热议问题