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

合集下载

第二章 光栅图形学2--多边形的扫描转换与区域填充

第二章  光栅图形学2--多边形的扫描转换与区域填充

山东科技大学信息学院
区域的连贯性
根据这些性质,实际上只需知道该长方形 区域内任一梯形内一点关于多边形P的内 外关系后,即可确定区域内所有梯形关 于P的内外关系。
山东科技大学信息学院
扫描线的连贯性
设e为一整数,yi0≥e≥yin。若扫描线y=e与多边形P的 Pi-1Pi相交,则记其交点的横坐标为xei。 现设xei1,xei2,xei3,…,xeil 是该扫描线与P的边界各交点 横坐标的递增序列,称此序列为交点序列。由区域的连贯 性可知,此交点序列具有以下性质:
山东科技大学信息学院
三、多边形的扫描转换
山东科技大学信息学院
1 逐点判断法
• 逐个判断绘图窗口内的像素: • 如何判断点在多边形的内外关系?
1)射线法: 2)累计角度法 3)编码法;
山东科技大学信息学院
逐点判断法
1)射线法
• 步骤: 1. 从待判别点v发出射线 2. 求交点个数k 3. K的奇偶性决定了点与多边形的内外关系
山东科技大学信息学院
3 扫描线种子填充算法
• 扫描线种子填充算法
–目标:减少递归层次 –适用于边界表示的4连通区域 算法思想:在任意不间断区间中只取一个种子 像素(不间断区间指在一条扫描线上一组相 邻元素),填充当前扫描线上的该段区间; 然后确定与这一区段相邻的上下两条扫描线 上位于区域内的区段,并依次把它们保存起 来,反复进行这个过程,直到所保存的个区 段都填充完毕。
逐点判断法程序简单, 速度太慢,效率低。
P1
v P2
山东科技大学信息学院
逐点判断法
• 逐点判断的算法虽然程序简单,但不可取。原 因是速度太慢,主要是由于该算法割断了各象 素之间的联系,孤立地考察各象素与多边形的 内外关系,使得几十万甚至几百万个象素都要 一一判别,每次判别又要多次求交点,需要做 大量的乘除运算,花费很多时间。

扫描线多边形填充算法

扫描线多边形填充算法

扫描线多边形填充算法扫描线多边形填充算法(Scanline Polygon Fill Algorithm)是一种计算机图形学中广泛使用的算法,用于将一个封闭的多边形形状涂色填充。

它通过扫描线的方式,从上到下将多边形内的像素按照预设的填充颜色来进行填充。

本文将详细介绍扫描线多边形填充算法的原理、流程和实现细节。

1.算法原理:扫描线多边形填充算法基于扫描线的思想,在水平方向上扫描每一行像素,并检测多边形边界与扫描线的交点。

通过将扫描线从上到下扫过整个多边形,对于每一行像素,找出与多边形边界交点的水平线段,然后根据填充颜色将像素点进行填充。

2.算法流程:-找出多边形的最小和最大Y坐标,确定扫描线的范围。

-从最小Y坐标开始,到最大Y坐标结束,逐行进行扫描。

-对于每一行,找出与多边形边界交点的水平线段。

-根据填充颜色,为每个水平线段上的像素点进行填充。

3.算法实现:-首先,需要根据给定的多边形描述边界的顶点坐标,计算出每条边的斜率、最小和最大Y值以及每条边的X坐标交点。

-然后,对于每一扫描线,找出与多边形边界交点的水平线段,即找出交点的X坐标范围。

-最后,根据填充颜色,将该范围内的像素点进行填充。

4.算法优化:- 针对复杂多边形,可以使用活性边表(AET,Active Edge Table)来管理边界信息,加快查找交点的速度。

-可以使用桶排序来排序边界事件点,提高扫描速度。

-根据多边形边的特征,对算法进行优化,减少不必要的计算和内存消耗。

5.算法应用:-扫描线多边形填充算法广泛应用于计算机图形学中的图形渲染、图像处理等领域。

-在游戏开发、CAD绘图、虚拟现实等应用中,扫描线多边形填充算法被用于快速绘制和渲染复杂多边形。

总结:扫描线多边形填充算法是一种经典的计算机图形学算法,通过扫描线的方式对多边形进行填充。

它可以高效地处理各种形状的多边形,包括凸多边形和凹多边形。

算法虽然简单,但在实际应用中具有广泛的用途。

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

c语言多边形区域填充算法

c语言多边形区域填充算法

c语言多边形区域填充算法C语言多边形区域填充算法一、介绍多边形区域填充算法是计算机图形学中的一项重要技术,用于将给定的多边形区域进行填充,使其呈现出丰富的颜色或纹理,增强图形的效果和表现力。

本文将介绍一种常用的C语言多边形区域填充算法——扫描线填充算法。

二、扫描线填充算法原理扫描线填充算法是一种基于扫描线的填充方法,其基本思想是将多边形区域按照水平扫描线的顺序,从上到下逐行扫描,通过判断扫描线与多边形边界的交点个数来确定是否进入多边形区域。

具体步骤如下:1. 首先,确定多边形的边界,将其存储为一个边表。

边表中的每个边都包含起点和终点的坐标。

2. 创建一个活性边表(AET),用于存储当前扫描线与多边形边界的交点。

初始时,AET为空。

3. 从上到下逐行扫描多边形区域,对每一条扫描线,从边表中找出与该扫描线相交的边,并将其加入AET中。

4. 对于AET中的每一对交点,按照从左到右的顺序两两配对,形成水平线段,将其填充为指定的颜色或纹理。

5. 在扫描线的下一行,更新AET中的交点的坐标,然后重复步骤4,直到扫描到多边形区域的底部。

三、代码实现下面是一个简单的C语言实现扫描线填充算法的示例代码:```#include <stdio.h>#include <stdlib.h>#include <stdbool.h>typedef struct {int x;int y;} Point;typedef struct {int yMax;float x;float dx;int next;} Edge;void fillPolygon(int n, Point* points, int color) {// 获取多边形的边界int yMin = points[0].y;int yMax = points[0].y;for (int i = 1; i < n; i++) {if (points[i].y < yMin) {yMin = points[i].y;}if (points[i].y > yMax) {yMax = points[i].y;}}// 创建边表Edge* edges = (Edge*)malloc(sizeof(Edge) * n);int k = n - 1;for (int i = 0; i < n; i++) {if (points[i].y < points[k].y) {edges[i].yMax = points[k].y;edges[i].x = points[i].x;edges[i].dx = (float)(points[k].x - points[i].x) / (points[k].y - points[i].y);edges[i].next = k;} else {edges[i].yMax = points[i].y;edges[i].x = points[k].x;edges[i].dx = (float)(points[i].x - points[k].x) / (points[i].y - points[k].y);edges[i].next = i;}k = i;}// 扫描线填充for (int y = yMin; y < yMax; y++) {int xMin = INT_MAX;int xMax = INT_MIN;for (int i = 0; i < n; i++) {if (y >= edges[i].yMax) {continue;}edges[i].x += edges[i].dx;if (edges[i].x < xMin) {xMin = edges[i].x;}if (edges[i].x > xMax) {xMax = edges[i].x;}int j = edges[i].next;while (j != i) {edges[j].x += edges[j].dx; if (edges[j].x < xMin) {xMin = edges[j].x;}if (edges[j].x > xMax) {xMax = edges[j].x;}j = edges[j].next;}}for (int x = xMin; x < xMax; x++) { drawPixel(x, y, color);}}free(edges);}int main() {// 定义多边形的顶点坐标Point points[] = {{100, 100},{200, 200},{300, 150},{250, 100}};// 填充多边形区域为红色fillPolygon(4, points, RED);return 0;}```四、总结通过扫描线填充算法,我们可以实现对多边形区域的填充,从而提升图形的表现效果。

