扫描线填充算法
扫描线填充算法讲解
扫描线算法(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 扫描线种子填充算法
程序代码
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:多边形区域扫描线填充或种子填充计科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)”。
通用扫描线填充算法存在的问题及其解决方法
通 用扫描 线 填充 算法 亦 称 扫 描线 多 边 形 填 充 算 法 、 扫 描 或
线算法 , 是多边形 扫描转换填充算法中重要 的一种。在 国内外 多种计算机图形学文献 ( 尤其是教材) 对此算法 的介绍尽管 中, 有所差异, 但基本原理都是一致的 ¨ “ 。但是, 这个原理却存
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区域填充算法可以通过以下步骤来实现:
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]大,但是仍然得到了正确的填充。
扫描线填充算法讲解
扫描线算法(Scan-Line F illing)扫描线算法适合对矢量图形进行区域填充,只需要直到多边形区域的几何位置,不需要指定种子点,适合计算机自动进行图形处理的场合使用,比如电脑游戏和三维CAD软件的渲染等等。
对矢量多边形区域填充,算法核心还是求交。
《计算几何与图形学有关的几种常用算法》一文给出了判断点与多边形关系的算法――扫描交点的奇偶数判断算法,利用此算法可以判断一个点是否在多边形内,也就是是否需要填充,但是实际工程中使用的填充算法都是只使用求交的思想,并不直接使用这种求交算法。
究其原因,除了算法效率问题之外,还存在一个光栅图形设备和矢量之间的转换问题。
比如某个点位于非常靠近边界的临界位置,用矢量算法判断这个点应该是在多边形内,但是光栅化后,这个点在光栅图形设备上看就有可能是在多边形外边(矢量点没有大小概念,光栅图形设备的点有大小概念),因此,适用于矢量图形的填充算法必须适应光栅图形设备。
扫描线算法的基本思想扫描线填充算法的基本思想是:用水平扫描线从上到下(或从下到上)扫描由多条首尾相连的线段构成的多边形,每根扫描线与多边形的某些边产生一系列交点。
将这些交点按照x坐标排序,将排序后的点两两成对,作为线段的两个端点,以所填的颜色画水平直线。
多边形被扫描完毕后,颜色填充也就完成了。
扫描线填充算法也可以归纳为以下4个步骤:(1)求交,计算扫描线与多边形的交点(2)交点排序,对第2步得到的交点按照x值从小到大进行排序;(3)颜色填充,对排序后的交点两两组成一个水平线段,以画线段的方式进行颜色填充;(4)是否完成多边形扫描?如果是就结束算法,如果不是就改变扫描线,然后转第1步继续处理;整个算法的关键是第1步,需要用尽量少的计算量求出交点,还要考虑交点是线段端点的特殊情况,最后,交点的步进计算最好是整数,便于光栅设备输出显示。
对于每一条扫描线,如果每次都按照正常的线段求交算法进行计算,则计算量大,而且效率底下,如图(6)所示:图(6)多边形与扫描线示意图观察多边形与扫描线的交点情况,可以得到以下两个特点:(1)每次只有相关的几条边可能与扫描线有交点,不必对所有的边进行求交计算;(2)相邻的扫描线与同一直线段的交点存在步进关系,这个关系与直线段所在直线的斜率有关;第一个特点是显而易见的,为了减少计算量,扫描线算法需要维护一张由“活动边”组成的表,称为“活动边表(AET)”。
计算机图形学5多边形扫描转换和区域填充
多边形分为凸多边形、凹多边形、含内环的多边 形等:
(1)凸多边形 任意两顶点间的连线均在多边形内。
(2)凹多边形
任意两顶点间的连线有不在多边形内的部分。
凸多边形
凹多边形
含内环的多边形
有关概念
1) 区域:一组相邻而且又相连的像素,而且具有 相同属性的封闭区域。 2)种类:①单域 ②复合域
3) 区域填充:以某种属性对整个区域进行设置的过 程。
另外使用增量法计算时,我们需要知道一条边何时不再与下 一条扫描线相交,以便及时把它从有效边表中删除出去,避免 下一步进行无谓的计算。 综上所述,有效边表AET的每个结点存放对应边的有关信息 如下:
x
△x
ymax
next
其中x为当前扫描线与边的交点,ymax是边所在的最大扫描 线值,通过它可以知道何时才能“抛弃”该边,△x表示从 当前扫描线到下一条扫描线之间的x增量即斜率的倒数。 next为指向下一条边的指针
P6(2,7)
P4(11,8) F G B P5(5,5) P3(11,3) C D
A
1
0 1
P1(2,2) P2(5,1) 2 3 4 5 6 7
E
8
9
10
11
一个多边形与若干扫描线
7
把多边形所有 的边全部填成这 样的结构,插到 这个指针数组里 面来。
计算机图形学——区域填充算法(基本光栅图形算法)
计算机图形学——区域填充算法(基本光栅图形算法)⼀、区域填充概念区域:指已经表⽰成点阵形式的填充图形,是象素的集合。
区域填充:将区域内的⼀点(常称【种⼦点】)赋予给定颜⾊,然后将这种颜⾊扩展到整个区域内的过程。
区域填充算法要求区域是连通的,因为只有在连通区域中,才可能将种⼦点的颜⾊扩展到区域内的其它点。
1、区域有两种表⽰形式1)内点表⽰:枚举出区域内部的所有象素,内部所有象素着同⼀个颜⾊,边界像素着与内部象素不同的颜⾊。
2)边界表⽰:枚举出区域外部的所有象素,边界上的所有象素着同⼀个颜⾊,内部像素着与边界象素不同的颜⾊。
21)四向连通区域:从区域上⼀点出发可通过【上、下、左、右】四个⽅向移动的组合,在不越出区域的前提下,到达区域内的任意象素。
2)⼋向连通区域:从区域上⼀点出发可通过【上、下、左、右、左上、右上、左下、右下】⼋个⽅向移动的组合,在不越出区域的前提下,到达区域内的任意象素。
⼆、简单种⼦填充算法给定区域G⼀种⼦点(x, y),⾸先判断该点是否是区域内的⼀点,如果是,则将该点填充为新的颜⾊,然后将该点周围的四个点(四连通)或⼋个点(⼋连通)作为新的种⼦点进⾏同样的处理,通过这种扩散完成对整个区域的填充。
这⾥给出⼀个四连通的种⼦填充算法(区域填充递归算法),使⽤【栈结构】来实现原理算法原理如下:种⼦像素⼊栈,当【栈⾮空】时重复如下三步:这⾥给出⼋连通的种⼦填充算法的代码:void flood_fill_8(int[] pixels, int x, int y, int old_color, int new_color){if(x<w&&x>0&&y<h&&y>0){if (pixels[y*w+x]==old_color){pixels[y*w+x]== new_color);flood_fill_8(pixels, x,y+1,old_color,new_color);flood_fill_8(pixels, x,y-1,old_color,new_color);flood_fill_8(pixels, x-1,y,old_color,new_color);flood_fill_8(pixels, x+1,y,old_color,new_color);flood_fill_8(pixels, x+1,y+1,old_color,new_color);flood_fill_8(pixels, x+1,y-1,old_color,new_color);flood_fill_8(pixels, x-1,y+1,old_color,new_color);flood_fill_8(pixels, x-1,y-1,old_color,new_color);}}}简单种⼦填充算法的不⾜a)有些像素会多次⼊栈,降低算法效率,栈结构占空间b)递归执⾏,算法简单,但效率不⾼,区域内每⼀像素都要进/出栈,费时费内存c)改进算法,减少递归次数,提⾼效率三、扫描线种⼦填充算法基本思想从给定的种⼦点开始,填充当前扫描线上种⼦点所在的⼀区段,然后确定与这⼀段相邻的上下两条扫描线上位于区域内的区段(需要填充的区间),从这些区间上各取⼀个种⼦点依次把它们存起来,作为下次填充的种⼦点。
计算机图形学-区域填充的扫描线算法
计算机图形学——区域填充的扫描线算法一.实验名称:区域填充的扫描线算法二.实验目的: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.此扫描线填充算法能够对多种图案进行填充,展现了算法的实用性。
实验六 扫描线填充算法
实验六扫描线填充算法一、实验目的编写多边形的扫描线填充算法程序,加深对扫描线算法的理解,验证算法的正确性。
二、实验任务(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”下添加实现不同功能的成员函数。
压入新、旧区段的区域填充扫描线算法
压入新 、 旧区段的区域填充扫描线算法
降爱莲 谢 克 明
( 太原理 工 大 学计 算机 与软件 学院 , 太原 0 0 2 ) 3 0 4
E m i alni g 8@sh . r — a :iaj n 6 5 oucn l i a o
摘
要
指 出压入 区段 端 点 的 区域 填 充扫 描 线 算 法对 一 类 特 殊 4 连 通 区域 有 可 能 产 生 漏 填 . 用像 素 间 的相 关性 和 区 一 利
K y o d :a afl g 4 ajcn o nc o ,- i co —x a dn to , a , ak e w rs r ln ,- daetcn et n 4 dr t n ep n igme d s n s c e ii i ei h p t
区 域填 充是 指 用 某 种 颜 色或 图案 来 填 充 一个 有 界 区域 。 种
域 在 扫 描 线上 的连 贯 性 提 出 了采 用“ 写 区段 左 端 点 ” 4向 填 充 方法 进 行 改 进 : 过 分 析 原 算 法 中 仍 然 存 在 的 像 素 点 重 的 通 颜 色判 读 的 重 复操 作 , 出 了压 入 新 、 区段 的 区域 填 充扫 描 线 算 法 并 给 出算 法 的描 述 ; 型 的填 充 测 试 证 明 了本 算 法 提 旧 典
l 算法 l中的漏填 现 象
压 人 区 段 端 点 的 区 域 填 充 扫 描 线 算 法 ( 法 1 可 描 述 为 算 ) 如 下 几个 步骤 : 步骤 1( 找种子点所 在 区段 ) 给定 的种子点 ( , 出 寻 从 , ) ,
子 填 充算 法 要 求 区域 是 连通 的 只有 在 连 通 的 区 域 中 , 有 可 才
扫描线填充种子填充算法(原创完整版)
扫描线填充种子填充算法(原创完整版)// 计算机图形学View.cpp : implementation of the CMyView class//// 种子填充和扫描线填充算法// Author:: codeants_for_sdau2012// Date::2014/10/24#include "stdafx.h"#include "计算机图形学.h"#include "计算机图形学Doc.h"#include "计算机图形学View.h"#include "DDA.h"#include "afxtempl.h"#include#include#include#include "debug1.h"#include "xyz_dialog.h"#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endifusing namespace std;/////////////////////////////////////////////////////////////////// //////////// CMyViewIMPLEMENT_DYNCREATE(CMyView, CView)BEGIN_MESSAGE_MAP(CMyView, CView)//{{AFX_MSG_MAP(CMyView)ON_COMMAND(ID_MENUITEM32771, OnDDA)ON_COMMAND(ID_MENUITEM32772, OnBrem)ON_COMMAND(ID_MENUITEM32773, OnSqureBrush)ON_COMMAND(ID_MENUITEM32774, Onseek1)ON_COMMAND(ID_MENUITEM32775, OnSeekin8)ON_COMMAND(ID_MENUITEM32776, OnAETLine)//}}AFX_MSG_MAP// Standard printing commandsON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW,CView::OnFilePrintPreview) END_MESSAGE_MAP()/////////////////////////////////////////////////////////////////// //////////// CMyView construction/destructionCMyView::CMyView(){// TODO: add construction code here}CMyView::~CMyView(){}BOOL CMyView::PreCreateWindow(CREATESTRUCT& cs){// TODO: Modify the Window class or styles here by modifying// the CREATESTRUCT csreturn CView::PreCreateWindow(cs);}/////////////////////////////////////////////////////////////////// //////////// CMyView drawingvoid CMyView::OnDraw(CDC* pDC){CMyDoc* pDoc = GetDocument();ASSERT_V ALID(pDoc);// TODO: add draw code for native data here}/////////////////////////////////////////////////////////////////// //////////// CMyView printingBOOL CMyView::OnPreparePrinting(CPrintInfo* pInfo){// default preparationreturn DoPreparePrinting(pInfo);}void CMyView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) {// TODO: add extra initialization before printing}void CMyView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) {// TODO: add cleanup after printing}/////////////////////////////////////////////////////////////////// //////////// CMyView diagnostics#ifdef _DEBUGvoid CMyView::AssertValid() constCView::AssertValid();}void CMyView::Dump(CDumpContext& dc) const{CView::Dump(dc);}CMyDoc* CMyView::GetDocument() // non-debug version is inline{ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMyDo c)));return (CMyDoc*)m_pDocument;}#endif //_DEBUG/////////////////////////////////////////////////////////////////// //////////// CMyView message handlersvoid swap(int& xt,int& yt){int tmp=xt;xt=yt;yt=tmp;}void DDAxt(int x0,int y0,int x1,int y1,int color,CDC* pDC){int x;float dx,dy,k;dx=x1-x0;dy=y1-y0;if(dx==0)if(y1<y0)< bdsfid="172" p=""></y0)<>swap(y1,y0);for(int y=y0;y<y1;y++)< bdsfid="175" p=""></y1;y++)<> {pDC->SetPixel(x1,y,color);}return;}k=dy/dx; double y=y0;if(x0>x1){swap(x0,x1);swap(y0,y1);}for(x=x0;x<=x1;x++){pDC->SetPixel(x,int (y+0.5), color);y=y+k;}}int x0,y0,x1,y1;void CMyView::OnDDA(){DDA s1;//debug1 d1;s1.DoModal();//Invalidate();//UpdateData(true);x0=s1.m_x0;y0=s1.m_y0;x1=s1.m_x1;y1=s1.m_y1;//UpdateData(false);//s1.EndDialog(3);CDC* pDC;pDC=GetDC();CMyDoc* pDoc = GetDocument();ASSERT_V ALID(pDoc);DDAxt(x0,y0,x1,y1,RGB(255,0,255),pDC);}void Brem(int x0,int y0,int x1,int y1,int color,CDC* pDC) { double xt,yt,k,b;double dx,dy,d0,dt1,dt2;if(x0>x1){swap(x0,x1);swap(y0,y1);}dx=x1-x0,dy=y1-y0;if(x1==x0){if(y1<y0)< bdsfid="225" p=""></y0)<>swap(y1,y0);for(int y=y0;y<y1;y++)< bdsfid="228" p=""></y1;y++)<> {pDC->SetPixel(x1,y,color);}return;}k=(y1-y0)/(x1-x0);b=(x1*y0-x0*y1)/(x1-x0);if(k<=1&&k>=0){d0=dx-2*dy;dt1=2*dx-2*dy;dt2=-2*dy;int y=y0;for(int x=x0;x<=x1;x++){if(d0<0){y++;d0+=dt1;}else d0+=dt2;pDC->SetPixel(x,y,color);}}else if(k>1){if(y1<y0)< bdsfid="256" p=""></y0)<> {swap(x1,x0);swap(y1,y0);dx=-dx;dy=-dy;}d0=dx-2*dy;dt1=2*dy-2*dx;dt2=-2*dx;int x=x0;for(int y=y0;y<=y1;y++){if(d0<0){x++;d0+=dt1;}else d0+=dt2;pDC->SetPixel(x,y,color);}}else if(k<0&&k>=-1){if(x1<x0)< bdsfid="281" p=""></x0)<> {swap(x1,x0);swap(y1,y0);dx=-dx;dy=-dy;}d0=-dx;dt1=-2*dx-2*dy;dt2=-2*dy;int y=y0;for(int x=x0;x<=x1;x++){if(d0>=0){y--;d0+=dt1;}else d0+=dt2;pDC->SetPixel(x,y,color);}}else if(k<-1){if(y1<y0)< bdsfid="306" p=""></y0)<> {swap(x1,x0);swap(y1,y0);dx=-dx;dy=-dy;}d0=-dy;dt1=-2*dy-2*dx;dt2=-2*dx;int x=x0;for(int y=y0;y<=y1;x++){if(d0>=0){x--;d0+=dt1;}else d0+=dt2;pDC->SetPixel(x,y,color);}}}void CMyView::OnBrem(){DDA s1;//debug1 d1;s1.DoModal();//Invalidate();//UpdateData(true);x0=s1.m_x0;y0=s1.m_y0;x1=s1.m_x1;y1=s1.m_y1;//UpdateData(false);//s1.EndDialog(3);CDC* pDC;pDC=GetDC();CMyDoc* pDoc = GetDocument();ASSERT_V ALID(pDoc);Brem(x0,y0,x1,y1,RGB(255,0,255),pDC);}void squr(int x0,int y0,int x1,int y1,int color,CDC* pDC) { pDC->SetPixel(x0,y0,color);pDC->SetPixel(x0+1,y0,color);pDC->SetPixel(x0+1,y0+1,color);pDC->SetPixel(x0+1,y0-1,color);pDC->SetPixel(x0,y0+1,color);pDC->SetPixel(x0,y0-1,color);pDC->SetPixel(x0-1,y0,color);pDC->SetPixel(x0-1,y0+1,color);pDC->SetPixel(x0-1,y0-1,color);pDC->SetPixel(x1,y1,color);pDC->SetPixel(x1+1,y1,color);pDC->SetPixel(x1+1,y1+1,color);pDC->SetPixel(x1+1,y1-1,color);pDC->SetPixel(x1,y1+1,color);pDC->SetPixel(x1,y1-1,color);pDC->SetPixel(x1-1,y1,color);pDC->SetPixel(x1-1,y1+1,color);pDC->SetPixel(x1-1,y1-1,color);}void CMyView::OnSqureBrush(){DDA s1;s1.DoModal();//Invalidate();//UpdateData(true);x0=s1.m_x0;y0=s1.m_y0;x1=s1.m_x1;y1=s1.m_y1;//UpdateData(false);//s1.EndDialog(3);CDC* pDC;pDC=GetDC();CMyDoc* pDoc = GetDocument(); ASSERT_V ALID(pDoc);Brem(x0,y0,x1,y1,RGB(255,0,255),pDC); Brem(x0,y0,101,1001,RGB(255,0,255),pDC); //squr(x0,y0,x1,y1,RGB(255,0,255),pDC);}void seekIn(int x,int y,int color,CDC* pDC)//4方向的填充{ CArray my1;//CArray *first,*rear;my1.Add(CPoint(x,y));pDC->SetPixel(x,y,color);//first=&my1.ElementAt(0);//int first=0,rear=1;while(my1.GetSize()!=0){CPoint p=my1.GetAt(0);my1.RemoveAt(0);CPoint tmp=CPoint(p.x+1,p.y);if(pDC->GetPixel(tmp)!=color){pDC->SetPixel(p.x+1,p.y,color);my1.Add(tmp);}tmp=CPoint(p.x-1,p.y);if(pDC->GetPixel(tmp)!=color){pDC->SetPixel(p.x-1,p.y,color);my1.Add(tmp);}tmp=CPoint(p.x,p.y+1);if(pDC->GetPixel(tmp)!=color){pDC->SetPixel(p.x,p.y+1,color);my1.Add(tmp);}tmp=CPoint(p.x,p.y-1);if(pDC->GetPixel(tmp)!=color){pDC->SetPixel(p.x,p.y-1,color);my1.Add(tmp);}}}void seekIn8(int x,int y,int color,CDC* pDC)//8方向的填充{ CArray my1;//CArray *first,*rear;my1.Add(CPoint(x,y));pDC->SetPixel(x,y,color);//first=&my1.ElementAt(0);//int first=0,rear=1;while(my1.GetSize()!=0){CPoint p=my1.GetAt(0);my1.RemoveAt(0);CPoint tmp=CPoint(p.x+1,p.y);if(pDC->GetPixel(tmp)!=color){pDC->SetPixel(p.x+1,p.y,color);my1.Add(tmp);}tmp=CPoint(p.x-1,p.y);if(pDC->GetPixel(tmp)!=color){pDC->SetPixel(p.x-1,p.y,color);my1.Add(tmp);tmp=CPoint(p.x,p.y+1);if(pDC->GetPixel(tmp)!=color) {pDC->SetPixel(p.x,p.y+1,color); my1.Add(tmp);}tmp=CPoint(p.x,p.y-1);if(pDC->GetPixel(tmp)!=color) {pDC->SetPixel(p.x,p.y-1,color); my1.Add(tmp);}tmp=CPoint(p.x+1,p.y+1);if(pDC->GetPixel(tmp)!=color) {pDC->SetPixel(p.x+1,p.y+1,color); my1.Add(tmp);}tmp=CPoint(p.x-1,p.y+1);if(pDC->GetPixel(tmp)!=color) {pDC->SetPixel(p.x-1,p.y+1,color); my1.Add(tmp);}tmp=CPoint(p.x-1,p.y-1);if(pDC->GetPixel(tmp)!=color) {pDC->SetPixel(p.x-1,p.y-1,color); my1.Add(tmp);tmp=CPoint(p.x+1,p.y-1);if(pDC->GetPixel(tmp)!=color){pDC->SetPixel(p.x+1,p.y-1,color);my1.Add(tmp);}}}void CMyView::Onseek1()//四方向的种子填充算法{ CDC* pDC;pDC=GetDC();CMyDoc* pDoc = GetDocument();ASSERT_V ALID(pDoc);CPoint p[4];p[0]=CPoint(20,40);p[1]=CPoint(20,400);p[2]=CPoint(200,400);p[3]=CPoint(200,40);int color=RGB(255,0,255);pDC->MoveTo(p[0]);for(int i=1;i<=3;i++){pDC->LineT o(p[i]);}pDC->LineT o(p[0]);color=pDC->GetPixel(p[1]);seekIn(110,220,color,pDC);}void CMyView::OnSeekin8() //8方向的种子填充算法{CDC* pDC;pDC=GetDC();CMyDoc* pDoc = GetDocument(); ASSERT_V ALID(pDoc);CPoint p[4];p[0]=CPoint(320,40);p[1]=CPoint(320,400);p[2]=CPoint(500,400);p[3]=CPoint(500,40);int color=RGB(255,0,255);pDC->MoveTo(p[0]);for(int i=1;i<=3;i++){pDC->LineT o(p[i]);}pDC->LineT o(p[0]);color=pDC->GetPixel(p[1]);seekIn8(410,220,color,pDC);}//扫描线填充struct edge//edge信息{double xi;double dx;int ymax;bool operator <(edge& S)const{return xi<s.xi;< bdsfid="539" p=""></s.xi;<> }};void initLineNewedge(vector< list >& ve,vector& py,int ymin,int ymax)//初始化边参数{edge e;int sz=py.size();for(int i=0;i<sz;i++)< bdsfid="549" p=""></sz;i++)<>{CPoint& ps=py[i];CPoint& pe=py[(i+1)%sz];CPoint& pee=py[(i+2)%sz];CPoint& pss=py[(i-1+sz)%sz];if(pe.y!=ps.y){e.dx=(double)(pe.x-ps.x)/(double)(pe.y-ps.y);if(pe.y>ps.y){e.xi=ps.x;if(pee.y>=pe.y) e.ymax=pe.y-1;else e.ymax=pe.y;ve[ps.y-ymin].push_front(e);}else{e.xi=pe.x;if(pss.y>=ps.y) e.ymax=ps.y-1;else e.ymax=ps.y;ve[pe.y-ymin].push_front(e);}}}void insertNetListT oAet(list& st,list& aet)//插入活动边{for(list::iterator it=st.begin();it!=st.end();it++)aet.push_front((*it));}void fillScannLine(list& st,int y,int color,CDC* pDC)//填充{CPen pen;pen.CreatePen(PS_SOLID,2,RGB(255,0,255));CPen* pOldPen=pDC->SelectObject(&pen);int sz=st.size();for(list::iterator it=st.begin();it!=st.end();++it){pDC->MoveTo(CPoint((*it).xi,y));++it;pDC->LineT o(CPoint((*it).xi,y));}pDC->SelectObject(pOldPen);}void RemoveNonActiveLine(list& st,int y)//删除非活动边{for(list::iterator it=st.begin();it!=st.end();){if((*it).ymax==y)it=st.erase(it);else it++;}}void UpdateAetEdgeInfo(edge& e)e.xi += e.dx;}void updateAndResortAet(list& st)//更新活动边和对活动边进行排序{for(list::iterator it=st.begin();it!=st.end();it++){(*it).xi+=(*it).dx;}st.sort();}void hzLine(vector& py,CDC* pDC){int sz=py.size();CPen newpen;newpen.CreatePen(PS_SOLID,1,RGB(255,0,255));CPen* pOldPen=pDC->SelectObject(&newpen);for(int i=1;i<sz;i++)< bdsfid="631" p=""></sz;i++)<>{if(py[i].y==py[i-1].y){pDC->MoveTo(CPoint(py[i-1].x,py[i-1].y));pDC->LineT o(CPoint(py[i].x,py[i].y));}}if(py[sz-1].y==py[0].y){pDC->MoveTo(CPoint(py[0].x,py[0].y));pDC->LineT o(CPoint(py[sz-1].x,py[sz-1].y));}pDC->SelectObject(pOldPen);}void CMyView::OnAETLine(){vector py;py.clear();int ymax=-1000001,ymin=1000001;/*xyz_dialog xz[6];for(int i=0;i<6;i++){xz[i].DoModal();UpdateData(true);if(ymaxelse if(ymin>xz[i].m_y) ymin=xz[i].m_y; py.push_back(CPoint(xz[i].m_x,xz[i].m_y)); UpdateData(false);}*/CPoint p[6];p[0]=CPoint(200,40);p[1]=CPoint(200,300);p[2]=CPoint(350,170);p[3]=CPoint(400,170);p[4]=CPoint(500,300);p[5]=CPoint(500,40);ymin=40;ymax=300;for(int i=0;i<6;i++){py.push_back(p[i]);}vector< list > ve(ymax-ymin+1);CDC* pDC;pDC=GetDC();CMyDoc* pDoc = GetDocument(); ASSERT_V ALID(pDoc); initLineNewedge(ve,py,ymin,ymax); hzLine(py,pDC);list aet;int color=RGB(255,0,255);//AfxMessageBox("hello world!"); for(int y=ymin;y<=ymax;y++) {insertNetListToAet(ve[y-ymin],aet); fillScannLine(aet,y,color,pDC); RemoveNonActiveLine(aet,y); updateAndResortAet(aet);}}。
计算机图形学第3章二维基本图(4)
二、扫描线种子填充算法实现
借助于堆栈,上述算法实现步骤如下:
1、初始化堆栈。 2、种子压入堆栈。 3、while(堆栈非空) { (1)从堆栈弹出种子象素。 (2)如果种子象素尚未填充,则:
a.求出种子区段:xleft、xright; b.填充整个区段。 c.检查相邻的上扫描线的xleft≤x≤xright区间内, 是否存在需要填充的新区段,如果存在的话, 则把每个新区段在xleft≤x≤xright范围内的最 右边的象素,作为新的种子象素依次压入堆栈。 d.检查相邻的下扫描线的xleft≤x≤xright区间内, 是否存在需要填充的新区段,如果存在的话, 则把每个新区段在 xleft≤x≤xright范围内的 最右边的象素,作为新的种子象素依次压入堆 栈。 }
扫描线种子填充算法步骤 (1)种子象素入栈。 (2)栈非空时象素出栈,否则结束。 (3)对出栈象素及左、右两边象素填充,直到遇边界XL、XR。 (4)在(XL ,XR) 内查相临的上、下两条扫描线是否为边界或已填充, 如不是,则将每区间的最右边的象素入栈。回到(2)。
练习: 用扫描线种子填充算法,写出图中顺序进栈的种子坐标及 所需最大栈空间
2、国标码 我国除了采用ASCII码外,还制定了汉字编 码的国家标准字符集:中华人民共和国国家标准 信息交换编码,代号为“GB2312-80”。该字符 集共收录常用汉字6763个,图形符号682个。 它规定所有汉字和图形符号组成一个94×94 的矩阵,在此方阵中,每一行称为“区”,用区 码来标识;每一列称为“位”,用位码来标识, 一个符号由一个区码和一个位码共同标识。 区码和位码分别需要7个二进制位,同样, 为了方便,各采用一个字节表示。所以在计算机 中,汉字(符号)国标码占用两个字节。
椭圆填充aa算法
椭圆填充算法可以使用多种方法来实现,其中一种常见的方法是使用扫描线填充算法。
该算法的基本思想是从椭圆的一个交点开始,沿着扫描线向另一个交点移动,并在移动过程中根据一定的规则判断是否填充像素点。
以下是使用扫描线填充算法实现椭圆填充的步骤:
1.找到椭圆与x轴的两个交点A和B,分别作为起始点和终止点。
2.从起始点A开始,沿x轴正方向扫描,直到到达终止点B。
3.在扫描过程中,对于每个像素点P,计算其到椭圆中心的距离d,
并根据距离d判断是否填充该像素点。
具体规则如下:•如果d小于等于椭圆短轴的一半,则填充该像素点。
•如果d大于椭圆长轴的一半且小于等于椭圆短轴的一半,则根据一定的规则判断是否填充该像素点。
•如果d大于椭圆长轴的一半,则不填充该像素点。
4.重复步骤3,直到扫描完整个椭圆。
需要注意的是,在实际应用中,椭圆的中心可能不在坐标原点,需要根据实际情况进行调整。
另外,为了提高填充速度,可以采用一些优化技术,例如使用双缓冲技术、并行处理等。
扫描线填充算法
扫描线填充算法
扫描线填充算法是一种用于图形学中填充多边形的算法。
算法的基本思想是:对于一个多边形,从上到下扫描所有的行,在扫描的过程中确定哪些行的像素应该被填充。
具体实现过程如下:
1.对于每一行,找到多边形在该行的交点,并将它们按从左到右的顺序排序。
2.对于每个交点,找到它左边的交点,并在这两个交点之间填充颜色。
3.重复步骤1和2直到所有行都被扫描完。
这种算法是一种时间复杂度为O(n)的算法,其中n是多边形顶点数,因此是一种非常高效的算法。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 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作图非常方便,故日益流行,但对许多人来说,是在微机上进行的,首先碰到的问题是,如何适应微机环境。