チャートのティックラベルとティックレンジを手動で計算する必要があります。
ニースティックの「標準」アルゴリズムを知っています( http://books.google.de/books?id=fvA7zLEFWZgC&pg=PA61&lpg=PA61&redir_esc=y#v=onepage&q&f=false を参照)。知っている this Java implementation 。
問題は、このアルゴリズムでは、ティックが「スマートすぎる」ということです。つまり、アルゴリズムは表示するティックの量を決定します。私の要件は、常に5つのティックがあることですが、これらはもちろん「きれい」である必要があります。素朴なアプローチは、最大値を取得し、5で除算し、ティック番号を乗算することです。ここでの値は-もちろん-最適ではなく、ティックはかなり醜いです。
誰かが問題の解決策を知っているか、正式なアルゴリズムの説明のヒントを持っていますか?
Java実装を、わずかな修正を加えて使用できるはずです。
Maxticksを5に変更します。
計算方法を次のように変更します。
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ティックのスペースを確保します。これは場合によっては見苦しいかもしれません。
私は「 チャート軸上の最適スケーリングのアルゴリズム "」の著者です。以前はtrollop.orgでホストされていましたが、最近ドメイン/ブログエンジンを移動しました。とにかく、アクセスしやすいようにここに内容を掲載します。
私は割り当て用のAndroidグラフ作成アプリケーションに取り組んでいますが、適切にスケーリングされた形式でグラフを表示する際に少し問題が発生しました。このアルゴリズムを自分で作成し、非常に近づきましたが、最終的に、Andrew S. Glassnerによる「GraphicsGems、Volume 1」という本で擬似コードの例を見つけました。この問題の優れた説明は、この章に記載されています。 on " グラフラベルの素敵な数字 ":
コンピューターでグラフを作成する場合は、x軸とy軸に「素敵な」数字(単純な10進数)のラベルを付けることが望ましいです。たとえば、データ範囲が105〜543の場合、100〜600の範囲をプロットし、100単位ごとにチェックマークを付ける必要があります。または、データ範囲が2.04〜2.16の場合、ティック間隔0.05で2.00〜2.20の範囲をプロットする可能性があります。人間はそのような「素敵な」数字を選ぶのは得意ですが、単純なアルゴリズムはそうではありません。ナイーブなラベル選択アルゴリズムは、データ範囲を取得してn個の等間隔に分割しますが、これは通常、醜い目盛りラベルになります。ここでは、ニースグラフラベルを生成する簡単な方法について説明します。
主な観察結果は、10進数の「最も適切な」数値は、1、2、および5であり、これらの数値の10の累乗の倍数であるということです。目盛り間隔にはそのような数値のみを使用し、目盛り間隔の倍数に目盛りを配置します。
この本の擬似コードの例を使用して、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
これが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();
}
楽しい!
上記の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
このアルゴリズムを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;
}
}
これはObjectiveCでも同じです
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);
いくつかの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;
}
}
</ code>
これが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)));
}
これがTypeScriptです!
/**
* 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);
}
これが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()
}
}
みんなと彼の犬が他の人気のある言語への翻訳を投稿しているので、これが私のバージョンです Nimrodプログラミング言語 。また、ティック数が2未満の場合の処理を追加しました。
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 私のプロジェクトに統合されて読むことができます。
これはSwiftバージョン:
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)
}
}
これは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
多くの[〜#〜]より良い[〜#〜]および[〜#〜]より単純な[〜#〜] Swiftのアルゴリズム。サイズは固定されており、値は「ハードコード」されていません。
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)
}
}
正の数でのみテストされています。
これは、より適切に編成されたC#コードです。
public class NiceScale
{
public double NiceMin { get; set; }
public double NiceMax { get; set; }
public double TickSpacing { get; private set; }
private double _minPoint;
private double _maxPoint;
private double _maxTicks = 5;
private double _range;
/**
* 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)
{
_minPoint = min;
_maxPoint = max;
Calculate();
}
/**
* Calculate and update values for tick spacing and Nice
* minimum and maximum data points on the axis.
*/
private void Calculate()
{
_range = NiceNum(_maxPoint - _minPoint, false);
TickSpacing = NiceNum(_range / (_maxTicks - 1), true);
NiceMin = Math.Floor(_minPoint / TickSpacing) * TickSpacing;
NiceMax = Math.Ceiling(_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, bool 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)
{
_minPoint = minPoint;
_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)
{
_maxTicks = maxTicks;
Calculate();
}
}