什么是多边形?
多边形的表示方法有哪些?
多边形的扫描转换就是从顶点表示法转换到点阵表示法的过程。
基础的填充多边形方式:
光栅究竟是什么?
光栅化究竟是什么?
有效边表填充算法:
算法实现:
将VC 6.0 调整到ClassView视图
创建有效边表和桶表类
将VC 6.0调整到FileView视图,对两个类进行定义
AET.h
class CAET
{
public:
CAET();
virtual ~CAET();
public:
double x;//当前扫描线与有效边交点的x坐标
int yMax;//边的最大y值
double k;//斜率的倒数(x的增量)
CAET *pNext;
};
Bucket.h
#include "AET.h"
#include "Bucket.h"
class CBucket
{
public:
CBucket();
virtual ~CBucket();
public:
int ScanLine; //扫描线
CAET *pET; //桶上的边表指针
CBucket *pNext;
};
回到ClassView视图,像之前那样创建CFill类,再回到FileView视图,对CFill类进行定义
Fill.h
#include "AET.h"
#include "Bucket.h"
class CFill
{
public:
CFill();
virtual ~CFill();
void SetPoint(CPoint *p,int);//初始化顶点和顶点数目
void CreateBucket();//创建桶
void CreateEdge();//边表
void AddET(CAET *);//合并ET表
void ETOrder();//ET表排序
void Gouraud(CDC *);//填充多边形
void ClearMemory();//清理内存
void DeleteAETChain(CAET* pAET);//删除边表
protected:
int PNum;//顶点个数
CPoint *P;//顶点坐标动态数组
CAET *pHeadE,*pCurrentE,*pEdge; //有效边表结点指针
CBucket *pHeadB,*pCurrentB; //桶表结点指针
};
Fill.cpp
// Fill.cpp: implementation of the CFill class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Study3.h"
#include "Fill.h"
#include "AET.h"
#include "Bucket.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CFill::CFill()
{
PNum=0;
P=NULL;
pEdge=NULL;
pHeadB=NULL;
pHeadE=NULL;
pCurrentB = NULL;
pCurrentE = NULL;
}
CFill::~CFill()
{
if(P!=NULL)
{
delete[] P;
P=NULL;
}
ClearMemory();
}
void CFill::SetPoint(CPoint *p,int m)////初始化顶点和顶点数目
{
P=new CPoint[m];//创建一维动态数组,转储CTestView类中P数组中的顶点
for(int i=0;iyMax)
{
yMax = P[i].y;
}
}
for(int y = yMin;y<=yMax;y++)
{
if(yMin == y)
{
pHeadB = new CBucket;
pCurrentB = pHeadB;
pCurrentB->ScanLine = yMin;
pCurrentB->pET = NULL;
pCurrentB->pNext = NULL;
}
else
{
pCurrentB->pNext = new CBucket;
pCurrentB=pCurrentB->pNext;
pCurrentB->ScanLine = y;
pCurrentB->pET=NULL;
pCurrentB->pNext=NULL;
}
}
}
void CFill::CreateEdge()//创建边表,即将各边链入到相应的桶节点
{
for(int i = 0;ix=P[i].x;
pEdge->yMax=P[j].y;
pEdge->k = (double)(P[j].x-P[i].x)/((double)(P[j].y-P[i].y));
pEdge->pNext = NULL;
while(pCurrentB->ScanLine!=P[i].y)
{
pCurrentB=pCurrentB->pNext;
}
}
if(P[j].yx=P[j].x;
pEdge->yMax=P[i].y;
pEdge->k = (double)(P[i].x-P[j].x)/((double)(P[i].y-P[j].y));
pEdge->pNext = NULL;
while(pCurrentB->ScanLine!=P[j].y)
{
pCurrentB=pCurrentB->pNext;
}
}
if(P[j].y!=P[i].y)
{
pCurrentE=pCurrentB->pET;
if(pCurrentE==NULL)
{
pCurrentE=pEdge;
pCurrentB->pET=pCurrentE;
}
else
{
while(NULL!=pCurrentE->pNext)
{
pCurrentE=pCurrentE->pNext;
}
pCurrentE->pNext=pEdge;
}
}
}
}
void CFill::AddET(CAET *pNewEdge)//合并ET表
{
CAET *pCE=pHeadE;//边表头结点
if(pCE==NULL)//若边表为空,则pNewEdge作为边表头结点
{
pHeadE=pNewEdge;
pCE=pHeadE;
}
else//将pNewEdge链接到边表末尾(未排序)
{
while(pCE->pNext!=NULL)
{
pCE=pCE->pNext;
}
pCE->pNext=pNewEdge;
}
}
void CFill::ETOrder()//边表的冒泡排序算法
{
CAET *pT1,*pT2;
int Count=1;
pT1=pHeadE;
if(pT1==NULL)//没有边,不需要排序
{
return;
}
if(pT1->pNext==NULL)//如果该ET表没有再连ET表
{
return;//只有一条边,不需要排序
}
while(pT1->pNext!=NULL)//统计边结点的个数
{
Count++;
pT1=pT1->pNext;
}
for(int i=0;ipNext;
if ((pT1->x>pT2->x)||((pT1->x==pT2->x)&&(pT1->k>pT2->k)))//满足条件,则交换当前两个边结点的位置
{
pT1->pNext=pT2->pNext;
pT2->pNext=pT1;
*pPre=pT2;
pPre=&(pT2->pNext);//调整位置为下次遍历准备
}
else//不交换当前两个边结点的位置,更新pPre和pT1
{
pPre=&(pT1->pNext);
pT1=pT1->pNext;
}
}
}
}
void CFill::Gouraud(CDC *pDC)//填充多边形
{
CAET *pT1=NULL,*pT2=NULL;
pHeadE=NULL;
for(pCurrentB=pHeadB;pCurrentB!=NULL;pCurrentB=pCurrentB->pNext)
{
for(pCurrentE=pCurrentB->pET;pCurrentE!=NULL;pCurrentE=pCurrentE->pNext)
{
pEdge=new CAET;
pEdge->x=pCurrentE->x;
pEdge->yMax=pCurrentE->yMax;
pEdge->k=pCurrentE->k;
pEdge->pNext=NULL;
AddET(pEdge);
}
ETOrder();
pT1=pHeadE;
if(pT1==NULL)
{
return ;
}
while(pCurrentB->ScanLine>=pT1->yMax)
{
CAET *pAETTEmp = pT1;
pT1=pT1->pNext;
delete pAETTEmp;
pHeadE=pT1;
if(pHeadE==NULL)
{
return;
}
}
if(pT1->pNext!=NULL)
{
pT2=pT1;
pT1=pT2->pNext;
}
while(pT1!=NULL)
{
if(pCurrentB->ScanLine>=pT1->yMax)
{
CAET *pAETTemp=pT1;
pT2->pNext=pT1->pNext;
pT1=pT2->pNext;
delete pAETTemp;
}
else
{
pT2=pT1;
pT1=pT2->pNext;
}
}
BOOL In = FALSE;
int xb,xe;
for(pT1=pHeadE;pT1!=NULL;pT1=pT1->pNext)
{
if(FALSE==In)
{
xb = (int)pT1->x;
In=TRUE;
}
else
{
xe = (int)pT1->x;
for(int x = xb;xSetPixel(x,pCurrentB->ScanLine,RGB(0,0,255));
}
In = FALSE;
}
}
for(pT1=pHeadE;pT1!=NULL;pT1=pT1->pNext)
{
pT1->x=pT1->x+pT1->k;
}
}
}
void CFill::ClearMemory()//安全删除所有桶与桶上连接的边
{
DeleteAETChain(pHeadE);//删除边表
CBucket *pBucket=pHeadB;
while (pBucket!=NULL)//针对每一个桶
{
CBucket *pBucketTemp=pBucket->pNext;
DeleteAETChain(pBucket->pET);//删除桶上面的边
delete pBucket;
pBucket=pBucketTemp;
}
pHeadB=NULL;
pHeadE=NULL;
}
void CFill::DeleteAETChain(CAET *pAET)//删除边表
{
while (pAET!=NULL)
{
CAET *pAETTemp=pAET->pNext;
delete pAET;
pAET=pAETTemp;
}
}
一切准备就绪,进入CXXXView.cpp(“XXX”为你项目名称)
给OnDraw函数添加以下语句
CRect rect;
GetClientRect(&rect);
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetWindowExt(rect.Width(),rect.Height());
pDC->SetViewportExt(rect.Width(),-rect.Height());
pDC->SetViewportOrg(rect.Width()/2,rect.Height()/2);
rect.OffsetRect(-rect.Width()/2,-rect.Height()/2);
//声明Fill类
CFill *cFill = new CFill;
//声明多边形的七个顶点
CPoint points[7] = {CPoint(50,70),CPoint(-150,270),CPoint(-250,20),CPoint(-150,-280),CPoint(0,-80),CPoint(100,-280),CPoint(300,120)};
//设置顶点
cFill->SetPoint(points,7);
//创建桶表
cFill->CreateBucket();
//创建边表
cFill->CreateEdge();
//填充多边形
cFill->Gouraud(pDC);
运行结果
大家对于填充算法要多看看,很重要