扫描线算法代码
扫描线填充算法讲解

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

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

直线段的扫描转换算法数值微分(DDA)法设过端点P0(x0 ,y0)、P1(x1 ,y1)的直线段为L(P0 ,P1),那么直线段L的斜率L的起点P的横坐标x0向L的终点P1的横坐标x1步进,取步长=1(个象素),用L的直线方程y=kx+b计算相应的y坐标,并取象素点(x,round(y))作为当前点的坐标。
因为:y= kx i+1+bi+1= k1x i+b+k x= y i+k x因此,当x =1; y i+1 = y i+k。
也确实是说,当x每递增1,y递增k(即直线斜率)。
依照那个原理,咱们能够写出DDA画线算法程序。
DDA画线算法程序void DDALine(int x0,int y0,int x1,int y1,int color){ int x;float dx, dy, y, k;dx = x1-x0; dy=y1-y0;k=dy/dx,;y=y0;for (x=x0;x< x1;x++){ drawpixel (x, int(y+, color);y=y+k;}}注意:咱们那个地址用整型变量color表示象素的颜色和灰度。
举例:用DDA方式扫描转换连接两点P0(0,0)和P1(5,2)的直线段。
x int(y+ y+0 0 01 0 +2 1 +3 1 +4 2 + 图2.1.1 直线段的扫描转换注意:上述分析的算法仅适用于|k| ≤1的情形。
在这种情形下,x每增加1,y最多增加1。
当|k| 1时,必需把x,y地位互换,y每增加1,x相应增加1/k。
在那个算法中,y与k必需用浮点数表示,而且每一步都要对y进行四舍五入后取整,这使得它无益于硬件实现。
中点画线法假定直线斜率k在0~1之间,当前象素点为(x p,y p),那么下一个象素点有两种可选择点P1(x p+1,y p)或P2(x p+1,y p+1)。
假设P1与P2的中点(x p+1,y p+)称为M,Q为理想直线与x=x p+1垂线的交点。
多边形的扫描转换算法、区域填充算法

贵州大学计算机图形学实验报告学院:计算机科学与信息学院专业:软件工程班级:反映)根据扫描线的连贯性可知:一条扫描线与多边形的交点中,入点和出点之间所有点都是多边形的内部点。
所以,对所有的扫描线填充入点到出点之间的点就可填充多边形。
如何具体实现(如何找到入点、出点)?根据区域的连贯性,分为3个步骤:(1)求出扫描线与多边形所有边的交点;(2)把这些交点按x坐标值以升序排列;(3)对排序后的交点进行奇偶配对,对每一对交点间的区域进行填充。
步骤(3)如上图:对y=8的扫描线,对交点序列按x坐标升序排序得到的交点序列是(2,4,9,13),然后对交点2与4之间、9与13之间的所有象素点进行填充。
求交点、排序、配对、填色利用链表:与当前扫描线相交的边称为活性边(Active Edge),把它们按与扫描线交点x坐标递增的顺序存入一个链表中,称为活性边表AEL (AEL, Active Edge List)。
它记录了多边形边沿扫描线的交点序列。
AEL中每个对象需要存放的信息:ymax:边所交的最高扫描线;x:当前扫描线与边的交点;Δx:从当前扫描线到下一条扫描线之间的x增量next:指向下一对象的指针。
伪码:建立ET,置y为ET中非空桶的最小序号;置AEL表为空,且把y桶中ET表的边加入AEL表中;while AEL表中非空do begin对AEL表中的x、Δx按升序排列;按照AEL表中交点前后次序,在每对奇偶交点间的x段予以填充;计算下一条扫描线:y=y+1;if 扫描线y=ymax then 从AEL表中删除这些边;对在AEL表中的其他边,计算与下一条扫描线的交点:x=x +Δx 按照扫描线y值把ET表中相应桶中的边加入AEL表中;endend of algorithm二、区域填充算法:区域可采用两种表示形式:内点表示枚举区域内部的所有像素;内部的所有像素着同一个颜色;边界像素着不同的颜色。
边界表示:枚举出边界上所有的像素;边界上的所有像素着同一颜色;内部像素着不同的颜色。
计算机图形学-区域填充的扫描线算法

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

11
9 8
7
Ⅰ
Ⅱ
3
1
o
3
6 78
图 7.15 要消隐的物体
10 11 x
Z缓冲器算法和扫描线算法
• 建立一个边Y筒
–每个筒的深度和显示屏幕行数相同。
–根据边两端点较大的Y坐标值为决定放入边Y筒的相应行数。 –边Y筒中记录的每一条边要保存下列信息:
• 和该边在oxy平面上的投影相交的扫描线条数Δy, • 该投影和相邻的两条扫描线交点的x坐标的差Δx,(由上到下扫描) • 该边所属多边形的编号IP
• 建立一个多边形Y筒
– 每个筒的深度和显示屏幕行数相同。
– 根据多边形顶点Y坐标最大值来决定放入多边形Y筒的相应行数。 – 多边形Y筒要记录多边形所在平面方程ax+by+cz+d=0系数a,b,c和d, – 还要记录和该多边形在oxy平面上的投影相交的扫描线的条数Δy, – 以及多边形的属性colour和编号IP。
–一是和多边形Ⅰ在oxy平面上的投影相交的两条边 –另一是和多边形Ⅱ投影相交的两条边。
11
9 8
7
Ⅰ
Ⅱ
3
1
o
3
6 78
图 7.15 要消隐的物体
10 11 x
xl 4, xl 1, yl 3; xr 7, xr 0, yr 3; zl1 , zx1 , zy1 , IP1
xl
6
5 6
,
xl
这点的z坐标值和z缓冲器中相应单元内的值作
比较。
只有前者大于后者时才改变帧缓冲器的那一个单元的值,同时z缓冲器中相应 单元的值也要改成这点的z坐标值。 如果这点的z坐标值小于z缓冲器中相应单元的值,则说明对应象素已显示了 物体上一个点的属性,该点比要考虑的点更接近观察者。这样,无论帧缓冲 器或z缓冲器相应单元的值均不应改变。
多边形区域填充算法--扫描线填充算法(有序边表法)有代码

多边形区域填充算法--扫描线填充算法(有序边表法)有代码⼆、扫描线算法(Scan-Line Filling)转载 https:///u013044116/article/details/49737585扫描线算法适合对⽮量图形进⾏区域填充,只需要直到多边形区域的⼏何位置,不需要指定种⼦点,适合计算机⾃动进⾏图形处理的场合使⽤,⽐如电脑游戏和三维CAD软件的渲染等等。
对⽮量多边形区域填充,算法核⼼还是求交。
⼀⽂给出了判断点与多边形关系的算法――扫描交点的奇偶数判断算法,利⽤此算法可以判断⼀个点是否在多边形内,也就是是否需要填充,但是实际⼯程中使⽤的填充算法都是只使⽤求交的思想,并不直接使⽤这种求交算法。
究其原因,除了算法效率问题之外,还存在⼀个光栅图形设备和⽮量之间的转换问题。
⽐如某个点位于⾮常靠近边界的临界位置,⽤⽮量算法判断这个点应该是在多边形内,但是光栅化后,这个点在光栅图形设备上看就有可能是在多边形外边(⽮量点没有⼤⼩概念,光栅图形设备的点有⼤⼩概念),因此,适⽤于⽮量图形的填充算法必须适应光栅图形设备。
2.1扫描线算法的基本思想扫描线填充算法的基本思想是:⽤⽔平扫描线从上到下(或从下到上)扫描由多条⾸尾相连的线段构成的多边形,每根扫描线与多边形的某些边产⽣⼀系列交点。
将这些交点按照x坐标排序,将排序后的点两两成对,作为线段的两个端点,以所填的颜⾊画⽔平直线。
多边形被扫描完毕后,颜⾊填充也就完成了。
扫描线填充算法也可以归纳为以下4个步骤:(1)求交,计算扫描线与多边形的交点(2)交点排序,对第2步得到的交点按照x值从⼩到⼤进⾏排序;(3)颜⾊填充,对排序后的交点两两组成⼀个⽔平线段,以画线段的⽅式进⾏颜⾊填充;(4)是否完成多边形扫描?如果是就结束算法,如果不是就改变扫描线,然后转第1步继续处理;整个算法的关键是第1步,需要⽤尽量少的计算量求出交点,还要考虑交点是线段端点的特殊情况,最后,交点的步进计算最好是整数,便于光栅设备输出显⽰。
扫描线种子填充算法 openGL算法程序

{push(x–1,y);右端点进栈
span-need–fill:=false;} while(getpixel(FB,x,y)=B-color or getpixel(FB,x,y)=N-color)and x<xright do {x:=x+1} } ; *在上一条扫描上检查完* y:=y–2; *在扫描线y–1上从左向右地检查位于区间 [xletft, xright]上的象素,其方法与在扫描线
算法
while stack–not–empty do {pop(x,y);*从堆栈中取一种子象素* savex:=x: *保存横坐标x的值* while getpixel (FB, x, y), <> B–color do {setpixel (FB, x, y, N–color); x:=x+1} xright:=x–1 *保存线段的右端点* while getpixel (FB,x,y)<>N–color and getpixel (FB,x,y), <> B–color do {span–need–fill:=true; x:=x+1;} if span–need–fill then
x:=savex–1; {*向种子的左边填充*}
while getpixel (FB, x, y) <> B–color do { setpixel (FB, x, y, N–color); x:=x–1}
xleft:=x+1 *保存线段的左端点*
x:=xleft; y:=y+1; while x<=xright do {span–need–fill:=false;
y+1上检查的情况完全一样,故略去详情*
详解直线扫描算法之---bresenham改进算法(任何斜率,任何方向)

先标明这转载自/xxxxxx91116/article/details/6295714直线扫描算法之---bresenham改进算法(任何斜率,任何方向)by zxx图形学神马的全都是数学,看来以后我不能搞这个,伤脑筋,所以先把我现在懂得先记录下来吧。
不过呢,我的水平实在有限,对于算法这种东西实在难以说明白,请大家包涵。
书上讲的实在是太过简略,所以这里我把一些简单的推导过程都记录下来:1.重温bresenham未改进算法(斜率在0-1之间的直线)我想要记录的是bresenham改进算法,所以在讲解改进算法之前,我先用一个简单的例子说明一下未改进算法的思想:这是一个斜率k在0-1之间的一条直线,我就用斜率为0-1之间的直线来重温:首先,如图1所示,假设x列的像素已定,其坐标为(x,y),那么下一个坐标一定是:(x+1,y+1)或者(x+1,y)。
而是哪一个取决于d的值,如果d>0.5那么就是(x+1,y+1),如果d<0.5,那么就是(x+1,y),而d是什么呢?当然是斜率了。
(原因如下:y=kx+b当x增加1时:y=kx+k+b所以当x增加1是,y方向的增量是d。
)所以每次我们只需要让d=d+k(k是斜率)即可,当d>=1时,就让d减一,这样就保证了d在0-1之间。
当d>0.5,下一个点取(x+1,y+1)当d<0.5,下一个点取(x+1,y)然后呢,我们为了判断的方便,让e=d-0.5,这样就变成了:当e>0,下一个点取(x+1,y+1)当e<0,下一个点取(x+1,y)2.过渡,重温之后,我们就想要改进,为什么要改进呢?因为我们这里面有0.5,还有k,k里面有dx/dy,这些除法和小数都不是我们想要的,我们想要的是,只有整数,且只有加法的算法,下面就全面讨论一下改进算法。
3.改进算法篇(不同斜率,不同方向)这里,我们主要分为4个角度来说明:A.斜率在0-1只间B.斜率在1-无穷之间C.斜率在0-(-1)之间D.斜率在(-1)-负无穷之间E.两种特殊情况,两条直线。
计算机图形学常用算法及代码大全

2.1.1 生成直线的DDA算法数值微分法即DDA法(Digital Differential Analyzer),是一种基于直线的微分方程来生成直线的方法。
一、直线DDA算法描述:设(x1,y1)和(x2,y2)分别为所求直线的起点和终点坐标,由直线的微分方程得可通过计算由x方向的增量△x引起y的改变来生成直线:也可通过计算由y方向的增量△y引起x的改变来生成直线:式(2-2)至(2-5)是递推的。
二、直线DDA算法思想:选定x2-x1和y2-y1中较大者作为步进方向(假设x2-x1较大),取该方向上的增量为一个象素单位(△x=1),然后利用式(2-1)计算另一个方向的增量(△y=△x·m=m)。
通过递推公式(2-2)至(2-5),把每次计算出的(x i+1,y i+1)经取整后送到显示器输出,则得到扫描转换后的直线。
之所以取x2-x1和y2-y1中较大者作为步进方向,是考虑沿着线段分布的象素应均匀,这在下图中可看出。
另外,算法实现中还应注意直线的生成方向,以决定Δx及Δy是取正值还是负值。
三、直线DDA算法实现:1、已知直线的两端点坐标:(x1,y1),(x2,y2)2、已知画线的颜色:color3、计算两个方向的变化量:dx=x2-x1dy=y2-y14、求出两个方向最大变化量的绝对值:steps=max(|dx|,|dy|)5、计算两个方向的增量(考虑了生成方向):xin=dx/stepsyin=dy/steps6、设置初始象素坐标:x=x1,y=y17、用循环实现直线的绘制:for(i=1;i<=steps;i++){ putpixel(x,y,color);/*在(x,y)处,以color色画点*/x=x+xin;y=y+yin;}五、直线DDA算法特点:该算法简单,实现容易,但由于在循环中涉及实型数的运算,因此生成直线的速度较慢。
//@brief 浮点数转整数的宏实现代码#define FloatToInteger(fNum)((fNum>0)?static_cast<int>(fNum+0.5):static_cast<int>(fNum-0.5))/*!* @brief DDA画线函数** @param pDC [in]窗口DC* @param BeginPt [in]直线起点* @param EndPt [in]直线终点* @param LineCor [in]直线颜色* @return 无*/void CDrawMsg::DDA_DrawLine(CDC *pDC,CPoint &BeginPt,CPoint &EndPt,COLORREF LineCor){l ong YDis = (EndPt.y - BeginPt.y);l ong XDis = (EndPt.x-BeginPt.x);l ong MaxStep = max(abs(XDis),abs(YDis)); // 步进的步数f loat fXUnitLen = 1.0f; // X方向的单位步进f loat fYUnitLen = 1.0f; // Y方向的单位步进f YUnitLen = static_cast<float>(YDis)/static_cast<float>(MaxStep);f XUnitLen = static_cast<float>(XDis)/static_cast<float>(MaxStep);// 设置起点像素颜色p DC->SetPixel(BeginPt.x,BeginPt.y,LineCor);f loat x = static_cast<float>(BeginPt.x);f loat y = static_cast<float>(BeginPt.y);// 循环步进f or (long i = 1;i<=MaxStep;i++){x = x + fXUnitLen;y = y + fYUnitLen;pDC->SetPixel(FloatToInteger(x),FloatToInteger(y),LineCor);}}2.1.2 生成直线的B resenham算法从上面介绍的DDA算法可以看到,由于在循环中涉及实型数据的加减运算,因此直线的生成速度较慢。
扫描线填充种子填充算法(原创完整版)

扫描线填充种子填充算法(原创完整版)// 计算机图形学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);}}。
扫描线算法代码

#include<gl/glut.h>#include<stdio.h>#include "stdlib.h"void init (void){glClearColor (1.0, 1.0, 1.0, 0.0); // 指定清空颜色(背景色)为白色 glMatrixMode (GL_PROJECTION); //指定投影矩阵gluOrtho2D (0.0, 400.0, 0.0, 400.0); //指定二维坐标系中被显示的区域}typedef struct tEdge {int yUpper;float xIntersect, dxPerScan;struct tEdge * next;} Edge;struct dcPt { //dcPt 实际上是一个点的结构体int x;int y;};void setPixel(GLint x, GLint y){glBegin(GL_POINTS);glVertex2i(x, y);glEnd();}/* Inserts edge into list in order of increasing xIntersect field. */ 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;}/* For an index, return y-coordinate of next nonhorizontal line */int yNext (int k, int cnt, dcPt * 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);}/* Store lower-y coordinate and inverse slope for each edge. Adjust and store upper-y coordinate for edges that are the lower member of a monotically increasing ordecreasing pair of edges */void makeEdgeRec(dcPt lower, dcPt upper, int yComp, Edge * edge, Edge * edges[]){edge->dxPerScan =(float) (upper.x - lower.x) / (upper.y - lower.y);edge->xIntersect = lower.x;if (upper.y < yComp)edge->yUpper = upper.y - 1;elseedge->yUpper = upper.y;insertEdge (edges[lower.y], edge);}void buildEdgeList (int cnt, dcPt * pts, Edge * edges[]){Edge *edge;dcPt 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++) {v2 = pts[i];if (v1.y != v2.y) { /* nonhorizontal line */edge=(Edge *) malloc (sizeof (Edge));if (v1.y < v2.y) /* up-going edge */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){Edge * p1, * p2;int i;p1 = active->next;while (p1) {p2 = p1->next;for (i=p1->xIntersect; i<p2->xIntersect; i++)setPixel ((int) i, scan);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->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;active->next = NULL;while (p) {q = p->next;insertEdge (active, p);p = q;}}void scanFill (int cnt, dcPt * pts){Edge *edges[400], * active;int i, scan;for (i=0; i<400; i++) {edges[i] = (Edge *) malloc (sizeof (Edge));edges[i]->next = NULL;}buildEdgeList (cnt, pts, edges);active = (Edge *) malloc (sizeof (Edge));active->next = NULL;for (scan=0; scan<200; scan++) {buildActiveList (scan, active, edges);if (active->next) {fillScan (scan, active);updateActiveList (scan, active);resortActiveList (active);}}/* Free edge records that have been malloc'ed ... */}void myDisplay (void){glClear (GL_COLOR_BUFFER_BIT); // 清空显示窗口glColor3f (1.0, 0.0, 0.0); // 指定前景色(当前绘制颜色)为蓝色dcPt pts[] = {250,50,550,150,550,400,250,250,100,350,100,100,120,30}; glBegin(GL_LINES);glVertex2i(180,15); //Specify line-segment geometry. glVertex2i(10,145);glEnd();scanFill (7,pts);glFlush ( ); // 使绘制立即反映到屏幕上}void main(int argc, char *argv[]){glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); glutInitWindowPosition(100, 100);glutInitWindowSize(400, 400);glutCreateWindow("扫描线算法!");init();glutDisplayFunc(&myDisplay);glutMainLoop();}(注:可编辑下载,若有不当之处,请指正,谢谢!)。
扫描线算法——精选推荐

扫描线算法先从⼀道模板题⼊⼿吧..这道题需要我们求n个矩形的⾯积并。
~~ 数据还很变态 ~~给出这n的矩形的左下⾓和右上⾓坐标,~~ ≤10^9 只能⽤离散化,离散化之后最多到n也就是10^5,能够维持。
怎么做?for循环?T(LE)M(LE)⼤套餐等着你离散化之后循环都要超时还存不下 ~~先把所有的⾯积相加再减去重复?~~ 现实点,你做不到 ~~扫描线算法基础就是应对这种问题的。
它的思想是分割图形⽐如说,有n个矩形组成这张图。
我们设想,有⼀条⽆限长度的竖线⾃左往右扫过这⼀⽚图形。
只保留这些矩形被竖线扫到的左右两条线段,组成包含2*n条线段的⼀张图。
对于每个矩形,把左边那条线段记为+1,右边的记为-1.像这样:对于两两相邻的部分,我们可以分别计算⾯积,这样就得到了整个并集图形的⾯积。
怎么记录这⼀条条线段?结构体。
要存储的东西有:这条竖线段的横坐标,上下端点的竖坐标,以及1/-1(记录是左还是右)按照题⽬表述记为:{x,y1,y1,1/-1}显然,我们只要把这些线的横坐标拿来排序,对于⼀次遍历来说,每对对应线段之间的距离是已知的,那么我们需要解决的问题只有纵坐标的影响范围。
不妨把所有纵坐标都取出来,离散化映射到[1,t]的区间中的t的整数值,并把这些纵坐标表⽰为t-1段,其中第i段表⽰第i个纵坐标和第i+1个纵坐标之间的部分,然后⽤c[i]表⽰第i段被覆盖的次数。
这样就可以计算⾯积并,算法流程⼤致是这样:对于每⼀个线段,将其的k值累加到这个线段对应的若⼲个纵坐标区间计算⾯积:所有T−1个纵坐标区间对应的c值⼤于零的就说明这些部分的区间还存在,将存在的区间的长度累加起来,乘上当前线段与下⼀条线段之间的横坐标之差就是这两条线段之间的⾯积。
显然,这⾥就需要⽤到区间求和的操作。
这种事情,就丢给线段树吧!因为这道题中的区间修改都是成对出现的(+1/-1),所以不需要懒标记这个操作。
只需要在线段树的每个端点多维护两个值:cnt和len,分别记这段区间被覆盖的次数以及当前区间的纵坐标长度。
扫描线算法——精选推荐

扫描线算法扫描线算法给出⼏个矩形对⾓端点坐标,求这些矩形整体覆盖的⾯积。
基本思想如下图:1. 先离散化。
2. 【扫描线】是⼀根想象中的虚线,从左往右扫描,遇到【矩形】则成为【事件】。
3. 遇到【起始边】,则Update相应区间的【厚度】或者【覆盖次数】CoverCnt+1。
4. 遇到【结束边】,则Update相应区间的【厚度】CoverCnt-1。
5. ⽤【线段树】维护【区间】的厚度CovertCnt,以及区间CovertCnt > 0 的线段的总长度Len。
求⾯积poj1511求⾯积⽐较简单:S=Δx∗∑cnt>0(raw(i+1)−raw(i))即可。
也就是每次Update后,增加⾯积即可。
如何处理CovertCnt的不⼀致?CovertCnt不⼀致,出现在“断点”,即Update后,区间不连续。
⽐如: Range[1-4].CovertCnt=2,现在Update(R[1-2]), 如何处理?1. 标记为【⽆效】,查询时,如果有【⽆效标记】,则继续往下查。
2. ⼲脆直接维护SumLen,出现这种情况,由下往上PushUp更新SumLen即可。
从本质上来讲,两者效果差不多,⼀个是马上维护SumLen,⼀个是查询时再计算SumLen。
后者省了⼀次函数调⽤,和有可能再次被“全区间覆盖”时简化计算,效率能够⾼⼀些。
所以熟悉哪种就⽤哪种,切记切记,会10种不如精⼀种!Query时需要pushdown吗?因为查询的是整个区间,不存在“交叉区间”,所以不需要。
(当然PushDown【没⽑病】,如果没有⼗⾜的把握,还是PushDown,反正没有什么副作⽤。
)Query,当【查询区间】和【更新区间】出现【交叉】的时候,需要PushDown,⽐如:更新到:[1-2]和[3-4]但要查询[2-3],则只能由[2]``[3]两部分构成,所以你必须要从[1-2]PushDown到[2],从[3-4]pushdown到[3]。
Leetcode252.会议室253.会议室II贪心算法-扫描线技巧

Leetcode252.会议室253.会议室II贪⼼算法-扫描线技巧题⽬:给定⼀个会议时间安排的数组 intervals ,每个会议时间都会包括开始和结束的时间 intervals[i] = [starti, endi] ,请你判断⼀个⼈是否能够参加这⾥⾯的全部会议。
⽰例 1:输⼊:intervals = [[0,30],[5,10],[15,20]]输出:false⽰例 2:输⼊:intervals = [[7,10],[2,4]]输出:true提⽰:0 <= intervals.length <= 104intervals[i].length == 20 <= starti < endi <= 106思路:求区间是否有重叠。
可以将end按⼤⼩排序,然后⽐较后⼀个start是否⼤于前⼀个endclass Solution {public:bool canAttendMeetings(vector<vector<int>>& intervals) {sort(intervals.begin(),intervals.end(),[](vector<int>& a,vector<int>& b){return a[1]<b[1];});int n=intervals.size();for(int i=1;i<n;++i){if(intervals[i][0]<intervals[i-1][1])return false;}return true;}};题⽬:给你⼀个会议时间安排的数组 intervals ,每个会议时间都会包括开始和结束的时间 intervals[i] = [starti, endi] ,返回所需会议室的最⼩数量。
输⼊:intervals = [[0,30],[5,10],[15,20]]输出:2思路:实际上求最⼤重叠数。
n条线段求交的扫描线算法

n条线段求交的扫描线算法扫描线算法是一种用于求解线段交点的方法,它通过模拟一条扫描线从上到下扫描线段,并通过判断线段与扫描线的相交情况来找到交点。
以下是使用Python实现扫描线算法求解线段交点的示例代码:```pythonclass LineSegment:def __init__(self, start, end):self.start = startself.end = enddef __repr__(self):return f"({self.start}, {self.end})"def sweep_line(line_segments):events = []for segment in line_segments:events.append((segment.start, 'start', segment))events.append((segment.end, 'end', segment))events.sort(key=lambda x: x[0]) # 按事件点的y坐标排序active_segments = []intersections = []def insert(segment):index = 0while index < len(active_segments) and segment.start > active_segments[index].end:index += 1active_segments.insert(index, segment)def remove(segment):active_segments.remove(segment)def intersect(segment1, segment2):x1, y1 = segment1.startx2, y2 = segment1.endx3, y3 = segment2.startx4, y4 = segment2.endden = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)if den == 0: # 两线段平行return None# 计算交点的坐标x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / deny = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4- y3 * x4)) / den# 判断交点是否在线段内部if min(x1, x2) <= x <= max(x1, x2) and min(x3, x4) <= x <= max(x3, x4) and min(y1, y2) <= y <= max(y1, y2) and min(y3, y4) <= y <= max(y3, y4):return (x, y)else:return Nonefor event in events:point, event_type, segment = eventif event_type == 'start':for active_segment in active_segments:intersection = intersect(segment, active_segment) if intersection is not None:intersections.append(intersection)insert(segment)elif event_type == 'end':remove(segment)for i in range(len(active_segments) - 1): intersection = intersect(segment,active_segments[i])if intersection is not None:intersections.append(intersection)return intersections# 测试示例segments = [LineSegment((1, 2), (7, 2)),LineSegment((3, 4), (9, 4)),LineSegment((5, 1), (10, 5)),LineSegment((2, 3), (8, 3)),LineSegment((6, 6), (11, 6))]intersections = sweep_line(segments)print(intersections)```在上面的代码中,我们首先定义了一个`LineSegment`类来表示线段,包含线段的起点和终点坐标。
实验六 扫描线填充算法

实验六扫描线填充算法一、实验目的编写多边形的扫描线填充算法程序,加深对扫描线算法的理解,验证算法的正确性。
二、实验任务(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”下添加实现不同功能的成员函数。
scan算法

return 0; case 1: // 自动生成请求序列
InitRequest(); binit = true; // 初始化完成,可以进行调度
continue; case 2: // 动态修改请求序列
ChangeRequest(binit); continue; case 3: // 执行调度算法 scan(binit); binit = false; // 调度完成后,重新进行初始化 request.clear(); scanen.clear(); scanres.clear(); continue; default: continue; } } return 0; } // 自动生成初始化请求序列 void InitRequest() { int arrtime, discnum; srand((unsigned int)time(0)); for (int i = 0; i < MAXREQUESTNUM; i++) { arrtime = i + 1; // 请求到达时间:1-MAXREQUESTNUM discnum = (int)((200 * rand())/(RAND_MAX + 1.0)) + 1; // 请求调度的磁道号:1-200 request.insert(make_pair(arrtime, discnum)); } display(); // 打印请求队列 }
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
#include<gl/glut.h>#include<stdio.h>#include "stdlib.h"void init (void){glClearColor (1.0, 1.0, 1.0, 0.0); // 指定清空颜色(背景色)为白色glMatrixMode (GL_PROJECTION); //指定投影矩阵gluOrtho2D (0.0, 400.0, 0.0, 400.0); //指定二维坐标系中被显示的区域}typedef struct tEdge {int yUpper;float xIntersect, dxPerScan;struct tEdge * next;} Edge;struct dcPt { //dcPt 实际上是一个点的结构体int x;int y;};void setPixel(GLint x, GLint y){glBegin(GL_POINTS);glVertex2i(x, y);glEnd();}/* Inserts edge into list in order of increasing xIntersect field. */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;}/* For an index, return y-coordinate of next nonhorizontal line */int yNext (int k, int cnt, dcPt * 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);}/* Store lower-y coordinate and inverse slope for each edge. Adjust and storeupper-y coordinate for edges that are the lower member of a monotically increasing or decreasing pair of edges */void makeEdgeRec(dcPt lower, dcPt upper, int yComp, Edge * edge, Edge * edges[]){edge->dxPerScan =(float) (upper.x - lower.x) / (upper.y - lower.y);edge->xIntersect = lower.x;if (upper.y < yComp)edge->yUpper = upper.y - 1;elseedge->yUpper = upper.y;insertEdge (edges[lower.y], edge);}void buildEdgeList (int cnt, dcPt * pts, Edge * edges[]){Edge *edge;dcPt 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++) {v2 = pts[i];if (v1.y != v2.y) { /* nonhorizontal line */edge=(Edge *) malloc (sizeof (Edge));if (v1.y < v2.y) /* up-going edge */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){Edge * p1, * p2;int i;p1 = active->next;while (p1) {p2 = p1->next;for (i=p1->xIntersect; i<p2->xIntersect; i++)setPixel ((int) i, scan);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->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;active->next = NULL;while (p) {q = p->next;insertEdge (active, p);p = q;}}void scanFill (int cnt, dcPt * pts){Edge *edges[400], * active;int i, scan;for (i=0; i<400; i++) {edges[i] = (Edge *) malloc (sizeof (Edge));edges[i]->next = NULL;}buildEdgeList (cnt, pts, edges);active = (Edge *) malloc (sizeof (Edge));active->next = NULL;for (scan=0; scan<200; scan++) {buildActiveList (scan, active, edges);if (active->next) {fillScan (scan, active);updateActiveList (scan, active);resortActiveList (active);}}/* Free edge records that have been malloc'ed ... */}void myDisplay (void){glClear (GL_COLOR_BUFFER_BIT); // 清空显示窗口glColor3f (1.0, 0.0, 0.0); // 指定前景色(当前绘制颜色)为蓝色dcPt pts[] = {250,50,550,150,550,400,250,250,100,350,100,100,120,30}; glBegin(GL_LINES);glVertex2i(180,15); //Specify line-segment geometry.glVertex2i(10,145);glEnd();scanFill (7,pts);glFlush ( ); // 使绘制立即反映到屏幕上}void main(int argc, char *argv[]){glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); glutInitWindowPosition(100, 100); glutInitWindowSize(400, 400); glutCreateWindow("扫描线算法!");init();glutDisplayFunc(&myDisplay);glutMainLoop();}。