用扫描线算法实现多边形填充

用扫描线算法实现多边形填充

用扫描线算法实现多边形填充扫描线算法是一种用于多边形填充的有效方法。

它的思想是遍历扫描线,并在每条扫描线与多边形边界相交时填充相应的像素。

首先,我们需要了解多边形表示的方法。

多边形可以用一系列有序的边来表示,每条边由起点和终点坐标组成。

例如,一个三角形可以表示为三条线段的集合。

接下来,我们将介绍如何使用扫描线算法来实现多边形填充:1.首先,找到多边形的最大和最小y坐标,即多边形的上边界和下边界。

2.从上边界开始,逐条扫描线遍历到下边界。

3.在每条扫描线上,确定与多边形边界相交的线段。

4.根据与多边形边界相交的线段的起点和终点,找到对应的x坐标范围。

5.根据x坐标的范围,填充相应的像素。

下面是一个使用扫描线算法填充多边形的伪代码示例:```ScanLineFill(polygon):ymin = polygon.minYymax = polygon.maxYfor y from ymin to ymax:intersections = FindIntersections(polygon, y)sort(intersections)for i from 0 to length(intersections) - 1 by 2:xstart = intersections[i]xend = intersections[i+1]FillPixels(xstart, xend, y)```此伪代码的`FindIntersections`函数是用来找到多边形边界与当前扫描线相交的点,而`FillPixels`函数则用来填充相应的像素。

在实际实现时,可以使用一些数据结构来存储多边形的边界信息和扫描线与边界相交的点。

例如,可以使用边表来存储多边形的边界,使用活性边表来存储与当前扫描线相交的边界,使用扫描线来表示当前的扫描线位置。

算法的时间复杂度主要取决于扫描线与边界相交点的计算,可以通过使用边表和一些优化技巧来降低时间复杂度。

多边形扫描线填充算法技巧

多边形扫描线填充算法技巧

多边形扫描线填充算法技巧扫描线填充算法是计算机图形学中常用的一种填充算法,用于对多边形进行填充。

其基本原理是通过扫描线与多边形边界的交点来确定需要填充的像素点。

本文将介绍多边形扫描线填充算法的基本思想以及一些常用的优化技巧。

一、基本思想多边形扫描线填充算法的基本思想是将多边形分解成一系列水平线段,然后对每条水平线段进行扫描,找出与多边形边界相交的点,并进行填充。

具体步骤如下:1. 确定多边形的边界:对于给定的多边形,首先需要确定其边界。

可以使用边界表(edge table)来存储多边形的边界信息,包括每条边的起点和终点坐标以及斜率等。

2. 初始化扫描线:从多边形边界中找出最小的y坐标和最大的y坐标,作为扫描线的起点和终点。

3. 扫描线算法:对于每条扫描线,通过遍历边界表,找出与扫描线相交的边界线段。

根据相交点的x坐标,确定需要填充的像素点范围。

4. 填充像素点:根据上一步确定的像素点范围,将扫描线上的像素点进行填充。

二、技巧和优化1. 边界表的构建:为了提高算法的效率,可以对边界表进行排序,按照扫描线的y坐标来排序。

这样可以减少对边界表的遍历次数,提高算法的执行速度。

2. 边界交点的计算:在扫描线算法中,需要计算扫描线与多边形边界的交点。

可以使用活性边表(active edge table)来存储当前与扫描线相交的边界线段,并根据交点的x坐标进行排序。

这样可以减少计算交点的次数,提高算法的效率。

3. 填充像素点的优化:在填充像素点时,可以使用扫描线种子填充算法来进行优化。

该算法通过选择合适的填充起点,在填充过程中自动推进扫描线,减少不必要的计算和填充操作,提高填充的速度。

4. 填充规则的处理:在实际应用中,可能会遇到一些特殊情况,如多边形内部有孔洞或交叉等。

针对这些情况,可以通过修改填充规则来处理。

常用的填充规则有奇偶填充规则和非零填充规则,可以根据实际情况选择合适的填充规则。

5. 像素点颜色的处理:在多边形填充过程中,可以通过设置填充的颜色或纹理来实现不同的效果。

实验二:图形填充算法实验报告

实验二:图形填充算法实验报告

《计算机图形学》实验报告(实验二:图形填充算法)一、实验目的及要求用两种方法做图形的填充算法!二、理论基础1.边填充算法对于每一条扫描线和每条多边形的交点(x1,y1),将该扫描线上的交点右方的所有像素取补。

2.种子填充算法利用栈来实现种子填充算法。

