2011-12-14 55 views
19

我需要手動計算圖表的標籤和Tickrange。具有最小刻度的圖表的好標籤算法

我知道好的刻度的「標準」算法(見http://books.google.de/books?id=fvA7zLEFWZgC&pg=PA61&lpg=PA61&redir_esc=y#v=onepage&q&f=false),我也知道this Java implementation

問題是,用這種算法,滴答「太聰明」了。這意味着,該算法決定應顯示多少刻度。我的要求是,總有5個蜱蟲,但這些當然應該是「漂亮」的。天真的做法是獲得最大值,用5除以乘以ticknumber。這裏的值 - 當然 - 不是最佳的,並且蜱非常難看。

有沒有人知道該問題的解決方案或提示正式的算法描述?

+0

最小值和最大值始終是蜱或最低/最高蜱是下一個較小/較高的好數字? – Thomas 2011-12-14 15:35:36

+0

@Thomas在我的情況下,最小滴答總是0,最高滴答應該是下一個更好的數字 – 2011-12-14 15:47:29

回答

3

您應該可以稍微更正地使用Java實現。

變化maxticks到5

更改計算mehod這樣:

private void calculate() { 
     this.range = niceNum(maxPoint - minPoint, false); 
     this.tickSpacing = niceNum(range/(maxTicks - 1), true); 
     this.niceMin = 
      Math.floor(minPoint/tickSpacing) * tickSpacing; 
     this.niceMax = this.niceMin + tickSpacing * (maxticks - 1); // Always display maxticks 
    } 

免責聲明:請注意,我沒有測試過這一點,所以你可能需要調整它,使它看起來好。我建議的解決方案在圖表頂部增加了額外空間,以便爲5個刻度留出空間。在某些情況下,這可能看起來很醜。

49

我是「Algorithm for Optimal Scaling on a Chart Axis」的作者。它曾經託管在trollop.org上,但我最近移動了域/博客引擎。無論如何,我會在這裏發佈內容以方便訪問。

我一直在爲一個任務處理Android圖表應用程序,並遇到了一個問題,當它以一個很好的縮放格式呈現圖表時。我花了一段時間嘗試自己創建這個算法,並且非常接近,但最終我在Andrew S. Glassner的書「Graphics Gems,Volume 1」中找到了一個僞代碼示例。該問題的一個極好描述的章節中給出的「Nice Numbers for Graph Labels」:

當創建由計算機的曲線圖中,期望標記x和 y隨‘好’的數字軸:簡單十進制數。例如,如果 的數據範圍是105到543,我們可能想要將範圍 從100到600繪製出來,並將每100個單位的刻度標記。或者如果數據 的範圍是2.04到2.16,我們可能繪製範圍從2.00到2.20的 ,滴答間距爲0.05。人類善於選擇這樣「好」的數字,但簡單的算法卻不是。天真的標籤選擇 算法取數據範圍並將其分成n個相等的間隔,即 ,但這通常會導致難看的刻度標籤。我們在這裏描述了一個用於生成漂亮圖形標籤的簡單方法 。

主要的觀察結果是,十進制數中「最好」的數字是1,0,2和5,以及這些數字的所有冪數的倍數。我們將僅使用 這樣的數字作爲刻度間距,並將刻度線標記爲 倍數的刻度間距...

我曾經在這本書中的僞代碼示例使用Java創建下面的類:

public class NiceScale { 

    private double minPoint; 
    private double maxPoint; 
    private double maxTicks = 10; 
    private double tickSpacing; 
    private double range; 
    private double niceMin; 
    private double niceMax; 

    /** 
    * Instantiates a new instance of the NiceScale class. 
    * 
    * @param min the minimum data point on the axis 
    * @param max the maximum data point on the axis 
    */ 
    public NiceScale(double min, double max) { 
    this.minPoint = min; 
    this.maxPoint = max; 
    calculate(); 
    } 

    /** 
    * Calculate and update values for tick spacing and nice 
    * minimum and maximum data points on the axis. 
    */ 
    private void calculate() { 
    this.range = niceNum(maxPoint - minPoint, false); 
    this.tickSpacing = niceNum(range/(maxTicks - 1), true); 
    this.niceMin = 
     Math.floor(minPoint/tickSpacing) * tickSpacing; 
    this.niceMax = 
     Math.ceil(maxPoint/tickSpacing) * tickSpacing; 
    } 

    /** 
    * Returns a "nice" number approximately equal to range Rounds 
    * the number if round = true Takes the ceiling if round = false. 
    * 
    * @param range the data range 
    * @param round whether to round the result 
    * @return a "nice" number to be used for the data range 
    */ 
    private double niceNum(double range, boolean round) { 
    double exponent; /** exponent of range */ 
    double fraction; /** fractional part of range */ 
    double niceFraction; /** nice, rounded fraction */ 

    exponent = Math.floor(Math.log10(range)); 
    fraction = range/Math.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 * Math.pow(10, exponent); 
    } 

    /** 
    * Sets the minimum and maximum data points for the axis. 
    * 
    * @param minPoint the minimum data point on the axis 
    * @param maxPoint the maximum data point on the axis 
    */ 
    public void setMinMaxPoints(double minPoint, double maxPoint) { 
    this.minPoint = minPoint; 
    this.maxPoint = maxPoint; 
    calculate(); 
    } 

    /** 
    * Sets maximum number of tick marks we're comfortable with 
    * 
    * @param maxTicks the maximum number of tick marks for the axis 
    */ 
    public void setMaxTicks(double maxTicks) { 
    this.maxTicks = maxTicks; 
    calculate(); 
    } 
} 

然後,我們可以利用上面的代碼是這樣的:

NiceScale numScale = new NiceScale(-0.085, 0.173); 

System.out.println("Tick Spacing:\t" + numScale.getTickSpacing()); 
System.out.println("Nice Minimum:\t" + numScale.getNiceMin()); 
System.out.println("Nice Maximum:\t" + numScale.getNiceMax()); 

然後,它會輸出很好的格式化數字,以用於需要創建相當規模的任何應用程序。 = D

Tick Spacing: 0.05 
Nice Minimum: -0.1 
Nice Maximum: 0.2 
3

我按照我的要求將上面的java代碼轉換爲Python。

import math 

    class NiceScale: 
    def __init__(self, minv,maxv): 
     self.maxTicks = 6 
     self.tickSpacing = 0 
     self.lst = 10 
     self.niceMin = 0 
     self.niceMax = 0 
     self.minPoint = minv 
     self.maxPoint = maxv 
     self.calculate() 

    def calculate(self): 
     self.lst = self.niceNum(self.maxPoint - self.minPoint, False) 
     self.tickSpacing = self.niceNum(self.lst/(self.maxTicks - 1), True) 
     self.niceMin = math.floor(self.minPoint/self.tickSpacing) * self.tickSpacing 
     self.niceMax = math.ceil(self.maxPoint/self.tickSpacing) * self.tickSpacing 

    def niceNum(self, lst, rround): 
     self.lst = lst 
     exponent = 0 # exponent of range */ 
     fraction = 0 # fractional part of range */ 
     niceFraction = 0 # nice, rounded fraction */ 

     exponent = math.floor(math.log10(self.lst)); 
     fraction = self.lst/math.pow(10, exponent); 

     if (self.lst): 
      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 * math.pow(10, exponent) 

    def setMinMaxPoints(self, minPoint, maxPoint): 
      self.minPoint = minPoint 
      self.maxPoint = maxPoint 
      self.calculate() 

    def setMaxTicks(self, maxTicks): 
     self.maxTicks = maxTicks; 
     self.calculate() 

a=NiceScale(14024, 17756) 
print "a.lst ", a.lst 
print "a.maxPoint ", a.maxPoint 
print "a.maxTicks ", a.maxTicks 
print "a.minPoint ", a.minPoint 
print "a.niceMax ", a.niceMax 
print "a.niceMin ", a.niceMin 
print "a.tickSpacing ", a.tickSpacing 
2

這裏是目標C

YFRNiceScale.h

#import <Foundation/Foundation.h> 

@interface YFRNiceScale : NSObject 

@property (nonatomic, readonly) CGFloat minPoint; 
@property (nonatomic, readonly) CGFloat maxPoint; 
@property (nonatomic, readonly) CGFloat maxTicks; 
@property (nonatomic, readonly) CGFloat tickSpacing; 
@property (nonatomic, readonly) CGFloat range; 
@property (nonatomic, readonly) CGFloat niceRange; 
@property (nonatomic, readonly) CGFloat niceMin; 
@property (nonatomic, readonly) CGFloat niceMax; 


- (id) initWithMin: (CGFloat) min andMax: (CGFloat) max; 
- (id) initWithNSMin: (NSDecimalNumber*) min andNSMax: (NSDecimalNumber*) max; 

@end 

YFRNiceScale.m

#import "YFRNiceScale.h" 

@implementation YFRNiceScale 

@synthesize minPoint = _minPoint; 
@synthesize maxPoint = _maxPoint; 
@synthesize maxTicks = _maxTicks; 
@synthesize tickSpacing = _tickSpacing; 
@synthesize range = _range; 
@synthesize niceRange = _niceRange; 
@synthesize niceMin = _niceMin; 
@synthesize niceMax = _niceMax; 

- (id)init { 
    self = [super init]; 
    if (self) { 

    } 
    return self; 
} 

- (id) initWithMin: (CGFloat) min andMax: (CGFloat) max { 

    if (self) { 
     _maxTicks = 10; 
     _minPoint = min; 
     _maxPoint = max; 
     [self calculate]; 
    } 
    return [self init]; 
} 

- (id) initWithNSMin: (NSDecimalNumber*) min andNSMax: (NSDecimalNumber*) max { 

    if (self) { 
     _maxTicks = 10; 
     _minPoint = [min doubleValue]; 
     _maxPoint = [max doubleValue]; 
     [self calculate]; 
    } 
    return [self init]; 
} 


/** 
* Calculate and update values for tick spacing and nice minimum and maximum 
* data points on the axis. 
*/ 

- (void) calculate { 
    _range = [self niceNumRange: (_maxPoint-_minPoint) roundResult:NO]; 
    _tickSpacing = [self niceNumRange: (_range/(_maxTicks - 1)) roundResult:YES]; 
    _niceMin = floor(_minPoint/_tickSpacing) * _tickSpacing; 
    _niceMax = ceil(_maxPoint/_tickSpacing) * _tickSpacing; 

    _niceRange = _niceMax - _niceMin; 
} 


/** 
* Returns a "nice" number approximately equal to range Rounds the number if 
* round = true Takes the ceiling if round = false. 
* 
* @param range 
*   the data range 
* @param round 
*   whether to round the result 
* @return a "nice" number to be used for the data range 
*/ 
- (CGFloat) niceNumRange:(CGFloat) aRange roundResult:(BOOL) round { 
    CGFloat exponent; 
    CGFloat fraction; 
    CGFloat niceFraction; 

    exponent = floor(log10(aRange)); 
    fraction = aRange/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 = 2; 
     } else { 
      niceFraction = 10; 
     } 
    } 

    return niceFraction * pow(10, exponent); 
} 

