我是“图表轴上最佳缩放算法”的作者。它曾经托管在 trollop.org 上,但我最近移动了域/博客引擎。无论如何,我会在这里发布内容,以便于访问。
我一直在为一项任务开发Android图表应用程序,并且在以精美缩放的格式呈现图表时遇到了一些问题。我花了一些时间试图自己创建这个算法,并且非常接近,但最终我在Andrew S. Glassner的一本名为“Graphics Gems,Volume 1”的书中发现了一个伪代码示例。在“图形标签的漂亮数字”一章中给出了这个问题的精彩描述:
当通过计算机创建图形时,需要用“nice”数字标记x轴和y轴:简单的十进制数。例如,如果数据范围是 105 到 543,我们可能希望绘制从 100 到 600 的范围,并每隔 100 个单位放置刻度线。或者,如果数据范围是 2.04 到 2.16,我们可能会绘制一个从 2.00 到 2.20 的范围,刻度间距为 0.05。人类擅长选择这种“好”的数字,但简单的算法却不擅长。朴素的标签选择算法获取数据范围并将其划分为 n 个相等的区间,但这通常会导致丑陋的刻度标签。我们在这里描述了一种生成漂亮图形标签的简单方法。
主要观察结果是,十进制中“最好”的数字是 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