实验六 扫描线填充算法

合集下载

扫描线填充算法讲解

扫描线填充算法讲解

扫描线算法(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)”。

区域填充的扫描线算法

区域填充的扫描线算法

计算机图形学——区域填充的扫描线算法NORTHWESTUNIVER SITY一、实验目的1.通过实验,进一步理解和掌握几种常用多边形填充算法的基本原理2.掌握多边形区域填充算法的基本过程3.掌握在C/C++环境下用多边形填充算法编程实现指定多边形的填充。

4.利用TC2.0编写区域填充的扫描线算法。

二、实验内容算法基本思想:首先填充种子点所在扫描线上位于区域内的区段,然后确定与该区段相邻的上下两条扫描线上位于区域内的区段,并依次将各区段的起始位置保存, 这些区段分别被用区域边界色显示的像素点所包围。

随后,逐步取出一开始点并重复上述过程,直到所保存各区段都填充完毕为止。

算法描述:扫描线填充算法一般包括四个步骤:求交、排序、交点配对、区域填充。

正确求得扫描线与区域填内外轮廓线的交点是算法成败的关键问题。

另一方面,采用合适的数据结构又可以简化操作、提高算法的效率。

本论文由于采用链表结构记录轮廓线和交点,无需焦点排序的过程,因而提高了算法效率。

扫描线来源于光栅显示器的显示原理:对于屏幕上所有待显示像素的信息,将这些信息按从上到下、自左至右的方式显示。

扫描线多边形区域填充算法是按扫描线顺序,计算扫描线与多边形的相交区间,再用要求的颜色显示这些区间的象素,即完成填充工作。

区间的端点可以通过计算扫描线与多边形边界线的交点获得。

对于一条扫描线,多边形的填充过程可以分为四个步骤:(1)求交:计算扫描线与多边形各边的交点;(2)排序:把所有交点按x值递增顺序排序;(3)配对:第一个与第二个,第三个与第四个等等;每对交点代表扫描线与多边形的一个相交区间;(4)填色:把相交区间内的象素置成多边形颜色;三、实验原理扫描线填充算法的基本过程如下:当给定种子点(x,y)时,首先填充种子点所在扫描线上的位于给定区域的一个区段,然后确定与这一区段相连通的上、下两条扫描线上位于给定区域内的区段,并依次保存下来。

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

扫描线填充算法讲解

扫描线填充算法讲解

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

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

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

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

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

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

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

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

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

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

扫描线填充算法试验报告

扫描线填充算法试验报告

扫描线填充算法试验报告1.概要设计:使用的算法是扫描线填充算法,其中建立了五个类,类Application1是JBuilder9自动生成的主类,类POINT包含顶点的坐标信息(x,y),类Edge包含每条边的信息(ymax,xmin,1/m),而类Bc是在类Frame1利用Collections.sort()排序时的继承自类Comparator 的一个类,Frame1是主界面,算法的主要实现都在此类中。

Frame1中的createET()是建立吊桶的函数,scan()是扫描填充的函数。

其中createET()和scan()采用书上的数据结构和算法。

createET()中数据结构如下:1.对每条边建立对象2.将对象存到一个链表中3.在将这个链表存到另一个链表中4.循环以上操作以建立ET表5.调整这时的链表得到真正的ET表scan()中算法如下:1.计算所有的多边形顶点中y的最大和最小值,以此作为扫描线的处理范围2.对处理范围内的每条扫描线建立有序边表3.对处理范围内的每条扫描线,重复下列步骤:(1)用有序边表建立当前扫描线的活化边表(2)从活化边表中依次取出一对交点画线(3)为下一条扫描线更新活化边表,即增加交点的x值和删除不再相交的边(4)重排活化边表2.实现1.建立吊桶:每个点(POINT)都和自己前后的点相比,以y值从小到大将边(Edge)存到一个ArrayList中,其中要求出这条边的斜率。

在一个for循环语句中,将上面建立的对应每个顶点的边表加入另一个ArrayList中,如果没有边表,则加入一个空的ArrayList。

调整整个ET表,将非极值点的ymax值相上移一格。

2.扫描算法:令y=yminAET表中置ET表中的边While(AET.size()!=0)循环:Collections.sort(AET,new Bc() )对每对奇偶点画线if 扫描线y=ymax 从AET表中删除这些边对于其他边x=x+1/my++按照y的值再次把边从ET表中加入AET表中循环结束算法结束3.心得体会这次的作业主要是参照书上的算法,自己并没有做过多的改进,感觉上程序较慢,这可能与1. Java的速度较慢;2. 程序中生成的对象较多有关。

扫描线填色算法

扫描线填色算法

扫描线填色算法扫描线填色算法1、基本原理见幻灯2、多边形区域填充步骤(1)输入欲填充多边形的顶点数及其顶点坐标。

这里,顶点数为实际顶点数加1,最后一个顶点坐标与第一个顶点坐标相同。

(2)计算所有多边形顶点坐标中y的最大值和最小值,以此作为扫描线的处理范围。

(3)对处理范围内的每条扫描线建立有序边表。

(4)对处理范围内的每条扫描线重复下列步骤:A. 用有序边表建立当前扫描线的活化边表;B. 从活化边表中依次取出一对交点,对该两个交点内的像素进行填充;C. 为下一条扫描线更新活化边表,即增加交点的x值和删除不再相交的边。

D. 重排活化边表。

3、数据结构活化边表结点数据结构可以定义为:typedef struct tEdge{float x ; /*当前扫描线与边的交点的x值*/float dx ; /*从当前扫描线到下一条扫描线之间的x增量*/int ymax ; /*边所交的最高扫描线号*/}Edge;4、有序边表填充算法的C语言描述#include"graphics.h"#define WINDOW_HEIGHT 480#define NULL 0typedef struct tEdge /*设置有序边表和活化边表数据结构*/{int ymax;float x, dx;struct tEdge *next;}Edge;typedef struct point{ int x,y;}POINT;/*按交点x的升序对边进行插入排序*/void InsertEdge(Edge * list,Edge * edge) /*list为有序边表,edge为待插入的边*/{Edge * p,*q=list;p=q->next;while(p!=NULL){ if(edge->xx)p=NULL; /*停止循环,edge应插入到链表的头*/else /* 遍历list */{ q=p;p=p->next;}}edge->next=q->next; /*q指向edge应插入位置的前一个结点,edge插入到q之后*/q->next=edge;}/*计算下一条非水平线的y值*/int yNext(int k,int cnt,POINT * pts){int j;if((k+1)>(cnt-1))j=0;elsej=k+1;while(pts[k].y==pts[j].y)if((j+1)>(cnt-1))j=0;elsej++;return(pts[j].y);}/*生成有序边表的一条边*/void MakeEdgeRec(POINT lower,POINT upper,int yComp,Edge * edge,Edge * edges[]){ edge->dx=(float)(upper.x-lower.x)/(upper.y-lower.y);edge->x=lower.x;if(upper.y<ycomp)< bdsfid="129" p=""></ycomp)<>edge->ymax=upper.y-1;elseedge->ymax=upper.y;InsertEdge(edges[lower.y],edge);}/* 建立有序边表,每次从顶点数组中取出两个点,调用MakeEdgeRec来生成一条边,并调用InsertEdge把建好的边插入到有序边表中*/void BuildEdgeList(int cnt,POINT * pts, Edge * edges[]){Edge *edge;POINT v1,v2;int i,yPrev=pts[cnt-2].y;v1.x=pts[cnt-1].x;v1.y=pts[cnt-1].y;for(i=0;i<cnt;i++)< bdsfid="144" p=""></cnt;i++)<>{ v2=pts[i];if(v1.y!=v2.y) /* nonhorizontal line */{edge=(Edge *)malloc(sizeof(Edge));if(v1.y<="" bdsfid="150" edge="" p="">MakeEdgeRec(v1,v2,yNext(i,cnt,pts),edge,edges);else /*down-going edge */MakeEdgeRec(v2,v1,yPrev,edge,edges);}yPrev=v1.y;v1=v2;}}void BuildActiveList(int scan,Edge * active,Edge * edges[]) /*建立活化边表*/{Edge *p,*q;p=edges[scan]->next;while(p){ q=p->next;InsertEdge(active,p);p=q;}}void FillScan(int scan,Edge * active,int color) /* 对当前扫描线填充*/{Edge *p1, *p2;int i;p1=active->next;while(p1){p2=p1->next;for(i=p1->x;ix;i++)putpixel((int)i,scan,color);p1=p2->next;}}void DeleteAfter(Edge *q) /* 删除不再相交的边*/{Edge *p=q->next;q->next=p->next;free(p);}/*Delete completed edges. Update 'xIntersect' field for others */void UpdateActiveList(int scan,Edge * active) /* 为下一条扫描线进行更新*/{Edge *q=active,*p=active->next;while(p)if(scan>=p->ymax) /* 如果当前扫描线scan的值大于p所指向的边的ymax,则删除该边*/{ p=p->next;DeleteAfter(q);}else /* 否则p所指向的边的x值加一个dx */{ p->x=p->x+p->dx;q=p;p=p->next;}}void ResortActiveList(Edge * active) /* 重排活化边表*/{Edge *q,*p=active->next;active->next=NULL;while(p){ q=p->next;InsertEdge(active,p);p=q;}}void ScanFill(int cnt,POINT *pts,int color) /* cnt为多边形的顶点数,pts 为顶点坐标数组*/{Edge * edges[WINDOW_HEIGHT],*active;int i, scan, scanmax = 0, scanmin = WINDOW_HEIGHT;for (i = 0; i < cnt-1; i++) /* 求填充区域的扫描线最大值scanmax 和最小值scanmin*/{ if (scanmax < pts [i].y ) scanmax = pts [i].y;if (scanmin > pts [i].y ) scanmin = pts [i].y;}for(scan=scanmin;scan<=scanmax;scan++) /*初始化每条扫描线的边链表*/{ edges[scan]=(Edge *)malloc(sizeof(Edge));edges[scan]->next=NULL;}BuildEdgeList(cnt,pts,edges); /* 用顶点坐标pts建立有序边表,放在edges中*/active=(Edge *)malloc(sizeof(Edge)); /* 初始化活化边表*/active->next=NULL;for(scan=scanmin;scan<=scanmax;scan++){BuildActiveList(scan,active,edges); /* 建立当前扫描线scan的活化边表*/if(active->next){ FillScan(scan,active,color); /* 填充当前扫描线*/UpdateActiveList(scan,active); /*为下一条扫描线更新活化边表*/ResortActiveList(active); /*重排活化边表*/}}}。