种子像素入栈,当栈非空时重复执行如下步骤:将栈顶像素出栈,将出栈像素置成多边形色,按左,上,右,下顺序检查与出栈像素相邻的四个像素,若其中某个像素不再边界且未置成多边形,则把该像素入栈!三、算法设计与分析1、边填充算法void CEdge_mark_fillView::OnDraw(CDC* pDC){CEdge_mark_fillDoc* pDoc = GetDocument();ASSERT_V ALID(pDoc);int d[500][500]={0};int inside;int x,y;Bresenham(80,101,100,400,d);Bresenham(100,300,290,400,d);Bresenham(292,400,382,50,d);Bresenham(380,50,202,150,d);Bresenham(200,150,82,101,d);for(y=0;y<500;y++){inside=0;for(x=0;x<500;x++){if(d[x][y]==1)if(d[x+1][y]!=1){inside=!(inside);}if(inside!=0)pDC->SetPixel(x,y,12);}}}2、种子填充int x=299,y=51;COLORREF oldcolor;COLORREF newcolor;oldcolor=RGB(256,256,256);newcolor=RGB(123,123,123);pDC->MoveTo (40,40);pDC->LineTo (80,40);pDC->LineTo (70,80);pDC->LineTo (40,40);FloodFill(51,51,RGB(255,255,255),RGB(0,0,255));pDC->LineTo (40,40);void CMyView::FloodFill(int x,int y,COLORREF oldcolor,COLORREF newcolor) {CDC* pDC;pDC=GetDC();if(pDC->GetPixel(x,y)==oldcolor){pDC->SetPixel(x,y,newcolor);FloodFill(x,y-1,oldcolor,newcolor);FloodFill(x,y+1,oldcolor,newcolor);FloodFill(x-1,y,oldcolor,newcolor);FloodFill(x+1,y,oldcolor,newcolor);}四、程序调试及结果的分析1、2、四、实验心得及建议由于很多不会,所以这次没能按时当堂完成,下来花了不少时间才弄出来,第二种尤其比较麻烦,在同学的帮助下才做出来了。

OSG多边形的填充实验二

OSG多边形的填充实验二

天津理工大学计算机科学与技术学院实验报告2015 至2016 学年第二学期源代码:#include<osg/Geode>#include<osgDB/ReadFile>#include<osgUtil/SmoothingVisitor>#include<osgViewer/Viewer>#include<osg/Matrixd>#include<osg/MatrixTransform>#include<osg/ShapeDrawable>#include<osg/Texture2D>#include<osgGA/GUIEventHandler>#include<vector>#include<osgText/Text>#include <osg/PositionAttitudeTransform>#include<stdlib.h>#include<time.h>#define MIN 0 //随机数产生的范围#define MAX 1osg::Group* createLight2(osg::Node*);class UseEventHandler:public osgGA::GUIEventHandler{private:float angle;float move;float scale;public :UseEventHandler(){angle=0;move=0;scale=1;}virtual bool handle (const osgGA::GUIEventAdapter&ea,osgGA::GUIActionAdapter& aa){osgViewer::Viewer *viewer =dynamic_cast<osgViewer::Viewer*>(&aa);if(!viewer)return false;osg::ref_ptr<osg::MatrixTransform> root =dynamic_cast<osg::MatrixTransform*>(viewer ->getSceneData());if(!root)return false;switch(ea.getEventType()){case osgGA::GUIEventAdapter::KEYDOWN:{if(ea.getKey()=='w'){angle +=osg::PI_2/90;root->setMatrix(osg::Matrix::translate(-5,-5,0)*osg::Matrix::rota te(angle,osg::Vec3(0,0,1)*osg::Matrix::translate(5,5,0)));}else if(ea.getKey()=='s'){angle -=osg::PI_2/90;root->setMatrix(osg::Matrix::translate(-5,-5,0)*osg::Matrix::rota te(angle,osg::Vec3(0,0,1)*osg::Matrix::translate(5,5,0)));}else if(ea.getKey()=='q'){scale+=0.1;root->setMatrix(osg::Matrix::scale(scale,scale,scale));}else if(ea.getKey()=='a'){scale-=0.1;root->setMatrix(osg::Matrix::scale(scale,scale,scale));}else if(ea.getKey()=='e'){move+=1;root->setMatrix(osg::Matrix::translate(0,0,move));}else if(ea.getKey()=='d'){move-=1;root->setMatrix(osg::Matrix::translate(0,0,move));}return true ;}break;default:break;}return false;}};osg::Group* PaintMountainImprove(){osg::ref_ptr<osg::Node> node = new osg::Node;osg::ref_ptr<osg::Group> group = new osg::Group;osg::ref_ptr<osg::Geode> geode =new osg::Geode;osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;osg::ref_ptr<osg::MatrixTransform> transform =newosg::MatrixTransform;osg::ref_ptr<osg::MatrixTransform> childTransform[10];osg::ref_ptr<osg::Geometry> geometry1[10];osg::ref_ptr<osg::Geode> geode1[10];osg::ref_ptr<osg::Vec3Array> point[10];osg::ref_ptr<osg::Vec3Array> colors[10];osg::ref_ptr<osg::Vec3Array>normals=new osg::Vec3Array;srand((unsigned)time(0));double hight=0;int trans=0;for(int i=0;i<10;i++){childTransform[i]=new osg::MatrixTransform;geometry1[i] = new osg::Geometry;geode1[i] = new osg::Geode;point[i]=new osg::Vec3Array;colors[i]=new osg::Vec3Array;geometry1[i]->setVertexArray(point[i].get());geode1[i]->addDrawable(geometry1[i].get());childTransform[i]->addChild(geode1[i].get());transform->addChild(childTransform[i]);for(int j=0;j<10;j++){if(i==0||j==0||j==9||i==9){hight=0;}else{hight=MIN + 5*(int)MAX * rand() / (RAND_MAX);}trans=int(hight*4096/5);point[i]->push_back (osg::Vec3(j,i,hight));colors[i]->push_back(osg::Vec3(0,((trans/16)%16)/16.0,0));}}normals->push_back(osg::Vec3(0,0,1));osg::ref_ptr<osg::Vec3Array> point1=new osg::Vec3Array;osg::ref_ptr<osg::Vec3Array>colors1=new osg::Vec3Array;osg::ref_ptr<osg::Vec2Array> texCoord =new osg::Vec2Array;for(int i=0;i<9;i++){for(int j=0;j<9;j++){point1->push_back(point[i]->at(j));point1->push_back(point[i]->at(j+1));point1->push_back(point[i+1]->at(j));point1->push_back(point[i+1]->at(j+1));colors1->push_back(colors[i]->at(j));colors1->push_back(colors[i]->at(j+1));colors1->push_back(colors[i+1]->at(j));colors1->push_back(colors[i+1]->at(j+1));texCoord->push_back(osg::Vec2(0.1*j,0.1*i));texCoord->push_back(osg::Vec2(0.1*(j+1),0.1*i));texCoord->push_back(osg::Vec2(0.1*j,0.1*(i+1)));texCoord->push_back(osg::Vec2(0.1*(j+1),0.1*(i+1)));geometry->addPrimitiveSet(newosg::DrawArrays(osg::DrawArrays::TRIANGLE_STRIP,i*36+j*4,4));}}point1->push_back(osg::Vec3(0,0,0));point1->push_back(osg::Vec3(9,0,0));point1->push_back(osg::Vec3(9,9,0));point1->push_back(osg::Vec3(0,9,0));colors1->push_back(osg::Vec3(0,0,0));colors1->push_back(osg::Vec3(0,0,0));colors1->push_back(osg::Vec3(0,0,0));colors1->push_back(osg::Vec3(0,0,0));texCoord->push_back(osg::Vec2(0,0));texCoord->push_back(osg::Vec2(0,1));texCoord->push_back(osg::Vec2(1,0));texCoord->push_back(osg::Vec2(1,1));geometry->setVertexArray(point1.get());//geometry->setTexCoordArray(0,texCoord.get());geometry->setColorArray(colors1.get());geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);geometry->setNormalArray(normals.get());geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);geometry->addPrimitiveSet(newosg::DrawArrays(osg::DrawArrays::QUADS,9*36,4));//geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, new osg::Texture2D(osgDB::readImageFile("map.png")));geode->addDrawable(geometry);group->addChild(geode.get());group->addChild(createLight2(group.get()));return group.release();}osg::Group* createLight2(osg::Node* pNode){osg::Group* lightGroup = new osg::Group;osg::BoundingSphere bs = pNode->getBound();osg::ref_ptr<osg::Light> light = new osg::Light();light->setLightNum(0);light->setDirection(osg::Vec3(0.0f, 0.0f, -1.0f));light->setPosition(osg::Vec4(bs.center().x(), bs.center().y(), bs.center().z() + bs.radius(), 0.0f));light->setAmbient(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));light->setDiffuse(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));light->setConstantAttenuation(1.0f);light->setLinearAttenuation(0.0f);osg::ref_ptr<osg::LightSource> lightSource = newosg::LightSource();lightSource->setLight(light.get());return lightSource.release();}int main(int argc,char **argv){osg::ref_ptr<osg::MatrixTransform> root = new osg::MatrixTransform;root->addChild(PaintMountainImprove());osgViewer::Viewer viewer;viewer .setSceneData(root.get());viewer.addEventHandler(new UseEventHandler());//viewer.setSceneData(createHouseWall());//while(1){return viewer.run();//}}。

多边形填充算法本

多边形填充算法本

多边形填充算法本在计算机图形学中,使用多边形填充算法可以实现各种图形的绘制,例如:圆形、椭圆形、字母等。

对于任意形状的多边形来说,其内部像素点的坐标是无法直接计算得到的,因此需要通过一定的算法来实现。

常见的多边形填充算法有扫描线填充算法和边界填充算法。

接下来我们来详细了解这两种算法。

扫描线填充算法是通过扫描多边形上的每一条水平线,找到与多边形相交的线段,并进行填充操作。

具体步骤如下:1.找到多边形的最高点和最低点,作为扫描线的起点和终点。

2.将扫描线从起点依次向下移动,直到到达终点。

3.在每一条扫描线上,找到与多边形相交的线段。

4.根据线段的起点和终点,计算交点的x坐标,并从起点到终点对应的像素点进行填充。

5.重复步骤4,直到所有的扫描线都处理完毕。

扫描线填充算法的优点是简单易懂,适用于一般情况。

但是对于复杂的多边形来说,会存在边界交叉的情况,需要特殊处理。

边界填充算法是通过检测多边形的边界点,并进行填充操作。

具体步骤如下:1.找到多边形的最左边、最右边、最上边和最下边的点,作为边界点。

2.从最上边的点开始,依次向下遍历每一行像素点。

3.在每一行中,寻找与多边形边界相交的点,并进行填充操作。

4.重复步骤3,直到到达最下边的点。

边界填充算法的优点是对具有复杂交叉边界的多边形也能进行正确的填充操作。

但是对于非凸多边形来说,边界填充算法可能会有空隙出现。

除了以上两种常见的多边形填充算法,还有其他一些算法也可以实现多边形的填充操作,例如:扫描转换填充算法、边界边框填充算法等。

在实际应用中,多边形填充算法通常结合图形处理库或者计算机图形学软件来实现。

这些软件提供了丰富的函数和方法,可以直接调用进行多边形的填充操作。

综上所述,多边形填充算法是计算机图形学中的一个重要算法。

通过扫描线填充算法或者边界填充算法,可以实现对任意形状多边形的填充操作。

随着计算机图形学的发展,多边形填充算法也不断进化和优化,以满足不同应用场景的需求。

多边形填充算法实验报告

多边形填充算法实验报告

学生实验报告
实验课名称:计算机图形学
实验项目名称:多边形填充算法
专业名称:计算机科学与技术
班级:
学号:
学生姓名:
教师姓名:
2016年4月30 日
六.运行结果与分析:
图1:扫描线种子填充算法
图2:种子填充算法
七.实验中遇到的问题、解决方法及体会:
多边形的填充对我来说很困难,因为一开始我不知道要输入什么数据,后来我决定要输入五个点来形成一个五边形,但是输入的顺序是一个大问题。

后来我采取顺序输入的方法,但是程序运行时常常崩溃,结果不尽人意。

最后,我在同班同学的帮助之下,找到了自己的问题,完成了填充。

计算机图形学实验报告-多边形的扫描转换与区域填充

计算机图形学实验报告-多边形的扫描转换与区域填充

计算机科学与技术学院2013-2014学年第一学期《计算机图形学》实验报告班级:学号:姓名:教师:成绩:实验项目(2、多边形的扫描转换与区域填充)一、实验目的与要求(1)了解多边形扫描转换的各种算法,掌握多边形的扫描转换与区域填充算法。

(2)进一步掌握在VC集成环境中实现图形算法的方法与过程。

二、实验内容设计菜单程序,利用消息处理函数,完成以下要求:(1)给出凸多边形的若干顶点(3 ~ 5个),实现多边形的“x扫描算法”。

(2)实现种子填充,泛填充算法(四邻法)。

(3)设计程序,实现判断一个点是否在多边形区域内部。

三、重要算法分析(一)边界表示的四连通区域种子填充算法此方法的基本思想是,从多边形内部任一像素出发,按照“左上右下”的顺序判断相邻像素,若不是边界像素且没有被填充过,则对其填充,并且重复上述过程,直到所有像素填充完毕。

(1)从种子点出发,向左判断多边形内部颜色,如果不是填充颜色并且不是边界颜色,则填充,直到遇到边界为止。

(2)从种子点出发,向右判断多边形内部颜色,如果不是填充颜色并且不是边界颜色,则填充,直到遇到边界为止。

(3)将种子点的坐标y值上移一个像素,重复步骤(1)、(2)直到遇到上面边界为止。

(4)将种子点的坐标y值下移一个像素,重复步骤(1)、(2)直到遇到上面边界为止。

(二)判断一个点是否在多边形内部解决方案是将测试点的y坐标与多边形的每一个点进行比较,我们会得到一个测试点所在的行与多边形边的交点的列表。

如果测试点的两边点的个数都是奇数个则该测试点在多边形内,否则在多边形外。

如图1所示,判断点(红点)y值左边与多边形有5个交点,右边与多边形有3个交点,则该点在多边形内部。

图1如图2所示,判断点(红点)y值左边与多边形有2个交点,右边与多边形有2个交点,则该点在多边形外部。

图2但是有一种特殊情况须特别处理一下,当与多边形顶点相交时,需要将改点计算为两个交点,如图3所示:图3四、程序运行截图1.种子四连通域填充法,如图4所示。

(计算机图形学)多边形区域扫描线填充或种子填充

(计算机图形学)多边形区域扫描线填充或种子填充

实验2:多边形区域扫描线填充或种子填充实验类型:验证、设计所需时间:3学时主要实验内容及要求:实现多边形区域扫描线填充的有序边表算法,并将实现的算法应用于任意多边形的填充,要求多边形的顶点由键盘输入或鼠标拾取,填充要准确,不能多填也不能少填。

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

参考试验步骤:1)分析多边形区域扫描线填充算法的原理,确定算法流程①初始化:构造边表,AET表置空②将第一个不空的ET表中的边插入AET表③由AET表取出交点进行配对(奇偶)获得填充区间,依次对这些填充区间着色④y=y i+1时,根据x=x i+1/k修改AET表所有结点中交点的x坐标。