- (NSString*) description { 
    return [NSString stringWithFormat:@"NiceScale [minPoint=%.2f, maxPoint=%.2f, maxTicks=%.2f, tickSpacing=%.2f, range=%.2f, niceMin=%.2f, niceMax=%.2f]", _minPoint, _maxPoint, _maxTicks, _tickSpacing, _range, _niceMin, _niceMax ]; 
} 

@end 

使用同樣的事情:

YFRNiceScale* niceScale = [[YFRNiceScale alloc] initWithMin:0 andMax:500]; 
NSLog(@"Nice: %@", niceScale); 
1

我在編寫一些php時發現了這個線程,所以現在在php中也有相同的代碼!

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; 
    } 

} 

1

既然大家和他的狗被髮布翻譯到其他流行的語言,這裏是我的版本爲Nimrod programming language。我還添加了處理蜱數少於兩個的情況:

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 

這是註釋剝離版本。完整的一個可以在GitHub閱讀我的項目集成。

2

我需要這個算法轉換爲C#,所以這裏是......

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; 
    } 

} 
1

這是斯威夫特版本:

class NiceScale { 
    private var minPoint: Double 
    private var maxPoint: Double 
    private var maxTicks = 10 
    private(set) var tickSpacing: Double = 0 
    private(set) var range: Double = 0 
    private(set) var niceMin: Double = 0 
    private(set) var niceMax: Double = 0 

