代码改变世界

Bresenham画线算法之我见

2009-04-26 23:06  hbb  阅读(704)  评论(0编辑  收藏  举报
Bresenham画线算法

一般的教程讲的比较隐晦,这里有一篇推导过程,用纯数学语言讲的,也比较晦涩...

个人做了一点形象上的理解,核心思想就不说了。

首先算法在判断下一个点的位置时,到底是向右移一格还是向右上移一格是根据斜率是否大于0.5来判断的。
为什么是0.5呢?
因为向右一格等于dx=1,dy=0,即斜率等于0。而向右上一格等于dx=1,dy=1,即斜率等于1。折中一下就有了0.5这个系数。

接着累加斜率直到超过0.5之后,右上一格再右面的判断就要看是否超过1.5了。
这是因为向右上移了一格,y加了1,而它右面的格子只可能比它高或一样高,不可能比它低。

这样一直到最后,只要向上移一格,判断系数就加1。

 1 dy=y1-y0;
 2 dx=x1-x0;
 3 k=dy/dx;
 4 d=0;
 5 b=0.5;
 6 ix=x0;
 7 iy=y0;
 8 while(ix<=x1){
 9    ++ix;
10    d += k;
11    if(d > b){
12       ++iy;
13       ++b;
14    }
15 }

 

这里面d就是用来累加斜率的,b是判别系数。


以上是我对Bresenham算法的理解,但市面上常见的是没有b这个判别系数的。因为判别系数跟着加1和坐标轴跟着加1是一样的,而坐标轴向上平移1个单位等于d减去1。

这样的好处很显然,少用一个变量。这就是传统的算法。

 1 dy=y1-y0;
 2 dx=x1-x0;
 3 k=dy/dx;
 4 d=0;
 5 ix=x0;
 6 iy=y0;
 7 while(ix<=x1){
 8    ++ix;
 9    d += k;
10    if(d > 0.5){
11       ++iy;
12       --d;
13    }
14 

 


然后是优化上的理解。

先考虑,斜率是否大于0.5,即k-0.5是否大于0。如果是就说明要向上,那么在累加斜率时要再减去1使得坐标轴也跟着向上。否则只加斜率就可以了。

 1 d=k-0.5
 2 while(ix<=x1){
 3    ++ix;
 4    if(d>0){
 5       ++iy;
 6       d+=k-1;
 7    }else{
 8       d+=k;
 9    }
10 

 

接下来是要去掉浮点数和除法运算。
考虑 d=k-0.5 两边同时放大一倍,等式不变
2*d=2*k-1
同样把dy/dx=k代入
2*d=2*dy/dx-1
2*d*dx=2*dy-dx

因为2和dx都是正数,只要之后的运算都放大2*dx倍,等号、不等号都不会受到影响。最终简化如下:

 1 dy=y1-y0;
 2 dx=x1-x0;
 3 D=2*dy-dx;
 4 ix=x0;
 5 iy=y0;
 6 while(ix<=x1){
 7    ++ix;
 8    if(D>0){
 9       ++iy;
10       D+=2*(dy-dx);
11    }else{
12       D+=2*dy;
13    }
14 }

 

我用D表示放大了的d,实际代码中可以直接用同一个变量d。


最后是一般化

如果k<0,也就是本来往上走的,要往下走。这时仍用以前的思路,就要判断是否k-0.5<0,并且在是的时候坐标轴跟着往下移,然后y每次减1。
考虑让k>0,那么所有判断都不变,只要让y每次减1就可以了。因为判断条件只和斜率有关,和y的走向没有关系。我们完全可以根据同一个斜率,通过+-来改变y的走向,+的话就和斜率同向,-的话就和负斜率同向。而k本身是<0的,现在被我们变了向,那么y的走向再变一次,y的走向就成了我们需要的走向。
而此处是垂直翻转,改变k的符号,只要把dy取绝对值就可以了。

如果|k|>1,所谓的折中系数0.5就没意义了,这时可以把x轴看作y轴,y轴看作x轴,这样斜率又回到了0<|k|<1。只要最后在画线的时候,把x当y,y当x来填充就可以了。

最后考虑反向画线,也就是起点大于终点的情况,那很简单把它们颠倒一下就可以了。

注意,以上|k|>1时xy轴的互换的过程要放在最前面。因为在k>-1时,xy轴的互换会使起点终点关系互换,这时需要后面有检测机制。


xy2yx= abs(y1-y0) > abs(x1-x0);
if(xy2yx){
   swap(x0,y0);
   swap(x1,y1);
}
if(x0>x1){
   swap(x0,x1);
   swap(y0,y1);
}
ydir 
= y0>y1 ? -1 : 1;

dy
=abs(y1-y0);
dx
=x1-x0;
d
=2*dy-dx;
ix
=x0;
iy
=y0;
while(ix<=x1){
   
if(xy2yx){
      draw(iy,ix);
   }
else{
      draw(ix,iy);
   }

   
++ix;
   
if(d>0){
      iy 
+= ydir;
      d
+=2(dy-dx);
   }
else{
      d
+=2*dy;
   }
}