直线的生成算法

在光栅显示器的荧光屏上生成一个对象,实质上是往帧缓存寄存器的相应单元中填入数据。画一条从(x1, y1)到(x2, y2)的直线,实质上是一个发现最佳逼近直线的象素序列,并填入色彩数据的过程。这个过程也称为直线光栅化。

直线的DDA算法

DDA是数字微分分析式(Digital Differential Analyzer)的缩写。设直线之起点为(x1y1),终点为(x2y2),则斜率m为:

m = (y2-y1)/(x2-x1)=dy/dx


   直线中的每一点坐标都可以由前一点坐标变化一个增量(
Dx, Dy)而得到,即表示为递归式:

xi+1=xi+Dx

yi+1=yi+Dy

并有关系:Dy = m · Dx

递归式的初值为直线的起点(x1, y1),这样,就可以用加法来生成一条直线。具体方法是:

 

2.1.1 直线方向的8个象限

                                             

                                                        

2.1.1

象限

|dx|>|dy|?

D x

D y

1a

1b

2a

2b

3a

3b

4a

4b

Ö

´

Ö

´

Ö

´

Ö

´

1

1/m

-1

-1/m

-1

-1/m

1

1/m

m

1

m

1

-m

-1

-m

-1

按照直线从(x1y1)到(x2y2)的方向不同,分为8个象限(图 2.1.1 )。对于方向在第1a象限内的直线而言,D x=1D y=m。对于方向在第1b象限内的直线而言,取值Dy=1Dx=1/m。各象限中直线生成时Dx, Dy的取值列在表2.1.1之中。

研究表中的数据,可以发现两个规律:

1、当|dx|>|dy|

|D x|=1, |D y|=m

否则:

Dx=1/m|Dy|=1

 

2Dx, Dy的符号与dx, dy的符号相同。

这两条规律可以导致程序的简化。由上述方法写成的程序如程序 2.1.1 所示。其中steps变量的设置,以及D x=dx/steps; D y=dy/steps等语句,正是利用了上述两条规律,使得程序变得简练。

使用DDA算法,每生成一条直线做两次除法,每画线中一点做两次加法。因此,用DDA法生成直线的速度是相当快的。

#include  < stdlib.h >

#include 
< math.h >

inline 
int  round( const   float  a) {return int(a+0.5);}

 

void lineDDA(
int  xa,  int  ya,  int  xb,  int  yb, int  c)

{

float delta_x, delta_y, x, y;

int dx, dy, steps, k;

dx
=xb-xa;

dy
=yb-ya;

if (abs(dx)>abs(dy)) steps=abs(dx);

else steps=abs (dy);

delta_x
=(float)dx / (float)steps;

delta_y
=(float)dy / (float)steps;

x
=xa;

y
=ya;

set_pixel(x, y, c);

for (k=1; k<=steps; k++)

{

x
+=delta_x;

y
+=delta_y;

set_pixel(x, y, c);

}


}


程序2.
1.1  DDA直线生成程序

 

直线的Bresenham算法


   本算法由
Bresenham1965年提出。设直线从起点(x1, y1)到终点(x2, y2)。直线可表示为方程y=mx+b。其中

b = y1 - m * x1, 

m = (y2-y1)/(x2-x1)=dy/dx


   我们的讨论先将直线方向限于
1a 象限(图 2.1.1 )在这种情况下,当直线光栅化时,x每次都增加1个单元,即

xi+1=xi+1。y的相应增加应当小于1。为了光栅化,yi+1只可能选择如下两种位置之一。

yi+1的位置选择yi+1=yi 或者 yi+1=yi+1。选择的原则是看精确值yyiyi+1的距离d1d2的大小而定。计算式为:

y=m(xi+1)+b ( 2.1.1 )

d1=y-yi ( 2.1.2 )

d2=yi+1-y ( 2.1.3 )


如果
d1-d2>0,则yi+1=yi+1,否则yi+1=yi。因此算法的关键在于简便地求出d1-d2的符号。将式( 2.1.1 )、(2.1.2)、(2.1.3)代入d1-d2,得