同时如果相应的ET表不空,则将其中的结点插入AET表,形成新的AET表⑤AET表不空,则转(3),否则结束。

2)编程实现①首先确定多边形顶点和ET/AET表中结点的结构②编写链表相关操作(如链表结点插入、删除和排序等)③根据1)中的算法结合上述已有的链表操作函数实现多边形区域扫描线填充的主体功能④编写主函数,测试该算法源代码:#include<gl/glut.h>#include<iostream>using namespace std;typedef struct dePt{int x;int y;}dePt;void fill(GLint x1,GLint y1,GLint z1){glBegin(GL_POINTS);glVertex3f(x1,y1,0.0f);glEnd();}typedef struct Edge{int yUpper;float xIntersect, dxPerScan;struct Edge *next;}Edge;void insertEdge(Edge *list, Edge *edge){Edge *p,*q=list;p=q->next;while(p!=NULL){if(edge->xIntersect<p->xIntersect)p=NULL;else{q=p;p=p->next;}}edge->next=q->next;q->next=edge;}int yNext(int k, int cnt, dePt*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;else j++;return (pts[j].y);}void makeEdgeRec(dePt lower, dePt 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,dePt *pts,Edge *edges[]){Edge *edge;dePt 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){edge=(Edge *)malloc(sizeof(Edge));if(v1.y<v2.y)makeEdgeRec(v1,v2,yNext(i,cnt,pts),edge,edges);elsemakeEdgeRec(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++)fill((int)i,scan,3);p1=p2->next;}}void deleteAfter(Edge *q){Edge *p=q->next;q->next=p->next;free(p);}void updateActiveList(int scan,Edge *active) {Edge *q=active, *p=active->next;while(p)if(scan>=p->yUpper){p=p->next;deleteAfter(q);}else{p->xIntersect=p->xIntersect+p->dxPerScan; q=p;p=p->next;}}void resortActiveList(Edge *active){Edge *q,*p=active->next;active->next=NULL;while(p){q=p->next;insertEdge(active,p);p=q;}}void scanFill(int cnt,dePt *pts){Edge *edges[1024],*active;int i,scan;for(i=0;i<1024;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<1024;scan++)buildActiveList(scan,active,edges);if(active->next){fillScan(scan,active);updateActiveList(scan,active);resortActiveList(active);}}}void ChangeSize(GLsizei w,GLsizei h){GLfloat nRange=400.0f;if(h==0) h=1;glViewport(0,0,w,h);glMatrixMode(GL_PROJECTION);glLoadIdentity();if(w<=h)glOrtho(-nRange,nRange,-nRange*h/w,nRange*h/w,-nRange,nRange);elseglOrtho(-nRange*h/w,nRange*h/w,-nRange,nRange,-nRange,nRange); glMatrixMode(GL_MODELVIEW);glLoadIdentity();}void Display(void){glClear(GL_COLOR_BUFFER_BIT);glLineWidth(5.0);int n,x,y,i;cout<<"请输入多边形顶点数:"<<endl;cin>>n;dePt *t=new dePt[n];for(i=0;i<n;i++){cout<<"请输入第"<<i+1<<"个顶点坐标"<<endl;cin>>x>>y;t[i].x=x;t[i].y=y;glVertex2i(t[i].x,t[i].y);} glEnd();glFlush();scanFill(n,t);glFlush();}void SetupRC()glClearColor(1.0f,1.0f,1.0f,1.0f); glColor3f(1.0f,0.0f,0.0f);}实验结果:。