扫描线种子填充算法

扫描线种子填充算法

扫描线种子填充算法扫描线种子填充算法的基本过程如下:当给定种子点(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]大,但是仍然得到了正确的填充。

扫描线填充算法讲解

扫描线填充算法讲解

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

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

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

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

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

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

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

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

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

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

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

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

实验二4-10一、实验题目扫描线种子填充算法是通过扫描线来填充多边形内的水平像素段,处理每条扫描线时仅需将其最右端像素入栈,可以有效提高填充效率。

请使用MFC编程填充图4-60所示的空心体汉字(四连通),填充效果如图4-61所示。

二、实验思想扫描线种子填充算法:先将种子像素入栈,种子像素为栈底像素,如果栈不为空,执行如下4步操作。

(1)栈顶像素出栈。

(2)沿扫描线对出栈像素的左右像素进行填充,直至遇到边界像素为止。

即每出栈一个像素,就对区域内包含该像素的整个连续区间进行填充。

(3)同时记录该区间,将区间最左端像素记为x left,最右端像素记为x right。

(4)在区间〔x left,x right〕中检查与当前扫描线相邻的上下两条扫描线的有关像素是否全为边界像素或已填充像素,若存在非边界且未填充的像素,则把未填充区间的最右端像素取作种子像素入栈。

三、实验代码void CTestView::OnLButtonDown(UINT nFlags, CPoint point)//左键按下函数{// TODO: Add your message handler code here and/or call defaultSeed=point;//选择种子位置CharFill();//进行填充CView::OnLButtonDown(nFlags, point);}void CTestView::CharFill()//文字填充函数{CRect Rect;GetClientRect(&Rect);CClientDC dc(this);COLORREF BoundColor;//边界色int Width=Rect.right-Rect.left;int Hight=Rect.bottom-Rect.top ;int Flag;int x0,y0,x,y;CPoint Point;std::vector<CPoint> FillBuffle;//定义CPoint类型的数组序列对象FillBuffle.reserve(10);//定义数组序列的大小FillBuffle.push_back(CPoint(Seed)); //把种子结点压入数组序列BoundColor=RGB(0,0,0);//定义边界色为黑色while(!FillBuffle.empty())//如果数组序列非空{Point=FillBuffle.front();//弹出数组序列头元素x=Point.x;y=Point.y;FillBuffle.erase(FillBuffle.begin());//清除数组序列内的元素dc.SetPixel(Point,Fillcolor);//绘制像素//判断像素的位置是否在图形内部x0=x+1;//右方判断while(dc.GetPixel(x0,y)!=BoundColor&&dc.GetPixel(x0,y)!=Fillcolor) {x0=x0+1;if(x0>=Width)//到达屏幕最右端{MessageBox("种子超出范围","警告");RedrawWindow();return;}}y0=y+1;//下方判断while(dc.GetPixel(x,y0)!=BoundColor&&dc.GetPixel(x,y0)!=Fillcolor) {y0=y0+1;if(y0>=Hight)//到达屏幕最下端{MessageBox("种子超出范围","警告");RedrawWindow();return;}}RightPoint.x=x0;//右边界内的左邻点x0=x-1;while(dc.GetPixel(x0,y)!=Fillcolor&&dc.GetPixel(x0,y)!=BoundColor){dc.SetPixel(x0,y,Fillcolor);x0=x0-1;if(x0<=0)//到达屏幕最左端{MessageBox("种子超出范围","警告");RedrawWindow();return;}}y0=y-1;while(dc.GetPixel(x,y0)!=BoundColor&&dc.GetPixel(x,y0)!=Fillcolor){y0=y0-1;if(y0<=0)//到达屏幕最上端{MessageBox("种子超出范围","警告");RedrawWindow();return;}}LeftPoint.x=x0+1;//左边界内的右邻点x0=LeftPoint.x;y=y+1;//下一条扫描线while(x0<RightPoint.x){Flag=0;while((dc.GetPixel(x0,y)!=Fillcolor)&&(dc.GetPixel(x0,y)!=BoundColor)) {if(Flag==0)Flag=1;x0++ ;}if(Flag==1){if((x0==RightPoint.x)&&(dc.GetPixel(x0,y)!=Fillcolor)&&(dc.GetPixel(x0,y)!=BoundColor))FillBuffle.push_back(CPoint(x0,y));//进入数组序列else{FillBuffle.push_back(CPoint(x0-1,y));}Flag=0;}PointNext.x=x0;while(((dc.GetPixel(x0,y)==Fillcolor)&&(x0<RightPoint.x))||((dc.GetPixel(x0,y)==BoundColor) &&(x0<RightPoint.x))){x0 ++;}}x0=LeftPoint.x;y=y-2;while(x0<RightPoint.x){Flag=0;while((dc.GetPixel(x0,y)!=Fillcolor)&&(dc.GetPixel(x0,y)!=BoundColor)&&(x0<RightPoint.x)) {if(Flag==0)Flag=1;x0++ ;}if(Flag==1){if((x0==RightPoint.x)&&(dc.GetPixel(x0,y)!=Fillcolor)&&(dc.GetPixel(x0,y)!=BoundColor))FillBuffle.push_back(CPoint(x0,y));else{FillBuffle.push_back(CPoint(x0-1,y));}Flag=0;}PointNext.x=x0;while((dc.GetPixel(x0,y)==Fillcolor&&x0<RightPoint.x)||(dc.GetPixel(x0,y)==BoundColor&&x 0<RightPoint.x)){x0++;}}}FillBuffle.clear();return;}void CTestView::OnMENUFill(){// TODO: Add your command handler code hereRedrawWindow();MessageBox("请在空心字体内部单击鼠标左键!","提示");}四、实验结果截图。

计算机图形学-区域填充的扫描线算法

计算机图形学-区域填充的扫描线算法

计算机图形学——区域填充的扫描线算法一.实验名称:区域填充的扫描线算法二.实验目的:1、理解区域填充扫描线算法的原理;2、实现区域填充的扫描线算法并测试;三.算法原理:算法基本思想: 首先填充种子点所在扫描线上位于区域内的区段,然后确定与该区段相邻的上下两条扫描线上位于区域内的区段,并依次将各区段的起始位置保存, 这些区段分别被用区域边界色显示的像素点所包围。

随后,逐步取出一开始点并重复上述过程,直到所保存各区段都填充完毕为止。

借助于栈结构,区域填充的扫描线算法之步骤如下:Step 1. 初始化种子点栈:置种子点栈为空栈,并将给定的种子点入栈;Step 2. 出栈:若种子点栈为空,算法结束;否则,取栈顶元素(x,y)为种子点;Step 3. 区段填充:从种子点(x, y) 开始沿纵坐标为y 的当前扫描线向左右两个方向逐像素点进行填色,其颜色值置为newcolor 直至到达区域边界。

