目录
一.DDA算法和中点画线算法的回顾
1.DDA算法(Digtal Differential Analyzer)
假设两个端点坐标值是和
,规定
,且
由于对直线方程求微分,有
整理(1)可得
如果我们把x的步长变得无穷小,就可以近似地估计出下一个点的坐标,从而准确地画出一条直线。但是由于设备的精度是有限的,我们需要将x或者y的点的移动变成单位步长,也就是一次移动1,算法在最大位移方向上每次走一步。也就是说,如果直线当前如图中所示(m<1),此时的最大位移方向显然是x,x每次增加一个单位,y增加m。下一个坐标应该如
,线段上实际点的坐标应当是
,在这里应当四舍五入,即int(
+0.5)来选取比较接近的点。类似地,如果m>1,最大位移方向显然是y方向,此时x的增量应当是1/m。
综上所述,DDA画线算法如下:
void DDALine(int x1,int y1,int x2,int y2){
double dx,dy,e,x,y;
dx = x2-x1;
dy = y2-y1;
e = (fabs(dx)>fabs(dy)?fabs(dx):fabs(dy);
dx/=e;
dy/=e;
x = x1;
y = y1;
for(int i=0;i<=e;i++){
SetPixel((int)(x+0.5),(int)(y+0.5));
x+=dx;
y+=dy;
}
}
2.中点画线算法
假定:直线的斜率在0~1之间
实际上,对于直线扫描算法来说,已知了当前,讨论下一个点的坐标,就相当于我们做一个选择题,选右上的点呢还是右边的点呢?(你妈和老婆掉水里了你先救谁?这当然要分情况讨论了。比如我妈会游泳,可以连着我老婆一起拉上来。)
所以说选点的情形是这样的:
很显然,直线在处有直线的坐标Q(x,y),我们只需要判断一下这个点(x,y)和中点的位置关系,它要是在中点上面,选上面的点,反之选下面的点。这就是中点画线法的基本思想,下面给出数学角度的讨论。
假设直线的起点和中点分别是和
,直线满足
F(x,y) = ax+by+c=0,其中a = y0-y1,b = x1-x0,c = x0y1-x1y0
从而对于直线上的点,F(x,y)=0,直线下方的点F(x,y)<0,直线上方的点F(x,y)>0
为了判断M和直线的位置关系,可以将M的坐标带入直线方程中,得到
根据d的值,当d<0时,M在Q的下方,应当取上面的点,如图所示:
进一步讨论,可以对每一个像素都计算判别式d得到下一个像素的位置,在d<0时,考虑下一个点的位置,此时的坐标向右移动2个单位,而中点的位置也就是
,如图中所示
从而我们得到判别式
类似地,在d>=0时,应当取得下方的点,即正右的点,它y的坐标相同和左侧点相同,将M点坐标带入可得到判别式d的值:
初值的计算,此时第一个像素应当取左侧端点(x0,y0),则相应地我们可以得到中点,带入判别式就可以得到初值为:
综上所述,实际上中点画线算法就是根据中点相对直线的位置进行选点,我们选择了直线的隐式方程得到了判别式d,然后在当前点得到了下一个点的像素选择之后进行了递推,分成了d>=0和d<0两种不同的情况,得到了误差项的递推公式。在下面代码中,为了保证整数运算,将d用2d来代替。
void MidpointLine(int x0,int y0,int x1,int y1){
int a,b,delta1,delta2,d,x,y;
a = y0-y1;
b = x1-x0;
d = 2*a+b;//设定初值,乘了个2
delta1 = 2*a;//方便整数运算
delta2 = 2*(a+b);
x = x0;
y = y0;
SetPixel(x,y);
while(x<x1){
if(d<0){//d<0时,选择右上方的点,增量是a+b,这里乘2
x++;
y++;
d+=delta2
}
else{//右侧点,增量2a
x++;
d+=delta1;
}
SetPixel(x,y) //一直到最右侧的端点再结束描像素
}
}
二.Bresenham画线算法
1967年,一个叫J.Bresenham的IBM秃头程序员提出了这个算法,这个算法只用整数运算而没有四舍五入,我们还是先看看这个秃头程序员到底想干啥。先看下面这个图:
我们现在依然是做选择题,注意那个黄色点它不再是中点,而是直线此刻的与坐标线的交点了,我们不妨设它的值是yi,那么依然是这个直线,我们可以得到d1和d2的值。
可以通过这个差值来衡量接近程度,如果插值是正的,显然d2更小,我们选择上方点,反之取下方点,在这里又引入了新的判别量p,来减少m这个分式带来的计算不便。
这就是斜率在0~1之间的Bresenham算法。
你问我为什么不打了?
累了886.