实验报告(种子填充算法)

实验报告(种子填充算法)

实验报告(种子填充算法)一、实验目的熟悉种子填充算法,并编写程序实现。

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

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

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

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

从区域内任意一点出发,通过上、下、左、右四个方向到达区域内的任意像素。

用这种方法填充的区域就称为四连通域;这种填充方法称为四向连通算法。

从区域内任意一点出发,通过上、下、左、右、左上、左下、右上和右下八个方向到达区域内的任意像素。

用这种方法填充的区域就称为八连通域;这种填充方法称为八向连通算法。

一般来说,八向连通算法可以填充四向连通区域,而四向连通算法有时不能填充八向连通区域。

四向连通填充算法:1、初始化种子点栈。

置种子点栈为空栈,并将给定的种子点入栈;2、出栈。

若种子点栈为空,算法结束;否则,取栈顶元素(x,y )为种子点;3、区域填充。

从种子点(x,y )开始沿着坐标为y 的当前扫描线向左右两个方向逐像素进行填色,其颜色值置为F_color ,直至到达区域边界。

分别以left x 和right x 表示该填充区段两端点的横坐标;4、新种子点入栈。

分别确定当前扫描线上、下相邻的两条扫描线上位于区段[]right left x x ,内的区域内的区段。

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

四向连通填充方法可以用递归函数实现如下:void BoundaryFill4(int x, int y, long FilledColor, long BoundaryColor){long CurrentColor;CurrentColor = GetPixelColor(x,y);if (CurrentColor != BoundaryColor && CurrentColor != FilledColor){SetColor(FilledColor);SetPixel (x,y);BoundaryFill4(x+1, y, FilledColor, BoundaryColor);BoundaryFill4(x-1, y, FilledColor, BoundaryColor);BoundaryFill4(x, y+1, FilledColor, BoundaryColor);BoundaryFill4(x, y-1, FilledColor, BoundaryColor);}}三、实验程序#include <graphics.h>#include <math.h>#include <conio.h>#include <vector>using namespace std;vector<int> x_vector;vector<int> y_vector;void ScanLineFill4 ( int x, int y, int oldcolor, int newcolor){int xl, xr,ox,oy;//扫描线的左边界和右边界,or,oy是x,y点的备份bool spanNeedFill;//将来用于标记,是否有需要填充的点,在扫描的时候x_vector.push_back(x); y_vector.push_back(y);//出栈while (!x_vector.empty()){//y = pt.y; x = pt.x;//x,y是处理的目标点y=y_vector.back(); oy = y; y_vector.pop_back();x=x_vector.back(); ox=x; x_vector.pop_back();while (getpixel (x,y) == oldcolor)// 从种子点开始向右填充{putpixel (x, y, newcolor);x ++;}xr = x -1;//右边界以确定x = ox - 1;//更新x为种子点的左边一个点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)// 把这个需要填充的点入栈{x_vector.push_back(x-1); y_vector.push_back(y);spanNeedFill = FALSE;}while ((getpixel (x, y) != oldcolor) && (x < xr)) //继续向右搜索,为了快速不需要填充的点用这个while循环还跳过x ++;} // 上一条扫描线上检查完毕x = xl; y = y - 2; // 处理下面一条扫描线,种子点是左边界下面那个点while (x < xr){spanNeedFill = FALSE;while (getpixel (x, y) == oldcolor)//向右检查{spanNeedFill = TRUE; x ++;}if (spanNeedFill)//遇到需要填充的点就入栈{x_vector.push_back(x-1); y_vector.push_back(y);spanNeedFill = FALSE;}while ((getpixel (x,y) != oldcolor) && (x < xr))//下载开始之前,先跳过,不需要填充的点,加快速度x ++;}}}int customCode(int argc,char** argv){line(312,20,20,320);line(20,320,500,400);line(500,400,610,300);line(610,300,48,56);line(48,56,312,20);ScanLineFill4(312,200,BLACK,RED);return 0;}int main(int argc,char** argv){int code;int gdriver = DETECT,gmode;initgraph(&gdriver, &gmode, " ");//初始化,窗口大小为639*479cleardevice();//清屏setbkcolor(BLACK);//背景色setcolor(WHITE);//图形色code = customCode(argc,argv);//用户代码getch();//暂停程序closegraph();//退出图形库return code;}/*可用颜色BLACK 0 黑色BLUE 1 蓝色GREEN 2 绿色CYAN 3 青红色RED 4 红色MAGENTA 5 紫红色BROWN 6 棕色LIGHTGRAY 7 浅灰色DARKGRAY 8 深灰色LIGHTBLUE 9 浅兰色LIGHTGREEN 10 浅绿色LIGHTCYAN 11 浅青色LIGHTRED 12 浅红色LIGHTMAGENTA 13 浅紫色YELLOW 14 黄色WHITE 15 白色*/四、测试结果五、实验总结上述算法的优点是非常简单,缺点是需要大量栈空间来存储相邻的点。

