扫描线填充算法

合集下载

扫描线填充算法讲解

扫描线填充算法讲解

扫描线算法(S c a n-L i n e F i l l i n g)扫描线算法适合对矢量图形进行区域填充,只需要直到多边形区域的几何位置,不需要指定种子点,适合计算机自动进行图形处理的场合使用,比如电脑游戏和三维CAD软件的渲染等等。

对矢量多边形区域填充,算法核心还是求交。

《计算几何与图形学有关的几种常用算法》一文给出了判断点与多边形关系的算法――扫描交点的奇偶数判断算法,利用此算法可以判断一个点是否在多边形内,也就是是否需要填充,但是实际工程中使用的填充算法都是只使用求交的思想,并不直接使用这种求交算法。

究其原因,除了算法效率问题之外,还存在一个光栅图形设备和矢量之间的转换问题。

比如某个点位于非常靠近边界的临界位置,用矢量算法判断这个点应该是在多边形内,但是光栅化后,这个点在光栅图形设备上看就有可能是在多边形外边(矢量点没有大小概念,光栅图形设备的点有大小概念),因此,适用于矢量图形的填充算法必须适应光栅图形设备。

2.1扫描线算法的基本思想扫描线填充算法的基本思想是:用水平扫描线从上到下(或从下到上)扫描由多条首尾相连的线段构成的多边形,每根扫描线与多边形的某些边产生一系列交点。

将这些交点按照x坐标排序,将排序后的点两两成对,作为线段的两个端点,以所填的颜色画水平直线。

多边形被扫描完毕后,颜色填充也就完成了。

扫描线填充算法也可以归纳为以下4个步骤:(1)求交,计算扫描线与多边形的交点(2)交点排序,对第2步得到的交点按照x值从小到大进行排序;(3)颜色填充,对排序后的交点两两组成一个水平线段,以画线段的方式进行颜色填充;(4)是否完成多边形扫描?如果是就结束算法,如果不是就改变扫描线,然后转第1步继续处理;整个算法的关键是第1步,需要用尽量少的计算量求出交点,还要考虑交点是线段端点的特殊情况,最后,交点的步进计算最好是整数,便于光栅设备输出显示。

对于每一条扫描线,如果每次都按照正常的线段求交算法进行计算,则计算量大,而且效率底下,如图(6)所示:图(6)多边形与扫描线示意图观察多边形与扫描线的交点情况,可以得到以下两个特点:(1)每次只有相关的几条边可能与扫描线有交点,不必对所有的边进行求交计算;(2)相邻的扫描线与同一直线段的交点存在步进关系,这个关系与直线段所在直线的斜率有关;第一个特点是显而易见的,为了减少计算量,扫描线算法需要维护一张由“活动边”组成的表,称为“活动边表(AET)”。

案例10 扫描线种子填充算法

案例10  扫描线种子填充算法

程序代码
PointTemp.x=xleft;PointTemp.y=PointTemp.y-2; //处理下一条扫描线 while(PointTemp.x<xright) { bSpanFill=FALSE; while(pDC->GetPixel(Round(PointTemp.x),Round(PointTemp.y))!=BoundaryClr && pDC->GetPixel(Round(PointTemp.x),Round(PointTemp.y))!=SeedClr) { bSpanFill=TRUE; PointTemp.x++; } if(bSpanFill) { if(PointTemp.x==xright && pDC->GetPixel(Round(PointTemp.x), Round(PointTemp.y))!=BoundaryClr && pDC->GetPixel(Round(PointTemp.x),Round(PointTemp.y))!=SeedClr) PopPoint=PointTemp; else PopPoint.x=PointTemp.x-1;PopPoint.y=PointTemp.y; Push(PopPoint); bSpanFill=FALSE; } while((pDC->GetPixel(Round(PointTemp.x),Round(PointTemp.y))==BoundaryClr && PointTemp.x<xright) || (pDC->GetPixel(Round(PointTemp.x),Round(PointTemp.y)) ==SeedClr && PointTemp.x<xright)) PointTemp.x++; } }

实验2:多边形区域扫描线填充或种子填充

实验2:多边形区域扫描线填充或种子填充