分别以xl 和xr 表示该填充区段两端点的横坐标;Step 4. 新种子点入栈: 分别确定当前扫描线上、下相邻的两条扫描线上位于区段[xl, xr] 内的区域内的区段。

若这些区段内的像素点颜色值为newolor ,则转至Step 2;否则以区段的右端点为种子点入种子点栈,再转至Step 2。

四.原程序代码:/*****************************************//*4-ScanLineFill 区域填充的扫描线算法实现*//*****************************************/#include <stdio.h>#include <conio.h>#include <graphics.h>#include <malloc.h>#define Stack_Size 100 //栈的大小常量//定义结构体,记录种子点typedef struct{int x;int y;}Seed;//定义顺序栈(种子点)typedef struct{Seed Point[Stack_Size];int top;}SeqStack;//初始化栈操作void InitStack(SeqStack *&S){S=(SeqStack *)malloc(sizeof(SeqStack));S->top=-1;}//种子点栈置空;void setstackempty (SeqStack *S){S->top==-1;}//种子点栈状态检测函数int isstackempty (SeqStack *S){if(S->top==-1)return true; //空栈返回trueelsereturn false; //非空栈返回false}//种子点入栈;int stackpush (SeqStack *&S,Seed point){if(S->top==Stack_Size-1)//栈已满,返回false return false;S->top++;//栈未满,栈顶元素加1S->Point[S->top]= point;return true;}//取栈顶元素;int stackpop (SeqStack *&S,Seed &point){if(S->top==-1)//栈为空,返回falsereturn false;point=S->Point[S->top];S->top --;//栈未空,top减1return true;}//画圆void CirclePoints (int xc, int yc, int x, int y, int Color) {putpixel (xc + x, yc + y, Color);putpixel (xc + x, yc - y, Color);putpixel (xc - x, yc + y, Color);putpixel (xc - x, yc - y, Color);putpixel (xc + y, yc + x, Color);putpixel (xc + y, yc - x, Color);putpixel (xc - y, yc + x, Color);putpixel (xc - y, yc - x, Color); }//中点画圆算法void MidpointCircle(int radius, int Color) {int x, y;float d;x=0;y=radius;d=5.0/4-radius;CirclePoints(250,250,x,y,Color);while(x<y){if (d<0){d+=x*2.0+3;}else{d+=(x-y)*2.0+5;y--;}x++;CirclePoints(250,250,x,y,Color);}}//四连通扫描线算法void ScanLineFill4(int x, int y, int oldcolor, int newcolor) {int xl, xr, i;bool SpanNeedFill;Seed pt;//种子点SeqStack *S;//定义顺序栈InitStack(S);//定义了栈之后必须把栈先初始化setstackempty(S);//种子点栈置空;pt.x = x;pt.y = y;stackpush (S,pt); // 种子点(x, y)入栈while (!isstackempty(S)){stackpop (S,pt);//取种子点y = pt.y;x = pt.x;while (getpixel (x,y)==oldcolor) {// 从种子点开始向右填充putpixel (x, y, newcolor);x++;}xr = x -1;x = pt.x -1;while (getpixel (x,y)==oldcolor) { // 从种子点开始向左填充putpixel (x, y, newcolor);x--;}xl = x + 1;x = xl;y = y +1; // 处理上面一条扫描线while (x < xr){SpanNeedFill = false;while (getpixel (x, y)==oldcolor){SpanNeedFill = true;x++ ;} // 待填充区段搜索完毕if (SpanNeedFill){// 将右端点作为种子点入栈pt.x = x - 1;pt.y = y;stackpush (S,pt);SpanNeedFill = false;} //继续向右检查以防遗漏while ((getpixel (x, y)!=oldcolor) && (x< xr)) x++;} //上一条扫描线上检查完毕x = xl;y=y-2; // 处理下面一条扫描线while (x < xr){SpanNeedFill = false;while (getpixel (x, y)==oldcolor){SpanNeedFill=true;x++ ;}if (SpanNeedFill){pt.x= x - 1;pt.y = y;stackpush (S,pt);SpanNeedFill=false;}while ((getpixel (x, y)!=oldcolor) && (x < xr))x++;}}}//主函数检测void main(){int radius,color;int x,y;//种子点int oldcolor,newcolor;//原色与填充色//输入参数值printf("input radius and color:\n");//画圆参数scanf("%d,%d",&radius,&color);printf("input x and y:\n"); //读入内点scanf("%d,%d", &x, &y);printf("input oldcolor and newcolor:\n"); //读入原色与填充色scanf("%d,%d", &oldcolor, &newcolor);int gdriver = DETECT,gmode;initgraph(&gdriver, &gmode, "c:\\tc");// 用背景色清空屏幕cleardevice();// 设置绘图色为红色setcolor(RED);MidpointCircle(radius,color);//用中点画圆算法画圆rectangle(150, 150, 350, 350);//再画一个矩形区域ScanLineFill4 (x,y,oldcolor,newcolor);//扫描线区域填充getch();closegraph();}五.运行结果与讨论:测试结果1:测试结果2:六.实验分析与讨论:1.通过借助栈这一数据结构,完成了区域填充的扫描线算法的实现,并利用以前所学的画圆等算法,进行综合运用,在此基础上进行扩充,设计多种图案,进行扫描线填充算法的检测,都得到了理想的结果,体现了算法的有效性;2.栈的数据结构给种子点的操作带来了极大的方便,为算法的实现提供了便利,同时还提高了算法的复用性和可靠性;3.此扫描线填充算法能够对多种图案进行填充,展现了算法的实用性。

区域填充的扫描线算法

区域填充的扫描线算法

实验四.区域填充一:扫描线算法1)算法分析:扫描线算法是按扫描线顺序,计算扫描线与多边形的相交区间,再用要求的颜色显示这些区间的象素,完成转换工作。

区间的端点可以通过计算扫描线与多边形边界线的交点获得。

对于一条扫描线,多边形的扫描转换过程可以分为四个步骤:求交:计算扫描线与多边形各边的交点;排序:把所有交点按x值递增顺序排序;配对:第一个与第二个,第三个与第四个等等;每对交点代表扫描线与多边形的一个相交区间,着色:把相交区间内的象素置成多边形颜色,把相交区间外的象素置成背景色。

2)代码实现void CHuXiaoHua_graphics4View::OnScanfill(){// TODO: 在此添加命令处理程序代码RedrawWindow();CDC* pDC=GetDC();CPen newpen(PS_SOLID,3,RGB(65,34,53));CPen *old=pDC->SelectObject(&newpen);CPoint spt [10];spt[0]=CPoint(100,30);spt[1]=CPoint(150,80);spt[2]=CPoint(150,150);spt[3]=CPoint(115,150);spt[4]=CPoint(115,115);spt[5]=CPoint(85,115);spt[6]=CPoint(85,150);spt[7]=CPoint(50,150);spt[8]=CPoint(50,80);spt[9]=CPoint(100,30);pDC->Polyline(spt,10);CDC* ptr = GetDC();int color =RGB(64,68,255);for(int j=30;j<=80;j++){for(int i=50;i<=150;i++){if((i+j-130)>0&&(i-j-70)<0){ptr->SetPixel(i,j,color);}}Sleep(1);}for(int j=80;j<=115;j++){for(int i=50;i<=150;i++){ptr->SetPixel(i,j,color);}Sleep(1);}for(int j=115;j<=150;j++){for(int i=50;i<=150;i++){if(i>=85&&i<=115){continue;}ptr->SetPixel(i,j,color);}Sleep(1);}}3)运行结果。

4-区域填充-扫描线算法

4-区域填充-扫描线算法

实验-区域填充实验名称:区域填充实验学时:2学时实验目的:1.掌握扫描线填充算法2.利用扫描线算法实现任意区域的填充实验内容:1.画出任意区域,并填充内部,然后调用填充函数ScanLineFill()实现区域的填充。

2.本程序不完整,需要修改的部分为:a)缺Span类型b)栈的大小c)ScanLineFill()函数不能连续地填充(隔行填充)。

3.给ScanLineFill()函数加注释源代码如下。