区域填充算法区域填充算法

区域填充算法区域填充算法

区域填充算法区域填充算法
下面将介绍两种常见的区域填充算法:扫描线填充算法和种子填充算法。

1. 扫描线填充算法(Scanline Fill Algorithm):
-扫描线填充算法基于扫描线的原理,从图像的上方向下扫描,对每条扫描线上的像素进行填充。

-算法流程如下:
-选择一个初始扫描线,例如选择图像的最上面一条扫描线;
-遍历该扫描线上的每一个像素,判断是否需要填充该像素;
-如果需要填充,则向区域内部延伸扫描线,同时判断该扫描线上的相邻像素是否需要填充;
-一直延伸扫描线,直到整个区域被填充完毕。

-扫描线填充算法的优点是简单、易于实现,但是算法的效率较低,在处理大尺寸区域时耗时较长。

2. 种子填充算法(Seed Fill Algorithm):
-种子填充算法基于种子点的概念,选择一个起始点作为种子点,然后根据预设的填充规则进行填充。

-算法流程如下:
-选择一个起始点作为种子点,将该点填充上颜色;
-判断该种子点的相邻像素是否需要填充,如果需要则将其填充;
-一直延伸填充,直到整个区域被填充完毕。

-种子填充算法的优点是效率较高,能够处理较大的区域,但是需要选择合适的填充规则,否则可能会导致填充区域不准确或者出现漏填的情况。

以上两种区域填充算法在实际应用中会根据具体的场景和需求选择合适的算法进行使用。

在实际实现时,还需要考虑一些特殊情况,如图像边界处理、扫描顺序等,以确保算法的正确性和效率。

任意多边形区域的快速填充算法

任意多边形区域的快速填充算法

任意多边形区域的快速填充算法一、前言任意多边形区域的快速填充算法是计算机图形学中的一个重要问题,其应用广泛,例如在计算机游戏、数字地图等领域中都有广泛的应用。

本文将介绍几种常见的任意多边形区域的快速填充算法,包括扫描线算法、边界填充算法、种子填充算法等。

二、扫描线算法扫描线算法是一种基于扫描线原理的填充算法,其基本思想是将区域划分为若干个水平方向上的扫描线,然后在每条扫描线上找到交点,并根据交点进行填充。

具体步骤如下:1. 将多边形顶点按照纵坐标从小到大排序;2. 从最小纵坐标开始,依次向上扫描每条水平方向上的线段;3. 对于每条水平方向上的线段,找到与之相交的多边形边界,并记录下所有交点;4. 根据相邻两个交点之间是否为奇数个来确定是否需要进行填充。

三、边界填充算法边界填充算法也是一种常见的任意多边形区域的快速填充算法,其基本思想是通过递归调用来进行填充。

具体步骤如下:1. 对于每个多边形边界上的像素点,将其标记为“边界点”;2. 从任意一个未填充的内部像素点开始,向四周搜索,如果遇到“边界点”则停止搜索,并将搜索路径上的所有像素点标记为已填充;3. 重复步骤2直到所有内部像素点都被填充。

四、种子填充算法种子填充算法也是一种常见的任意多边形区域的快速填充算法,其基本思想是通过找到一个内部像素点作为“种子”,然后向四周扩散进行填充。

具体步骤如下:1. 随机选择一个内部像素点作为“种子”,并将其标记为已填充;2. 向四周搜索,如果遇到未被标记为已填充的像素,则将其标记为已填充,并加入到待处理列表中;3. 重复步骤2直到待处理列表为空。

五、总结以上介绍了几种常见的任意多边形区域的快速填充算法,每种算法都有其特定的优缺点,选择合适的算法需要根据具体的应用场景进行考虑。

在实际应用中,还需要考虑算法的效率、稳定性、可扩展性等方面的问题。

扫描线多边形填充算法

扫描线多边形填充算法

扫描线多边形填充算法扫描线多边形填充算法是计算机图形学中常用的一种算法。

该算法在计算机图形学中的应用非常广泛,主要用于生成3D模型、游戏场景绘制、平面设计和室内设计等领域。

扫描线多边形填充算法是一种基于扫描线的绘制算法,利用扫描线的方法逐行扫描多边形,根据多边形的边界信息实现多边形的填充效果。

该算法能够处理任意形状的多边形,准确率较高且填充速度较快,广泛被应用于图形处理和计算机图形学领域,成为常用的图形算法之一。

扫描线多边形填充算法的基本思路是利用扫描线的思想,从上到下逐行扫描多边形,记录下所有交点,然后分别对相邻的两个交点进行连线,将扫描线与多边形的交点看做是线段的端点,用线段相交的奇偶性来决定颜色的填充,最后形成连续的填充区域。

扫描线多边形填充算法的实现需要经过以下步骤:1. 对多边形边界进行预处理,将所有边界的交点按照从上到下的顺序排列,如果存在重合的交点则去重。

2. 从上到下遍历扫描线,记录扫描线与多边形边界的交点。

3. 将相邻的两个交点之间的线段按照扫描线的方向进行连线。