实验2:多边形区域扫描线填充或种子填充计科102 蓝广森 1007300441一、实验目的通过实验,进一步理解和掌握几种常用多边形填充算法的基本原理掌握多边形区域填充算法的基本过程掌握在C/C++环境下用多边形填充算法编程实现指定多边形的填充。

二、实验内容及要求实现多边形区域扫描线填充的有序边表算法,并将实现的算法应用于任意多边形的填充,要求多边形的顶点由键盘输入或鼠标拾取,填充要准确,不能多填也不能少填。

要求掌握边形区域扫描线填充的有序边表算法的基本原理和算法设计,画出算法实现的程序流程图,使用C或者VC++实现算法,并演示。

三、实验原理种子填充算法又称为边界填充算法。

其基本思想是:从多边形区域的一个内点开始,由内向外用给定的颜色画点直到边界为止。

如果边界是以一种颜色指定的,则种子填充算法可逐个像素地处理直到遇到边界颜色为止。

种子填充算法常用四连通域和八连通域技术进行填充操作。

四向连通填充算法:a)种子像素压入栈中;b)如果栈为空,则转e);否则转c);c)弹出一个像素,并将该像素置成填充色;并判断该像素相邻的四连通像素是否为边界色或已经置成多边形的填充色,若不是,则将该像素压入栈;d)转b);e)结束。

扫描线填充算法的基本过程如下:当给定种子点(x,y)时,首先填充种子点所在扫描线上的位于给定区域的一个区段,然后确定与这一区段相连通的上、下两条扫描线上位于给定区域内的区段,并依次保存下来。

反复这个过程,直到填充结束。

区域填充的扫描线算法可由下列四个步骤实现:(1)初始化:堆栈置空。

将种子点(x,y)入栈。

(2)出栈:若栈空则结束。

否则取栈顶元素(x,y),以y作为当前扫描线。

(3)填充并确定种子点所在区段:从种子点(x,y)出发,沿当前扫描线向左、右两个方向填充,直到边界。

分别标记区段的左、右端点坐标为xl和xr。

(4)并确定新的种子点:在区间[xl,xr]中检查与当前扫描线y上、下相邻的两条扫描线上的象素。

扫描线填充算法讲解

扫描线填充算法讲解

扫描线算法(Scan-Line F illing)扫描线算法适合对矢量图形进行区域填充,只需要直到多边形区域的几何位置,不需要指定种子点,适合计算机自动进行图形处理的场合使用,比如电脑游戏和三维CAD软件的渲染等等。

对矢量多边形区域填充,算法核心还是求交。

《计算几何与图形学有关的几种常用算法》一文给出了判断点与多边形关系的算法――扫描交点的奇偶数判断算法,利用此算法可以判断一个点是否在多边形内,也就是是否需要填充,但是实际工程中使用的填充算法都是只使用求交的思想,并不直接使用这种求交算法。

究其原因,除了算法效率问题之外,还存在一个光栅图形设备和矢量之间的转换问题。

比如某个点位于非常靠近边界的临界位置,用矢量算法判断这个点应该是在多边形内,但是光栅化后,这个点在光栅图形设备上看就有可能是在多边形外边(矢量点没有大小概念,光栅图形设备的点有大小概念),因此,适用于矢量图形的填充算法必须适应光栅图形设备。

2.1扫描线算法的基本思想扫描线填充算法的基本思想是:用水平扫描线从上到下(或从下到上)扫描由多条首尾相连的线段构成的多边形,每根扫描线与多边形的某些边产生一系列交点。

将这些交点按照x坐标排序,将排序后的点两两成对,作为线段的两个端点,以所填的颜色画水平直线。

多边形被扫描完毕后,颜色填充也就完成了。

扫描线填充算法也可以归纳为以下4个步骤:(1)求交,计算扫描线与多边形的交点(2)交点排序,对第2步得到的交点按照x值从小到大进行排序;(3)颜色填充,对排序后的交点两两组成一个水平线段,以画线段的方式进行颜色填充;(4)是否完成多边形扫描?如果是就结束算法,如果不是就改变扫描线,然后转第1步继续处理;整个算法的关键是第1步,需要用尽量少的计算量求出交点,还要考虑交点是线段端点的特殊情况,最后,交点的步进计算最好是整数,便于光栅设备输出显示。