    init(min: Double, max: Double) { 
     minPoint = min 
     maxPoint = max 
     calculate() 
    } 

    func setMinMaxPoints(min: Double, max: Double) { 
     minPoint = min 
     maxPoint = max 
     calculate() 
    } 

    private func calculate() { 
     range = niceNum(maxPoint - minPoint, round: false) 
     tickSpacing = niceNum(range/Double((maxTicks - 1)), round: true) 
     niceMin = floor(minPoint/tickSpacing) * tickSpacing 
     niceMax = floor(maxPoint/tickSpacing) * tickSpacing 
    } 

    private func niceNum(range: Double, round: Bool) -> Double { 
     let exponent = floor(log10(range)) 
     let fraction = range/pow(10, exponent) 
     let niceFraction: Double 

     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) 
    } 
} 
0

這是VB.NET版本。

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 
1

下面是JavaScript版本:

var minPoint; 
var maxPoint; 
var maxTicks = 10; 
var tickSpacing; 
var range; 
var niceMin; 
var niceMax; 

/** 
* Instantiates a new instance of the NiceScale class. 
* 
* min the minimum data point on the axis 
* max the maximum data point on the axis 
*/ 
function niceScale(min, max) { 
    minPoint = min; 
    maxPoint = max; 
    calculate(); 
    return { 
     tickSpacing: tickSpacing, 
     niceMinimum: niceMin, 
     niceMaximum: niceMax 
    }; 
} 