4. 判断连线与多边形每条边的交点数目,通过判断奇偶性来确定线段的颜色填充方向,奇数填充,偶数不填充。

5. 当扫描线遍历到最后一行时,多边形的填充工作完成。

扫描线多边形填充算法的优点是填充精度高、处理速度快、容易实现、适用于各种形状的图形等,但也存在一些缺点,例如,不能处理自交和孔洞性多边形,所以需要在实际应用中注意多边形的选择。

总的来说,扫描线多边形填充算法具有重要的应用价值,对于计算机图形学领域中的制图、绘制和渲染方面有着广泛的应用,实现算法的关键在于正确处理多边形的交点和判断线段的奇偶性,只有正确理解这些问题才能够在实践中使用扫描线多边形填充算法进行有效的图形填充处理。

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

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

2007-11-06 16:20 区域填充的扫描线算法(C源程序)-------计算机图形学实验/** 区域填充的扫描线算法* (适合用于内点表示的4连通区域)*算法的基本过程:* 当给定种子点(x,y)时,首先填充种子点所在的扫描线上的位于给定区域的*一个区段,然后确定与这一区段连通的上下两条扫描线上位于给定区域的区段,并*依次保存下来。

反复这个过程,直到填充结束.** 操作系统: Windows XP* 运行环境:Win-TC* 作者:范雷* 时间: 2007.10.27*/#include "Conio.h"#include "graphics.h" /*for initgr()*/#include "stdio.h" /*for NULL */#define closegr closegraphvoid initgr(void) /* BGI初始化*/{int gd = DETECT, gm = 0; /* 和gd = VGA,gm = VGAHI是同样效果*/ registerbgidriver(EGA VGA_driver);/* 注册BGI驱动后可以不需要.BGI文件的支持运行*/ initgraph(&gd, &gm, "");}enum BOOL{FALSE = 0, TRUE = 1};typedef struct{int y;int xLeft;int xRight;}Span;/*区段*/typedef struct stacknode{Span span;struct stacknode *next;}stacknode;typedef struct{stacknode *top;}linkstack;/*-----------------进栈操作----------------------------------------*/void PushStack(linkstack *s, Span *span)stacknode *p=(stacknode*)malloc(sizeof(stacknode));p->span.y = span->y;p->span.xLeft = span->xLeft;p->span.xRight = span->xRight;p->next=s->top;s->top=p;}/*-----------------出栈操作------------------------------------------*/ void PopStack(linkstack *s,Span *span){int x;stacknode *p=s->top;span->y = p->span.y;span->xLeft = p->span.xLeft;span->xRight = p->span.xRight;s->top=p->next;free(p);}/*-----------------将栈清空------------------------------------------*/ void SetStackEmpty(linkstack *s){stacknode *p=s->top;while( s->top != NULL){free(p);s->top=p->next;}}/*--------------判断栈是否为空----------------------------------------*/ int IsStackEmpty(linkstack *s){if(s->top == NULL)return 1;elsereturn 0;}/*----------------核心程序开始----------------------------------------*/ void ScanLineFill4(int x,int y,int oldColor,int newColor){int xLeft,xRight;int i;enum BOOL isLeftEndSet, spanNeedFill;Span span;linkstack *s=(linkstack*)malloc(sizeof(linkstack));s->top = NULL;/*填充并确定种子点(x,y)所在的区段*/i = x;while(getpixel(i,y) == oldColor)/*向右填充*/{putpixel(i,y,newColor);i++;}span.xRight = i - 1; /*确定区段右边界*/i = x - 1;while(getpixel(i,y) == oldColor)/*向左填充*/{putpixel(i,y,newColor);i--;}span.xLeft = i + 1; /*确定区段左边界*//*初始化*/SetStackEmpty(s);span.y = y;PushStack(s,&span);/*将前面生成的区段压入堆栈*/while( ! IsStackEmpty(s) )/*终止判断*/{/*出栈*/PopStack(s, &span);/*处理上面扫描线*/y = span.y + 1;xRight = span.xRight;i = span.xLeft - 1;isLeftEndSet = FALSE;while(getpixel(i,y) == oldColor)/*向左填充*/{putpixel(i, y, newColor);i--;}if( i != span.xLeft - 1)/*确定区段左边界*/{isLeftEndSet = TRUE;xLeft = i + 1;}i = span.xLeft;while( i < xRight){spanNeedFill = FALSE;while(getpixel(i,y) == oldColor) /*向右填充*/{if( ! spanNeedFill){spanNeedFill = TRUE;if( ! isLeftEndSet){isLeftEndSet = TRUE;xLeft = i;}}putpixel(i,y,newColor);i++;}if( spanNeedFill ){span.y = y;span.xLeft = xLeft;span.xRight = i - 1;PushStack(s, &span); /*将区段压入堆栈*/isLeftEndSet = FALSE;spanNeedFill = FALSE;}/* while(getpixel(i,y) != oldColor) */i++;}/*end of while( i < xRight) *//*处理下面一条扫描线,与处理上面一条扫描线完全类似*/ y = y - 2;xRight = span.xRight;i = span.xLeft - 1;isLeftEndSet = FALSE;while(getpixel(i,y) == oldColor)/*向左填充*/{putpixel(i, y, newColor);i--;}if( i != span.xLeft - 1)/*确定区段左边界*/{isLeftEndSet = TRUE;xLeft = i + 1;}i = span.xLeft;while( i < xRight){spanNeedFill = FALSE;while(getpixel(i,y) == oldColor) /*向右填充*/{if( ! spanNeedFill){spanNeedFill = TRUE;if( ! isLeftEndSet){isLeftEndSet = TRUE;xLeft = i;}}putpixel(i,y,newColor);i++;}if( spanNeedFill ){span.y = y;span.xLeft = xLeft;span.xRight = i - 1;PushStack(s, &span); /*将区段压入堆栈*/isLeftEndSet = FALSE;spanNeedFill = FALSE;}/* while(getpixel(i,y) != oldColor) */i++;}/*end of while( i < xRight) */delay(2000); /*延时*/}/*end of while( ! isStackEmpty() ) */}/*end of ScanLineFill4() *//*---------------------main()------------------------------------------*/ int main(){initgr(); /* BGI初始化*/setbkcolor(3);setcolor(5);moveto(50, 50); /*绘制4连通区域*/lineto(400, 50);lineto(400,300);lineto(150,300);lineto(150,400);lineto(50, 400);lineto(50, 50);ScanLineFill4(150,150,0,14); /*相与后oldColor == 0*/getch(); /* 暂停一下,看看前面绘图代码的运行结果*/ closegr(); /* 恢复TEXT屏幕模式*/return 0;。

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

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

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

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

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

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

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

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

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

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

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

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

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

算法系列之十二:多边形区域填充算法--扫描线种子填充算法

算法系列之十二:多边形区域填充算法--扫描线种子填充算法

