扫描线填充算法讲解

合集下载

案例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++; } }

贵州大学--实验三-多边形填充算法

贵州大学--实验三-多边形填充算法

贵州大学实验报告学院:计算机科学与信息专业:软件工程班级:102班边界上的象素填充所遵循的规则为:“左闭右开”,“下闭上开”(将左边界和下边界的点算为内部,而将右边界和上边界算为外部)顶点:“上开下闭”。

几种特殊情况:1.扫描线交于一顶点,共享的两条边分另处于扫描线的两边,这时交点只取一个。

2.共享交点的两条边处于扫描线的上方,这时交点取二个。

3.共享交点的两条边处于扫描线的下方,这时交点取0个。

4.水平边在算法中不起任何作用,可不考虑。

活性边表(提高效率):为了减少求交的计算量,要利用一条边与相继的两条扫描线的交点的连贯性。

在处理一条扫描线时只对活性边(与它相交的多边形的边)进行求交运算。

把交点按x增加方向存在一个链表(活性边表)中。

活性边:与当前扫描线相交的边。

活性边表(AEL):按交点x的增量顺序存放在一个链表中,该链表称作活性边表(AEL)。

二、种子填充算法种子填充首先假定区域由封闭轮廓线围成,且轮廓线内某点是已知的,然后开始搜索与种子点相邻且位于轮廓线内的点。

如果这相邻点不在轮廓线内,则已达到轮廓线的边界;如果相邻点在轮廓线之内,则这相邻点成为新的种子点,继续搜索下去。

只适用于光栅扫描设备。

区域分类(区域采用边界定义,即区域边界上与边界外的象素取相同值,区域内部的点取不同值)1、四向连通区域:各象素在水平垂直四个方向是边通的。

即从区域内任一点出发,可水平/垂直移动到达区域内任一点。

实验结果运用扫描线算法进行多边形的填充时,截图显示如下:运用扫描线种子算法进行多边形的填充时,截图显示如下:(注:可编辑下载,若有不当之处,请指正,谢谢!)[文档可能无法思考全面,请浏览后下载,另外祝您生活愉快,工作顺利,万事如意!]。

实验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上、下相邻的两条扫描线上的象素。

扫描线种子填充算法

扫描线种子填充算法

扫描线种子填充算法扫描线种子填充算法的基本过程如下:当给定种子点(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)”。

计算机图形学——区域填充算法(基本光栅图形算法)

计算机图形学——区域填充算法(基本光栅图形算法)

计算机图形学——区域填充算法(基本光栅图形算法)⼀、区域填充概念区域:指已经表⽰成点阵形式的填充图形,是象素的集合。

区域填充:将区域内的⼀点(常称【种⼦点】)赋予给定颜⾊,然后将这种颜⾊扩展到整个区域内的过程。

区域填充算法要求区域是连通的,因为只有在连通区域中,才可能将种⼦点的颜⾊扩展到区域内的其它点。

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.初始化扫描线列表:-从上到下依次生成扫描线,建立一个扫描线列表。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

计算机图形学第3章二维基本图(4)

计算机图形学第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个二进制位,同样, 为了方便,各采用一个字节表示。所以在计算机 中,汉字(符号)国标码占用两个字节。

多边形的填充——扫描线算法(原理)

多边形的填充——扫描线算法(原理)

多边形的填充——扫描线算法(原理)2007年10月05日星期五 11:52多边形在计算机中有两种表示:点阵表示和顶点表示。

顶点表示是用多边形的顶点的序列来描述多边形,该表示几何意义强、占内存少,但它不能直观地说明哪些像素在多边形内。

点阵表示是用位于多边形内的象素的集合来刻划多边形,该方法虽然没有多边形的几何信息,但具有面着色所需要的图像表示形式。

多边形填充就是把多边形的顶点表示转换为点阵表示,即从多边形的给定边界出发,求出位于其内部的各个像素,并将帧缓冲器内的各个对应元素设置为相应的灰度或颜色。

多边形填充最常用的方法就是扫描线算法。

下面分两篇文章介绍这种算法的原理和具体实现。

这里所介绍的算法只是针对非自交多边形,这些多边形可以是凸的、凹的或者带有空洞的。

所谓扫描线算法就是找到多边形的最小y值和最大y值,然后用这个范围内的每一条水平线与多边形相交,求得交点,再绘制线段。

所以我们只需要对一条水平线进行分析就可以。