/** 
* Calculate and update values for tick spacing and nice 
* minimum and maximum data points on the axis. 
*/ 
function calculate() { 
    range = niceNum(maxPoint - minPoint, false); 
    tickSpacing = niceNum(range/(maxTicks - 1), true); 
    niceMin = 
     Math.floor(minPoint/tickSpacing) * tickSpacing; 
    niceMax = 
     Math.ceil(maxPoint/tickSpacing) * tickSpacing; 
} 

/** 
* Returns a "nice" number approximately equal to range Rounds 
* the number if round = true Takes the ceiling if round = false. 
* 
* localRange the data range 
* round whether to round the result 
* a "nice" number to be used for the data range 
*/ 
function niceNum(localRange, round) { 
    var exponent; /** exponent of localRange */ 
    var fraction; /** fractional part of localRange */ 
    var niceFraction; /** nice, rounded fraction */ 

    exponent = Math.floor(Math.log10(localRange)); 
    fraction = localRange/Math.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 * Math.pow(10, exponent); 
} 

/** 
* Sets the minimum and maximum data points for the axis. 
* 
* minPoint the minimum data point on the axis 
* maxPoint the maximum data point on the axis 
*/ 
function setMinMaxPoints(localMinPoint, localMaxPoint) { 
    minPoint = localMinPoint; 
    maxPoint = localMaxoint; 
    calculate(); 
} 

/** 
* Sets maximum number of tick marks we're comfortable with 
* 
* maxTicks the maximum number of tick marks for the axis 
*/ 
function setMaxTicks(localMaxTicks) { 
    maxTicks = localMaxTicks; 
    calculate(); 
} 

