【发布时间】:2015-06-10 09:11:18
【问题描述】:
我已经编写了 Bresenham 的画圆算法的实现。 该算法利用了圆的高度对称性(它只计算第一个八分圆的点,并利用对称性绘制其他点)。因此,我期望它会非常快。图形编程黑皮书,第 35 章的标题是“Bresenham 很快,而且很快就是好”,虽然是关于画线算法,但我可以合理地期望画圆算法也是快(因为原理是一样的)。
这是我的 java,swing 实现
public static void drawBresenhamsCircle(int r, double width, double height, Graphics g) {
int x,y,d;
y = r;
x = 0;
drawPoint(x, y, width, height,g);
d = (3-2*(int)r);
while (x <= y) {
if (d <= 0) {
d = d + (4*x + 6);
} else {
d = d + 4*(x-y) + 10;
y--;
}
x++;
drawPoint(x, y, width, height,g);
drawPoint(-x, y, width, height,g);
drawPoint(x, -y, width, height,g);
drawPoint(-x, -y, width, height,g);
drawPoint(y, x, width, height,g);
drawPoint(-y, x, width, height,g);
drawPoint(y, -x, width, height,g);
drawPoint(-y, -x, width, height,g);
}
}
此方法使用以下drawPoint方法:
public static void drawPoint(double x, double y,double width,double height, Graphics g) {
double nativeX = getNativeX(x, width);
double nativeY = getNativeY(y, height);
g.fillRect((int)nativeX, (int)nativeY, 1, 1);
}
getNativeX 和 getNativeY 这两个方法用于将坐标从屏幕左上角切换到以更经典的轴方向为原点的面板中心的系统。
public static double getNativeX(double newX, double width) {
return newX + (width/2);
}
public static double getNativeY(double newY, double height) {
return (height/2) - newY;
}
我还创建了一个基于三角公式(x=R*Math.cos(angle)和y= R*Math.sin(angle))的圆绘制算法的实现,以及使用标准 drawArc 方法(在 Graphics 对象上可用)的第三个实现。这些附加实现的唯一目的是比较 Bresenham 的算法。
然后我创建了绘制一堆圆圈的方法,以便能够很好地衡量所花费的时间。这是我使用 Bresenham 算法绘制一堆圆圈的方法
public static void drawABunchOfBresenhamsCircles(int numOfCircles, double width, double height, Graphics g) {
double r = 5;
double step = (300.0-5.0)/numOfCircles;
for (int i = 1; i <= numOfCircles; i++) {
drawBresenhamsCircle((int)r, width, height, g);
r += step;
}
}
最后,我重写了我正在使用的 JPanel 的绘制方法,以绘制一堆圆圈并测量每种类型绘制所花费的时间。这是paint方法:
public void paint(Graphics g) {
Graphics2D g2D = (Graphics2D)g;
g2D.setColor(Color.RED);
long trigoStartTime = System.currentTimeMillis();
drawABunchOfTrigonometricalCircles(1000, this.getWidth(), this.getHeight(), g);
long trigoEndTime = System.currentTimeMillis();
long trigoDelta = trigoEndTime - trigoStartTime;
g2D.setColor(Color.BLUE);
long bresenHamsStartTime = System.currentTimeMillis();
drawABunchOfBresenhamsCircles(1000, this.getWidth(), this.getHeight(), g);
long bresenHamsEndTime = System.currentTimeMillis();
long bresenDelta = bresenHamsEndTime - bresenHamsStartTime;
g2D.setColor(Color.GREEN);
long standardStarTime = System.currentTimeMillis();
drawABunchOfStandardCircles(1000, this.getWidth(), this.getHeight(),g);
long standardEndTime = System.currentTimeMillis();
long standardDelta = standardEndTime - standardStarTime;
System.out.println("Trigo : " + trigoDelta + " milliseconds");
System.out.println("Bresenham :" + bresenDelta + " milliseconds");
System.out.println("Standard :" + standardDelta + " milliseconds");
}
这是它会生成的渲染类型(每种类型绘制 1000 个圆圈)
不幸的是,我的 Bresenham 的实施非常缓慢。我采取了许多比较措施,Bresenham 的实现不仅比Graphics.drawArc 慢,而且比三角方法慢。查看以下针对绘制的不同数量的圆圈的度量。
我的实施的哪一部分更耗时?有什么解决方法可以用来改进它吗?感谢您的帮助。
[EDITION]:根据@higuaro 的要求,这是我绘制圆的三角算法
public static void drawTrigonometricalCircle (double r, double width, double height, Graphics g) {
double x0 = 0;
double y0 = 0;
boolean isStart = true;
for (double angle = 0; angle <= 2*Math.PI; angle = angle + Math.PI/36) {
double x = r * Math.cos(angle);
double y = r * Math.sin(angle);
drawPoint((double)x, y, width, height, g);
if (!isStart) {
drawLine(x0, y0, x, y, width, height, g);
}
isStart = false;
x0 = x;
y0 = y;
}
}
以及画一堆三角圆的方法
public static void drawABunchOfTrigonometricalCircles(int numOfCircles, double width, double height, Graphics g) {
double r = 5;
double step = (300.0-5.0)/numOfCircles;
for (int i = 1; i <= numOfCircles; i++) {
drawTrigonometricalCircle(r, width, height, g);
r += step;
}
}
【问题讨论】:
-
如果我见过这样的问题,这是一个很好的问题。
-
您是否也在使用
fillRect在三角解中画一个点?我的第一个想法是,如果您正在考虑速度,那可能不是画一个点的最佳方式。您还可以通过消除getNativeX和getNativeY来节省一点时间;您必须在您的drawBressenhamsCircle方法中保留 8 个值,您将在增加或减少x和y的同时增加或减少这些值。但是,这会使事情变得丑陋,我怀疑它会节省那么多时间。 -
Native 会更快,原因有很多(例如,直接访问帧缓冲区以进行无中断像素绘制),因此它应该不在比较范围内。如果你能发布你的三角圆绘图程序来看看为什么比你的 Bressenham 跑得快,那就太好了
-
只是一个愚蠢的事情,但你的
width和height变量是double而不是integer,像素绘图也使用double所以浮点数和整数之间的转换是我的赌注瓶颈....(但我不是 JAVA 编码器,所以可能是双重意味着不同于我习惯的东西)
标签: java algorithm swing graphics bresenham