VC++多边形的填充.docx
《VC++多边形的填充.docx》由会员分享,可在线阅读,更多相关《VC++多边形的填充.docx(14页珍藏版)》请在冰豆网上搜索。
VC++多边形的填充
实验二有效边表填充算法
1.实验目的:
设计有效边表结点和边表结点数据结构
设计有效边表填充算法
编程实现有效边表填充算法
2.实验描述:
下图1所示多边形覆盖了12条扫描线,共有7个顶点和7条边。
7个顶点分别为:
P0(7,8),P1(3,12),P2(1,7),P3(3,1),P4(6,5),P5(8,1),P6(12,9)。
在1024×768的显示分辩率下,将多边形顶点放大为P0(500,400),P1(350,600),P2(250,350),P3(350,50),P4(500,250),P5(600,50),P6(800,450)。
请使用有效边表算法填充该多边形。
图1示例多边形
图2屏幕显示多边形
3.算法设计:
1.根据多边形顶点坐标值,计算扫描线的最大值ScanMax和最小值ScanMin。
2.用多边形覆盖的扫描线动态建立桶结点。
3.循环多边形的所有顶点,根据边的终点y值比起点y值高或边的终点y值比起点y值低两种情况(边的终点y值和起点y值相等的情况属于扫描线,不予考虑),计算每条边的yMin。
在桶中寻找与该yMin相应的桶结点,计算该边表的x|yMin、yMax、k(代表斜率倒数1/k),并依次连接该边表结点到桶结点。
4.对每个桶结点连接的边表,根据x|yMin值的大小进行排序,若x|yMin相等,则按照k由小到大排序。
5.对每个桶结点进行循环,将桶内每个结点的边表合并为有效边表,并进行有效边表循环。
6.从有效边表中取出相邻两条边的交点对进行填充。
填充时设置一个逻辑变量In(初始值为假),每访问一个结点,把In值取反一次,若In为真,则把从当前结点的x值开始到下一结点的x-1值结束的区间用指定颜色填充。
(左闭右开)
7.循环下一桶结点,按照xi+1=xi+k(k的值为1/k)修改有效边表,同时合并桶结点内的新边表,形成新的有效边表。
8.如果桶结点的扫描线值大于等于有效边表中某个结点的yMax值,则放弃该有效边表。
9.当桶结点不为空则转⑸,否则删除桶结点和边结点的头结点,算法结束。
4.源程序:
1)//AET.h和AET..cpp
classAET
{
}
2)//Bucket.h和Bucket.cpp
classBucket
{
}
3)//TestView.h
#include"AET.h"//包含有效边表类
#include"Bucket.h"//包含桶类
#defineNumber7//N为闭合多边形顶点数,顶点存放在整型二维数组Point[N]中
classCTestView:
publicCView
{
。
。
。
。
。
。
。
。
。
public:
voidPolygonFill();//上闭下开填充多边形
voidCreatBucket();//建立桶结点桶
voidEt();//构造边表
voidAddEdge(AET*);//将边插入AET表
voidEdgeOrder();//对AET表进行排序
。
。
。
。
。
。
。
。
。
。
protected:
COLORREFGetColor;//调色板
CPointPoint[7];//定义多边形
Bucket*HeadB,*CurrentB;//桶的头结点和当前结点
AETE[Number],*HeadE,*CurrentE,*T1,*T2;//有效边表的结点
4)//TestView.cpp
CTestView:
:
CTestView()
{
//设置多边形的7个顶点
Point[0]=CPoint(550,400);//P0
Point[1]=CPoint(350,600);//P1
Point[2]=CPoint(250,350);//P2
Point[3]=CPoint(350,50);//P3
Point[4]=CPoint(500,250);//P4
Point[5]=CPoint(600,50);//P5
Point[6]=CPoint(800,450);//P6
}
voidCTestView:
:
OnDraw(CDC*pDC)
{
CTestDoc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
pDC->Polygon(Point,7);//绘制多边形
//输出多边形的顶点编号
pDC->TextOut(550,410,"P0");
pDC->TextOut(350,600,"P1");
pDC->TextOut(230,340,"P2");
pDC->TextOut(350,30,"P3");
pDC->TextOut(490,220,"P4");
pDC->TextOut(600,30,"P5");
pDC->TextOut(805,450,"P6");
}
voidCTestView:
:
OnMenuAET()//菜单函数
{
AfxGetMainWnd()->SetWindowText("多边形有效边表填充算法");//显示标题
CColorDialogccd(GetColor);
if(ccd.DoModal()==IDOK)//调用调色板选取前景色
{
GetColor=ccd.GetColor();
}
RedrawWindow();//刷新屏幕
CreatBucket();//初始化桶
Et();//建立边表
PolygonFill();//多边形填充
}
voidCTestView:
:
CreatBucket()//初始化桶
{
intScanMin,ScanMax;//确定扫描线的最小值和最大值
ScanMax=ScanMin=Point[0].y;
for(inti=1;i{
if(Point[i].y{
ScanMin=Point[i].y;//扫描线的最小值
}
if(Point[i].y>ScanMax)
{
ScanMax=Point[i].y;//扫描线的最大值
}
}
for(i=ScanMin;i<=ScanMax;i++)//建立桶结点
{
if(ScanMin==i)//桶头结点,以所有点的最小的Ymin值作为桶表的头结点
{
HeadB=newBucket;//建立桶的头结点
CurrentB=HeadB;//CurrentB为Bucket当前结点指针
CurrentB->ScanLine=ScanMin;
CurrentB->p=NULL;//没有连接边链表
CurrentB->next=NULL;
}
else//建立桶的其它结点
{
CurrentB->next=newBucket;//新建一个桶结点
CurrentB=CurrentB->next;//使CurrentB指向新建的桶结点
CurrentB->ScanLine=i;
CurrentB->p=NULL;//没有连接边链表
CurrentB->next=NULL;
}
}
//桶表的顺序是从小到大
}
//桶表是每一个扫描线对应一个结点,每一个桶表结点连接一个边链表
voidCTestView:
:
Et()//构造边表
{
for(inti=0;i{
CurrentB=HeadB;//从桶链表的头结点开始循环
intj=i+1;//边的第二个顶点,Point[i]和Point[j]构成边
if(j==Number)j=0;//保证多边形的闭合
if(Point[j].y>Point[i].y)//终点比起点高
{
while(CurrentB->ScanLine!
=Point[i].y)//在桶内寻找该边的yMin
{
CurrentB=CurrentB->next;//移到下一个桶结点
}
E[i].x=Point[i].x;//计算AET表的值
E[i].yMax=Point[j].y;
E[i].k=double((Point[j].x-Point[i].x))/(Point[j].y-Point[i].y);//代表1/k
E[i].next=NULL;
CurrentE=CurrentB->p;//获得桶上链接边表的地址
if(CurrentB->p==NULL)//当前桶结点上没有链接边结点
{
CurrentE=&E[i];//赋边的起始地址
CurrentB->p=CurrentE;//第一个边结点直接连接到对应的桶中
}
else
{
//什么情况下会将不为空?
?
当有两个点的纵坐标是一样的时候会产生这种情况,所以之后要对桶表对应的边表中的每个结点进行按X的排序
while(CurrentE->next!
=NULL)//如果当前边已连有边结点
{
CurrentE=CurrentE->next;//移动指针到当前边的最后一个边结点
}
CurrentE->next=&E[i];//把当前边接上去
}
}
if(Point[j].y{
while(CurrentB->ScanLine!
=Point[j].y)
{
CurrentB=CurrentB->next;
}
E[i].x=Point[j].x;
E[i].yMax=Point[i].y;
E[i].k=double((Point[i].x-Point[j].x))/(Point[i].y-Point[j].y);
E[i].next=NULL;
CurrentE=CurrentB->p;
if(CurrentE==NULL)
{
CurrentE=&E[i];
CurrentB->p=CurrentE;
}
else
{
while(CurrentE->next!
=NULL)
{
CurrentE=CurrentE->next;
}
CurrentE->next=&E[i];
}
}
}
CurrentB=NULL;
CurrentE=NULL;
}
voidCTestView:
:
AddEdge(AET*NewEdge)//插入临时边表函数
{
T1=HeadE;
if(T1==NULL)//边表为空,将边表置为TempEdge
{
T1=NewEdge;
HeadE=T1;
}
else
{
while(T1->next!
=NULL)//边表不为空,将TempEdge连在该边之后
{
T1=T1->next;
}
T1->next=NewEdge;
}
}
voidCTestView:
:
EdgeOrder()//对边表进行排序函数
{