享受!

1

這是C++版本。作爲獎勵,您會得到一個返回最小小數點數的函數,以在軸上顯示刻度標籤。

頭文件:

class NiceScale 
{ public: 

    float minPoint; 
    float maxPoint; 
    float maxTicks; 
    float tickSpacing; 
    float range; 
    float niceMin; 
    float niceMax; 

    public: 
    NiceScale() 
    { maxTicks = 10; 
    } 

    /** 
    * Instantiates a new instance of the NiceScale class. 
    * 
    * @param min the minimum data point on the axis 
    * @param max the maximum data point on the axis 
    */ 
    NiceScale(float min, float max) 
    { minPoint = min; 
     maxPoint = max; 
     calculate(); 
    } 

    /** 
    * Calculate and update values for tick spacing and nice 
    * minimum and maximum data points on the axis. 
    */ 
    void calculate() ; 

    /** 
    * Returns a "nice" number approximately equal to range Rounds 
    * the number if round = true Takes the ceiling if round = false. 
    * 
    * @param range the data range 
    * @param round whether to round the result 
    * @return a "nice" number to be used for the data range 
    */ 
    float niceNum(float range, boolean round) ; 

    /** 
    * Sets the minimum and maximum data points for the axis. 
    * 
    * @param minPoint the minimum data point on the axis 
    * @param maxPoint the maximum data point on the axis 
    */ 
    void setMinMaxPoints(float minPoint, float maxPoint) ; 

    /** 
    * Sets maximum number of tick marks we're comfortable with 
    * 
    * @param maxTicks the maximum number of tick marks for the axis 
    */ 
    void setMaxTicks(float maxTicks) ; 
    int decimals(void); 
}; 

而CPP文件:

/** 
* Calculate and update values for tick spacing and nice 
* minimum and maximum data points on the axis. 
*/ 
void NiceScale::calculate() 
{ 
    range = niceNum(maxPoint - minPoint, false); 
    tickSpacing = niceNum(range/(maxTicks - 1), true); 
    niceMin = floor(minPoint/tickSpacing) * tickSpacing; 
    niceMax = ceil(maxPoint/tickSpacing) * tickSpacing; 
} 

/** 
* Returns a "nice" number approximately equal to range 
    Rounds the number if round = true Takes the ceiling if round = false. 
* 
* @param range the data range 
* @param round whether to round the result 
* @return a "nice" number to be used for the data range 
*/ 
float NiceScale::niceNum(float range, boolean round) 
{ float exponent; /** exponent of range */ 
    float fraction; /** fractional part of range */ 
    float niceFraction; /** nice, rounded fraction */ 

    exponent = floor(log10(range)); 
    fraction = range/pow(10.f, 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); 
} 

/** 
* Sets the minimum and maximum data points for the axis. 
* 
* @param minPoint the minimum data point on the axis 
* @param maxPoint the maximum data point on the axis 
*/ 
void NiceScale::setMinMaxPoints(float minPoint, float maxPoint) 
{ 
    this->minPoint = minPoint; 
    this->maxPoint = maxPoint; 
    calculate(); 
} 

/** 
* Sets maximum number of tick marks we're comfortable with 
* 
* @param maxTicks the maximum number of tick marks for the axis 
*/ 
void NiceScale::setMaxTicks(float maxTicks) 
{ 
    this->maxTicks = maxTicks; 
    calculate(); 
} 

// minimum number of decimals in tick labels 
// use in sprintf statement: 
// sprintf(buf, "%.*f", decimals(), tickValue); 
int NiceScale::decimals(void) 
{ 
    float logTickX = log10(tickSpacing); 
    if(logTickX >= 0) 
     return 0; 
    return (int)(abs(floor(logTickX))); 
} 
0

這是打字稿!