很显然,一条扫描线和多边形有偶数个交点,将这些交点按照x值从小到大排列,然后取第1、2个绘制,第3、4个绘制......直到所有交点都被取完。

所以,对于一条扫描线,我们需要做的工作可以分为三个步骤:1)求出扫描线与多边形边的交点 2)将交点按照x升序排列 3)将排好序的交点两两配对,然后绘制相应线段。

这三个步骤中,后两个步骤很简单,没有特别的内容需要介绍。

但是第一个步骤比较麻烦。

这里有几个问题需要解决。

一是当扫描线与顶点相交时,交点的取舍。

当与那个顶点关联的边在扫描线同侧时,交点自然算两次,当与那个顶点关联的边在扫描线两侧时,交点只能算一次。

我们使用“下闭上开”的办法。

二是多边形边界上的像素取舍,我们采用“左闭右开”的办法。

三是如何减少计算量。

在绘制直线时,有一种DDA算法,它是利用(x,y)直接求出下一个点位于(x+1,y+m)或者(x+1/m, y+1)。

在这里可以利用这一点。

当已经得到y = e和多边形所有边的交点时,对于下一条扫描线y=e+1,如果没有新边与y=e+1相交,就可以推出y = e+1 和多边形所有边的交点。

计算机图形学实验1------Y-X扫描线填充

计算机图形学实验1------Y-X扫描线填充

实验一 多边形彩色填充一、实验目的学习并掌握Y-X 扫描线算法掌握基于 Win32、Visual C++环境下MFC 编程绘制图形二、实验原理1. 对任一条扫描线,确定该扫描线与多边形边的交点位置,自左向右存储,并对每对内部交点间的帧缓存填写指定颜色2.算法步骤3.扫描线链表123456784. 边表ET :初始化时建立全局边表(ET),包含多边形所有边,按边端点的ymin排序存放。

即:若该边的下端点为ymin,则该边就放在扫描线ymin对应的链表中。

每条扫描线交的多边形的边按其下端点的x坐标增序排列。

5. 活跃边表AET:与当前扫描线相交的边称为活性边,按与扫描线交点x坐标递增的顺序存放在一个链表中,称此链表为活性边表(AET)三、实验关键代码CPolyFill::CPolyFill(){m_pEDGEs = NULL;m_pET = NULL;m_pAET = NULL;MinY = 10000;MaxY = -1;MinX = 10000;MaxX = -1;}CPolyFill::~CPolyFill(){if(m_pEDGEs) delete[] m_pEDGEs;if(m_pET) delete[] m_pET;if(m_pAET) delete[] m_pAET;}// 统计多边形各条边的信息,生成Edge结构。

