组员
学号
姓名
实验名称
直线的扫描转换
实验室
9#204
实
验
目
的
或
要
求
1. 实验内容
用基本增量算法和 Bresenham 算法画直线
2.实验目的
1)理解在显示器上画图与在纸上画图的本质区别;
2)掌握直线的光栅扫描转换过程;
3)掌握不同算法绘制直线的思路和优缺点。
3. 实验要求
1)将像素网格表现出来,建立网格坐标系;
2)用橡皮筋的形式输入参数;
3)鼠标移动时,显示鼠标当前位置;
4)显示判别式的计算过程和下一点的选择策略;
5)记录生成点的坐标,建议用表的形式;
6)图形生成过程可以重复进行。
一、 生成直线的 DDA 算法
数值微分法即 DDA 法(Digital Differential Analyzer),是一种基于直线的微分方程来生成直线的方法。
设(x1,y1)和(x2,y2)分别为所求直线的起点和终点坐标,由直线的微分方程得
= m =直线的斜率
(2-1)
可通过计算由 x 方向的增量△x 引起 y 的改变来生成直线:
(2-2)
(2-3)
(2-4)
(2-5)
也可通过计算由 y 方向的增量△y 引起 x 的改变来生成直线:
xi+1=xi+△x
yi+1=yi+△y=yi+△x·m
yi+1=yi+△y
xi+1=xi+△x=xi+△y/m
式(2-2)至(2-5)是递推的。
选定 x2-x1 和 y2-y1 中较大者作为步进方向(假设 x2-x1 较大),取该方向上的增量为一个象素单位
(Δx=1),然后利用式(2-1)计算另一个方向的增量(Δy=Δx·m=m)。通过递推公式(2-2)至(2-5),把每次计
算出的(xi+1,yi+1)经取整后送到显示器输出,则得到扫描转换后的直线。
之所以取 x2-x1 和 y2-y1 中较大者作为步进方向,是考虑沿着线段分布的象素应均匀,这在下图中
可看出。
另外,算法实现中还应注意直线的生成方向,以决定Δx 及Δy 是取正值还是负值。
实
验
原
理
(
算
法
基
本
思
想
)
实
验
原
理
(
算
法
基
本
思
想
)
二、 生成直线的 Bresenham 算法
它也是采用递推步进的办法,令每次最大变化方向的坐标步进一个象素,同时另一个方向的坐标依据
误差判别式的符号来决定是否也要步进一个象素。
首先讨论 m=△y/△x,当 0≤m≤1 且 x1
ε(xi+2)= yi+1+m-yir-0.5=ε(xi+1)+m
(2-11')
初始时:
ε(xs+1)=BC-AC=m-0.5
(2-12')
为了运算中不含实型数,同时不影响不等式的判断,将方程两边同乘一正整数。
令方程两边同乘 2·Δx,即 d=2·Δx·ε,则:
初始时:
递推式:
d = 2·Δy-Δx
(2-13')
当 d≥0 时:{ d=d+2·(Δy-Δx);
否则:
y++;
x++;
}
{ d=d+2·Δy;
x++;
}
(2-14')
实
验
原
理
(
算
法
基
本
思
想
)
一、 直线扫描转换 DDA 算法和 Bresenham 算法公用函数部分
程
序
代
码
nod FixP(int x,int y) //坐标修正函数
{
nod temp;
int weight =x / 20;
int height = y / 20;
int m1 = x % 20;
int m2 = y% 20;
if (m1 >= 10) weight += 1;
if (m2 >= 10) height += 1;
temp.x=weight;
temp.y=height;
return temp;
}
void DrawRe()//网格绘制函数
{
int height= Form1->Image1->Height;
int width = Form1->Image1->Width;
int temp=20;
Form1->Image1->Canvas->Pen->Color =clBlack;
Form1->Image1->Canvas->Pen->Width=1;
Form1->Image1->Canvas->Brush->Color = clWhite;
Form1->Image1->Canvas->Pen->Style = psSolid;
Form1->Image1->Canvas->Rectangle(0,0,width,height);
for(int i=1;i<20;i++)
{
Form1->Image1->Canvas->MoveTo(0,temp); //划横线
Form1->Image1->Canvas->LineTo(width,temp);
Form1->Image1->Canvas->MoveTo(temp,0);
Form1->Image1->Canvas->LineTo(temp,height);
temp+=20;
//划竖线
}
}
void LinePublic(int X,int Y)//绘制校正起始点直线
{
nod temp;
temp =FixP(x1,y1);
x1=temp.x*20;
y1=temp.y*20;
source= temp;
(接下页)
temp=FixP(X,Y);
int XX =temp.x*20;
int YY=temp.y*20;
dest =temp;
Form1->Image1->Canvas->Pen->Width=2;
Form1->Image1->Canvas->Pen->Color=clBlue;
//输出直线坐标
Form1->StatusBar1->Panels->Items[2]->Text= " 直 线 起 点 : "+ IntToStr(source.x)+
":"+IntToStr(source.y);
Form1->StatusBar1->Panels->Items[3]->Text= " 直 线 终 点 : "+ IntToStr(dest.x)+
":"+IntToStr(dest.y);
Form1->Image1->Canvas->MoveTo(x1,y1);
Form1->Image1->Canvas->LineTo(XX,YY);
Form1->Memo1->Clear();
Form1->Memo2->Clear();
}
void LinePublic2(int X,int Y)//鼠标移动橡皮筋绘制直线
{
if(md==1)
{
//重新绘制网格
DrawRe();
ClearB();
Line( X,Y);
}
}
void ClearB()//第二次绘制图形时清空状态栏数据
{
程
序
代
码
Form1->Memo1->Clear();
Form1->Memo2->Clear();
Form1->StatusBar1->Panels->Items[2]->Text= "
Form1->StatusBar1->Panels->Items[3]->Text= "
Form1->StatusBar1->Panels->Items[5]->Text= "
";
";
"; //把上次状态栏的东西清空
}
void Line(int X,int Y)//绘制校正直线
{
Form1->Image1->Canvas->Pen->Width=2;
Form1->Image1->Canvas->Pen->Color=clOlive;
Form1->Image1->Canvas->MoveTo(x1,y1);
Form1->Image1->Canvas->LineTo(X,Y);
}
二、 直线扫描转换 DDA 算法实现
void LineDDA()
{ //直线的变量初始化
k= abs(dest.x-source.x);
if(abs(dest.y-source.y)>k)
{
");
k= abs(dest.y-source.y);
Form1->Memo2->Lines->Add("
Form1->Memo2->Lines->Add("| 斜率 | > 1 , 沿着 Y 方向步进 ");
Form1->Memo2->Lines->Add("************************************");
Form1->Memo2->Lines->Add("
Form1->Memo2->Lines->Add("***********************************");
Form1->Memo2->Lines->Add("
Y=Y+1");
X=X+1/k
递推公式:
");
;
}
else
{
}
");
Form1->Memo2->Lines->Add("
Form1->Memo2->Lines->Add("| 斜率 | <= 1 , 沿着 X 方向步进 ");
Form1->Memo2->Lines->Add("**********************************");
Form1->Memo2->Lines->Add("
Form1->Memo2->Lines->Add("***********************************");
Form1->Memo2->Lines->Add("
Y=Y+k");
递推公式:
X=X+1
");
;
dx =(dest.x-source.x)/float(k);// 斜 率 <1 dx=dx+1,dy=dy+KK; 斜 率 >1 dx=dx+1/KK,dy=dy+1;
//DDA 核心算法
dy =(dest.y-source.y)/float(k);
xi= source.x;
yi= source.y;
}
逐点扫描:
if(m <= k)
{
程
序
代
码
DrawPie((int)(xi+0.5),(int)(yi+0.5),4);//四舍五入
tx=AnsiString((int)(xi+0.5))+"
:
Form1->Memo1->Lines->Add(tx);
xi+= dx;
Form1->StatusBar1->Panels->Items[5]->Text= "
Form1->StatusBar1->Panels->Items[5]->Text=
yi+= dy;
"+AnsiString((int)(yi+0.5));
";
" 下 一 个 点 : "+
IntToStr((int)(xi+0.5))+ ":"+IntToStr((int)(yi+0.5));
m++ ;
}
else
ShowMessage("完毕");
三、 Bresenham 算法实现
void BresenhamLine( )
{
if((dest.x!=source.x) && (dest.y!=source.y))
{
");
Form1->Memo2->Lines->Add("
Form1->Memo2->Lines->Add("| 斜率 | > 1 , 沿着 Y 方向步进 ");
Form1->Memo2->Lines->Add("**************************");
Form1->Memo2->Lines->Add("
");
Form1->Memo2->Lines->Add("**************************");
Form1->Memo2->Lines->Add(" d>=0:curx =curx -+1;cury = cury -+1");
递推公式:
Form1->Memo2->Lines->Add("d<0:|k|<1,curx=curx-+1;|k|>1,cury=cury-+1");
dx1 = abs(dest.x - source.x);
if (dx1 < dy1)
{
//Y 方向长,|斜率|>1
dy1 = abs(dest.y- source.y);
程
序
代
码
int t =dx1;
dx1 = dy1;
dy1 =t;
iFlag = 1;
}
else
//X 方向长,|斜率|<=1
iFlag = 0;
d0 = (dy1 << 1) - dx1; //判别式初始值
tx1 = (dest.x - source.x) > 0 ? 1 : -1;
ty = (dest.y - source.y) > 0 ? 1 : -1;
curx = source.x; cury = source.y;d1=dy1<<2; d2=(dy1 - dx1)<<2;
DrawPie(source.x, source.y,4);
tx=AnsiString(curx)+"
Form1->Memo1->Lines->Add(tx);
"+AnsiString(cury);
:
}
if((dest.x == source.x) || (dest.y == source.y))
{
Flag1=1;
}
}
逐点扫描:
if(Flag1==1)
{
if(dest.y == source.y) //水平线
{
Form1->Memo2->Clear();
Form1->Memo2->Lines->Add("Bresenham 画水平直线");
}
if(dest.x == source.x) //垂直线
{