直线的生成算法

在光栅显示器的荧光屏上生成一个对象,实质上是往帧缓存寄存器的相应单元中填入数据。画一条从(x1, y1)到(x2, y2)的直线,实质上是一个发现最佳逼近直线的象素序列,并填入色彩数据的过程。这个过程也称为直线光栅化。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

直线的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),这样,就可以用加法来生成一条直线。具体方法是:

 

图<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" /><chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">2.1.1</span></chsdate> 直线方向的8个象限

200742801.JPG

表<chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">2.1.1</span></chsdate>

象限

|dx|>|dy|?

D x

D y

<chmetcnv w:st="on" unitname="a" sourcevalue="1" hasspace="False" negative="False" numbertype="1" tcsc="0"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">1a</span></chmetcnv>

1b

<chmetcnv w:st="on" unitname="a" sourcevalue="2" hasspace="False" negative="False" numbertype="1" tcsc="0"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">2a</span></chmetcnv>

2b

<chmetcnv w:st="on" unitname="a" sourcevalue="3" hasspace="False" negative="False" numbertype="1" tcsc="0"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">3a</span></chmetcnv>

3b

<chmetcnv w:st="on" unitname="a" sourcevalue="4" hasspace="False" negative="False" numbertype="1" tcsc="0"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">4a</span></chmetcnv>

4b

Ö

´

Ö

´

Ö

´

Ö

´

1

1/m

-1

-1/m

-1

-1/m

1

1/m

m

1

m

1

-m

-1

-m

-1

按照直线从(x1y1)到(x2y2)的方向不同,分为8个象限(图<chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">2.1.1</span></chsdate>)。对于方向在第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的符号相同。

这两条规律可以导致程序的简化。由上述方法写成的程序如程序<chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">2.1.1</span></chsdate>所示。其中steps变量的设置,以及D x=dx/steps; D y=dy/steps等语句,正是利用了上述两条规律,使得程序变得简练。

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

None.gif #include < stdlib.h >
None.gif
None.gif#include
< math.h >

ExpandedBlockStart.gifContractedBlock.gifinline
int round( const float a) dot.gif {returnint(a+0.5);}
None.gif
None.gif
None.gif
None.gifvoidlineDDA(
int xa, int ya, int xb, int yb, int c)
None.gif
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
InBlock.gif
floatdelta_x,delta_y,x,y;
InBlock.gif
InBlock.gif
intdx,dy,steps,k;
InBlock.gif
InBlock.gifdx
=xb-xa;
InBlock.gif
InBlock.gifdy
=yb-ya;
InBlock.gif
InBlock.gif
if(abs(dx)>abs(dy))steps=abs(dx);
InBlock.gif
InBlock.gif
elsesteps=abs(dy);
InBlock.gif
InBlock.gifdelta_x
=(float)dx/(float)steps;
InBlock.gif
InBlock.gifdelta_y
=(float)dy/(float)steps;
InBlock.gif
InBlock.gifx
=xa;
InBlock.gif
InBlock.gify
=ya;
InBlock.gif
InBlock.gifset_pixel(x,y,c);
InBlock.gif
InBlock.gif
for(k=1;k<=steps;k++)
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif
dot.gif{
InBlock.gif
InBlock.gifx
+=delta_x;
InBlock.gif
InBlock.gify
+=delta_y;
InBlock.gif
InBlock.gifset_pixel(x,y,c);
InBlock.gif
ExpandedSubBlockEnd.gif}

InBlock.gif
ExpandedBlockEnd.gif}

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

直线的Bresenham算法


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

b = y1 - m * x1,

m = (y2-y1)/(x2-x1)=dy/dx
<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /><shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype>


