项目结构:
首先先声明两个全局变量int num;CPoint* points;
同时初始化。在view的构造函数里面:
CCbianbiaotianchongmonth10day302View::CCbianbiaotianchongmonth10day302View()
{
// TODO: add construction code here
num=0;
points=new CPoint[20];
}
- CAET类
// AET.h: interface for the CAET class.
//
//
#if !defined(AFX_AET_H__05106C7A_B056_438D_8CA1_E7953002D089__INCLUDED_)
#define AFX_AET_H__05106C7A_B056_438D_8CA1_E7953002D089__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CAET
{
public:
CAET();
virtual ~CAET();
public:
double x;//当前扫描线与有效边交点的x坐标
int yMax;//边的最大y值
double k;//斜率的倒数(x的增量)
CAET *pNext;
};
#endif // !defined(AFX_AET_H__05106C7A_B056_438D_8CA1_E7953002D089__INCLUDED_)
2.CBucket类
// Bucket.h: interface for the CBucket class.
//
//
#if !defined(AFX_BUCKET_H__344AAEBF_5708_4B4B_BA14_67CC2969C43D__INCLUDED_)
#define AFX_BUCKET_H__344AAEBF_5708_4B4B_BA14_67CC2969C43D__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "AET.h"
class CBucket
{
public:
CBucket();
virtual ~CBucket();
public:
int ScanLine; //扫描线
CAET *pET; //桶上的边表指针
CBucket *pNext;
};
#endif // !defined(AFX_BUCKET_H__344AAEBF_5708_4B4B_BA14_67CC2969C43D__INCLUDED_)
- CFill类
// Fill.h: interface for the CFill class.
//
//
#if !defined(AFX_FILL_H__55C2E97E_0929_42CD_9555_59EF429F1283__INCLUDED_)
#define AFX_FILL_H__55C2E97E_0929_42CD_9555_59EF429F1283__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "AET.h"
#include "Bucket.h"
class CFill
{
public:
void ClearMemory();//清理内存
void DeleteAETChain(CAET* pAET);//删除边表
CFill();
virtual ~CFill();
void SetPoint(CPoint *p,int m);
void CreateBucket();//创建桶
void CreateEdge();//边表
void AddET(CAET *);//合并ET表
void ETOrder();//ET表排序
void Gouraud(CDC *);//填充多边形
protected:
int PNum;//顶点个数
CPoint *P;//顶点坐标动态数组
CAET *pHeadE,*pCurrentE,*pEdge; //有效边表结点指针
CBucket *pHeadB,*pCurrentB; //桶表结点指针
};
#endif // !defined(AFX_FILL_H__55C2E97E_0929_42CD_9555_59EF429F1283__INCLUDED_)
4.鼠标左键传入点,右键连线。
void CCbianbiaotianchongmonth10day302View::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CDC* pDC=GetDC();
pDC->Ellipse(point.x-2,point.y-2,point.x+2,point.y+2);
points[num]=point;
num++;
CView::OnLButtonDown(nFlags, point);
}
void CCbianbiaotianchongmonth10day302View::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if(num>=3){
CDC * pDC=GetDC();
pDC->Polygon(points,num);
ReleaseDC(pDC);
//num=0;
}
CView::OnRButtonDown(nFlags, point);
}
5.CFill类中所有函数的实现:
// Fill.cpp: implementation of the CFill class.
//
//
#include "stdafx.h"
#include "Cbianbiaotianchongmonth10day302.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];
for(int i=0;i<m;i++)
{
P[i]=p[i];
}
PNum=m;
}
void CFill::CreateBucket()
{
int yMin,yMax;
yMin = yMax = P[0].y;
for(int i = 0;i<PNum;i++)
{
if(P[i].y<yMin)
{
yMin = P[i].y;
}
if(P[i].y>yMax)
{
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;i<PNum;i++)
{
pCurrentB=pHeadB;
int j = (i+1)%PNum;
if(P[i].y<P[j].y)
{
pEdge = new CAET;
pEdge->x=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].y<P[i].y)
{
pEdge=new CAET;
pEdge->x=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;i<Count-1;i++)//冒泡排序
{
CAET **pPre=&pHeadE;//pPre记录当面两个节点的前面一个节点,第一次为头节点
pT1=pHeadE;
for (int j=0;j<Count-1-i;j++)
{
pT2=pT1->pNext;
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;x<xe;x++)
{
pDC->SetPixel(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;
}
}
6.菜单的类向导:
上图填充的类向导函数如下:
void CCbianbiaotianchongmonth10day302View::OnFillllll()
{
// TODO: Add your command handler code here
CDC* pDC = GetDC();
//声明Fill类
CFill *cFill = new CFill;
cFill->SetPoint(points,num);
cFill->CreateBucket();
cFill->CreateEdge();
cFill->Gouraud(pDC);
}