当前位置:文档之家› 扫描线填充算法

扫描线填充算法

任意封闭多边形的扫描线填充算法类收藏这个代码不是我写的,但是我肯定这代码是一个牛人写的,放在这里供大家学习和使用啦!感谢原作者!我在这里做了些改进:1 去除了绘制多边形的函数,使其成为了一个纯的填充算法模块2 改进了其成员变量,使其更容易让大多数人所使用3 改进了填充,使其“看”(代码上)起来更像用扫描线在填充改进后的扫描线算法类如下://扫描线填充算法类class CPFill{public:CPoint *Point;//指向点坐标的指针int Count;//多边形点的个数public:CPFill(int,int[],int[]);//构造函数bool FillPolygon(CDC*);//填充多边形bool CrossJudge(CPoint,CPoint,CPoint,CPoint,CPoint&);//判断两条线段是否相交int GetAi(int);//获取下一个点的索引号int GetBi(int);//获取前一个点的索引号bool Sort(int*,int);//冒泡排序~CPFill();//析构函数};//构造函数(模块入口,koradji 注,合理的设计这个地方,就可以完全不用改动其他的地方就可以使用这个类)CPFill::CPFill(){ }//获取前一个点的索引号int CPFill::GetBi(int i){return (i==0)? Count-1:i-1;}//获取下一个点的索引号int CPFill::GetAi(int i){return (i==Count-1)?0:i+1;}//在指定的pDC设备中,填充多边形bool CPFill::FillPolygon(CDC* pDC){//获取多边形中所有坐标点的最大值和最小值,作为扫描线循环的范围int minX=Point[0].x , minY=Point[0].y;int maxX=Point[0].x , maxY=Point[0].y;for(int i=1;i<Count;i++){if(minX>Point[i].x) minX=Point[i].x;if(minY>Point[i].y) minY=Point[i].y;if(maxX<Point[i].x) maxX=Point[i].x;if(maxY<Point[i].y) maxY=Point[i].y;}CUIntArray xArray;int y;for(y=minY;y<maxY;y++){//扫描线从minY开始到maxYfor(i=0;i<Count;i++){//对每条边进行循环CPoint PointCross;int Bi=GetBi(i),Ai=GetAi(i);//判断是否跟线段相交if(CrossJudge(Point[Bi],Point[i],CPoint(minX,y),CPoint(maxX,y),PointCross)){//若是存在交点,则进行相应的判断,即判断x的坐标取两次、一次还是不取if(PointCross==Point[i]){if((Point[Bi].y>PointCross.y)&&(Point[Ai].y>PointCross.y)){//边顶点的y值大于交点的y值,x坐标取两次xArray.Add(PointCross.x); xArray.Add(PointCross.x);}else{//边顶点的y值在交点的y值之间,即一个顶点的y值大于交点的y值,而另一个小于,相应的x坐标取一次if((Point[Bi].y-PointCross.y)*(Point[Ai].y-PointCross.y)<0) xArray.Add(PointCross.x);else if(PointCross.y==Point[Ai].y) xArray.Add(PointCross.x);}}else{if(PointCross==Point[Bi]) continue;else xArray.Add(PointCross.x);//当交点不在线段的顶点时,x坐标只取一次}}}int *scanLineX,num=xArray.GetSize();scanLineX=new int[num];for(i=0;i<num;i++) scanLineX[i]=xArray.GetAt(i);//获取扫描线x值,以构成填充区间xArray.RemoveAll();Sort(scanLineX,num);//对scanLine(扫描线x坐标进行排序)for(i=0;i<num;i=i+2){if(i+1>=num) break;pDC->MoveTo(scanLineX[i],y);pDC->LineTo(scanLineX[i+1],y);//填充(Koradji改进)}Sleep(1);//CPU暂停1ms,以体现出多边形是以扫描线的方式,一条一条的填充的delete scanLineX;}return true;}//判断两条线段是否相交bool CPFill::CrossJudge(CPoint L1P1,CPoint L1P2,CPoint L2P1,CPoint L2P2,CPoint& coordinate){//L1P1、L1P2是一条线段的顶点坐标,而L2P1、L2P2是另一条线段的顶点坐标if(L1P1==L1P2) return false;//若L1P1、L1P2相等,则构不成线段,退出if(L2P1==L2P2) return false;//若L2P1、L2P2等,则构不成线段,退出if((L1P1.y-L1P2.y)*(L2P1.x-L2P2.x)==(L2P1.y-L2P2.y)*(L1P1.x-L1P2.x))//对斜率相等的情况下的处理{if((L1P1.y-L1P2.y)*(L2P1.x-L1P1.x)==(L1P1.x-L1P2.x)*(L2P1.y-L1P1.y))//判断两条线段是不是同一条线段{coordinate=L1P2;return true;}else return false;}if(L1P1.x==L1P2.x)//当第一条线段斜率不存在时的{double x,y;x=L1P1.x;y=(L2P1.y-L2P2.y)*1.0/(L2P1.x-L2P2.x)*(L1P1.x-L2P1.x)+L2P1.y;y=(float)((int)(y+0.5));if(((L1P1.y-y)*(y-L1P2.y)>=0)&&((L1P1.x-x)*(x-L1P2.x)>=0))//判断交点是不是在该两条线段上{coordinate.x=L1P1.x;coordinate.y=(int)(y+0.5);return true;}return false;}else{if(L2P1.x==L2P2.x)//当第二条线段斜率不存在时{double x,y;x=L2P1.x;y=(L1P1.y-L1P2.y)*1.0/(L1P1.x-L1P2.x)*(L2P1.x-L1P1.x)+L1P1.y;y=(float)((int)(y+0.5));if(((L1P1.y-y)*(y-L1P2.y)>=0) && ((L1P1.x-x)*(x-L1P2.x)>=0))//判断交点是不是在该两条线段上{coordinate.x=L2P1.x;coordinate.y=(int)(y+0.5);return true;}return false;}else//两条线段斜率都存在时{double k1,k2;k1=(L1P1.y-L1P2.y)*1.0/(L1P1.x-L1P2.x);k2=(L2P1.y-L2P2.y)*1.0/(L2P1.x-L2P2.x);//k1,k2为计算的两线段的斜率double x,y;x=(L2P1.y-L1P1.y-k2*L2P1.x+k1*L1P1.x)/(k1-k2);y=(k1*k2*L2P1.x-k1*k2*L1P1.x+k2*L1P1.y-k1*L2P1.y)/(k2-k1);x=(float)((int)(x+0.5));y=(float)((int)(y+0.5));if(((L1P1.y-y)*(y-L1P2.y)>=0)&&((L1P1.x-x)*(x-L1P2.x)>=0))//判断交点是不是在该两条线段上{coordinate.x=(int)(x+0.5);coordinate.y=(int)(y+0.5);return true;}return false;}}return true;}//冒泡排序bool CPFill::Sort(int* iArray,int iLength){int i,j,iTemp;bool bFlag;for(i=0;i<iLength;i++){bFlag=true;for(j=0;j<iLength-i-1;j++){if(iArray[j] > iArray[j+1]){iTemp=iArray[j];iArray[j]=iArray[j+1];iArray[j+1]=iTemp;bFlag=false;}}if(bFlag) break;}return true;}//析构函数,删除动态生成的Point指针CPFill::~CPFill(){if(Point) delete [] Point;}下面说说怎么为我所用这个类。

