图形学算法--Bresenham画直线

  上次写了一下DDA直线算法的过程和实现,但DDA算法也有一些缺点:
  主要它涉及到了实数的除法运算,效率不太高。
  今天,我介绍另外一种画直线的算法:Bresenham算法(中点画线)
  Bresenham算法的基本思想就是一步一步生成直线上的结点,可以说是生成式算法。它的优点就在于整个算法过程中全是整数运算,没有实数运算。

  

  首先假设直线的k(斜率)是 0<k<1 ,如上图所示,可以看到直线的起点是 (x1,y1) 终点是 (x2,y2) 。现在的状态是我当前点位于 (xp,yp) ,目标是计算出下一个应该画的节点,从图中可以看到,下一个点不是 P1 就是 P2 ,那怎么确定呢?很显然我们只需要计算 P1 P2 的中点 M 在直线的上侧还是下侧,如果在上侧就选 P2 ,如果在下侧就选 P1 。这样就计算得出了下一个将要画的点,然后重复上述计算过程,一个点一个点的计算出来,将直线画出。   

  上面只是简单描述了一下算法的思想,具体实现过程还有一些事情需要处理:

  1、构造一个判别式

  F(x,y)=ax+by+c  ,用于计算点到直线的距离。将起点和终点代入判别式,可以得出其参数 a=y1y2 , b=x2x1 , c=x1y2x2y1
  若 F(x,y)=0 ,则 (x,y) 在直线上
  若 F(x,y)>0 ,则 (x,y) 在直线上方
  若 F(x,y)<0 ,则 (x,y) 在直线下方
  2、计算判别式
  假设当前点为 (xp,yp) ,则相应的M点为 (xp+1,yp+0.5) ,将其代入到判别式 F(x,y) 中,令 d=F(M)=F(x+1,y+0.5)=a(xp+1)+b(yp+0.5)+c ,若 d0 ,则取 P2 ;若 d<0 ,则取 P1
  3、使用增量法
  在这里,我们最主要的任务就是如何通过当前的 d 计算出下一步的 d 值。假设当前点为 (xp,yp) ,则 d=F(x+1,y+0.5)=a(xp+1)+b(yp+0.5)+c ,此时:
  若选 P1 :则 d1=F(M2)=F(xp+2,yp+1.5)=a(xp+2)+b(yp+1.5)+c =a(xp+1)+b(yp+0.5)+c+a+b=d+a+b
  若选 P1 :则 d2=F(M1)=F(xp+2,yp+0.5)=a(xp+2)+b(yp+0.5)+c =a(xp+1)+b(yp+0.5)+c+a=d+a
  到目前为止,由当前的d计算下一步的d的递推式已经得出,现在要做的就是确定d的初始值,初始值很容易确定:初始点是 (x1,y1) ,所以初始 d0=F(x1+1,y1+0.5)=a(x1+1)+b(y1+0.5)+c=a+0.5b
  注意由于对于 d ,我们只需要考察其正负性,不考虑其具体的值,所以, d0d1d2 可以写成:

d0=2a+b

d1=d+2a+2b

d2=d+2a


  注意:此时的k必须满足 0<k<1 ,也就是说,要全方向的画出直线,需要有8组 d0d1d2 的取值。
  具体实现( 0<k<1 的情况):

  

void CLine::DrawX1(CDC *pDC)
{ int a=sp.y-ep.y, b=ep.x-sp.x;
int d=2*a+b, d1=2*a, d2=2*(a+b);
pDC->SetPixel(sp.x,sp.y,RGB(0,0,0));
for(int x=sp.x+1,y=sp.y; x=0) d=d+d1;
else { y++; d=d+d2; }
pDC->SetPixel(x,y,RGB(0,0,0));
}
  以上代码只是针对 0<k<1 的情况,下一次我们将说明如何利用Bresenham算法实现所有方向上的直线画法。

  作者博客:http://www.flyaway-blog.com/%E5%9B%BE%E5%BD%A2%E5%AD%A6%E7%AE%97%E6%B3%95-bresenham%E7%94%BB%E7%9B%B4%E7%BA%BF.html

你可能感兴趣的:(算法,图形学)