计算机图形学实验报告2.docx
- 文档编号:7827835
- 上传时间:2023-01-26
- 格式:DOCX
- 页数:23
- 大小:94.42KB
计算机图形学实验报告2.docx
《计算机图形学实验报告2.docx》由会员分享,可在线阅读,更多相关《计算机图形学实验报告2.docx(23页珍藏版)》请在冰豆网上搜索。
计算机图形学实验报告2
《计算机图形学》实验报告
实验名称:
区域填充算法和裁剪算法
实验四区域填充算法
一.实验目的及要求
1.掌握区域填充的基本算法原理;
2.会使用字符的图形输出。
3.实践与巩固区域填充的基本生成算法。
4.掌握这些算法的原理及实现;
二.理论基础
1.了解光栅图形显示器的特点;
2.熟悉C环境下图形程序的绘图方法;
3.对多边形的扫描线、边标志及扫描线种子填充算法有一定的了解;
4.对计算机绘图的原理有一定的认识。
三.算法设计与分析
1)扫描线算法充分利用了扫描线连贯性原理,避免了针对于象素点的逐点判别,有效地选择象素点来进行多边形的填充。
扫描线算法的基本思想:
对给定的多边形,用一组水平(垂直)的扫描线进行扫描,对每一条扫描线均可求出与多边形边的交点;这些交点将扫描线分割成落在多边形内部和落在多边形外部的线段,二者相间排列;将落在多边形内部的扫描线段上的所有象素点赋以给定的色彩值。
算法求解:
对于一条扫描线的处理,可以分为四个步骤:
(1)求交点:
首先求出扫描线与多边形各边的交点
(2)交点排序:
将这些交点按X坐标递增顺序排序(3)交点匹配:
即从左到右确定落在多边形内部的那些线段;(4)区间填充:
填充落在多边形内部的线段。
两个特殊问题:
一是当扫描线与多边形顶点相交时,交点的取舍问题;二是多边形边界上像素的取舍问题。
问题一的解决:
扫描线交于一个顶点,而共享顶点的二条边分别落在扫描线的两边。
这时交点只算一个;当共享交点的二条边在扫描线的同一边,这时交点作为0个或者2个(取决于该点是多边形的局部最高点还是局部的最低点)。
具体实现时,只需检查顶点两条边的另外两个端点的y值。
按这两个y值中大于顶/交点y值的个数是0、1或2来决定是取零个、一个、还是二个。
问题二的解决:
规定落在右/上边界的像素不予以填充,而落在左/下边界的像素予以填充。
求扫描线与多边形边交点的方法:
最简单的方法是将多边形的所有边放在一个表中,在处理每条扫描线时,从表中顺序取出所有的边,分别求这些边与扫描线的交点。
活性边表每个节点存放对应边的有关信息。
根据边和扫描线的连贯性更新AEL:
假定当前扫描线与多边形某一条边的交点的X坐标为x,则下一条扫描线与该边的交点只要加一个增量△x。
设该边的直线方程为:
ax+by+c=0,当前若y=yi,x=xi;则当y=yi+1时,xi+1=xi-b/a=xi+Δx(其中Δx=-b/a为常数)
使用增量法计算时,我们需要知道一条边何时不再与下一条扫描线相交,以便及时把它从活性边表中删除出去:
边的最大Y值(最高扫描线号)≤扫描线号建立AEL,每个结点结构至少如下:
x:
当前扫描线与边交点的x坐标;Δx:
从当前扫描线到下一条扫描线之间的x增量;ymax:
边的最大Y值(最高扫描线号)。
具体求解步骤:
处理扫描线步骤为:
(1)对于扫描线Y=yc,是否有新边加入AEL,若对应的AEL中非空,并对AEL中各边按X递增排序;
(2)若相对于当前扫描线的活化边表AEL非空,则将AEL中的边两两依次配对,即第1,2边为一对,第3,4边为一对,依次类推,每一对边与当前扫描线的交点所构成的区段位于多边形内,依次将这些区段上的点进行着色;(3)将边的活化链表AEL中满足Y=ymax的边删去;(4)将边的活化链表AEL中剩下的每一条边的X域累加ΔX,即X=X+ΔX;(5)将当前的扫描线的纵坐标Y累加1,即Y=Y+1,重复执行
(1)。
2)边填充算法:
基本思想是:
针对每一条扫描线和每条多边形边的交点(x,y),将该扫描线上交点右方的所有象素取补。
引入栅栏,可以减少边填充算法访问象素的次数栅栏指的是一条与扫描线垂直的直线,通常把栅栏取成过多边形顶点、且把多边形分为左右两半的直线。
思想:
把每个扫描线与多边形边的交点与栅栏之间的象素取补。
若交点位于栅栏左边,则将交点之右,栅栏之左的所有象素取补;若交点位于栅栏右边,则将栅栏之右,交点之左的象素取补。
3)种子填充算法
区域可分为四连通区域和八连通区域两种:
四连通区域,区域内每一个象素可以通过四个方向(上、下、左、右)组合到达;八连通区域,区域内的每个象素可通过上、下、左、右以及四个对角线方向的移动组合到达。
算法基本思想
简单的种子填充算法如下:
(1)将种子象素压入栈中;
(2)当栈非空时,重复执行以下三步操作:
a)从栈中弹出一个象素;b)将该象素置成所要求的色彩值;c)检查每个与当前象素邻接的四连接象素是否是边界色或已置成所要求的色彩值,否则将该象素入栈。
4)扫描线种子填充算法
所谓扫描线种子填充算法,是在任意不间断扫描线区间中只取一个种子象素。
不间断区间即指在一条扫描线上一组相邻的元素。
种子象素入栈;当栈非空时做以下几步:
1)栈顶象素出栈;(算法结束条件为栈空)2)沿着扫描线对出栈象素的左、右象素进行填充,直到遇到边界象素为止,即对包含该象素的整个区间进行填充;3)区间内最左、最右元素为XleftXright;则在Xleft≤X≤Xright中检查与当前扫描线相邻的上、下两条扫描线是否全为边界象素或已填充过的象素。
若不是,则把每一区间的最右象素作为种子压入栈中。
四、程序调试及结果的分析
本程序运行环境为VC下的MFC环境。
本实验将列出种子填充算法的源代码和扫描线种子填充的源代码。
实验代码:
1.种子填充算法源代码
intCZhongzhitianchongView:
:
stackpop()//出栈
{
intval;
val=stack[stack_top];
stack_top=stack_top-1;
returnval;
}
voidCZhongzhitianchongView:
:
stackpush(intp_xy)//入栈
{
stack_top+=1;
stack[stack_top]=p_xy;
}
intCZhongzhitianchongView:
:
isstackempty()//判断栈是否为空
{
if(stack_top>0)
return1;
else
return0;
}
voidCZhongzhitianchongView:
:
setstackempty()//清空栈
{
inti;
for(i=0;i<=stack_top;i++)
stack[i]=0;
stack_top=0;
}
voidCZhongzhitianchongView:
:
zhongzhitianchong(CDC*pDC,intx,inty,COLORREFoldcolor,COLORREFnewcolor)
{
setstackempty();
stackpush(x);
stackpush(y);
while(isstackempty()!
=0)
{
y=stackpop();
x=stackpop();
//pDC->SetPixel(x,y,newcolor);
if((pDC->GetPixel(x-1,y)!
=oldcolor)&&(pDC->GetPixel(x-1,y)!
=newcolor))
{
pDC->SetPixel(x-1,y,newcolor);
stackpush(x-1);
stackpush(y);
}
if((pDC->GetPixel(x,y-1)!
=oldcolor)&&(pDC->GetPixel(x,y-1)!
=newcolor))
{
pDC->SetPixel(x,y-1,newcolor);
stackpush(x);
stackpush(y-1);
}
if((pDC->GetPixel(x+1,y)!
=oldcolor)&&(pDC->GetPixel(x+1,y)!
=newcolor))
{
pDC->SetPixel(x+1,y,newcolor);
stackpush(x+1);
stackpush(y);
}
if((pDC->GetPixel(x,y+1)!
=oldcolor)&&(pDC->GetPixel(x,y+1)!
=newcolor))
{
pDC->SetPixel(x,y+1,newcolor);
stackpush(x);
stackpush(y+1);
}
}
}
voidCZhongzhitianchongView:
:
OnDraw(CDC*pDC)
{
CZhongzhitianchongDoc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
//TODO:
adddrawcodefornativedatahere
CPenPenRed(PS_SOLID,3,RGB(255,0,0));//定义红色笔
pDC->SelectObject(&PenRed);
POINTpolygon1[5]={{300,200},{350,150},{400,250},{360,300},{320,380}};
POINTpolygon2[10]={{96,43},{84,75},{46,75},{77,101},{66,137},{95,115},{127,135},{116,101},{142,75},{108,75}};
pDC->Polygon(polygon1,5);
pDC->Polygon(polygon2,10);
COLORREFnewcolor=RGB(0,225,0);
COLORREFoldcolor=RGB(255,0,0);
zhongzhitianchong(pDC,350,300,oldcolor,newcolor);
zhongzhitianchong(pDC,100,100,oldcolor,newcolor);
}
2.扫描线种子填充算法源代码
intCTianchongsuanfaView:
:
stackpop()//出栈
{
intval;
val=stack[stack_top];
stack_top=stack_top-1;
returnval;
}
voidCTianchongsuanfaView:
:
stackpush(intp_xy)//入栈
{
stack_top+=1;
stack[stack_top]=p_xy;
}
intCTianchongsuanfaView:
:
isstackempty()//判断栈是否为空
{
if(stack_top>0)
return1;
else
return0;
}
voidCTianchongsuanfaView:
:
setstackempty()//清空栈
{
inti;
for(i=0;i<=stack_top;i++)
stack[i]=0;
stack_top=0;
}
voidCTianchongsuanfaView:
:
zhongzhifloodfill(intx,inty,COLORREFoldcolor,COLORREFnewcolor)//填充函数
{
CDC*pDC=GetDC();
intxl,xr,x0,y0,xid,xnextspan;
boolspanNeedFill;
//将栈清空
setstackempty();
//种子入栈
stackpush(x);
stackpush(y);
while(isstackempty()!
=0)
{
//栈顶出栈,注意出栈顺序
y=stackpop();
x=stackpop();
pDC->SetPixel(x,y,newcolor);
x0=x+1;
while(pDC->GetPixel(x0,y)!
=oldcolor)//向右填充
{
pDC->SetPixel(x0,y,newcolor);
x0++;
}
xr=x0-1;//最右元素
x0=x-1;
while(pDC->GetPixel(x0,y)!
=oldcolor)//向左填充
{
pDC->SetPixel(x0,y,newcolor);
x0--;
}
xl=x0+1;//最左元素
//处理上面一条扫描线
y0=y;
for(inti=1;i>=-1;i-=2)
{
x0=xr;
y=y0+i;
while(x0>=xl)
{
spanNeedFill=FALSE;
while((pDC->GetPixel(x0,y)!
=oldcolor)&&(pDC->GetPixel(x0,y)!
=newcolor)&&(x0>xl))
{
if(spanNeedFill==FALSE)
{
spanNeedFill=TRUE;
xid=x0;
}
x0--;
}
if(spanNeedFill==1)
{
stackpush(xid);
stackpush(y);
spanNeedFill=FALSE;
}
xnextspan=x0;
while((pDC->GetPixel(x0,y)==oldcolor)||(pDC->GetPixel(x0,y)==newcolor)&&(x0>=xl))
x0--;
if(xnextspan==x0)
x0--;
}
}
}//Endofwhile(!
isstackempty())
}
voidCTianchongsuanfaView:
:
OnDraw(CDC*pDC)
{
CTianchongsuanfaDoc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
//TODO:
adddrawcodefornativedatahere
CPenPenRed(PS_SOLID,3,RGB(255,0,0));//定义红色笔
//用红色笔画出多变形的边界
pDC->SelectObject(&PenRed);
POINTpolygon1[5]={{200,20},{290,60},{330,220},{80,200},{50,140}};
pDC->Polygon(polygon1,5);
BresenhamCircle1(pDC,600,200,80,RGB(255,0,0));
COLORREFnewcolor=RGB(0,225,0);
COLORREFoldcolor=RGB(255,0,0);
zhongzhifloodfill(103,103,oldcolor,newcolor);
zhongzhifloodfill(600,200,oldcolor,newcolor);
}
运行结果展示:
1.种子填充算法
2.扫描线种子填充算法
实验五裁剪算法算法
一.实验目的及要求
熟悉裁剪算法的基本原理。
二.理论基础
1.巩固C环境下的绘图方法;
2.对直线的Cohen-Sutherland、中点分割和参数化裁剪算法有所了解
对MFC下的绘图有了一定的基础
三.算法设计与分析
1.Cohen-Sutherland算法
由DanCohen和IvanSutherLand提出的,该算法的思想是对于每条线段P1P2分为三种情况处理:
(1)若两端点P1P2完全在窗口内,则完全可见该线段P1P2。
否则进入下一步;
(2)若线段P1P2明显在窗口外,即显然不可见,则丢弃该线段,否则进入下一步; (3)若线段既不满足
(1)的条件,也不满足
(2)的条件,则在交点处把线段分为两段。
其中一段完全在窗口外,可弃之。
然后对另一段重复上述处理。
为使计算机能够快速判断一条直线段与窗口属何种关系,采用四位数码来标识线段的端点与窗口区域的关系,然后进行裁剪的算法-Cohen-SutherLand算法。
编码的含义:
编码规则,从左起:
第一位为1表示线段端点位于窗口上侧,
0表示线段端点不位于窗口上侧;
第二位为1表示线段端点位于窗口下侧,
0表示线段端点不位于窗口下侧;
第三位为1表示线段端点位于窗口右侧,
0表示线段端点不位于窗口右侧;
第四位为1表示线段端点位于窗口左侧,
0表示线段端点不位于窗口左侧。
基本思想:
•若线段完全在窗口内部,则显示该线段,为“取”之;
•若线段明显在窗口外,丢弃该线段,为“弃”之;
•若线段即不满足“取”的条件,也不满足“弃”的条件,则把线段分为两段,其中一段完全在窗口外,则“弃”之,然后对另一段线段重复上面的处理。
根据Cohen-SutherLand算法思想,线段:
a:
(0000,0000)完全在窗口内部;取
b:
(0000,0000)完全在窗口内部;取
c:
(0000,0100)部分完全在窗口内部;处理
d:
(0100,0010)部分完全在窗口内部;处理
e:
(0000,0010)部分完全在窗口内部;处理
f:
(0001,0101)完全在窗口外部;弃
g:
(1000,1010)完全在窗口外部;弃
裁剪一条线段时
先求出P1P2所在的区号code1,code2。
⏹若code1=0,且code2=0,则线段P1P2在窗口内,应取之。
⏹若按位与运算code1&code2≠0,则说明两个端点同在窗口的上方、下方、左方或右方。
可判断线段完全在窗口外,可弃之。
⏹否则,按第三种情况处理。
求出线段与窗口某边的交点,在交点处把线段一分为二,其中必有一段在窗口外,可弃之。
在对另一段重复上述处理。
边界求交
在实现本算法时,不必把线段与每条窗口边界依次求交,只要按顺序检测到端点区位的某位编码不为0,才把线段与对应的窗口边界进行求交。
判断直线和哪条边由交点,具体方法为:
若编码&0001<>0,端点与左边界有交点;
若编码&0010<>0,端点与右边界有交点;
若编码&0100<>0,端点与下边界有交点;
若编码&1000<>0,端点与上边界有交点;
2.中点分割法
与Cohen-Sutherland算法类似
⏹首先对线段端点进行编码
⏹并把线段与窗口的关系分为三种情况:
–全在、完全不在和线段与窗口有交
–对前两种情况,进行一样的处理
⏹用中点分割的方法求出线段与窗口的交点
设要裁减的线段是P0P1。
中点分割算法可分成两个平行的过程进行:
•即从P0点出发找出离P0最远的可见点(图中的B点)
•从P1点出发找出离P1最远的可见点(图中的A点)
•这两个可见点的连线AB就是原线段的可见部分。
这两点求法类似,这里以P0点为例介绍。
•若P1可见,则P1就是离P0最远的可见点。
否则(P1不可见)
(1)对两端点区号作按位与运算,若不为零,则P0P1全部不可见,弃之。
否则
(2)在中点Pm处把线段P0P1分为两段。
从P0出发找最远可见点的方法:
若Pm可见,把原问题转化为对PmP1求离P0最远的可见点。
若Pm不可见:
若P0Pm完全在窗口外,在P1Pm中找离P0
最远的可见点;若P1Pm完全在窗口外,在P0Pm中找离P0最远的可见点。
重复上述过程,直到线段长度小于给定的精度ε为止。
这时分点即为所求点。
从P0出发找最近可见点的方法是:
•先求P0P1的中点Pm,若P0Pm不能定为显然不可见,则取P0Pm代替P0P1,否则取PmP1代替P0P1
•再对新的P0P1求中点Pm。
•重复上述过程,直到P1Pm长度小于给定的精度ε为止。
此时P0即为所求A点!
3.参数法(Cyrus-Beck算法)
假定A是区域R边界上的一点,N是区域边界在A点内法向量。
线段P1P2用参数方程表示:
P(t)=(P2-P1)t+P1
(0≤t≤1)
对于线段上任意一点P(t),有三种可能性:
N·[P(t)-A]<0,这时P(t)必在多边形外侧;
N·[P(t)-A]=0,这时P(t)必在多边形边界或其延长线上;
N·[P(t)-A]>0,这时P(t)必在多边形内侧
由凸多边形的性质知,P(t)在多边形内的充要条件是,对于凸多边形边界上任意一点A和该处内法量N,都有N·(P(t)-A)>0。
即
Ni·[P(t)-Ai]≥0,(i=1,2,...,k) (0≤t≤1)
Ni·{[(P2-P1)t+P1]-Ai}≥0
Ni·(P1-Ai)+Ni·(P2-P1)t≥0
当Ni·(P2-P1)=0时,Ni⊥(P2-P1),即P1P2与对应边平行,与其无交点。
此时
当Ni·(P1-Ai)<0,线段在区域外侧,
(对凸多边形)线段在多边形之外。
当Ni·(P1-Ai)≥0,线段在区域内侧
当Ni·(P2-P1)≠0时,令
Ni·(P1-Ai)/[Ni·(P2-P1)]=ti
则
当Ni·(P1-Ai)>0时,t≥-ti
当Ni·(P1-Ai)<0时,t≤-ti
其中,0≤t≤1
这里ti是线段与第i条边(或延长线)的交点参数
则取
tl=max{0,max{-ti:
Ni·(P2-P1)>0}}tu=min{1,min{-ti:
Ni·(P2-P1)<0}}
最后还要判断其合理性若tl≤tu,则tl,tu是可见线段的端点参数,否则tl>tu,则整条线段在区域外部。
4.梁友栋-Barskey算法
梁友栋和Barskey提出了更快的参数化裁剪算法。
首先按参数化形式写出裁剪条件:
这四个不等式可以表示为形式:
其中,参数pk,qk定义为:
任何平行于裁剪边界之一的直线pk=0,其中k对应于裁剪边界(k=1,2,3,4对应于左、右、下、上边界)如果还满足qk<0,则线段完全在边界外,舍弃该线段。
如果qk≥0,则该线段平行于裁剪边界并且在窗口内。
当pk<0,线段从裁剪边界延长线的外部延伸到内部。
当pk>0,线段从裁剪边界延长线的内部延伸到外部。
当pk≠0,可以计算出线段与边界k的延长线的交点的u值:
u=qk/pk
对于每条直线,可以计算出参数u1和u2,它们定义了在裁剪矩形内的线段部分。
u1的值由线段从外到内遇到的矩形边界所决定(p<0)。
对这些边界计算r
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 计算机 图形学 实验 报告