算法系列之⼗⼆:多边形区域填充算法--扫描线种⼦填充算法1.3扫描线种⼦填充算法1.1和1.2节介绍的两种种⼦填充算法的优点是⾮常简单,缺点是使⽤了递归算法,这不但需要⼤量栈空间来存储相邻的点,⽽且效率不⾼。

为了减少算法中的递归调⽤,节省栈空间的使⽤,⼈们提出了很多改进算法,其中⼀种就是扫描线种⼦填充算法。

扫描线种⼦填充算法不再采⽤递归的⽅式处理“4-联通”和“8-联通”的相邻点,⽽是通过沿⽔平扫描线填充像素段,⼀段⼀段地来处理“4-联通”和“8-联通”的相邻点。

这样算法处理过程中就只需要将每个⽔平像素段的起始点位置压⼊⼀个特殊的栈,⽽不需要象递归算法那样将当前位置周围尚未处理的所有相邻点都压⼊堆栈,从⽽可以节省堆栈空间。

应该说,扫描线填充算法只是⼀种避免递归,提⾼效率的思想,前⾯提到的注⼊填充算法和边界填充算法都可以改进成扫描线填充算法,下⾯介绍的就是结合了边界填充算法的扫描线种⼦填充算法。

扫描线种⼦填充算法的基本过程如下:当给定种⼦点(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)步,就是从当前扫描线的上⼀条扫描线和下⼀条扫描线中寻找新的种⼦点。

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

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

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

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

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

(1)栈顶像素出栈。

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

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

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

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

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

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

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

二、实验内容
用种子填充算法和扫描线填充算法等任意两种算法实现指定多边形的区域填充。

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

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

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

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

四向连通填充算法:
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上、下相邻的两条扫描线上的象素。

若存在非边界、未填充的象素,则把每一区间的最右象素作为种子点压入堆栈,返回第(2)步。

四、实验步骤
1.复习有关算法,明确实验目的和要求;
2.依据算法思想,绘制程序流程图(指定填充多边形);
3.设计程序界面,要求操作方便;
4.用C/C++语言编写源程序并调试、执行(最好能用动画显示填充过程);
5.分析实验结果
6.对程序设计过程中出现的问题进行分析与总结;
7.打印源程序或把源程序以文件的形式提交;
8.按格式要求完成实验报告。

五、实验结果及分析
种子填充算法的优点是非常简单,缺点是需要大量栈空间来存储相邻的点。

扫描线填充算法就是它的改进的方法。

它是通过沿扫描线填充水平像素段,来处理四连通或八连通相邻点,这样就仅仅只需要将每个水平像素段的起始位置压入栈,而不需要将当前位置周围尚未处理的相邻像素都压入栈,从而可以节省大量的栈空间。

六、实验结果
种子填充算法
扫描线填充算法七、实验主要代码
//种子填充算法
void CZhztchView::boundaryfill4(int x, int y, int boundarycolor, int newcolor)
{
int color;
CClientDC dc(this); //获取客户区设备描述表
color=dc.GetPixel(x,y);
if(color!=newcolor&&color!=boundarycolor)
{
dc.SetPixel(x,y,newcolor);
boundaryfill4(x,y+1,boundarycolor,newcolor);
boundaryfill4(x,y-1,boundarycolor,newcolor);
boundaryfill4(x-1,y,boundarycolor,newcolor);
boundaryfill4(x+1,y,boundarycolor,newcolor);
}
}
//////////////////////////////////////////////////////////////////// ////////////
//扫描线填充算法
void CZhztchView::OnScanfill()
{
RedrawWindow();
CDC* pDC=GetDC();
CPen newpen(PS_SOLID,3,RGB(255,0,0));
CPen *old=pDC->SelectObject(&newpen);
spt[0]=CPoint(100,100); //绘制多边形区域
spt[1]=CPoint(300,100);
spt[2]=CPoint(250,250);
spt[3]=CPoint(100,250);
spt[4]=CPoint(150,200);
spt[5]=CPoint(90,180);
spt[6]=CPoint(150,150);
spt[7]=CPoint(100,100);
pDC->Polyline(spt,8);
//pDC->SelectObject(old);
//ReleaseDC(pDC);
// TODO: Add your command handler code here
//CDC* pDC=GetDC();
CPen newpen2(PS_SOLID,1,RGB(0,255,0));
CPen *old2=pDC->SelectObject(&newpen2);
int j,k,s = 0;
int p[5]; //每根扫描线交点
int pmin = 0,pmax = 0;
for(int i=0;i<=6;i++)//建立边表
{
edge[i].dx=(float)(spt[i+1].x-spt[i].x)/(spt[i+1].y-spt[i].y);
if(spt[i].y<=spt[i+1].y){
edge[i].num=i;
edge[i].ymin=spt[i].y;
edge[i].ymax=spt[i+1].y;
edge[i].xmin=(float)spt[i].x;
edge[i].xmax=(float)spt[i+1].x;
if(spt[i+1].y > pmax)
pmax = spt[i+1].y;
if(spt[i].y < pmin)
pmin = spt[i].y;
}
else{
edge[i].num=i;
edge[i].ymin=spt[i+1].y;
edge[i].ymax=spt[i].y;
edge[i].xmax=(float)spt[i].x;
edge[i].xmin=(float)spt[i+1].x;
if(spt[i].y > pmax)
pmax = spt[i].y;
if(spt[i+1].y < pmin)
pmin = spt[i+1].y;
}
}
for(int r=1;r<=6;r++) //排序edge(yUpper,xIntersect),结果为从大到小
{
for(int q=0;q<=6-r;q++)
{
if(edge[q].ymin<edge[q+1].ymin)
{
newedge[0]=edge[q]; edge[q]=edge[q+1];
edge[q+1]=newedge[0];
}
}
}
for(int scan=pmax-1;scan>=pmin+1;scan--)
{
int b=0;
k=s;
for(j=k;j<=6;j++)
{
if((scan>edge[j].ymin)&&(scan<=edge[j].ymax))//判断与线段相交
{
if(scan==edge[j].ymax)
{
if(spt[edge[j].num+1].y<edge[j].ymax)
{
b++;
p[b]=(int)edge[j].xmax;
}
else if(spt[edge[j].num-1].y<edge[j].ymax)
{
b++;
p[b]=(int)edge[j].xmax;
}
}
if((scan>edge[j].ymin)&&(scan<edge[j].ymax))
{
b++;
p[b]=(int)(edge[j].xmax+edge[j].dx*(scan-edge[j].ymax));
}
}
//pDC->LineTo(spt[edge[0].num].x,spt[edge[0].num].y);
if(scan<=edge[j].ymin)//
s=j;
}
if(b>1)
{
for(int u=1;u<b;u++)
{
pDC->MoveTo(p[u]-1,scan);
u++;
pDC->LineTo(p[u],scan);
}
}
}
pDC->SelectObject(old);
pDC->SelectObject(old2); }。

相关文档
最新文档