#include <stdio.h>#include <stdlib.h>#include <graphics.h>#include <conio.h>enum {MAXNUM = 20 /* 栈中最大元素个数,应根据需要定义 */};struct SeqStack { /* 顺序栈类型定义 */int t; /* 栈顶位置指示 */Span s[MAXNUM];};typedef struct SeqStack SeqSack, *PSeqStack; /* 顺序栈类型和指针类型 */ PSeqStack pseqstack;/*创建一个空栈;为栈结构申请空间,并将栈顶变量赋值为-1*/PSeqStack createEmptyStack_seq( void ){PSeqStack pastack = (PSeqStack)malloc(sizeof(struct SeqStack));if (pastack==NULL)printf("Out of space!! \n");elsepastack->t = -1;return pastack;}/*判断pastack所指的栈是否为空栈,当pastack所指的栈为空栈时,则返回1,否则返回0*/int isEmptyStack_seq( PSeqStack pastack ){return pastack->t == -1;}/* 在栈中压入一元素x */void push_seq( PSeqStack pastack, Span x ){if( pastack->t >= MAXNUM - 1 )printf( "Stack Overflow! \n" );else {pastack->t++;pastack->s[pastack->t] = x;}}/* 删除栈顶元素 */void pop_seq( PSeqStack pastack ){if (pastack->t == -1 )printf( "Underflow!\n" );elsepastack->t--;}/* 当pastack所指的栈不为空栈时,求栈顶元素的值 */Span top_seq( PSeqStack pastack ){return pastack->s[pastack->t];}void ScanLineFill( CDC *pdc, int x, int y, COLORREF oldColor, COLORREF newColor) {int x0,y0;int xr,xl;int xn;int flag;Span span;pseqstack=createEmptyStack_seq( );span.xRight=x;span.y=y;push_seq( pseqstack, span );while(!isEmptyStack_seq( pseqstack )){span=top_seq( pseqstack );pop_seq(pseqstack );x0=span.xRight;y0=span.y;pdc->SetPixel(x0,y0,newColor);while(pdc->GetPixel(x0+1,y0)==oldColor){pdc->SetPixel(x0+1,y0,newColor);x0++;}xr=x0-1; x0=span.xRight; y0=span.y;while(pdc->GetPixel(x0-1,y0)==oldColor){pdc->SetPixel(x0-1,y0,newColor);x0--;}xl=x0+1; x0=xl; y0=span.y+1;while(x0<xr){flag=0;while(pdc->GetPixel(x0,y0)==oldColor && x0<= xr ){if(!flag)flag=1;x0++;}if(flag){span.y=y0;if(x0==xr && pdc->GetPixel(x0,y0)==oldColor){span.xRight=x0;push_seq(pseqstack, span );}else{span.xRight=x0-1;push_seq(pseqstack, span );}flag=0;}xn=x0;while(pdc->GetPixel(x0,y0)==newColor && x0<= xr ) x0++;if(xn==x0)x0++;}// while(x0<xr)x0=xl; y0=span.y-2;while(x0<=xr){flag=0;while(pdc->GetPixel(x0,y0)==oldColor && x0<= xr ){if(!flag)flag=1;x0++;}if(flag){span.y=y0;if(x0==xr && pdc->GetPixel(x0,y0)==oldColor){span.xRight=x0;push_seq(pseqstack, span );}else{span.xRight=x0-1;push_seq(pseqstack, span );}flag=0;}xn=x0;while(pdc->GetPixel(x0,y0)==newColor && x0<= xr ) x0++;if(xn==x0)x0++;}//while(x0<=xr)}//while(!isEmptyStack_seq( pseqstack ))}。

实验六 扫描线填充算法

实验六  扫描线填充算法

实验六扫描线填充算法一、实验目的编写多边形的扫描线填充算法程序,加深对扫描线算法的理解,验证算法的正确性。

二、实验任务(2学时)编写多边形的扫描线填充算法程序,利用数组实现AET,考虑与链表实现程序的不同。

三、实验内容1、算法对一条扫描线的填充一般分为以下4个步骤:(1)求交:计算扫描线与多边形各边的交点;(2)排序:把扫描线上所有交点按递增顺序进行排序;(3)配对:将第一个交点与第二个交点,第三个交点与第四个交点等等进行配对,每对交点代表扫描线与多边形的一个相交区间。

(4)着色:把区间内的像素置为填充色。

2、成员函数的关系主程序名为fill_area(count, x, y),其中参数x, y是两个一维数组,存放多边形顶点(共c ount个)的x和y坐标。

它调用8个子程序,彼此之间的调用关系图1所示为:图1 fill_area的程序结构3、算法的程序设计步骤1:创建“S_L_Fill”工程文件;步骤2:创建类class:“EACH_ENTRY”。