d1-d2=2y-2yi-1=2dy/dx (xi+1)-2yi+2b-1


dx乘等式两边,并以Pi=dx(d1-d2)代入上述等式,得

Pi=2xidy-2yidx+2dy+dx(2b-1) ( 2.1.4 )


d1-d2
是我们用以判断符号的误差。由于在 1a 象限,dx总大于0,所以Pi仍旧可以用作判断符号的误差。Pi-1为:

Pi+1=Pi+2dy-2dx(yi+1-yi) ( 2.1.5 )

误差的初值P1,可将x1, y1,和b代入式( 2.1.4 )中的xi, yi而得到:

P1=2dy-dx


综述上面的推导,第
1a象限内的直线Bresenham算法思想如下:

 

1、画点(x1, y2); dx=x2-x1; dy=y2-y1

计算误差初值P1=2dy-dx; i=1

 

2、求直线的下一点位置:

xi+1=xi+1

if Pi>0 yi+1=yi+1

否则yi+1=yi

 

3、画点(xi+1, yi-1);

 

4、求下一个误差Pi+1

if Pi>0 Pi+1=Pi+2dy-2dx;

否则Pi+1=Pi+2dy

 

5i=i+1; if i<dx+1则转2;否则end

 

Bresenham算法的优点是:

1、不必计算直线之斜率,因此不做除法;

 

2、不用浮点数,只用整数;

 

3、只做整数加减法和乘2运算,而乘2运算可以用硬件移位实现。

 

Bresenham算法速度很快,并适于用硬件实现。

 

    由上述算法思想编制的程序如程序 2.1.2 。这个程序适用于所有8个方向的直线(图2.1.1)的生成。程序用色彩C画出一条端点为(x1, y1)和(x2, y2)的直线。其中变量的含义是:P是误差;const1const2,是误差的逐点变化量;incy的单位递变量,值为1-1tmp是用作象限变换时的临时变量。程序以判断|dx|>|dy|为分支,并分别将 2a , 3a 象限的直线和3b, 4b象限的直线变换到 1a , 4a 2b, 1b方向去,以求得程序处理的简洁。

void  line ( int  x1, y1, x2, y2, c)
{
int dx;
int dy;
int x;
int y;
int p;
int const1;
int const2;
int inc;
int tmp;
dx
=x2-x1;
dy
=y2-y1;
if (dx*dy>=0/*准备x或y的单位递变值。*/
          inc
=1;
else
inc
=-1;
if (abs(dx)>abs(dy)){
      
if(dx<0){
               tmp
=x1; /*将2a, 3a象限方向*/
               x1
=x2; /*的直线变换到1a, 4a*/
               x2
=tmp;
               tmp
=y1; /*象限方向去*/
               y1
=y2;
               dx
=-dy;
               dy
=-dy;
           }

      p
=2*dy-dx;
      const1
=2*dy; /*注意此时误差的*/
      const2
=2*(dy-dy); /*变化参数取值. */
      x
=x1;
      y
=y1;
      set_pixel(x, y, c);
      
while (x<x2){
          x
++;
          
if (p<0)
                       p
+=const1;
             
else{
                       y
+=inc;
                       p
+=const2;
                    }

             set_piexl(x, y, c);
         }

}

else {
       
if (dy<0){
                    tmp
=x1; /* 将3b, 4b象限方向的*/
                    x1
=x2; /*直线变换到2b, 1b */
                    x2
=tmp; /*象限方向去. */
                    tmp
=y1;
y1
=y2;
dx
=-dy;
dy
=-dy;
}

p
=2*dx-dy; /*注意此时误差的*/
const1
=2*dx; /*变化参数取值. */
const2
=2*(dx-dy);
x
=x1;
y
=y1;
set_pixel (x, y, c);
      
while (y<y2){
             y
++;
             
if(p<0)
                    p
+=const1;
             
else{
x
+=inc;
p
+=const2;
set_pixel (x, y, c);
}

              }

}

 
程序2.
1.2 适用于直线所有八个方向的Bresenham直线生成算法

你可能感兴趣的:(算法)