我们的讨论先将直线方向限于<chmetcnv w:st="on" unitname="a" sourcevalue="1" hasspace="False" negative="False" numbertype="1" tcsc="0"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">1a</span></chmetcnv>象限(图<chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">2.1.1</span></chsdate>)在这种情况下,当直线光栅化时,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 (<chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on">2.1.1</chsdate>)

d1=y-yi (<chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on">2.1.2</chsdate>)

d2=yi+1-y (<chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on">2.1.3</chsdate>)


如果d1-d2>0,则yi+1=yi+1,否则yi+1=yi。因此算法的关键在于简便地求出d1-d2的符号。将式(<chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">2.1.1</span></chsdate>)、(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) (<chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on">2.1.4</chsdate>)


d1-d2
是我们用以判断符号的误差。由于在<chmetcnv w:st="on" unitname="a" sourcevalue="1" hasspace="False" negative="False" numbertype="1" tcsc="0"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">1a</span></chmetcnv>象限,dx总大于0,所以Pi仍旧可以用作判断符号的误差。Pi-1为:

Pi+1=Pi+2dy-2dx(yi+1-yi) (<chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on">2.1.5</chsdate>)

误差的初值P1,可将x1, y1,和b代入式(<chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">2.1.4</span></chsdate>)中的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算法速度很快,并适于用硬件实现。

 

由上述算法思想编制的程序如程序<chsdate year="1899" month="12" day="30" islunardate="False" isrocdate="False" w:st="on"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">2.1.2</span></chsdate>。这个程序适用于所有8个方向的直线(图2.1.1)的生成。程序用色彩C画出一条端点为(x1, y1)和(x2, y2)的直线。其中变量的含义是:P是误差;const1const2,是误差的逐点变化量;incy的单位递变量,值为1-1tmp是用作象限变换时的临时变量。程序以判断|dx|>|dy|为分支,并分别将<chmetcnv w:st="on" unitname="a" sourcevalue="2" hasspace="False" negative="False" numbertype="1" tcsc="0"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">2a</span></chmetcnv>, <chmetcnv w:st="on" unitname="a" sourcevalue="3" hasspace="False" negative="False" numbertype="1" tcsc="0">3a</chmetcnv>象限的直线和3b, 4b象限的直线变换到<chmetcnv w:st="on" unitname="a" sourcevalue="1" hasspace="False" negative="False" numbertype="1" tcsc="0"><span lang="EN-US" style="FONT-SIZE: 10pt; FONT-FAMILY: Verdana">1a</span></chmetcnv>, <chmetcnv w:st="on" unitname="a" sourcevalue="4" hasspace="False" negative="False" numbertype="1" tcsc="0">4a</chmetcnv>2b, 1b方向去,以求得程序处理的简洁。

None.gif void line( int x1,y1,x2,y2,c)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif
intdx;
InBlock.gif
intdy;
InBlock.gif
intx;
InBlock.gif
inty;
InBlock.gif
intp;
InBlock.gif
intconst1;
InBlock.gif
intconst2;
InBlock.gif
intinc;
InBlock.gif
inttmp;
InBlock.gifdx
=x2-x1;
InBlock.gifdy
=y2-y1;
ExpandedSubBlockStart.gifContractedSubBlock.gif
if(dx*dy>=0)/**//*准备x或y的单位递变值。*/
InBlock.gifinc
=1;
InBlock.gif
else
InBlock.gifinc
=-1;
ExpandedSubBlockStart.gifContractedSubBlock.gif
if(abs(dx)>abs(dy))dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif
if(dx<0)dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.giftmp
=x1;/**//*将2a,3a象限方向*/
ExpandedSubBlockStart.gifContractedSubBlock.gifx1
=x2;/**//*的直线变换到1a,4a*/
InBlock.gifx2
=tmp;
ExpandedSubBlockStart.gifContractedSubBlock.giftmp
=y1;/**//*象限方向去*/
InBlock.gify1
=y2;
InBlock.gifdx
=-dy;
InBlock.gifdy
=-dy;
ExpandedSubBlockEnd.gif}

InBlock.gifp
=2*dy-dx;
ExpandedSubBlockStart.gifContractedSubBlock.gifconst1
=2*dy;/**//*注意此时误差的*/
ExpandedSubBlockStart.gifContractedSubBlock.gifconst2
=2*(dy-dy);/**//*变化参数取值.*/
InBlock.gifx
=x1;
InBlock.gify
=y1;
InBlock.gifset_pixel(x,y,c);
ExpandedSubBlockStart.gifContractedSubBlock.gif
while(x<x2)dot.gif{
InBlock.gifx
++;
InBlock.gif
if(p<0)
InBlock.gifp
+=const1;
ExpandedSubBlockStart.gifContractedSubBlock.gif
elsedot.gif{
InBlock.gify
+=inc;
InBlock.gifp
+=const2;
ExpandedSubBlockEnd.gif}

InBlock.gifset_piexl(x,y,c);
ExpandedSubBlockEnd.gif}

ExpandedSubBlockEnd.gif}

ExpandedSubBlockStart.gifContractedSubBlock.gif
elsedot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif
if(dy<0)dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.giftmp
=x1;/**//*将3b,4b象限方向的*/
ExpandedSubBlockStart.gifContractedSubBlock.gifx1
=x2;/**//*直线变换到2b,1b*/
ExpandedSubBlockStart.gifContractedSubBlock.gifx2
=tmp;/**//*象限方向去.*/
InBlock.giftmp
=y1;
InBlock.gify1
=y2;
InBlock.gifdx
=-dy;
InBlock.gifdy
=-dy;
ExpandedSubBlockEnd.gif}

ExpandedSubBlockStart.gifContractedSubBlock.gifp
=2*dx-dy;/**//*注意此时误差的*/
ExpandedSubBlockStart.gifContractedSubBlock.gifconst1
=2*dx;/**//*变化参数取值.*/
InBlock.gifconst2
=2*(dx-dy);
InBlock.gifx
=x1;
InBlock.gify
=y1;
InBlock.gifset_pixel(x,y,c);
ExpandedSubBlockStart.gifContractedSubBlock.gif
while(y<y2)dot.gif{
InBlock.gify
++;
InBlock.gif
if(p<0)
InBlock.gifp
+=const1;
ExpandedSubBlockStart.gifContractedSubBlock.gif
elsedot.gif{
InBlock.gifx
+=inc;
InBlock.gifp
+=const2;
InBlock.gifset_pixel(x,y,c);
ExpandedSubBlockEnd.gif}

ExpandedSubBlockEnd.gif}

ExpandedSubBlockEnd.gif}

InBlock.gif 
InBlock.gif程序2.
1.2适用于直线所有八个方向的Bresenham直线生成算法
InBlock.gif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值