在MFC中,若多边形控制定点的变量是:CPoint *P,记录下标的变量为int 。

index 则构造函数则变为CPFill::CPFill(int index,CPoint *P){Point=new CPoint[index-1];Count=index-1;for(int i=0;i<Count;i++){Point[i]=P[i];}如果多边形控制定点的变量是:int px[MAX] int py[MAX],记录下标的变量为int index。

则构造函数是:CPFill::CPFill(int index,int px[],int py[]){Point=new CPoint[index-1];Count=index-1;for(int i=0;i<Count;i++){Point[i].x=px[i];Point[i].y=py[i];}}这也可以算作一个例子吧!但是从这个中可以看出OO编程的优越性,事实上也为我们写程序的同志指名了方向,写模块化的程序,这个OO方法,是一致的,我想如果大家都写模块话的程序的话,不仅自己可以减少代码数量,别人也可以方便的借鉴和使用你的代码,如果存在一个比较不错的源码交流平台的话!在今后的写程序的过程中,我将更加注重模块化程序的编写!今天就开始写!本文来自CSDN博客,转载请:处出明标/mekoradji/archive/2008/022106998.aspx/19/扫描线填充算法画多边形计算机图形学2006-11-03 21:11//////////////////////////////////////////////////////////////////////////////////////////////////// 功能: 填充多边形//// 参数: lpPoints: 指向顶点坐标数组的指针,数组类型为POINT,多边形由它们顺次封闭连接得到// nCount: 顶点的个数// nColor: 填充的颜色默认为黑色// pDC: 设备句柄指针//// 返回: 无返回值//// 说明: 可以是边相交的多边形/////////////////////////////////////////////////////////////////////// /////////////////////////////void FillPolygon(LPPOINT lpPoints,int nCount, CDC *pDC, intnColor/*=0*/){// 检查参数合法性ASSERT_VALID(pDC);ASSERT(lpPoints);ASSERT(nCount>2);ASSERT(nColor>=0);// 边结构数据类型typedef struct Edge{int ymax; // 边的最大y坐标float x; // 与当前扫描线的交点x坐标float dx; // 边所在直线斜率的倒数struct Edge * pNext; // 指向下一条边}Edge, * LPEdge;int i=0,j=0,k=0;int y0=0,y1=0; // 扫描线的最大和最小 y坐标LPEdge pAET=NULL; // 活化边表头指针LPEdge * pET=NULL; // 边表头指针pAET=new Edge; // 初始化表头指针,第一个元素不用pAET->pNext=NULL;方向扫描线边界y获取//y0=y1=lpPoints[0].y;for(i=1;i<nCount;i++){if(lpPoints[i].y<y0)y0=lpPoints[i].y;else if(lpPoints[i].y>y1)y1=lpPoints[i].y;}if(y0>=y1) return;// 初始化边表,第一个元素不用pET=new LPEdge[y1-y0+1];for(i=0;i<=y1-y0;i++){pET[i]= new Edge;pET[i]->pNext=NULL;}for(i=0;i<nCount;i++){j=(i+1)%nCount; // 组成边的下一点if(lpPoints[i].y != lpPoints[j].y)// 如果该边不是水平的则加入边表{LPEdge peg; // 指向该边的指针LPEdge ppeg; // 指向边指针的指针// 构造边peg =new Edge;k=(lpPoints[i].y>lpPoints[j].y)?i:j;peg->ymax=lpPoints[k].y; // 该边最大y坐标k=(k==j)?i:j;peg->x=(float)lpPoints[k].x; // 该边与扫描线焦点x坐标if(lpPoints[i].y != lpPoints[j].y)peg->dx=(float)(lpPoints[i].x-lpPoints[j].x)/(lpPoints[i].y-lpPoints[ j].y);// 该边斜率的倒数peg->pNext=NULL;// 插入边ppeg=pET[lpPoints[k].y-y0];while(ppeg->pNext)ppeg=ppeg->pNext;ppeg->pNext=peg;}// end if}// end for i// 扫描for(i=y0;i<=y1;i++){LPEdge peg0=pET[i-y0]->pNext;LPEdge peg1=pET[i-y0];if(peg0)// 有新边加入{while(peg1->pNext)peg1=peg1->pNext;peg1->pNext=pAET->pNext;pAET->pNext=peg0;}// 按照x递增排序pAETpeg0=pAET;while(peg0->pNext){LPEdge pegmax=peg0;LPEdge peg1=peg0;LPEdge pegi=NULL;while(peg1->pNext){if(peg1->pNext->x>pegmax->pNext->x) pegmax=peg1;peg1=peg1->pNext;}pegi=pegmax->pNext;pegmax->pNext=pegi->pNext;pegi->pNext=pAET->pNext;pAET->pNext=pegi;if(peg0 == pAET)peg0=pegi;}// 遍历活边表,画线peg0=pAET;while(peg0->pNext){if(peg0->pNext->pNext){DrawLine((int)peg0->pNext->x,i,(int)peg0->pNext->pNext->x,i,pD C,nColor);peg0=peg0->pNext->pNext;}elsebreak;}// 把ymax=i的节点从活边表删除并把每个节点的x值递增dxpeg0=pAET;while(peg0->pNext){if(peg0->pNext->ymax < i+2){peg1=peg0->pNext;peg0->pNext=peg0->pNext->pNext; // 删除delete peg1;continue;}peg0->pNext->x+=peg0->pNext->dx; //把每个节点的 x值递增dxpeg0=peg0->pNext;}}// 删除边表for(i=0;i<y1-y0;i++)if(pET[i])delete pET[i];if(pAET)delete pAET;if(pET)delete[] pET;}OpenGL作图非常方便,故日益流行,但对许多人来说,是在微机上进行的,首先碰到的问题是,如何适应微机环境。

相关主题