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