void CPolyFill::BuildEDGEs(){if(m_pEDGEs){delete[] m_pEDGEs; m_pEDGEs = NULL;}m_pEDGEs = new EDGE[m_PtNum];for(int i = 0; i < m_PtNum-1; i++){if (m_Pts[i].y > m_Pts[i+1].y){m_pEDGEs[i].Up = m_Pts[i];m_pEDGEs[i].Down = m_Pts[i+1];}else{m_pEDGEs[i].Up = m_Pts[i+1];m_pEDGEs[i].Down = m_Pts[i];}m_pEDGEs[i].EG.Ymax = m_pEDGEs[i].Up.y ;m_pEDGEs[i].EG.X = m_pEDGEs[i].Down.x;m_pEDGEs[i].EG.Dx = double((m_pEDGEs[i].Up.x - m_pEDGEs[i].Down.x))/(m_pEDGEs[i].Up.y - m_pEDGEs[i].Down.y);}if (m_Pts[0].y > m_Pts[m_PtNum-1].y){m_pEDGEs[m_PtNum-1].Up = m_Pts[0];m_pEDGEs[m_PtNum-1].Down = m_Pts[m_PtNum-1];}else{m_pEDGEs[m_PtNum-1].Up = m_Pts[m_PtNum-1];m_pEDGEs[m_PtNum-1].Down = m_Pts[0];}m_pEDGEs[m_PtNum-1].EG.Ymax = m_pEDGEs[m_PtNum-1].Up.y ;m_pEDGEs[m_PtNum-1].EG.X = m_pEDGEs[m_PtNum-1].Down.x;m_pEDGEs[m_PtNum-1].EG.Dx = double((m_pEDGEs[m_PtNum-1].Up.x - m_pEDGEs[m_PtNum-1].Down.x))/(m_pEDGEs[m_PtNum-1].Up.y - m_pEDGEs[m_PtNum-1].Down.y);}void CPolyFill::Polygon(CPoint *pts, int cnt){m_Pts = pts;m_PtNum = cnt;BuildEDGEs(); // 计算出每条多边形边的斜率,Ymax, Xmin等。

描述多边形扫描转换的扫描线算法的基本步骤

描述多边形扫描转换的扫描线算法的基本步骤

描述多边形扫描转换的扫描线算法的基本步骤多边形扫描转换是计算机图形学中一种常用的算法,用于将输入的多边形进行转换和填充。

其基本步骤包括初始化,活性边表的生成,活性边表的更新和扫描线的处理。

1.初始化首先,需要根据输入的多边形构造一个扫描线填充的边表。

这包括对多边形顶点的排序、计算多边形中的水平线交点,并将边表中的数据初始化为初始值。

2.活性边表的生成活性边表是用来存储和管理与扫描线相交的边的数据结构。

生成活性边表的过程包括两个步骤:-遍历多边形的每一条边,将边与当前扫描线的位置进行比较,如果两者相交,则将这条边添加到活性边表中。

-对活性边表中的边按照交点的水平位置进行排序。

这里可以使用插入排序等算法。

3.活性边表的更新活性边表需要在每次扫描线移动时进行更新。

这包括对活性边表中的边进行更新,以反映新的交点或边的状态的变化。

-对于与当前扫描线相交的边,需要计算其交点,并更新到活性边表中。

-对于已经处理完的边或超出当前扫描线范围的边,从活性边表中移除。

4.扫描线的处理在每次扫描线移动时,需要对当前的活性边表进行处理。

这包括两个子步骤:-将活性边表中的边按照两两成对的方式遍历,找到当前扫描线和这两条边所定义的三角形的上顶点和下顶点。

-将这个三角形的内部填充,并进行显示或存储等处理。

5.继续扫描线的移动在处理完一条扫描线后,需要将扫描线的位置向上移动一个单位,并继续执行第3步和第4步,直到所有的扫描线都被处理完毕。

总结:多边形扫描转换的基本步骤包括初始化、活性边表的生成、活性边表的更新和扫描线的处理。

这个算法通常用于实现对多边形的填充。

在每次扫描线移动时,活性边表需要进行更新,以反映新的交点或变化的边的状态。

扫描线的处理包括遍历活性边表中的边,并根据扫描线和这两条边所定义的三角形的顶点来进行填充。

最后,重复执行扫描线的移动和对活性边表的更新和处理,直到所有的扫描线都被处理完毕。

python扫描线填充算法详解

python扫描线填充算法详解

python扫描线填充算法详解本⽂实例为⼤家分享了python扫描线填充算法,供⼤家参考,具体内容如下介绍1.⽤⽔平扫描线从上到下扫描由点线段构成的多段构成的多边形。

2.每根扫描线与多边形各边产⽣⼀系列交点。

将这些交点按照x坐标进⾏分类,将分类后的交点成对取出,作为两个端点,以所填的⾊彩画⽔平直线。

3.多边形被扫描完毕后,填⾊也就完成。

数据结构活性边表:新边表:代码(使⽤数组)import numpy as npfrom PIL import Imagefrom PIL import ImageDrawfrom PIL import ImageFontarray = np.ndarray((660, 660, 3), np.uint8)array[:, :, 0] = 255array[:, :, 1] = 255array[:, :, 2] = 255for i in range(0,660,1):array[i,330]=(0,0,0)for j in range(0,660,1):array[330,j]=(0,0,0)def creat_Net(point, row, y_min,y_max ):Net = [([ ] * y_max ) for i in range(y_max )]point_count = point.shape[0]for j in range(0, point_count):x = np.zeros(10)first = int(min(point[(j+1)%point_count][1] , point[j][1]))x[1] = 1/((point[(j+1)%point_count][1]-point[j][1])/(point[(j+1)%point_count][0]-point[j][0])) # x 的增量 x[2] = max(point[(j+1)%point_count][1] , point[j][1])if(point[(j+1)%point_count][1] < point[j][1]):x[0] = point[(j+1)%point_count][0]else:x[0] = point[j][0]Net[first].append(x)return Netdef draw_line(i,x ,y ):for j in range(int(x),int(y)+1):array[330-i,j+330]=(20,20,20)def polygon_fill(point):y_min = np.min(point[:,1])y_max = np.max(point[:,1])Net = creat_Net(point, y_max - y_min + 1, y_min, y_max)x_sort = [] * 3for i in range(y_min, y_max):x = Net[i]if(len(x) != 0):for k in x :x_sort.append(k)x_image = [] * 3for cell in x_sort:x_image.append(cell[0])x_image.sort()if(len(x_image) >= 3 and x_image[0]==x_image[1] and x_image[2]>x_image[1]):x_image[1] = x_image[2]draw_line(i, x_image[0], x_image[1])linshi = [] * 3for cell in x_sort:if cell[2] > i:cell[0] += cell[1]linshi.append(cell)x_sort = linshi[:]x_image = [] * 3for cell in x_sort:x_image.append(cell[0])x_image.sort()draw_line(i, x_image[0],x_image[1])def main():point = [[55,40], [100,80], [100,160],[55,180], [10,160], [10,80]]point = np.array(point)polygon_fill( point )image = Image.fromarray(array)image.save('saomao.jpg')image.show()if __name__ == "__main__":main()实例:以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。

计算机图形学边缘填充算法

计算机图形学边缘填充算法

区域填充(种子填充算法)
区域(种子)填充是指先将区域内的一点 (种子点)赋予给定颜色,然后将颜色扩充到整个 区域内的过程(染色过程). 区域:已经表示成点阵形式的象素集合,具有相 同颜色.

区域的表示
区域的两种表示:内点表示、边界表示. 边界表示:给位于边界上的所有象素着色,而区 域内不着色. 内点表示:给区域内所有象素都着上同一种颜色 (特征值),边界上pixel不着色.
of the primitive
• Relative anchor • Absolute anchor

write mode
• Transparent
• Opaque
Relative anchor
To anchor the pattern at a vertex of the primitive: (x0,y0)
Example:
5
4 6 7
9
8
10 11
3 2
12
13 14 16 15 17 18 20 19
1 21 22
(2)扫描线算法
算法: (1)填充并确定种子点所在的区段; (2)将种子区段压栈; (3)若堆栈非空,栈顶区段出栈;否则算法 结束; (4)填充并确定新的区段, 将其区段信息 压栈.
以扫描线为中心的边缘填充算法
x3
x2
x0
x1
(d )从x3向右求余
以扫描线为中心的边缘填充算法
x3
x2
x0
x1
(d )从x3向右求余
以扫描线为中心的边缘填充算法
对各条扫描线循环上述处理过程。
以边为中心的边缘填充算法
原始多边形
以边置为多边形颜色的补色

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

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

计算机图形学——区域填充的扫描线算法一.实验名称:区域填充的扫描线算法二.实验目的: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.此扫描线填充算法能够对多种图案进行填充,展现了算法的实用性。

X-扫描线算法

X-扫描线算法

X-扫描线算法多边形的扫描转换多边形有两种重要的表⽰⽅法:顶点表⽰和点阵表⽰顶点表⽰是⽤多边形的顶点序列来表⽰多边形。

这种表⽰直观、⼏何意义强、占内存少,易于进⾏⼏何变换。

但由于它没有明确指出哪些象素在多边形内,故不能直接⽤于⾯着⾊点阵表⽰是⽤位于多边形内的象素集合来刻画多边形。

这种表⽰丢失了许多⼏何信息(如边界、顶点等),但它却是光栅显⽰系统显⽰时所需的表⽰形式。

光栅图形的⼀个基本问题是把多边形的顶点表⽰转换为点阵表⽰。

这种转换称为多边形的扫描转换X-扫描线算法X-扫描线算法填充多边形的基本思想是按扫描线顺序,计算扫描线与多边形的相交区间,再⽤要求的颜⾊显⽰这些区间的像素,即完成填充⼯作算法的核⼼是按X递增顺序排列交点的X坐标序列。

由此,可得到X-扫描线算法步骤如下:(1)确定多边形所占有的最⼤扫描线数,得到多边形顶点的最⼩和最⼤y值(ymin和ymax)(2)从y = ymin到y = ymax,每次⽤⼀条扫描线进⾏填充(3) 对⼀条扫描线填充的过程可分为四个步骤:a、求交:计算扫描线与多边形各边的交点b、排序:把所有交点按递增顺序进⾏排序c、交点配对:第⼀个与第⼆个,第三个与第四个d、区间填⾊:把这些相交区间内的像素置成不同于背景⾊的填充⾊当扫描线与多边形顶点相交时,交点的取舍问题(交点的个数应保证为偶数个)(1)若共享顶点的两条边分别落在扫描线的两边,交点只算⼀个(2)若共享顶点的两条边在扫描线的同⼀边,这时交点作为零个或两个检查共享顶点的两条边的另外两个端点的y值,按这两个y值中⼤于交点y值的个数来决定交点数存在的问题为了计算每条扫描线与多边形各边的交点,最简单的⽅法是把多边形的所有边放在⼀个表中。

在处理每条扫描线时,按顺序从表中取出所有的边,分别与扫描线求交这个算法效率低,为什么?关键问题是求交!⽽求交是很可怕的,求交的计算量是⾮常⼤的。

磁共振k空间的填充方式

磁共振k空间的填充方式

磁共振k空间的填充方式磁共振(Magnetic Resonance Imaging,MRI)是一种利用磁场和无线电波来观察和获取人体内部结构和功能信息的非侵入性医学影像技术。

在进行MRI扫描时,磁共振k空间的填充方式起着至关重要的作用。

k空间是指在MRI中用来描述图像空间的一种数学表示方法。

k空间中的每个点代表了图像中的一个频域信息。

而k空间的填充方式则决定了图像的分辨率、对比度和扫描时间等重要参数。

在MRI扫描中,填充方式通常由扫描顺序和参数决定。

常见的填充方式有直线扫描(Linear Scan)和螺旋扫描(Spiral Scan)等。

直线扫描是最常见的填充方式之一。

它采用线性的扫描路径,从k 空间的中心开始,逐渐填充整个k空间。

直线扫描的优点是扫描速度快,适用于对时间要求较高的临床应用,如头部和腰椎的扫描。

然而,直线扫描的缺点是分辨率较低,对细微结构的观察能力有限。

与直线扫描相比,螺旋扫描是一种更高级的填充方式。

螺旋扫描通过螺旋状的扫描路径填充k空间,可以提高图像的分辨率和对比度。

螺旋扫描的优点是扫描速度快、分辨率高,适用于对空间分辨率要求较高的应用,如关节和心脏的扫描。

然而,螺旋扫描的缺点是对系统的均匀性和非线性响应要求较高,需要更复杂的图像重建算法。

除了直线扫描和螺旋扫描,还有一些其他的填充方式。

例如,等角度扫描(Equiangular Scan)和随机扫描(Random Scan)。

等角度扫描通过等角度的方式填充k空间,可以提高图像的对比度和SNR(Signal to Noise Ratio)。

随机扫描则通过随机的方式填充k 空间,可以减少伪影和噪声,提高图像质量。

不同的填充方式适用于不同的临床应用。

在选择填充方式时,需要根据具体的病情和扫描目的来确定。

同时,填充方式的选择还需要考虑扫描时间和设备性能等因素。

磁共振k空间的填充方式在MRI扫描中起着重要的作用。

不同的填充方式可以影响图像的分辨率、对比度和扫描时间等重要参数。

03-扫描转换和区域填充

03-扫描转换和区域填充

(3)最后考虑多边形的连贯性,即当某条边与当前扫描线 相交时,它很可能也与下一条扫描线相交。 为了避免求交运算,需要引进一套特殊的数据结构。这套 数据结构在后面的消隐算法中还要出现。
数据结构:
活性边表(AET):把与当前扫描线相交的边称为活性边,并把 它们按与扫描线交点x坐标递增的顺序存放在一个链表中。 结点内容(一个结点在数据结构里可用结构来表示) x: 当前扫描线与边的交点坐标 △x: 从当前扫描线到下一条扫描线间x的增量 ymax: 该边所交的最高扫描线的坐标值ymax x △x ymax
4 3
2
1
0
5 -3 P1P2
从上面这个指针数组 里面就知道多边形是从 哪里开始的。在这个指 针数组里只有1、2、3、 5处有边,因此当从下往 上进行扫描转换的时候, 从y=1开始做,而在1这 条线上有两条边进来了, 然后就把这两条边放进 活性边表来处理。
8 7 6 5 4 3 2
P6(2,7)
P4(11,8) F G B P5(5,5) P3(11,3) C D
即每做一次新的扫描线时,要对已有的边进行三 个处理:一是否被去除掉;如果不被去除,第二就 要对它的数据进行更新,。所谓更新数据就是要更 新它的x值,即x+△x;最后,就是有没有新的边进 来,新的边在NET里,可以插入排序插进来。 这个算法过程从来没有求交,这套数据结构使得 你不用求交点!避免了求交运算。
对于每条穿越多边形的 扫描线,X-扫描线算法确 定扫描线与多边形边相交 区间的像素点位置。
如扫描线y=3与多边形的 边界相交于4点:(2,3) 、(4,3)、(7,3)、 (9,3)。
y 12 11 10 9 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9 10 11 12 x

扫描线算法

扫描线算法

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

对于一条扫描线填充过程可以分为四个步骤:(1) 求交:计算扫描线与多边形各边的交点(2) 排序:把所有交点按x 坐标递增顺序来排序(3) 配对:确定扫描线与多边形的相交区间,第一个与第二个,第三个与第四个等等,每对交点代表扫描线与多边形的一个相交区间(4) 填充:显示相交区间的象素存在问题1:当扫描线与多边形顶点相交时,交点的取舍问题解决方法:当扫描线与多边形的顶点相交时,若共享顶点的两条边分别落在扫描线的两边,交点只算一个;若共享顶点的两条边在扫描线的同一边,这时交点作为零个或两个,取决于该点是多边形的局部最高点或局部最低点。

具体实现:只需检查顶点的两条边的另外两个端点的y值,按这两个y值中大于交点y 值的个数是0,1,2 来决定。

存在问题2:多边形边界上象素的取舍解决方法:规定右/上边界的象素不予填充;左/下边界的象素予以填充。

具体实现:对扫描线与多边形的相交区间取左闭右开。

算法的实现求交一条扫描线往往只和少数几条边相交。

与当前扫描线相交的边称为活性边,把它们按与扫描线交点x 坐标递增的顺序存入一个链表中,称为活性边表( AET, Active Edge Table)由边的连贯性(当某条边与当前扫描线相交时,它很可能也与下一条扫描线相交)和扫描线的连贯性(当前扫描线与各边的交点顺序,与下一条扫描线与各边的交点顺序很可能相同或类似),只需对当前扫描线的活性边表作更新,即可得到下一条扫描线的活性边表。

计算下一条扫描线与边的交点设直线方程:a x + b y + c = 0,当前交点坐标:(xi , yi),下一交点坐标:(xi+1,yi+1)xi+1=((-b yi+1)-c)/a = ((-b yi+1)-c)/a = xi-b/a 增量为-b/a故在活性边表中需要存放的信息: x :当前扫描线与边的交点△x = -b /a :从当前扫描线到下一条扫描线之间的 x 增量 ymax :该边所交的最高扫描线P PA BCD△x y maxP P P P P PFGP 4P 5P 3P 4上图为扫描线 6 的活性边表 左图为扫描线 7 的活性边表 活性边表的更新为方便活性边表的更新,建立另一个表。

线生成面的算法

线生成面的算法

线生成面的算法在计算机图形学中,线生成面的算法是一种用于将线段转化为面的方法。

通过这种算法,可以将离散的线段连接起来,形成一张平面图像。

本文将介绍几种常用的线生成面算法,并对其原理和应用进行详细解析。

一、扫描线算法扫描线算法是将线段转化为面的常用方法之一。

其基本思想是通过按行扫描的方式,将线段与扫描线的交点进行连接,形成封闭的面。

具体而言,扫描线算法分为以下几个步骤:1. 初始化扫描线位置和线段列表。

2. 按行扫描,记录扫描线与线段的交点。

3. 对于每个交点,根据其位置和线段的属性进行判断,确定所要生成的面的属性。

4. 将生成的面加入到面列表中。

5. 重复以上步骤,直至扫描完整个图像。

扫描线算法的优点是简单易懂,适用于处理简单的图形。

然而,对于复杂的图形,扫描线算法的效率较低,需要较多的计算。

因此,在实际应用中,需要根据具体情况选择合适的算法。

二、边界填充算法边界填充算法是一种将线段转化为面的常用方法。

其基本思想是通过确定面的边界,将边界内的像素点填充为面的颜色。

具体而言,边界填充算法分为以下几个步骤:1. 初始化边界点和填充颜色。

2. 找到边界点的邻域点,判断其是否在边界内。

3. 如果邻域点在边界内,则将其填充为面的颜色。

4. 重复以上步骤,直至填充完整个面。

边界填充算法的优点是适用于处理复杂的图形,可以生成具有多边形形状的面。

然而,边界填充算法可能存在漏填或重复填充的问题,需要进行一定的优化处理。

三、逐点扫描算法逐点扫描算法是一种将线段转化为面的常用方法。

其基本思想是通过对每个像素点进行扫描,判断其是否在线段上,从而生成面。

具体而言,逐点扫描算法分为以下几个步骤:1. 初始化像素点和线段属性。

2. 对于每个像素点,计算其与线段的距离。

3. 判断距离是否小于等于一定阈值,如果是,则将该像素点归为面的一部分。

4. 重复以上步骤,直至扫描完整个图像。

逐点扫描算法的优点是精确度高,可以生成较为细腻的图像。

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

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

例如扫描线4的“活动边表”由P1P2和P3P4两条边组成,而扫描线7的“活动边表”由P1P2、P6P1、P5P6和P4P5四条边组成。

第二个特点可以进一步证明,假设当前扫描线与多边形的某一条边的交点已经通过直线段求交算法计算出来,得到交点的坐标为(x, y),则下一条扫描线与这条边的交点不需要再求交计算,通过步进关系可以直接得到新交点坐标为(x + △x, y + 1)。

前面提到过,步进关系△x是个常量,与直线的斜率有关,下面就来推导这个△x。

假设多边形某条边所在的直线方程是:ax + by + c = 0,扫描线y i和下一条扫描线y i+1与该边的两个交点分别是(x i,y i)和(x i+1,y i+1),则可得到以下两个等式:ax i + by i + c = 0 (等式 1)ax i+1 + by i+1 + c = 0 (等式 2)由等式1可以得到等式3:x i = -(by i + c) / a (等式 3)同样,由等式2可以得到等式4:x i+1 = -(by i+1 + c) / a (等式 4)由等式 4 –等式3可得到x i+1– x i = -b (y i+1 - y i) / a由于扫描线存在y i+1 = y i + 1的关系,将代入上式即可得到:x i+1– x i = -b / a即△x = -b / a,是个常量(直线斜率的倒数)。

“活动边表”是扫描线填充算法的核心,整个算法都是围绕者这张表进行处理的。

要完整的定义“活动边表”,需要先定义边的数据结构。

每条边都和扫描线有个交点,扫描线填充算法只关注交点的x坐标。

每当处理下一条扫描线时,根据△x直接计算出新扫描线与边的交点x 坐标,可以避免复杂的求交计算。

一条边不会一直待在“活动边表”中,当扫描线与之没有交点时,要将其从“活动边表”中删除,判断是否有交点的依据就是看扫描线y是否大于这条边两个端点的y坐标值,为此,需要记录边的y坐标的最大值。

根据以上分析,边的数据结构可以定义如下:65typedef struct tagEDGE66{67double xi;68double dx;69int ymax;74}EDGE;根据EDGE的定义,扫描线4和扫描线7的“活动边表”就分别如图(7)和图(8)所示:图(7)扫描线4的活动边表图(8)扫描线7的活动边表前面提到过,扫描线算法的核心就是围绕“活动边表(AET)”展开的,为了方便活性边表的建立与更新,我们为每一条扫描线建立一个“新边表(NET)”,存放该扫描线第一次出现的边。

当算法处理到某条扫描线时,就将这条扫描线的“新边表”中的所有边逐一插入到“活动边表”中。

“新边表”通常在算法开始时建立,建立“新边表”的规则就是:如果某条边的较低端点(y坐标较小的那个点)的y坐标与扫描线y相等,则该边就是扫描线y的新边,应该加入扫描线y的“新边表”。

上例中各扫描线的“新边表”如下图所示:图(9)各扫描线的新边表讨论完“活动边表(AET)”和“新边表(NET)”,就可以开始算法的具体实现了,但是在进一步详细介绍实现算法之前,还有以下几个关键的细节问题需要明确:(1)多边形顶点处理在对多边形的边进行求交的过程中,在两条边相连的顶点处会出现一些特殊情况,因为此时两条边会和扫描线各求的一个交点,也就是说,在顶点位置会出现两个交点。

当出现这种情况的时候,会对填充产生影响,因为填充的过程是成对选择交点的过程,错误的计算交点个数,会造成填充异常。

假设多边形按照顶点P1、P2和P3的顺序产生两条相邻的边,P2就是所说的顶点。

多边形的顶点一般有四种情况,如图(10)所展示的那样,分别被称为左顶点、右顶点、上顶点和下顶点:图(10)多边形顶点的四种类型左顶点――P1、P2和P3的y坐标满足条件:y1 < y2 < y3;右顶点――P1、P2和P3的y坐标满足条件:y1 > y2 > y3;上顶点――P1、P2和P3的y坐标满足条件:y2 > y1 && y2 > y3;下顶点――P1、P2和P3的y坐标满足条件:y2 < y1 && y2 < y3;对于左顶点和右顶点的情况,如果不做特殊处理会导致奇偶奇数错误,常采用的修正方法是修改以顶点为终点的那条边的区间,将顶点排除在区间之外,也就是删除这条边的终点,这样在计算交点时,就可以少计算一个交点,平衡和交点奇偶个数。

结合前文定义的“边”数据结构:EDGE,只要将该边的ymax修改为ymax – 1就可以了。

对于上顶点和下顶点,一种处理方法是将交点计算做0个,也就是修正两条边的区间,将交点从两条边中排除;另一种处理方法是不做特殊处理,就计算2个交点,这样也能保证交点奇偶个数平衡。

(2)水平边的处理水平边与扫描线重合,会产生很多交点,通常的做法是将水平边直接画出(填充),然后在后面的处理中就忽略水平边,不对其进行求交计算。

(3)如何避免填充越过边界线边界像素的取舍问题也需要特别注意。

多边形的边界与扫描线会产生两个交点,填充时如果对两个交点以及之间的区域都填充,容易造成填充范围扩大,影响最终光栅图形化显示的填充效果。

为此,人们提出了“左闭右开”的原则,简单解释就是,如果扫描线交点是1和9,则实际填充的区间是[1,9),即不包括x坐标是9的那个点。

2.2扫描线算法实现扫描线算法的整个过程都是围绕“活动边表(AET)”展开的,为了正确初始化“活动边表”,需要初始化每条扫描线的“新边表(NET)”,首先定义“新边表”的数据结构。

定义“新边表”为一个数组,数组的每个元素存放对应扫描线的所有“新边”。

因此定义“新边表”如下:510 std::vector< std::list<EDGE>> slNet(ymax - ymin +1);ymax和ymin是多边形所有顶点中y坐标的最大值和最小值,用于界定扫描线的范围。

slNet 中的第一个元素对应的是ymin所在的扫描线,以此类推,最后一个元素是ymax所在的扫描线。

在开始对每条扫描线处理之前,需要先计算出多边形的ymax和ymin并初始化“新边表”:503void ScanLinePolygonFill(const Polygon& py,int color)504{505 assert(py.IsValid());506507int ymin =0;508int ymax =0;509 GetPolygonMinMax(py, ymin, ymax);510 std::vector< std::list<EDGE>> slNet(ymax - ymin +1);511 InitScanLineNewEdgeTable(slNet, py, ymin, ymax);512//PrintNewEdgeTable(slNet);513 HorizonEdgeFill(py, color);//水平边直接画线填充514 ProcessScanLineFill(slNet, ymin, ymax, color);515}InitScanLineNewEdgeTable()函数根据多边形的顶点和边的情况初始化“新边表”,实现过程中体现了对左顶点和右顶点的区间修正原则:315void InitScanLineNewEdgeTable(std::vector< std::list<EDGE>>& slNet,316const Polygon& py,int ymin,int ymax)317{318 EDGE e;319for(int i =0; i < py.GetPolyCount(); i++)320{321const Point& ps = py.pts[i];322const Point& pe = py.pts[(i +1)% py.GetPolyCount()];323const Point& pss = py.pts[(i -1+ py.GetPolyCount())%py.GetPolyCount()];324const Point& pee = py.pts[(i +2)% py.GetPolyCount()];325332if(pe.y != ps.y)//不处理水平线333{334 e.dx =double(pe.x - ps.x)/double(pe.y - ps.y);335if(pe.y > ps.y)336{337 e.xi = ps.x;338if(pee.y >= pe.y)339 e.ymax = pe.y -1;340else341 e.ymax = pe.y;342343 slNet[ps.y - ymin].push_front(e);344}345else346{347 e.xi = pe.x;348if(pss.y >= ps.y)349 e.ymax = ps.y -1;350else351 e.ymax = ps.y;352 slNet[pe.y - ymin].push_front(e);353}354}355}356}多边形的定义Polygon和本系列第一篇《计算几何与图形学有关的几种常用算法》一文中的定义一致,此处就不再重复说明。

相关文档
最新文档