/** 
* Calculate and update values for tick spacing and nice 
* minimum and maximum data points on the axis. 
*/ 
function calculateTicks(maxTicks: number, minPoint: number, maxPoint: number): [number, number, number] { 
    let range = niceNum(maxPoint - minPoint, false); 
    let tickSpacing = niceNum(range/(maxTicks - 1), true); 
    let niceMin = Math.floor(minPoint/tickSpacing) * tickSpacing; 
    let niceMax = Math.ceil(maxPoint/tickSpacing) * tickSpacing; 
    let tickCount = range/tickSpacing; 
    return [tickCount, niceMin, niceMax]; 
} 

/** 
* Returns a "nice" number approximately equal to range Rounds 
* the number if round = true Takes the ceiling if round = false. 
* 
* @param range the data range 
* @param round whether to round the result 
* @return a "nice" number to be used for the data range 
*/ 
function niceNum(range: number, round: boolean): number { 
    let exponent: number; 
    /** exponent of range */ 
    let fraction: number; 
    /** fractional part of range */ 
    let niceFraction: number; 
    /** nice, rounded fraction */ 

    exponent = Math.floor(Math.log10(range)); 
    fraction = range/Math.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 * Math.pow(10, exponent); 
} 
0

這裏是Kotlin版本!

import java.lang.Math.* 

/** 
* Instantiates a new instance of the NiceScale class. 
* 
* @param min Double The minimum data point. 
* @param max Double The maximum data point. 
*/ 
class NiceScale(private var minPoint: Double, private var maxPoint: Double) { 

    private var maxTicks = 15.0 
    private var range: Double = 0.0 
    var niceMin: Double = 0.0 
    var niceMax: Double = 0.0 
    var tickSpacing: Double = 0.0 

    init { 
     calculate() 
    } 

    /** 
    * Calculate and update values for tick spacing and nice 
    * minimum and maximum data points on the axis. 
    */ 
    private fun calculate() { 
     range = niceNum(maxPoint - minPoint, false) 
     tickSpacing = niceNum(range/(maxTicks - 1), true) 
     niceMin = floor(minPoint/tickSpacing) * tickSpacing 
     niceMax = ceil(maxPoint/tickSpacing) * tickSpacing 
    } 

    /** 
    * Returns a "nice" number approximately equal to range. Rounds 
    * the number if round = true. Takes the ceiling if round = false. 
    * 
    * @param range Double The data range. 
    * @param round Boolean Whether to round the result. 
    * @return Double A "nice" number to be used for the data range. 
    */ 
    private fun niceNum(range: Double, round: Boolean): Double { 
     /** Exponent of range */ 
     val exponent: Double = floor(log10(range)) 
     /** Fractional part of range */ 
     val fraction: Double 
     /** Nice, rounded fraction */ 
     val niceFraction: Double 

     fraction = range/pow(10.0, exponent) 

     niceFraction = if (round) { 
      when { 
       fraction < 1.5 -> 1.0 
       fraction < 3 -> 2.0 
       fraction < 7 -> 5.0 
       else -> 10.0 
      } 
     } else { 
      when { 
       fraction <= 1 -> 1.0 
       fraction <= 2 -> 2.0 
       fraction <= 5 -> 5.0 
       else -> 10.0 
      } 
     } 

     return niceFraction * pow(10.0, exponent) 
    } 

    /** 
    * Sets the minimum and maximum data points. 
    * 
    * @param minPoint Double The minimum data point. 
    * @param maxPoint Double The maximum data point. 
    */ 
    fun setMinMaxPoints(minPoint: Double, maxPoint: Double) { 
     this.minPoint = minPoint 
     this.maxPoint = maxPoint 
     calculate() 
    } 

    /** 
    * Sets maximum number of tick marks we're comfortable with. 
    * 
    * @param maxTicks Double The maximum number of tick marks. 
    */ 
    fun setMaxTicks(maxTicks: Double) { 
     this.maxTicks = maxTicks 
     calculate() 
    } 
}