对于每一条扫描线,如果每次都按照正常的线段求交算法进行计算,则计算量大,而且效率底下,如图(6)所示:图(6)多边形与扫描线示意图观察多边形与扫描线的交点情况,可以得到以下两个特点:(1)每次只有相关的几条边可能与扫描线有交点,不必对所有的边进行求交计算;(2)相邻的扫描线与同一直线段的交点存在步进关系,这个关系与直线段所在直线的斜率有关;第一个特点是显而易见的,为了减少计算量,扫描线算法需要维护一张由“活动边”组成的表,称为“活动边表(AET)”。

通用扫描线填充算法存在的问题及其解决方法

通用扫描线填充算法存在的问题及其解决方法
文献标识码 : A 中图分类号 :T 0 . P3 16
通 用扫描 线 填充 算法 亦 称 扫 描线 多 边 形 填 充 算 法 、 扫 描 或
线算法 , 是多边形 扫描转换填充算法中重要 的一种。在 国内外 多种计算机图形学文献 ( 尤其是教材) 对此算法 的介绍尽管 中, 有所差异, 但基本原理都是一致的 ¨ “ 。但是, 这个原理却存
I C X顶 点 。 型 Q
图6 多 形区 在 一 扫 线 边 域 同 条 描 上
判断 I C X顶点 : A L 中某一边与 当前扫描线的交点满足如下相依的三个条件 , 型 Q 若 E表 此点即为 条件 n 交点所在边的上端点的 Ya : m值为交点所在扫描线的 Y值, x 即交点是其所在边的上端点 。 条件 b在条件 n基础上 , : 交点( 亦是端点 ) 的另一边为水平边 . 且水平边在交点的右侧 。 条件 c在条件 n 条件 6 : 、 基础上 , 交点是个凹拐点 , 即交点的左 、 上部 区域均为多边形内区域。判定
该 点 为 凹拐点 的方法 是 : A L表 中交 点所 在边 , 出前 一 条扫 捕 线 ( 其 Y值 为 Y ) 该边 的交 点 沿 E 求 设 与
( , , , 中: , ./ I为边的斜率 , 为边与 当前扫描线 交点 、 , )其 一 = ,m( 儿一 n ' 即被判断点的 值 ) 。进 而判断点( 一 一 , ) 1Y 是否已被填充 , 若是则被判断交点就是凹拐点( 因为连贯性原则) 也就是 I , 型 C X顶点 , Q 否则就不是 。 参考判断 I C X顶点的算法可制定出判断 Ⅱ型 C X顶点的算法。 型 Q Q 判断 Ⅲ型 、 Ⅳ型 C X顶点 的算法也可参考判 断 I C X顶点 的方法 , Q 型 Q 所不同处在于 Ⅲ型 、 型 Ⅳ C X顶点是其所在边 ( Q 非水平的那条边) 的下端 点. 且在判断是否凹拐点时只需 看点 ( ,¨ ) Y 是否被

cad 区域填充的算法

cad 区域填充的算法

cad 区域填充的算法
CAD区域填充是一种用于计算机辅助设计软件中的算法,用于在指定区域内填充颜色。

这个算法基于扫描线填充方法,它从图形的顶部开始
扫描,逐行地将颜色填充到区域中。

具体而言,CAD区域填充算法可以通过以下步骤来实现:
1. 选择一个起始点,并确保该点位于待填充区域内。

2. 在当前扫描线上,从起始点开始向左和向右移动,找到左和右边界。

这可以通过检测不同颜色或图形边界来确定。

3. 填充当前扫描线的像素点,从左边界到右边界之间的像素。

4. 移动到下一行,通过向下移动一行并在新的扫描线上重复步骤2和
步骤3,直到所有行都被处理完毕或者遇到边界。

5. 当遇到边界或结束时,停止填充过程。

需要注意的是,在实际应用中,为了提高效率和减少计算量,可以使
用一些优化策略来加速CAD区域填充算法的执行,例如边界框剪裁和
扫描线段合并。

总之,CAD区域填充算法是一种实现图形填充的重要方法,它可以在计算机辅助设计软件中帮助用户快速填充指定区域并实现更好的可视化
效果。

计算机图形学扫描线种子填充算法

计算机图形学扫描线种子填充算法

2.1 深度递归的种子填充算法
2.2 扫描线种子填充算法
2.1 深度递归的种子填充算法
种子填色又称边界填色(Boundary Filling)。 它的功能是,给出多边形光栅化后的边界位置及边 界色代码oundary_color,以及多边形内的一点(x, y)位置,要求将颜色fill_color填满多边形。
动画演示
扫描线种子填充算法特点
1. 该算法考虑了扫描线上象素的相关性,种子象 素不再代表一个孤立的象素,而是代表一个尚 未填充的区段。 2. 进栈时,只将每个区段选一个象素进栈(每个 区段最右边或最左边的象素),这样解决了堆 栈溢出的问题。 3. 种子出栈时,则填充整个区段。 4. 这样有机的结合:一边对尚未填充象素的登记 (象素进栈),一边进行填充(象素出栈), 既可以节省堆栈空间,又可以实施快速填充。

3. 已知有一个5边形如下。建立新边表 NET,并写出每一条扫描线经过时活性边 表AET中的数据状态。
X ΔX Ymax
第1项存当前扫描线与边的交点坐标x值; 第2项存从当前扫描线到下一条扫描线间x的增量Dx; 第3项存该边所交的最高扫描线号ymax; 第4项存指向下一条边的指针。
假定当前扫描线与多边形某一条边的交点的x 坐标为x,则下一条扫描线与该边的交点不要重计 算,只要加一个增量△x。(连贯性) 设该边的直线方程为:ax+by+c=0; 若y=yi,x=x i;则当y = y i+1时, x i+1=xi-b/a 其中ΔX= -b/a为常数, 另外使用增量法计算时,我们需要知道一条边 何时不再与下一条扫描线相交,以便及时把它从 活性边表中删除出去。
建立或调整AET(ActiveEdgeList);

扫描线种子填充算法

扫描线种子填充算法

扫描线种子填充算法扫描线种子填充算法的基本过程如下:当给定种子点(x, y)时,首先分别向左和向右两个方向填充种子点所在扫描线上的位于给定区域的一个区段,同时记下这个区段的范围[xLeft, xRight],然后确定与这一区段相连通的上、下两条扫描线上位于给定区域内的区段,并依次保存下来。

反复这个过程,直到填充结束。

扫描线种子填充算法可由下列四个步骤实现:(1) 初始化一个空的栈用于存放种子点,将种子点(x, y)入栈;(2) 判断栈是否为空,如果栈为空则结束算法,否则取出栈顶元素作为当前扫描线的种子点(x, y),y是当前的扫描线;(3) 从种子点(x, y)出发,沿当前扫描线向左、右两个方向填充,直到边界。

分别标记区段的左、右端点坐标为xLeft和xRight;(4) 分别检查与当前扫描线相邻的y - 1和y + 1两条扫描线在区间[xLeft, xRight]中的像素,从xLeft开始向xRight方向搜索,若存在非边界且未填充的像素点,则找出这些相邻的像素点中最右边的一个,并将其作为种子点压入栈中,然后返回第(2)步;这个算法中最关键的是第(4)步,就是从当前扫描线的上一条扫描线和下一条扫描线中寻找新的种子点。

如果新扫描线上实际点的区间比当前扫描线的[xLeft, xRight]区间大,而且是连续的情况下,算法的第(3)步就处理了这种情况。

如图所示:新扫描线区间增大且连续的情况假设当前处理的扫描线是黄色点所在的第7行,则经过第3步处理后可以得到一个区间[6,10]。

然后第4步操作,从相邻的第6行和第8行两条扫描线的第6列开始向右搜索,确定红色的两个点分别是第6行和第8行的种子点,于是按照顺序将(6, 10)和(8, 10)两个种子点入栈。

接下来的循环会处理(8, 10)这个种子点,根据算法第3步说明,会从(8, 10)开始向左和向右填充,由于中间没有边界点,因此填充会直到遇到边界为止,所以尽管第8行实际区域比第7行的区间[6,10]大,但是仍然得到了正确的填充。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

任意封闭多边形的扫描线填充算法类收藏这个代码不是我写的,但是我肯定这代码是一个牛人写的,放在这里供大家学习和使用啦!感谢原作者!我在这里做了些改进: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作图非常方便,故日益流行,但对许多人来说,是在微机上进行的,首先碰到的问题是,如何适应微机环境。

相关文档
最新文档