Nice Label Algorithm for Charts with minimum ticks

后端 未结 16 1924
清酒与你
清酒与你 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:40

    Much BETTER and SIMPLER algorythm on swift. Size is fixed, values are not "hardcoded":

    class NiceNumbers {
        /// Returns nice range of specified size. Result min <= min argument, result max >= max argument.
        static func getRange(forMin minInt: Int, max maxInt: Int, ofSize size: Int) -> [Int] {
            let niceMinInt = getMinCloseToZero(min: minInt, max: maxInt)
            let step = Double(maxInt - niceMinInt) / Double(size - 1)
            let niceStepInt = Int(get(for: step, min: false))
    
            var result = [Int]()
            result.append(niceMinInt)
            for i in 1...size - 1 {
                result.append(niceMinInt + i * Int(niceStepInt))
            }
            return result
        }
    
        /// Returns nice min or zero if it is much smaller than max.
        static func getMinCloseToZero(min: Int, max: Int) -> Int {
            let nice = get(for: Double(min), min: true)
            return nice <= (Double(max) / 10) ? 0 : Int(nice)
        }
    
        /// Get nice number. If min is true returns smaller number, if false - bigger one.
        static func get(for number: Double, min: Bool) -> Double {
            if number == 0 { return 0 }
            let exponent = floor(log10(number)) - (min ? 0 : 1)
            let fraction = number / pow(10, exponent)
            let niceFraction = min ? floor(fraction) : ceil(fraction)
            return niceFraction * pow(10, exponent)
        }
    }
    

    Tested only on positive numbers.

    0 讨论(0)
  • 2020-11-29 17:43

    I needed this algorithm converted to C#, so here it is...

    public static class NiceScale {
    
        public static void Calculate(double min, double max, int maxTicks, out double range, out double tickSpacing, out double niceMin, out double niceMax) {
            range = niceNum(max - min, false);
            tickSpacing = niceNum(range / (maxTicks - 1), true);
            niceMin = Math.Floor(min / tickSpacing) * tickSpacing;
            niceMax = Math.Ceiling(max / tickSpacing) * tickSpacing;
        }
    
        private static double niceNum(double range, bool round) {
            double pow = Math.Pow(10, Math.Floor(Math.Log10(range)));
            double fraction = range / pow;
    
            double niceFraction;
            if (round) {
                if (fraction < 1.5) {
                    niceFraction = 1;
                } else if (fraction < 3) {
                    niceFraction = 2;
                } else if (fraction < 7) {
                    niceFraction = 5;
                } else {
                    niceFraction = 10;
                }
            } else {
                if (fraction <= 1) {
                    niceFraction = 1;
                } else if (fraction <= 2) {
                    niceFraction = 2;
                } else if (fraction <= 5) {
                    niceFraction = 5;
                } else {
                    niceFraction = 10;
                }
            }
    
            return niceFraction * pow;
        }
    
    }
    
    0 讨论(0)
  • 2020-11-29 17:45

    This is the VB.NET version.

    Public Class NiceScale
    
    Private minPoint As Double
    Private maxPoint As Double
    Private maxTicks As Double = 10
    Private tickSpacing
    Private range As Double
    Private niceMin As Double
    Private niceMax As Double
    
    Public Sub New(min As Double, max As Double)
        minPoint = min
        maxPoint = max
        calculate()
    End Sub
    
    Private Sub calculate()
        range = niceNum(maxPoint - minPoint, False)
        tickSpacing = niceNum(range / (maxTicks - 1), True)
        niceMin = Math.Floor(minPoint / tickSpacing) * tickSpacing
        niceMax = Math.Ceiling(maxPoint / tickSpacing) * tickSpacing
    End Sub
    
    Private Function niceNum(range As Double, round As Boolean) As Double
        Dim exponent As Double '/** exponent of range */
        Dim fraction As Double '/** fractional part of range */
        Dim niceFraction As Double '/** nice, rounded fraction */
    
        exponent = Math.Floor(Math.Log10(range))
        fraction = range / Math.Pow(10, exponent)
    
        If round Then
            If (fraction < 1.5) Then
                niceFraction = 1
            ElseIf (fraction < 3) Then
                niceFraction = 2
            ElseIf (fraction < 7) Then
                niceFraction = 5
            Else
                niceFraction = 10
            End If
        Else
            If (fraction <= 1) Then
                niceFraction = 1
            ElseIf (fraction <= 2) Then
                niceFraction = 2
            ElseIf (fraction <= 5) Then
                niceFraction = 5
            Else
                niceFraction = 10
            End If
        End If
    
        Return niceFraction * Math.Pow(10, exponent)
    End Function
    
    Public Sub setMinMaxPoints(minPoint As Double, maxPoint As Double)
        minPoint = minPoint
        maxPoint = maxPoint
        calculate()
    End Sub
    
    Public Sub setMaxTicks(maxTicks As Double)
        maxTicks = maxTicks
        calculate()
    End Sub
    
    Public Function getTickSpacing() As Double
        Return tickSpacing
    End Function
    
    Public Function getNiceMin() As Double
        Return niceMin
    End Function
    
    Public Function getNiceMax() As Double
        Return niceMax
    End Function
    
    End Class
    
    0 讨论(0)
  • 2020-11-29 17:48

    I found this thread while writing some php, so now the same code is available in php too!

    class CNiceScale {
    
      private $minPoint;
      private $maxPoint;
      private $maxTicks = 10;
      private $tickSpacing;
      private $range;
      private $niceMin;
      private $niceMax;
    
      public function setScale($min, $max) {
        $this->minPoint = $min;
        $this->maxPoint = $max;
        $this->calculate();
      }
    
      private function calculate() {
        $this->range = $this->niceNum($this->maxPoint - $this->minPoint, false);
        $this->tickSpacing = $this->niceNum($this->range / ($this->maxTicks - 1), true);
        $this->niceMin = floor($this->minPoint / $this->tickSpacing) * $this->tickSpacing;
        $this->niceMax = ceil($this->maxPoint / $this->tickSpacing) * $this->tickSpacing;
      }
    
      private function niceNum($range, $round) {
        $exponent; /** exponent of range */
        $fraction; /** fractional part of range */
        $niceFraction; /** nice, rounded fraction */
    
        $exponent = floor(log10($range));
        $fraction = $range / pow(10, $exponent);
    
        if ($round) {
          if ($fraction < 1.5)
            $niceFraction = 1;
          else if ($fraction < 3)
            $niceFraction = 2;
          else if ($fraction < 7)
            $niceFraction = 5;
          else
            $niceFraction = 10;
        } else {
          if ($fraction <= 1)
            $niceFraction = 1;
          else if ($fraction <= 2)
            $niceFraction = 2;
          else if ($fraction <= 5)
            $niceFraction = 5;
          else
            $niceFraction = 10;
        }
    
        return $niceFraction * pow(10, $exponent);
      }
    
      public function setMinMaxPoints($minPoint, $maxPoint) {
        $this->minPoint = $minPoint;
        $this->maxPoint = $maxPoint;
        $this->calculate();
      }
    
      public function setMaxTicks($maxTicks) {
        $this->maxTicks = $maxTicks;
        $this->calculate();
      }
    
      public function getTickSpacing() {
        return $this->tickSpacing;
      }
    
      public function getNiceMin() {
        return $this->niceMin;
      }
    
      public function getNiceMax() {
        return $this->niceMax;
      }
    
    }
    

    0 讨论(0)
提交回复
热议问题