在工作区“S_L_Fill classes”单击右键-→“new class”-→选择类型“Generic Class”名称为“EACH_ENTRY”,添加成员变量(添加至“class EACH_ENTRY { public:”之内):int y_top;float x_int;int delta_y;float x_change_per_scan;步骤3:包含头文件,同时初始化定义多边形顶点数目。

在“class CS_L_FillView : public Cview……”之前添加代码“#include EACH_ENTRY.h”及“#define MAX_POINT 9”。

#define MAX_POINT 9#include "EACH_ENTRY.h"步骤4:在类“class CS_L_FillView”中添加成员变量(鼠标双击工作区“CS_L_FillView”,代码添加至“class CS_L_FillView : public Cview {protected: ……public:之后”):EACH_ENTRY sides[MAX_POINT];int x[MAX_POINT],y[MAX_POINT];int side_count,first_s,last_s,scan,bottomscan,x_int_count;步骤5:利用构造函数“CS_L_FillView::CS_L_FillView()”初始化顶点坐标(鼠标双击工作区“CS_L_FillView”,代码添加至“CS_L_FillView()之内”):x[0]=200;y[0]=100;x[1]=240;y[1]=160;x[2]=220;y[2]=340;x[3]=330;y[3]=100;x[4]=400;y[4]=180;x[5]=300;y[5]=400;x[6]=170;y[6]=380;x[7]=120;y[7]=440;x[8]=100;y[8]=220;步骤6:在“class CS_L_FillView”下添加实现不同功能的成员函数。

区域填充扫描线算法实验报告

区域填充扫描线算法实验报告

期中作业一实验内容区域填充扫描线算法:按照扫描线的顺序,计算扫描线与多边形的相交区间,再用要求的颜色显示这些区间的像素,完成填充工作。

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

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

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

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

Step1:将ET表中各元素置空,建立ET表Step2:为多边形P的每一条边建立边结构按该边的上端点的y值y 上插入ET表中的第y上类(组),即插入ET[y上].Step3 :(初始化)AEL置空//AEL=Null y:ET表中非空元素的区域号最大值。

Step4:扫描转化while(AEL or ET 非空)do{No.1 (边插入)如果ET[y]非空,则将ET[y]中各边插入AEL。

No.2 (排序)将AEL中的各边按照x(若x相等按Δx的递增顺序排序。

No.3(如果AEL非空填色)将AEL中各边依次组成对,在横坐标为y 的扫描线上,将以每对边的x坐标为端点的区间上填上多边形所需要的颜色.No.4(下一条扫描线)y--。

No.5(边删除)将AEL中满足y=ymin的边删除。

No.6(边更新)将AEL中的各边x值更新,x=x+ Δx }三源代码#include "stdafx.h"#include "demo.h"#include "ChildView.h"#include <math.h>#define Round(d) int(floor(d+0.5))//四舍五入宏定义#ifdef _DEBUG#define new DEBUG_NEW#endif// CChildViewCChildView::CChildView(){}CChildView::~CChildView(){}BEGIN_MESSAGE_MAP(CChildView, CWnd)ON_WM_PAINT()ON_WM_CREATE()ON_COMMAND(ID_DRAW_PIC, &CChildView::OnDrawPic) END_MESSAGE_MAP()// CChildView 消息处理程序BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs) {if (!CWnd::PreCreateWindow(cs))return FALSE;cs.dwExStyle |= WS_EX_CLIENTEDGE;cs.style &= ~WS_BORDER;cs.lpszClass =AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, ::LoadCursor(NULL, IDC_ARROW),reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);return TRUE;}void CChildView::OnPaint(){CPaintDC dc(this); // 用于绘制的设备上下文// TODO: 在此处添加消息处理程序代码DrawGraph();// 不要为绘制消息而调用CWnd::OnPaint()}void CChildView::ReadPoint() //点表{P[0].x = 50; P[0].y = 100;P[1].x = -150;P[1].y = 300;P[2].x = -250;P[2].y = 50;P[3].x = -150;P[3].y = -250;P[4].x = 0; P[4].y = -50;P[5].x = 100; P[5].y = -250;P[6].x = 300; P[6].y = 150;}void CChildView::DrawPolygon(CDC *pDC) //绘制多边形边界{CLine *line = new CLine;CP2 t;for(int i = 0; i < 7; i++) //绘制多边形{if(i == 0){line->MoveTo(pDC, P[i]);t = P[i];}else{line->LineTo(pDC, P[i]);}}line->LineTo(pDC, t); //闭合多边形delete line;}void CChildView::DrawGraph() //绘制图形{CRect rect; //定义客户区GetClientRect(&rect); //获得客户区的大小CDC *pDC = GetDC(); //定义设备上下文指针pDC->SetMapMode(MM_ANISOTROPIC); //自定义坐标系pDC->SetWindowExt(rect.Width(), rect.Height());//设置窗口比例pDC->SetViewportExt(rect.Width(), -rect.Height());//设置视区比例,且x轴水平向右,y轴垂直向上pDC->SetViewportOrg(rect.Width() / 2, rect.Height() / 2);//设置客户区中心为坐标系原点rect.OffsetRect(-rect.Width() / 2, -rect.Height() / 2);//矩形与客户区重合if(!bFill)DrawPolygon(pDC); //绘制多边形elseFillPolygon(pDC); //填充多边形ReleaseDC(pDC); //释放DC}void CChildView::FillPolygon(CDC *pDC) //填充多边形{for(int i = 0; i < 7; i++) //转储顶点坐标,y 坐标取为整数{P1[i].x = P[i].x;P1[i].y = Round(P[i].y);P1[i].c = CRGB(bRed / 255.0, bGreen / 255.0, bBlue / 255.0);}CFill *fill = new CFill; //动态分配内存fill->SetPoint(P1, 7); //初始化Fill对象fill->CreateBucket(); //建立桶表fill->CreateEdge(); //建立边表fill->Gouraud(pDC); //填充多边形delete fill; //撤销内存}int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct) {if (CWnd::OnCreate(lpCreateStruct) == -1)return -1;// TODO: 在此添加您专用的创建代码bFill = FALSE;ReadPoint();return 0;}void CChildView::OnDrawPic(){// TODO: 在此添加命令处理程序代码COLORREF GetClr = RGB(0, 0, 0); //调色板颜色CColorDialog ccd(GetClr, CC_SOLIDCOLOR);if(IDOK == ccd.DoModal()) //调用颜色对话框选取填充色GetClr = ccd.GetColor();elsereturn;bRed = GetRValue(GetClr); //获取红色分量bGreen = GetGValue(GetClr); //获取绿色分量bBlue = GetBValue(GetClr); //获取蓝色分量bFill = TRUE;Invalidate();}四运行结果1.填充颜色为黄色时运行结果。

-计算机图形学实验报告6-扫描线填充

-计算机图形学实验报告6-扫描线填充

《计算机图形学实验》报告2016年春季学期实验六:扫描线填充算法实验时间:实验地点:实验目的:掌握使用opengl 的扫描线填充算法,观察改变参数对生成图形的改变。

程序代码:使用的运行环境是vs2010#include "stdafx.h"#include "glut.h"#include "windows.h"const int POINTNUM=3; //多边形点数./******定义结构体用于活性边表AET和新边表NET***********************************/typedef struct XET{float x;float dx,ymax;XET* next;}AET,NET;/******定义点结构体point******************************************************/struct point{float x;float y;}polypoint[POINTNUM]={250,50,550,150,550,400};//多边形顶点void PolyScan(){/******计算最高点的y坐标(扫描到此结束)****************************************/int MaxY=0;int i;for(i=0;i<POINTNUM;i++)if(polypoint[i].y>MaxY)MaxY=polypoint[i].y;/*******初始化AET表***********************************************************/AET *pAET=new AET;pAET->next=NULL;/******初始化NET表************************************************************/NET *pNET[1024];for(i=0;i<=MaxY;i++){pNET[i]=new NET;pNET[i]->next=NULL;}glClear(GL_COLOR_BUFFER_BIT); //赋值的窗口显示.glColor3f(1.0,1.0,0.0); //设置扫描直线的颜色glBegin(GL_POINTS);/******扫描并建立NET表*********************************************************/for(i=0;i<=MaxY;i++){for(int j=0;j<POINTNUM;j++)if(polypoint[j].y==i){ //一个点跟前面的一个点形成一条线段,跟后面的点也形成线段if(polypoint[(j-1+POINTNUM)%POINTNUM].y>polypoint[j].y){NET *p=new NET;p->x=polypoint[j].x;p->ymax=polypoint[(j-1+POINTNUM)%POINTNUM].y;p->dx=(polypoint[(j-1+POINTNUM)%POINTNUM].x-polypoint[j].x)/(polypoint[(j-1+POIN TNUM)%POINTNUM].y-polypoint[j].y);p->next=pNET[i]->next;pNET[i]->next=p;}if(polypoint[(j+1+POINTNUM)%POINTNUM].y>polypoint[j].y){NET *p=new NET;p->x=polypoint[j].x;p->ymax=polypoint[(j+1+POINTNUM)%POINTNUM].y;p->dx=(polypoint[(j+1+POINTNUM)%POINTNUM].x-polypoint[j].x)/(polypoint[(j+1+POI NTNUM)%POINTNUM].y-polypoint[j].y);p->next=pNET[i]->next;pNET[i]->next=p;}}/******建立并更新活性边表AET*****************************************************/for(i=0;i<=MaxY;i++){//计算新的交点x,更新AETNET *p=pAET->next;while(p){p->x=p->x + p->dx;p=p->next;}//更新后新AET先排序*************************************************************/ //断表排序,不再开辟空间AET *tq=pAET;p=pAET->next;tq->next=NULL;while(p){while(tq->next && p->x >= tq->next->x)tq=tq->next;NET *s=p->next;p->next=tq->next;tq->next=p;p=s;tq=pAET;}//(改进算法)先从AET表中删除ymax==i的结点****************************************/AET *q=pAET;p=q->next;while(p){if(p->ymax==i){q->next=p->next;delete p;p=q->next;}else{q=q->next;p=q->next;}//将NET中的新点加入AET,并用插入法按X值递增排序**********************************/p=pNET[i]->next;q=pAET;while(p){while(q->next && p->x >= q->next->x)q=q->next;NET *s=p->next;p->next=q->next;q->next=p;p=s;q=pAET;}/******配对填充颜色***************************************************************/p=pAET->next;while(p && p->next){for(float j=p->x;j<=p->next->x;j++)glVertex2i(static_cast<int>(j),i);p=p->next->next;//考虑端点情况}}glEnd();glFlush();}void init(void){glClearColor(1.0,1.0,1.0,0.0);//窗口的背景颜色设置为白色glMatrixMode(GL_PROJECTION);gluOrtho2D(0.0,600.0,0.0,450.0);}void main(int argc,char* argv){glutInit(&argc,&argv); //I初始化GLUT.glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); //设置显示模式:单个缓存和使用RGB模型glutInitWindowPosition(50,100); //设置窗口的顶部和左边位置glutInitWindowSize(400,300); //设置窗口的高度和宽度glutCreateWindow("An Example OpenGL Program"); //创建显示窗口init(); //调用初始化过程glutDisplayFunc(PolyScan); //图形的定义传递给我window.glutMainLoop(); //显示所有的图形并等待}实验结果:。

扫描线区域填充算法

扫描线区域填充算法

扫描线区域填充算法算法步骤如下:1.找到图形区域的上下边界:-遍历图形的所有边界点,找到最大纵坐标和最小纵坐标,确定扫描线的上下边界。

2.初始化扫描线列表:-从上到下依次生成扫描线,建立一个扫描线列表。

3.计算扫描线与图形的交点:-遍历扫描线列表中的每个扫描线,与图形的所有边界进行求交。

-如果与边界有交点,将交点的横坐标存入一个集合中,集合中的数据按从小到大排序。

4.进行填充:-遍历扫描线列表中的每个扫描线,找到对应的交点集合。

-将集合中的每两个横坐标之间的像素点进行填充。

以下是对算法的详细解释:首先,我们需要遍历所有边界点,找到最大纵坐标和最小纵坐标。

这样就确定了我们需要扫描的区域范围,也就是扫描线的上下边界。

接下来,我们生成一个扫描线列表。

从上到下依次生成每一条扫描线,将其存储在扫描线列表中。

然后,对于每一条扫描线,我们需要计算该扫描线与图形的交点。

我们遍历图形的所有边界,并与当前扫描线进行求交。

如果与边界有交点,我们将交点的横坐标存入一个集合中。

这个集合中的数据按从小到大排序,以便后续的填充操作。

最后,我们对于每一条扫描线,找到对应的交点集合。

我们遍历集合中的每两个横坐标之间的像素点,将其进行填充。

这可以通过修改像素的颜色或设置像素的属性来实现。

总结一下,扫描线区域填充算法通过逐行扫描图形区域,计算每行与区域内部的交点,然后进行填充。

该算法的优点是简单、高效,适用于填充简单凸多边形等闭合图形。

然而,该算法对于非凸多边形或包含内孔的图形表现较差,可能需要额外的处理逻辑。

扫描线填充算法

扫描线填充算法

扫描线多边形填充:1、填充的主要思想:struct Edge{ float x;float Δx;float ymin;Edge *next;};Step1:(初始化)将ET表中各元素置空,建立ET表Step2:(basket sort,分桶分类)为多边形P的每一条边建立边结构按该边的上端点的y值y 上插入ET表中的第y上类(组),即插入ET[y上].Step3 :(初始化)AEL置空//AEL=Null y:ET表中非空元素的区域号最大值。

Step4:扫描转化while(AEL or ET 非空)do{No.1 (边插入)如果ET[y]非空,则将ET[y]中各边插入AEL。

No.2 (排序)将AEL中的各边按照x(若x相等按Δx的递增顺序排序。

No.3(如果AEL非空填色)将AEL中各边依次组成对,在横坐标为y的扫描线上,将以每对边的x坐标为端点的区间上填上多边形所需要的颜色.No.4(下一条扫描线)y--。

No.5(边删除)将AEL中满足y=ymin的边删除。

No.6(边更新)将AEL中的各边x值更新,x=x+ Δx }2、源程序C++源代码:CScanLineView::CScanLineView(){num=0;StartFlag=0;EndFlag=0;OriFlag=0;WindowFlag=0;ln=0;rn=0;bn=0;tn=0;l=200;r=350;b=200;t=350;sflag=0;lflag=0;// TODO: add construction code here}CScanLineView::~CScanLineView(){}BOOL CScanLineView::PreCreateWindow(CREATESTRUCT& cs) {// TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT csreturn CView::PreCreateWindow(cs);}/////////////////////////////////////////////////////////////////////////////// CScanLineView drawingvoid CScanLineView::OnDraw(CDC* pDC){CScanLineDoc* pDoc = GetDocument();ASSERT_V ALID(pDoc);if(WindowFlag){// SelectObject((HDC)*pDC,redPen);pDC->MoveTo(l,t);pDC->LineTo(l,b);pDC->LineTo(r,b);pDC->LineTo(r,t);pDC->LineTo(l,t);// SelectObject((HDC)*pDC,blackPen);}// TODO: add draw code for native data here}/////////////////////////////////////////////////////////////////////////////// CScanLineView printingBOOL CScanLineView::OnPreparePrinting(CPrintInfo* pInfo) {// default preparationreturn DoPreparePrinting(pInfo);}void CScanLineView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/){// TODO: add extra initialization before printing}void CScanLineView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/){// TODO: add cleanup after printing}/////////////////////////////////////////////////////////////////////////////// CScanLineView diagnostics#ifdef _DEBUGvoid CScanLineView::AssertValid() const{CView::AssertValid();}void CScanLineView::Dump(CDumpContext& dc) const{CView::Dump(dc);}CScanLineDoc* CScanLineView::GetDocument() // non-debug version is inline{ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CScanLineDoc)));return (CScanLineDoc*)m_pDocument;}#endif //_DEBUG/////////////////////////////////////////////////////////////////////////////// CScanLineView message handlersvoid CScanLineView::OnScanline(){CDC *pDC;pDC=GetDC();CPen redPen(PS_SOLID,2, RGB(250,0,0)),greenPen(PS_SOLID,1,RGB(20,250,100)), bluePen(PS_SOLID,1,RGB(0,0,700)),*oldPen,blackPen(PS_SOLID,1,RGB(0,0,0));oldPen=(CPen*)SelectObject((HDC)*pDC,blackPen);SetET();//½¨Á¢ET±íint ymax;for( int i=0;i<num;i++){if(p[i].y>p[i+1].y){PQ.yb=p[i+1].y;PQ.yt=p[i].y;PQ.x=double(p[i].x);PQ.dx=double(p[i+1].x-p[i].x)/double(p[i].y-p[i+1].y);//±íʾ¸÷±ß;Put_Edge_In_To_ET(PQ); //½«¸÷±ß²åÈëET±í;}if(p[i].y<p[i+1].y){PQ.yb=p[i].y;PQ.yt=p[i+1].y;PQ.x=double(p[i+1].x);PQ.dx=double(p[i+1].x-p[i].x)/double(p[i].y-p[i+1].y);//±íʾ¸÷±ß;Put_Edge_In_To_ET(PQ); //½«¸÷±ß²åÈëET±í;}//Çò½»µã/* if(polygont[i].y==polygont[i+1].y){pDC->MoveTo(polygont[i]);pDC->LineTo(polygont[i+1]);}*/}for(i=0;i<1000;i++){if(edge[i]->next!=NULL)ymax=edge[i]->data;} //È¡ETÖÐ×î´óÐòÌ–;qltype *u;u=(qltype *)malloc(sizeof qltype);head=(qltype *)malloc(sizeof qltype);Set_AEL_Empty();//½¨Á¢Á´±í£»int y=ymax;while(edge[y]->next!=NULL||head->next!=NULL){for(u=edge[y]->next;u!=NULL;u=u->next)//ÔÚ ET±íÖÐÈ¡±ßInsert_an_Edge(head,u->Data);//ÔÚAELÖвå±ßelemtype ee[100];i=0;for(u=head->next;u!=NULL;u=u->next){ ee[i]=u->Data;i++;}Sort_AEL(ee,i);for(int j=0;j<i;j=j+2){SelectObject((HDC)*pDC,redPen);pDC->MoveTo(int(ee[j].x),y);pDC->LineTo(int(ee[j+1].x),y);// SelectObject((HDC)*pDC,blackPen);}SelectObject((HDC)*pDC,blackPen);y--;Delete_an_Edge(head,y);u=head->next;while(u!=NULL){u->Data.x=(u->Data.x+u->Data.dx);u=u->next;}//±ßµÄ¸üС£}}void CScanLineView::OnLButtonDown(UINT nFlags, CPoint point){CDC *pDC;pDC=GetDC();CPen redPen(PS_SOLID,1,RGB(250,0,0)),greenPen(PS_SOLID,5,RGB(20,250,100)), bluePen(PS_SOLID,5,RGB(0,0,700)),*oldPen,blackPen(PS_SOLID,1,RGB(0,0,0));oldPen=(CPen*)SelectObject((HDC)*pDC,blackPen);if(StartFlag){p[num]=point;// SelectObject((HDC)*pDC,bluePen);// pDC->MoveTo(point);// pDC->LineTo(point);pDC->SetPixel(point,RGB(0,0,0));num++;pDC->MoveTo(p[0]);for(int i=1;i<num;i++)pDC->LineTo(p[i]);p[num]=p[0];// SelectObject((HDC)*pDC,blackPen);/*if(num>2)if(p[num-1]==p[num-2]){p[num-1]=p[0];for(int i=0;i<num-1;i++){// SelectObject((HDC)*pDC,redPen);pDC->MoveTo(p[i].x,p[i].y);pDC->LineTo(p[i+1].x,p[i+1].y);}// SelectObject((HDC)*pDC,blackPen);num--;}*/}if(OriFlag){po=point;}// TODO: Add your message handler code here and/or call defaultCView::OnLButtonDown(nFlags, point);}void CScanLineView::SetET(){for(int i=0;i<1000;i++){edge[i]=new table;edge[i]->next=(qltype *)malloc(sizeof qltype);edge[i]->data=i;edge[i]->next=NULL;}}void CScanLineView::Put_Edge_In_To_ET(elemtype PQ){for(int i=0;i<1000;i++)if(PQ.yt==i){qltype *s;s=(qltype *)malloc(sizeof qltype);if(edge[i]->next!=NULL){s->Data=PQ;s->next=edge[i]->next;edge[i]->next=s;}else{s->Data=PQ;s->next=NULL;edge[i]->next=s;}}}void CScanLineView::Set_AEL_Empty(){head=(qltype *)malloc(sizeof qltype);head->next=NULL;}void CScanLineView::Insert_an_Edge(qltype *h, elemtype PQ) {// Set_AEL_Empty(h);qltype *s;s=(qltype *)malloc(sizeof qltype);s->Data=PQ;s->next=h->next;h->next=s;}void CScanLineView::Sort_AEL(elemtype x[], int n){int k,m;elemtype s;for(k=0;k<n-1;k++){s=x[k+1];m=k;while(m>-1&&(s.x<x[m].x||(s.x==x[m].x&&s.dx<x[m].dx))) {x[m+1]=x[m];m--;}x[m+1]=s;}}void CScanLineView::Delete_an_Edge(qltype *h,int ymin){qltype *s,*v,*w;s=(qltype *)malloc(sizeof qltype);v=(qltype *)malloc(sizeof qltype);w=(qltype *)malloc(sizeof qltype);w=h;while(w!=NULL){v=h;while(v->next!=NULL&&v->next->Data.yb!=ymin)v=v->next;if(v->next==NULL);else{s=v->next;v->next=v->next->next;free(s);}if(v->next==NULL)w=NULL;elsew=v->next;}}void CScanLineView::OnInputpoints(){StartFlag=1;// TODO: Add your command handler code here}void CScanLineView::OnEndinputpoints(){CDC *pDC;pDC=GetDC();EndFlag=1;StartFlag=0;pDC->MoveTo(p[num-1]);pDC->LineTo(p[0]);// TODO: Add your command handler code here}3、实验结果:。

扫描线区域填充算法

扫描线区域填充算法

扫描线区域填充算法算法步骤如下:1.首先需要定义一个边表(ET)和活动边表(AET)。

-边表是存储多边形所有边的一张表格,将每条边的y坐标、x坐标以及斜率存储起来。

-活动边表是指当前扫描线与多边形边的交点,它是一个按照x坐标排列的列表。

2.初始化边表(ET)和活动边表(AET)。

-通过遍历多边形的边,将边表中存储对应的边信息。

-初始时活动边表为空。

3.根据多边形的顶点,将边添加到边表中。

-对于每条边,计算出其斜率,以及y坐标的最小值和最大值。

4.进行扫描线填充:a.设置当前扫描线的y坐标,从最小的y值开始,逐行向下扫描。

b.遍历边表,将与当前扫描线相交的边添加到活动边表中。

c.按照x坐标对活动边表进行排序。

d.遍历活动边表,两两配对,填充对应的像素点。

e.过滤掉扫描线下方的边。

f.更新活动边表,将不再与当前扫描线相交的边从活动边表中删除。

5.重复步骤4,直到扫描完整个区域。

然而,扫描线区域填充算法也存在一些局限性和问题:-对于包含小尺寸多边形的大区域填充,算法的迭代次数会增加,导致填充时间变长。

-在多边形内有重叠区域时,算法无法区分内外部,可能导致填充错误。

-当多边形内有孔洞时,算法无法正确填充孔洞。

为了解决以上问题,可以采用一些优化策略来改进算法性能和填充效果。

例如,可以使用边表索引和活动边表的双向链表结构,减少查找和插入操作的时间开销。

此外,还可以使用扫描线的上下两根线段来同时填充两个相邻区域,提高填充速度。

总结起来,扫描线区域填充算法是一种常用且有效的图形填充算法,通过逐行扫描和交点判断来填充封闭区域。

它可以广泛应用于计算机图形学领域,如图形渲染、图像处理等。

算法在实际应用中需根据具体场景进行优化和改进,以满足不同需求。

实验报告(扫描线算法)

实验报告(扫描线算法)

实验报告(扫描线算法)一、实验目的学习扫描线算法,掌握种子区域填充的程序设计方法。

二、实验原理A.多边形扫描转换根据区域的连贯性、扫描线的连贯性、边的连贯性以及奇点的处理方法,做出基于扫描线算法数据结构(ET&AEL )的扫描线算法程序。

其算法的实现步骤如下:1) (扫描线初始化)取扫描线纵坐标y 的初始值为ET 中非空元素的最小序号。

(对给定的多边形,y=constant );2) (AEL 初始化)将边的活化链表AEL 置为空表;3) 按从下到上的顺序对纵坐标值为y 的扫描线执行以下操作,指导边的分类表ET 和边的活化链表AEL 为空表为止;①若ET 中第y 类元素非空,则将属于该类的所有边从ET 中取出并插入到AEL 中。

AEL 中的各边按照x 的值(当x 值相等时,按x ∆值)递增的顺序排序。

②若相对当前扫描线,AEL 非空,则将AEL 中的边两两配对。

即第1、2边为一对,第3、4边为一对,以此类推。

每一对边与当前扫描线的交点所构成的区段位于多边形内。

依次对这些区段上的像素点按多边形颜色着色。

③将AEL 中满足条件y y =max 的边删去。

④将AEL 中剩余的每一条边的x 域累加x ∆,即x x x ∆+=。

⑤将当前扫描线的纵坐标值y 累加1,即y y y ∆+=。

B.多边形的填充(边界标志算法)先用一种特殊颜色在帧缓冲器中将多边形的边界(水平边界除外)勾画出来,然后再用类似扫描线算法的方法对于多边形内的各区段着上所需颜色。

三、实验程序//A.多边形扫描转换#include<stdio.h>#include<graphics.h>#include<easyx.h>#include<conio.h>#define YMAX 480 /*宏定义*/typedef struct tEdge //定义结构体{int yUpper,yLower;float xIntersect,dxPerScan;struct tEdge *next;struct tEdge *active;}Edge;void insertEdge (Edge *list,Edge *edge) //将结点插入边表{Edge *p,*q=list;p=q->next;while (p!=NULL){if(edge->xIntersect<p->xIntersect)p=NULL;else{q=p;p=p->next;}}edge->next=q->next;q->next=edge;}int yNext (int k,int count,int *y) //求奇异点{int j;if((k+1)>(count-1))j=0;elsej=k+1;while(y[k]==y[j])if((j+1)>(count-1))j=0;elsej++;return(y[j]);}void makeEdgeRecord(int xLower,int yLower,int xUpper,int yUpper,int yComp,Edge *edge,Edge *edges[])//生成边表结点,并插入到边表中{edge->dxPerScan=(float)(xUpper-xLower)/(yUpper-yLower);edge->yUpper=yUpper;if(yLower>yComp){edge->yLower=yLower+1;edge->xIntersect=xLower+edge->dxPerScan;}elseedge->xIntersect=(float)xLower;insertEdge(edges[yLower],edge);}void buildEdgeList(int count,int *x,int *y,Edge *edges[]) //创建边表的主体函数{Edge *edge;int x1,y1,x2,y2;int i,yPrev=y[count-2];x1=x[count-1];y1=y[count-1];for(i=0;i<count;i++){x2=x[i];y2=y[i];if(y1!=y2){edge=(Edge *)malloc(sizeof(Edge));if(y1<y2)makeEdgeRecord(x2,y2,x1,y1,yNext(i,count,y),edge,edges);elsemakeEdgeRecord(x1,y1,x2,y2,yPrev,edge,edges);}yPrev=y1;x1=x2;y1=y2;}}void buildActiveList(int scan,Edge *active,Edge *edges[]) //建立活动边表的主体函数{Edge *p,*q;p=edges[scan]->next;while(p){q=p->next;insertEdge(active,p);p=q;}}void fillScan(int scan,Edge *active) //填充当前扫描线{Edge *p1,*p2;int i,PolygonColor=25;p1=active->next;while(p1);{p2=p1->next;for(i=(int)p1->xIntersect;i<p2->xIntersect;i++)putpixel(i,scan,PolygonColor);p1=p2->next;}}void deleteAfter(Edge *q) //删除已经处理过的边{Edge *p=q->next;q->next=p->next;free(p);}void updateActiveList(int scan,Edge *active) //更新活化链表{Edge *q=active,*p=active->next;while (p)if(scan>=p->yUpper){p=p->next;deleteAfter(q);}else{p->xIntersect=p->xIntersect+p->dxPerScan;q=p;p=p->next;}}void resortActiveList(Edge *active) //活化链表排序{Edge *q, *p=active->next;while(p){q=p->next;insertEdge(active,p);p=q;}}void scanFillPolygon(int count,int *x,int *y) //多边形扫描转换{Edge *edges[YMAX],*active;int i,scan;//扫描线的边界for(i=0;i<YMAX;i++){edges[i]=(Edge *)malloc(sizeof(Edge));//申请空间edges[i]->next=NULL;}buildEdgeList( count, x, y, edges);active=(Edge *)malloc(sizeof(Edge));active->next=NULL;//初始化边表for(scan=0;scan<YMAX;scan++){buildActiveList(scan,active,edges);if (active->next){fillScan(scan,active);updateActiveList(scan,active);resortActiveList(active);}}free(active);}void main(){int gdriver = DETECT,gmode;initgraph(700,600); //初始化窗口大小int x[]={240,390,540,232};int y[]={150,230,430,370};int count=4;line(240,150,390,230);line(390,230,540,430);line(540,430,232,370);line(232,370,240,150);scanFillPolygon(count,x,y);getch();//暂停程序closegraph();//退出图形库}//B.多边形的填充(失败)四、测试结果A.多边形扫描转换B.多边形的填充(失败)。

扫描线填充算法

扫描线填充算法

扫描线填充算法任意封闭多边形的扫描线填充算法类收藏这个代码不是我写的,但是我肯定这代码是一个牛人写的,放在这里供大家学习和使用啦!感谢原作者!我在这里做了些改进: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++)< bdsfid="105" p=""></count;i++)<> {if(minX>Point[i].x) minX=Point[i].x;if(minY>Point[i].y) minY=Point[i].y;if(maxXif(maxY}CUIntArray xArray;int y;for(y=minY;y<maxy;y++)< bdsfid="117" p=""></maxy;y++)<>{//扫描线从minY开始到maxYfor(i=0;i<count;i++)< bdsfid="121" p=""></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;iSort(scanLineX,num);//对scanLine(扫描线x坐标进行排序)for(i=0;i< bdsfid="157" p=""><>{if(i+1>=num) break;pDC->MoveTo(scanLineX[i],y);pDC->LineT o(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++)< bdsfid="237" p=""></ilength;i++)<> {bFlag=true;for(j=0;j<ilength-i-1;j++)< bdsfid="241" p=""></ilength-i-1;j++)<>{if(iArray[j] > iArray[j+1]){iTemp=iArray[j];iArray[j]=iArray[j+1];iArray[j+1]=iT emp;bFlag=false;}}if(bFlag) break;}return true;}//析构函数,删除动态生成的Point指针CPFill::~CPFill(){if(Point) delete [] Point;}下面说说怎么为我所用这个类。

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

实验六扫描线填充算法一、实验目的编写多边形的扫描线填充算法程序,加深对扫描线算法的理解,验证算法的正确性。

二、实验任务(2学时)编写多边形的扫描线填充算法程序,利用数组实现AET,考虑与链表实现程序的不同。

三、实验内容1、算法对一条扫描线的填充一般分为以下4个步骤:(1)求交:计算扫描线与多边形各边的交点;(2)排序:把扫描线上所有交点按递增顺序进行排序;(3)配对:将第一个交点与第二个交点,第三个交点与第四个交点等等进行配对,每对交点代表扫描线与多边形的一个相交区间。

(4)着色:把区间内的像素置为填充色。

2、成员函数的关系主程序名为fill_area(count, x, y),其中参数x, y是两个一维数组,存放多边形顶点(共c ount个)的x和y坐标。

它调用8个子程序,彼此之间的调用关系图1所示为:图1 fill_area的程序结构3、算法的程序设计步骤1:创建“S_L_Fill”工程文件;步骤2:创建类class:“EACH_ENTRY”。

在工作区“S_L_Fill classes”单击右键-→“new class”-→选择类型“Generic Class”名称为“EACH_ENTRY”,添加成员变量(添加至“class EACH_ENTRY { public:”之内):int y_top;float x_int;int delta_y;float x_change_per_scan;步骤3:包含头文件,同时初始化定义多边形顶点数目。

在“class CS_L_FillView : public Cview……”之前添加代码“#include EACH_ENTRY.h”及“#define MAX_POINT 9”。

#define MAX_POINT 9#include "EACH_ENTRY.h"步骤4:在类“class CS_L_FillView”中添加成员变量(鼠标双击工作区“CS_L_FillView”,代码添加至“class CS_L_FillView : public Cview {protected: ……public:之后”):EACH_ENTRY sides[MAX_POINT];int x[MAX_POINT],y[MAX_POINT];int side_count,first_s,last_s,scan,bottomscan,x_int_count;步骤5:利用构造函数“CS_L_FillView::CS_L_FillView()”初始化顶点坐标(鼠标双击工作区“CS_L_FillView”,代码添加至“CS_L_FillView()之内”):x[0]=200;y[0]=100;x[1]=240;y[1]=160;x[2]=220;y[2]=340;x[3]=330;y[3]=100;x[4]=400;y[4]=180;x[5]=300;y[5]=400;x[6]=170;y[6]=380;x[7]=120;y[7]=440;x[8]=100;y[8]=220;步骤6:在“class CS_L_FillView”下添加实现不同功能的成员函数。

在工作区“CS_L_FillView”上单击鼠标右键,选择“Add Member Function”,分别完成以下成员函数的添加:(1)void put_in_sides_list(int entry,int x1,int y1,int x2,int y2,int next_y)函数说明:put_in_sides_list子程序的主要功能是将一条边存入活性边表之内。

操作步骤是:对该边判别是否左顶点或右顶点,如果将入边之终点删去,按照y_top的大小在活性边表中找到该点的合适位置,y值较大者,排在活性边表的靠前位置。

void put_in_sides_list(int entry,int x1,int y1,int x2,int y2,int next_y)// entry为剔除水平边之后的第entry条边,x1, y1,为起点,x2, y2为终点,next_y为终点相邻的下一个顶点y坐标{int maxy;float x2_temp,x_change_temp;x_change_temp=(float)(x2-x1)/(float)(y2-y1);//计算1/kx2_temp=float(x2);if((y2>y1)&&(y2<next_y))//x2,y2是左顶点,则(x2-1/m,y2-1)终点下缩{y2--;x2_temp-=x_change_temp;}else{if((y2<y1)&&(y2>next_y)) //x2,y2是右顶点,则(x2+1/m,y2+1)终点上缩{y2++;x2_temp+=x_change_temp;}}maxy=(y1>y2)?y1:y2;while((entry>1)&&(maxy>sides[entry-2].y_top)){sides[entry-1]=sides[entry-2];entry--;}// sides[]为边数组,边的y_top值越小,在数组中越靠后sides[entry-1].y_top=maxy;sides[entry-1].delta_y=abs(y2-y1)+1;if(y1>y2)// x2,y2为右顶点,扫描线与起点先求交sides[entry-1].x_int=float(x1);else// x2,y2左顶点,扫描线与终点先求交sides[entry-1].x_int=x2_temp;sides[entry-1].x_change_per_scan=x_change_temp;}(2)void sort_on_bigger_y(int n,CDC* pDC)函数说明:sort_on_bigger_y子程序的主要功能是按照输入的多边形,建立起活性边表。

操作步骤是:对每条边加以判断:如非水平边则调用put_in_side_list子程序放入活性边来;如是水平边则直接画出。

void sort_on_bigger_y(int n,CDC* pDC)//按照输入的多边形建立活性链表{int k,x1,y1;side_count=0;//全局变量,记录所有非水平边数目y1=y[n-1];x1=x[n-1];//(Pn-1,P0)为第一条边,开始建表bottomscan=y[n-1];for(k=0;k<n;k++)//sides数组存放所有非水平边,并且按照y值的由大到小顺序{if(y1!=y[k]&&(k+1)<=(n-1)){side_count++;put_in_sides_list(side_count,x1,y1,x[k],y[k],y[k+1]);}if(y1!=y[k]&&(k+1)==n)//当前边非水平边{side_count++;put_in_sides_list(side_count,x1,y1,x[k],y[k],y[0]);}if(y1==y[k])//如果平行就直接画出直线{side_count++;CPen myPen(PS_DASH,2,RGB(255,0,0));pDC->SelectObject(&myPen);pDC->MoveTo((short)x1,(short)y1);pDC->LineTo((short)x[k],(short)y[k]);}if(y[k]<bottomscan)// bottomscan为全局变量,为所有顶点的最小y值bottomscan=y[k];y1=y[k];x1=x[k];} //end for}(3)void update_first_and_last(int count,int scan)函数说明:update_first_and_last子程序的主要功能是刷新活性边表的first和last两根指针的所指位置,以保证指针指出激活边的范围。

void update_first_and_last(int count,int scan){//下一条边的y_top>当前扫描线scan,last_s下移while((sides[last_s+1].y_top>=scan)&&((last_s+1)<count))last_s++;while(sides[first_s].delta_y==0)first_s++;}(4)void swap(EACH_ENTRY *a,EACH_ENTRY *b)函数说明:swap子程序的主要功能是交换活性边表中两条相邻位置边的彼此位置。

void swap(EACH_ENTRY *a,EACH_ENTRY *b)//为使交换成功,需用引用或者指针。

{int i_temp;float f_temp;i_temp=a->y_top;a->y_top=b->y_top;b->y_top=i_temp;//y_top交换f_temp=a->x_int;a->x_int=b->x_int;b->x_int=f_temp;//x_int交换i_temp=a->delta_y;a->delta_y=b->delta_y;b->delta_y=i_temp;//delta_y交换f_temp=a->x_change_per_scan;a->x_change_per_scan=b->x_change_per_scan;b->x_change_per_scan=f_temp;//x_change_per_scan交换}(5)void sort_on_x(int entry,int first_s)函数说明:sort_on_x子程序主要功能是将一条边sides[retry]在活性表边的first到entry 之间按照x_int大小,递增排序。

操作步骤是:检查位于entry的边的x_int是否小于位置entry-1的边的x_int,如是,调用swap子程序交换两条边的彼此位置。

void sort_on_x(int entry,int first_s){int ent1=entry;int ent2=entry;while(1){ent1--;if((sides[ent2].x_int<sides[ent1].x_int)&&(sides[ent1].delta_y!=0))//如果两个的x_int需要交换则交换之{swap(&sides[ent2],&sides[ent1]);ent2=ent1;}if(ent1==first_s-1)break;}}(6)void process_x_intersections(int scan,int first_s,int last_s)函数说明:process_x_intersections子程序的主要功能是对活性边表中的激活边(即位于first和last之间的,并且delta_y> 0的边)按照x_int的大小排序。

相关文档
最新文档