实验报告5 多边形裁剪与填充
多边形裁剪报告范文

多边形裁剪报告范文多边形裁剪是计算机图形学中的一项重要技术,用于确定多边形在一个给定的裁剪窗口内的可见部分。
多边形裁剪在许多应用中都有广泛的应用,例如计算机辅助设计、游戏开发和计算机动画等。
本报告将介绍多边形裁剪的原理、算法和优化技术等内容。
多边形裁剪的基本原理是确定多边形在裁剪窗口的哪一部分是可见的,然后通过一些几何运算来截取多边形的可见部分。
多边形裁剪有两个主要的方法:线段裁剪和多边形裁剪。
线段裁剪是指对多边形的边进行裁剪,而多边形裁剪是指对整个多边形进行裁剪。
多边形裁剪的算法有许多种,其中比较常用的有Cohen-Sutherland裁剪算法和Liang-Barsky裁剪算法。
Cohen-Sutherland算法是一种用于线段裁剪的算法,它通过判断线段与裁剪窗口的位置关系来确定线段是否需要裁剪。
Liang-Barsky算法是一种用于多边形裁剪的算法,它通过计算多边形边界与裁剪窗口的交点来确定多边形需要裁剪的部分。
除了基本的多边形裁剪算法外,还有一些优化技术可以提高多边形裁剪的效率。
其中之一是使用边界框来提前判断多边形是否与裁剪窗口相交,从而减少不必要的计算量。
另一个优化技术是使用扫描线算法来处理多边形内部的裁剪,以提高裁剪效率。
此外,还可以利用并行计算来加速多边形裁剪的过程。
多边形裁剪的应用非常广泛。
在计算机辅助设计中,多边形裁剪可以用于剪裁复杂的几何图形,以提高绘制的效果和性能。
在游戏开发中,多边形裁剪可以用于处理游戏中的可见性问题,从而提高游戏的渲染效果和帧率。
在计算机动画中,多边形裁剪可以用于对动画中的对象进行裁剪,以实现更逼真的效果。
综上所述,多边形裁剪是计算机图形学中的一个重要技术,用于确定多边形在给定的裁剪窗口内的可见部分。
它有多种算法和优化技术可供选择,可以应用于各种领域,如计算机辅助设计、游戏开发和计算机动画等。
多边形裁剪的研究和应用将为计算机图形学的发展提供重要支持。
第五章多边形的扫描转换及区域填充

2211
多边形的扫描转换及区域填充
扫描线的连贯性
(1)交点个数为偶数。
(2)交点之间的区段按交替的顺序依次出现在多 边形内部和外部。
以上性质称为扫描线的连贯性,它是多边形区域 连贯性在一条扫描线上的反映。
第二十一页,编辑于星期五:十八点 二十分。
2222
多边形的扫描转换及区域填充
边的连贯性
3300
多边形的扫描转换及区域填充
扫描线算法-数据结构
为了提高效率,在处理一条扫描线时,仅对与它 相交的多边形的边进行求交运算。
活性边(active edge)
与当前扫描线相交的边;
活性边表(AET--Active edge table)
将活性边按与扫描线交点x坐标递增的顺序存入一个 链表中,它记录了多边形边沿扫描线的交点序列。
(1)x为小数,即交点(x ,e)落于扫描线y=e 上两个相邻像素之间
X为小数
第二十七页,编辑于星期五:十八点 二十分。
288
多边形的扫描转换及区域填充
交点取整规则
(2)交点(x ,e)正好落于像素点上
边界上象素的取舍问题,避免填充扩大化。
按照前面所介绍的对多边形区域边界的处理方法,若(x, e)位于多边形左边上,见图(a),则将它看作属于多边 形;若(x,e)在多边形右边上,见图(b).则它不属于 多边形。
扫描线算法(要求重点掌握)
利用相邻像素之间的连贯性,避免逐点判断和反复求 交运算。
边缘填充算法
利用求余运算,来达到填充的目的。
第八页,编辑于星期五:十八点 二十分。
9
多边形的扫描转换及区域填充
逐点判断算法
#define MAX 100 Typedef struct { int PolygonNum; // 多边形顶点个数
多边形的填充实验经典

试验实验一:图形的区域填充一、实验目的区域填充是指先将区域内的一点(常称为种子点)赋予给定颜色,然后将这种颜色扩展到整个区域内的过程。
区域填充技术广泛应用于交互式图形、动画和美术画的计算机辅助制作中。
本实验采用递归填充算法或打描线算法实现对光栅图形的区域填充。
通过本实验,可以掌握光栅图形编程的基本原理和方法。
实验内容掌握光栅图形的表示方法,实现种子算法或扫描线算法。
通过程序设计实现上述算法。
建议采用VC++实现OpenGL程序设计。
三、实验原理、方法和手段递归算法在要填充的区域内取一点(X, Y)的当前颜色记为oldcoloo用要填充的颜色ne wcolor去取代,递归函数如下:procedure flood-fill(XXoldcoloLnewcolor:integer); beginif getpixel(fiainebufier,x,y)=oldcolorthen beginsetpixel(fiamebuffer,x,y,newcolor); flood-fill(X.Y+1 .oldcoloLiiewcolor);flood-fill(X.Y^ 1 ,oldcoloi;newcolor); flood-fill(X-l,Y;oldcoloi;newcolor); flood-fill(X+l,Yoldcoloi;newcolor);endend扫描线算法扫描线算法的效率明显高于递归算法,其算法的基本思想如下:(1)(初始化)将算法设置的堆栈置为空,将给定的种子点(x,y)压入堆栈。
(2)(出栈)如果堆栈为空,算法结束;否则取栈顶元素(x,y)作为种子点。
(3)(区段填充)从种子点(x,y)开始沿纵坐标为y的当前扫描线向左右两个方向逐个象素进行填色,其值置为newcoloi;直到抵达边界为止。
(4)(定范围)以XleA和Xn血分别表示在步骤3中填充的区段两端点的横坐标。
(5)(进栈)分别在与当前扫描线相邻的上下两条打描线上,确定位于区间[Xldb Xn 曲]内的给定区域的区段。
多边形的区域填充

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);
4.用C/C++语言编写源程序并调试、执行(最好能用动画显示填充过程);
5.分析实验结果
6.对程序设计过程中出现的问题进行分析与总结;
7.打印源程序或把源程序以文件的形式提交;
8.按格式要求完成实验报告。
五、实验结果及分析
种子填充算法的优点是非常简单,缺点是需要大量栈空间来存储相邻的点。扫描线填充算法就是它的改进的方法。它是通过沿扫描线填充水平像素段,来处理四连通或八连通相邻点,这样就仅仅只需要将每个水平像素段的起始位置压入栈,而不需要将当前位置周围尚未处理的相邻像素都压入栈,从而可以节省大量的栈空间。
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++)
多边形的有效边表填充算法

实验三多边形的有效边表填充算法一、实验目的与要求1、理解多边形的扫描转换原理、方法;2、掌握有效边表填充算法;3、掌握链表的建立、添加结点、删除节点的基本方法;3、掌握基于链表的排序操作。
二、实验内容在实验二所实现工程的基础上,实现以下内容并把实现函数封装在类CMyGL 中。
1、C++实现有效边表算法进行多边形扫描转换2、利用1进行多边形扫描转换和区域填充的实现;三、实验原理请同学们根据教材及上课的PPT独立完成。
四、实验步骤(程序实现)。
1、建立并选择工程项目。
打开VC6.0->菜单File 的New 项,在projects 属性页选择MFC AppWizard(exe)项,在Project name 中输入一个工程名,如“Sample”。
单文档。
2、新建一个图形类。
选择菜单InsertNew class,Class type 选择“Generic Class”,Name 输入类名,如“CMyCG。
3、向新建的图形类中添加成员函数(实际就是加入实验要求实现的图形生成算法的实现代码)。
在工作区中直接鼠标右键单击,选择“Add Member Function…”项,添加绘制圆的成员函数。
void PolygonFill(int number, CPoint *p, COLORREF color, CDC* pDC)添加其他成员函数:CreatBucket();CreatET();AddEdge();EdgeOrder();4、成员函数的实现。
实现有效边表填充算法。
这一部分需要同学们去实现。
参考实现:多边形的有效边表填充算法的基本过程为:1、定义多边形:2、初始化桶3、建立边表4、多边形填充1)对每一条扫描线,将该扫描线上的边结点插入到临时AET表中,HeadE.2)对临时AET表排序,按照x递增的顺序存放。
3)根据AET表中边表结点的ymax抛弃扫描完的边结点,即ymax>=scanline4)扫描AET表,填充扫描线和多边形相交的区间。
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();//}}。
多边形填充算法实验报告

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

多边形裁剪实验报告一、实验内容1.实验目的:●理解多边形裁剪算法的基本思想,掌握多边形裁剪算法及其特点。
●能够应用多边形裁剪算法,编程实现裁剪指定多边形的功能。
2.常见的解决方法以及各方法的优点:a)Sutherland-Hodgman(逐边)裁剪算法优点:原理简单实用。
缺点:对于凸多边形的裁剪将显示出一条多余的裁剪边界直线。
这种情况在裁剪后的多边形有两个或多个分离部分的时候出现,因为只有一个输出顶点表,所以表中的最后一个顶点总是连着第一个顶点。
二、试验方法1.Sutherland-Hodgman裁剪算法所用方法的原理采用分割处理策略,将多边形关于矩形窗口的裁剪分解为多边形关于窗口四边所在直线的裁剪。
顺序是左上右下,前边的结果永远是后边的输入。
一次用窗口的一条边裁剪多边形,考虑窗口的一条边以及其延长线构成的裁剪线,该线把平面分为两个部分:可见一侧和不可见一侧。
对于两个端点均在可见一侧,则输出一个端点;对于两个端点均在不可见一侧,则输出0个端点;如果起始端点在可见一侧,终止端点在不可见一侧,则输出线段与裁剪边的交点;如果起始端点在不可见一侧,终止端点在可见一侧,则输出线段与裁剪边的交点以及终止端点。
程序设计思路(1)输入第一个顶点S,输入第一个顶点F(2)判断定点是否输入完毕,如果输入完毕,F—>P,处理线段SP(3)如果顶点未输入完毕,则输入顶点P,处理线段SP,P->S,然后再判断顶点是否输入完毕,转到第二步(4)处理线段SP,判断SP是否与裁剪线相交,如果与裁剪线相交,则求出SP与裁剪线的交点(5)输出交点(6)如果SP与裁剪线不相交,则判断P是否位于可见一侧,如果P位于可见一侧,则输出顶点P(7)如果P位于不可见一侧,则直接舍弃P(8)线段SP处理完毕,算法结束。
三、实验结果分析1.实验环境:windows xp操作系统、主频、内存、VC2.实验结果:3.分析解释实验结果初始化的背景为白色glClearColor(1.0,1.0,1.0,0.0);两个多边形的线段颜色设置为黑色glColor3f(0.0,0.0,0.0);将裁剪出的多边形设置为红色glColor3f(1.0,0.0,0.0); 然后利用循环进行裁剪。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
《计算机图形学》实验5实验报告实验题目:多边形裁剪与填充实验内容:1 阅读理解提供的参考资料。
2编写并调通一个多边形裁剪的java程序。
3编写并调通一个多边形填充的java程序。
参考资料:1fillPolygon.java2 clipSC2.java3变换与剪裁.ppt4多边形的填充.ppt基本概念:1变换与裁剪:(1)计算机处理图形的过程一般分为三个阶段:①图形的数字化;②图形操作;③图形输出。
(2)模型坐标系(局部坐标系):当构造单个对象的数字模型时,为了方便,可以将其置一个特定的坐标系下,即模型坐标系或局部坐标系。
(3)世界坐标系:为描述图形场景中所有图形之间的空间关系,将它们置于一个统一的坐标系中,该坐标系被称为世界坐标系。
(4)设备坐标系:要输出经过处理后的数字化图形,需要在输出设备上建立一个坐标系,称为设备坐标系。
(5)标准化设备坐标系:有些图形系统,对设备坐标系进行了规范化,将坐标范围限定在区间{x,y,z | 0≤x≤1, 0≤y≤1, 0≤z≤1}内,称标准化设备坐标系。
(6)三维图形的显示流程图(7)裁剪裁剪作用:选择显示的内容--图形在窗口内的部分被显示出来,窗口外的部分被裁剪掉。
图形中每个基本元素都要经过裁剪,因此裁剪直接影响整个图形系统的效率。
裁剪类型:二维裁剪、三维裁剪裁剪窗口:矩形,凸多边形,任意多边形视见体:棱台、立方体裁剪对象:直线段、多边形、文字等2多边形的填充:(1)多边形的填充指在给定区域填上所需要的颜色,就是把多边形的顶点表示转换为点阵表示,即从多边形的给定边界出发,求出位于其内部的各个像素,并将帧缓冲器内的各个对应元素设置相应的灰度或颜色。
(2)多边形的表示:1)顶点表示是用多边形的顶点的序列来描述多边形,该表示几何意义强、占内存少,但它不能直观地说明哪些像素在多边形内。
2)点阵表示是用位于多边形内的象素的集合来刻划多边形,该方法虽然没有多边形的几何信息,是面着色所需要的图像表示形式。
算法设计:1 多边形的裁剪:Sutherland-Cohen 算法:基本思想:对于每条线段P1P2分为三种情况处理。
(1)若P1P2完全在窗口内,则显示该线段P1P2简称“取”之。
(2)若P1P2明显在窗口外,则丢弃该线段,简称“弃”之。
(3)若线段既不满足“取”的条件,也不满足“弃”的条件,则在交点处把线段分为两段。
其中一段完全在窗口外,可弃之。
然后对另一段重复上述处理。
具体操作:分为两步第一步是判定:1) 完全在窗口内的直线段,称为完全可见的线段;2) 完全在窗口外的线段,称为完全不可见线段。
第二步处理不能断定为完全可见或完全不可见的线段。
这时需要计算出直线段和窗口边界的一个交点,这个交点把直线分成两段,其中一条为完全不可见的线段,被抛弃。
对余下部分再作第一步的判断,重复上述过程,直到直线段余下的部分可用第一步的判断得出肯定的结论为止。
2多边形的填充:(1) 多边形填充的扫描线算法a. 令y = c 为一个常数,扫描整个多边形的边 ,记录横坐标为xi 记为序列1.b. 设d 为一整数,d =c –1,且yi 0≥y ≥yi n ;设位于扫描线y =d 上的交点序列为 ,记为序列2。
c. 奇点的处理: 多边形P 的顶点可分为两类:极值点和非极值点。
如果,称顶点Pi 为极值点(P1,P2,P3,P5, P6,P8);否则称Pi 为非极值点(P0,P4,P7)。
若扫描线与多边形相交于多边形的顶点,则该交点(顶点)称为奇点。
为了使交点个数保持为偶数,规定当奇点是P 的极值点时,该点按两个交点计算;否则按一个交点计算。
(2) 边缘填充算法:对多边形P 的每一非水平边上的各像素做向右求反运算即可。
步骤:a. 以值为boundary-color 的特殊颜色勾画多边形P 的边界。
设多边形顶点为Pi = (xi , yi ),0≤i ≤n , xi , yi 均为整数;置Pn +1=P 0。
每一条扫描线上着上这种特殊颜色的点的个数必定是偶数(包括零)。
b. 设interior_point 是一布尔变量。
对每一条扫描线从左到右进行搜索,如果当前是像素位于多边形P 内,则interior_point=true ,需要填上值为polygon_color 的颜色;否则该像素在多边形P 外,需要填上值为background_color 的颜色。
(3) 区域填充:a. 区域是指已经表示成点阵形式的像素集合。
在光栅图形 中,区域可采用内点表示和边界表示两种形式进行描述。
内点表示法:把位于给定区域内的所有像素一一列举出来的方法称为内点表示法。
边界表示法:把位于给定区域边界上的像素一一列举出来的方法称为边界表示法。
b.区域的连通性:1) 连通的区域: 取区域内任意两点,在该区域内若从其中一点出发通过上、下、左\右四0))((11≥--+-i i i i y y y y种运动可到达另一点。
2)连通的区域: 取区域内任意两点,若从其中任一点出发,在该区域内通过沿水平方向、垂直方向和对角线方向的八种运动可到达另一点。
(4)扫描线种子填充算法:从给定的种子点开始,填充当前扫描线上种子点所在的一区段,然后确定与这一段相邻的上下两条扫描线上位于区域内的区段(需要填充的区间),从这些区间上各取一个种子点依次把它们存起来,作为下次填充的种子点。
反复进行这过程,直到所保存的各区段都填充完毕。
步骤:a.(初始化)将算法设置的堆栈置为空。
将给定的种子点(x, y)压入堆栈b.(出栈)如果堆栈为空,算法结束;否则取栈顶元素(x, y)作为种子点c.(区段填充)从种子点(x, y)开始,沿纵坐标为y的当前扫描线向左右两个方向逐个像素用新的颜色值进行填充,直到边界为止即象素颜色等于边界色。
设区间两边界的横坐标分别为xleft 和xright。
d. 在与当前扫描线相邻的上下两条扫描线上,以区间[xleft, xright]为搜索范围,求出需要填充的各小区间,把各小区间中最右边的点并作为种子点压入堆栈,转到步骤2。
代码:1 多边形的裁剪:public int code(float x,float y){int c=0;if(x<xL)c=c|1;else if(x>xR)c=c|2;if(y<yB)c=c|4;else if(y>yT)c=c|8;return c; //二进制分别为0 1 10 100 1000}//Sutherland_Cohen裁减算法public void Sutherland_Cohen(Graphics g,float x0,float y0,float x2,float y2){int c1,c2,c;float x,y,wx,wy;boolean accept=false,done=false;c1=code(x0,y0);c2=code(x2,y2);do {if ((c1|c2)==0)//两个编码都为0,表明在窗口内{accept=true;done=true;}else if((c1&c2)!=0)done=true;//两个编码的某一位为1,则必然在外侧显然在窗口外else{c=c1;if(c==0)c=c2;wx=x2-x0;wy=y2-y0;if ((c&8)==8) //求交点{x=x0+wx*(yT-y0)/wy;y=yT;}else if ((c&4)==4){x=x0+wx*(yB-y0)/wy;y=yB;}else if ((c&1)==1){y=y0+wy*(xL-x0)/wx;x=xL;}else//即(c&2)==2{y=y0+wy*(xR-x0)/wx;x=xR;}if (c==c1) //表明c1!=0,起始点不在窗口内,将交点作为新的起点重复判断步骤;{x0=x;y0=y;c1=code(x0,y0);}else//终点不在窗口内,交点作为新的终点{x2=x;y2=y;c2=code(x2,y2);}}//else} while (done==false);if(accept)g.drawLine((int)x0,(int)y0,(int)x2,(int)y2);}2多边形的填充:class activeEdgeList {activeEdgeListEntry header=null;activeEdgeListEntry tailer=null;public activeEdgeList(activeEdgeListEntry element) {header=tailer=element; //指向第一个边结点}//把新结点插入有序排列的多边形单链表public void insert(activeEdgeListEntry element) {activeEdgeListEntry sentinel;if(element==null || this.header==null) //新结点异常或者链表空throw new NullPointerException(); //出错,抛出异常sentinel=this.header; //当前指针指向表头结点int xt=element.topx; //新结点的topxint xtold=sentinel.topx;double oldDelta=sentinel.delta; //当前结点的deltadouble newDelta=element.delta;/* 排序第一关键字结点的topx,第二关键字结点的delta *//* 两个关键字由小到大*/if((xtold<xt)||((xtold==xt)&&(oldDelta<newDelta))){while(true){ //在链表头指针之后寻找新结点element的位置if(sentinel.next==null) {sentinel.next=element; //追加到表尾this.tailer=element;break; //结束while循环}activeEdgeListEntry mp=sentinel.next; //下一结点int xmt=mp.topx;double midDelta=mp.delta;if((xmt<xt)||((xmt==xt)&&(midDelta<newDelta))){sentinel=mp; //新结点仍然大于下一结点,当前结点指针后移,继续循环}else { //否则,新结点就应该插入当前位置sentinel.next=element;element.next=mp;break; //结束while循环,尾指针不动}}}else { //新结点作为单链表头结点sentinel=this.header;this.header=element;element.next=sentinel;}} //插入新结点结束//把新结点作为多边形链表的头结点或者尾结点public void append(activeEdgeListEntry element) {if(element==null) throw new NullPointerException();if(tailer==null) { //如果单链表是空tailer=element;header=element;}else { //否则把新结点作为单链表尾结点tailer.next=element;tailer=element;}}//两个多边形单链表的合并:自身单链表与list单链表的合并public activeEdgeList merge(activeEdgeList list,int y) {if(list==null) return this;activeEdgeListEntry a=this.header; //自身单链表的头指针activeEdgeListEntry b=list.header; //单链表list的头指针activeEdgeListEntry save=null; //指向链表结点的工作指针activeEdgeListEntry anext,bnext; //指向链表结点的工作指针activeEdgeList result=null; //合并结果链表while(true) {if(a==null&&b==null) return result; //两个空表else if(a==null) { //自身为空表if(save!=null) {save.next=b;result.tailer=list.tailer;return result;}}else if(b==null){ //list为空表if(save!=null) {save.next=a;result.tailer=this.tailer;return result;}}/*两个表都不空*/int xa=(int)(a.topx+(a.topy-y)*a.delta); //自身链表int xb=b.topx; //list链表头结点的topxif(xa<=xb) { //决定合并后两个表的先后anext=a.next;a.next=null;if(result==null) { //结果链表为空,则a表在前result=new activeEdgeList(a);save=result.tailer;}else result.append(a); //结果链表不空,则a表在后save=a;a=anext;}else {bnext=b.next;b.next=null;if(result==null) { //结果链表为空,则b表在前result=new activeEdgeList(b);save=result.tailer;}else result.append(b); //结果链表不空,则b表在后save=b;b=bnext;}} //结束while} //单链表合并结束//删除多边形单链表的结点public void remove(activeEdgeListEntry element) {activeEdgeListEntry p,q;if(header==element) { //要删除的是头结点header=element.next;if(tailer==element) tailer=header;return;}p=q=header;while(p!=element) {q=p;p=p.next;if(p==null) throw new NullPointerException(); //查无此结点}if(element==tailer) tailer=q; //要删除的是尾结点q.next=p.next; //要删除的是非头尾结点}//多边形单链表的相邻结点public void traverse() {activeEdgeListEntry p,q,r,tmp;p=r=header;while(p!=null){q=p.next;if(q==null) break; //到达末尾if(q.x<p.x) { //多边形单链表相邻结点的x坐标要由小到大tmp=q.next; //交换两个结点if(p==header) header=q; else r.next=q;q.next=p;p.next=tmp;}r=p; //指向尾结点p=p.next; //下一个结点} //结束whiletailer=r; //多边形单链表新的尾指针}} //结束多边形单链表类//*****定义直线类class Line {public double x1,y1;public double x2,y2;public Line(double x1,double y1,double x2,double y2) {this.x1=x1;this.y1=y1;this.x2=x2;this.y2=y2;}} //结束直线类//***********定义多边形的填充类*******//public class fillPolygon extends Applet //继承鼠标事件监听接口implements MouseListener,MouseMotionListener {protected MyCanvas m; //定义MyCanvas的对象//扫描行处理用数据protected activeEdgeListEntry[] edgeData=null; //边结点数组protected activeEdgeList[] bucket=null; //多边形单链表数组protected activeEdgeList activeHeader=null; //当前边单链表的头指针protected int numEdge=0; //多边形的边数//鼠标相关数据protected boolean isFirstClicked=true; //鼠标最初点击protected boolean isDoubleClicked=false; //双击标志protected boolean isSingleClicked=false; //单击标志//绘图区域protected int width,height; //Applet绘图区的大小protected Image image=null; //图像区域protected MemoryImageSource mis=null; //内存图像protected int[] pixel=null; //内存图像配色数组protected int pixelWidth; //图像宽度protected int pixelHeight; //图像高度protected int xoffset; //pixel数据的窗口内显示偏移量protected int yoffset;protected double leftTopx; //pixel数据的起始点protected double leftTopy;//多边形的绘图数据protected double x0,y0; //边的起点protected double lastx,lasty; //边的终点位置protected double movingx,movingy; //用户坐标系下一个点的坐标protected int numPoints=0; //顶点计数器protected boolean isPolygonMode=true; //标志protected Vector lines=new Vector(256,256); //Java的向量类//APPLET程序的初始化public void init(){m=new MyCanvas(this); //本APPLET容器的MyCanvas对象addMouseListener(this); //定义鼠标按钮监听器addMouseMotionListener(this); //定义鼠标按钮监听器Dimension d=getSize(); //本APPLET容器的大小width=d.width; height=d.height;}//成员变量初始化public void initData(){isFirstClicked=true; //鼠标最初点击isPolygonMode=true; //标志描绘多边形numPoints=0; //多边形顶点计数器bucket=new activeEdgeList[height+1]; //边的单链表数组for(int i=0;i<height+1;i++) bucket[i]=null;if(lines.size()>0) lines.removeAllElements(); //向量类的对象}//APPLET程序的绘图public void paint(Graphics g) {if(isFirstClicked) { //初始状态initData();Font f=m.MyFont(m.getFont().getName(),Font.BOLD+Font.ITALIC,1.5);m.setFont(f);m.drawString("单击画多边形",-0.5,0.2);m.drawString("双击填充多边形",-0.9,-0.15);}m.setBackground(new Color(220,220,220)); //背景色m.setColor(Color.black); //前景色for(int i=0;i<lines.size();i++) {Line l=(Line)lines.elementAt(i); //转换第i个向量m.drawLine(l.x1,l.y1,l.x2,l.y2);}if(isPolygonMode&&!isFirstClicked) //画线m.drawLine(lastx,lasty,movingx,movingy);if((!isPolygonMode)&&(image!=null)) //填充m.myDrawImage(image,leftTopx,leftTopy,this);} //APPLET程序的绘图//把多边形顶点数据生成边结点public void registerActiveEdgeEntry() {numEdge=lines.size(); //多边形边的数量edgeData=new activeEdgeListEntry[numEdge];for(int i=0;i<numEdge;i++) //每个边的单链表头结点edgeData[i]=new activeEdgeListEntry();/* 四个极值用于确定显示图像的大小和范围*/int LARGE=0x0ffffffff;int xmin=LARGE,xmax=-1,ymin=LARGE,ymax=-1;double dxmin=LARGE,dxmax=-1,dymin=LARGE,dymax=-1;for(int i=0;i<numEdge;i++) { //对每个边结点数组赋值Line l=(Line)lines.elementAt(i); //转换第i个向量int ix1=m.getX(l.x1); //将用户坐标的点转换到Java AWT坐标int iy1=height-m.getY(l.y1);int ix2=m.getX(l.x2);int iy2=height-m.getY(l.y2);edgeData[i].topx=0;edgeData[i].name=i;edgeData[i].next=null;if(iy1>iy2) { //边的斜向edgeData[i].isHorizontal=false;edgeData[i].topy=iy1;edgeData[i].topx=ix1;edgeData[i].x=ix1;edgeData[i].boty=iy2;edgeData[i].delta=(double)(-(ix2-ix1))/(iy2-iy1);if(iy2<ymin) { //进行yMin yMax 计算ymin=iy2;dymin=l.y2;}if(iy1>ymax) {ymax=iy1;dymax=l.y1;}}else if(iy1<iy2) { //边的斜向edgeData[i].isHorizontal=false;edgeData[i].topy=iy2;edgeData[i].topx=ix2;edgeData[i].x=ix2;edgeData[i].boty=iy1;edgeData[i].delta=(double)(-(ix1-ix2))/(iy1-iy2);if(iy1<ymin) { //进行yMin yMax 计算ymin=iy1;dymin=l.y1;}if(iy2>ymax) {ymax=iy2;dymax=l.y2;}}else { //水平边y1==iy2edgeData[i].isHorizontal=true;if(iy1<ymin) { //进行yMin yMax 计算ymin=iy1;dymin=l.y1;}if(iy1>ymax) {ymax=iy1;dymax=l.y1;}}if(ix1<ix2) { //进行xMin xMax 计算if(ix1<xmin) {xmin=ix1;dxmin=l.x1;}if(ix2>xmax) {xmax=ix2;dxmax=l.x2;}}else if(ix2<ix1) {if(ix2<xmin) {xmin=ix2;dxmin=l.x2;}if(ix1>xmax) {xmax=ix1;dxmax=l.x1;}}else { //垂直边ix2==ix1if(ix1<xmin) {xmin=ix1;dxmin=l.x1;}if(ix1>xmax) {xmax=ix1;dxmax=l.x1;}}} //结束for循环/*内存图像的数组空间及相关数据*/pixelWidth=xmax-xmin+1;pixelHeight=ymax-ymin+1;pixel=new int[pixelWidth*pixelHeight]; //图像的个点颜色for(int k=0;k<pixelWidth*pixelHeight;k++)pixel[k]=0x00000000; //初值透明色xoffset=xmin;yoffset=ymin;leftTopx=dxmin; //用于显示图像方法myDrawImage();leftTopy=dymax;} //生成边结点registerActiveEdgeEntry()//多边形单链表数组bucketd初始化public void bucketSort() {for(int i=0;i<lines.size();i++) {Line l=(Line)lines.elementAt(i); //转换第i个向量if(edgeData[i].isHorizontal) continue; //水平边不处理int yt=edgeData[i].topy;if(bucket[yt]==null) { //该行未建立链表bucket[yt]=new activeEdgeList(edgeData[i]);continue;}bucket[yt].insert(edgeData[i]); //该行有链表,按顺序插入到链表i } //结束for循环}//多边形的扫描转换public void scanPolygon() {activeHeader=null; //当前要处理的单链表for(int y=height-1;y>=0;y--) {if(bucket[y]!=null) { //该指针元素存在makeActiveEdgeList(bucket[y],y); //建立该行的边链表processActiveEdgeList(y); //处理该行的边链表}else if(activeHeader!=null&&activeHeader.header!=null)processActiveEdgeList(y); //处理该行的边链表} //结束for循环} //多边形的扫描转换//处理给定行的边链表public void processActiveEdgeList(int y) {int xleft,xright;double xl,xr;activeEdgeListEntry left=activeHeader.header;activeEdgeListEntry right=left.next;if(right==null) return;while(true) {xl=left.x;xr=right.x;xleft=(int)xl;xright=(int)(xr+0.5);if(xleft<=xright)fillScanline(xleft,xright,y); //填充y行像素列left.x+=left.delta; //起终点右移一个步长right.x+=right.delta;if(left.boty>=y-1 && right.boty>=y-1) {xleft=(int)left.x;xright=(int)(right.x+0.5);if(xleft<=xright)fillScanline(xleft,xright,y-1); //填充y-1行像素列}if(left.boty>=y-1) //将左面的边从当前链表中删去activeHeader.remove(left);if(right.boty>=y-1) //将左面的边从当前链表中删去activeHeader.remove(right);left=right.next; //选择当前链表中下一对结点if(left==null) break; //到达链表末尾,退出循环right=left.next;if(right==null) //边链表的结点数一定为偶数throw new NullPointerException(); //出错} //结束whileactiveHeader.traverse(); //边封闭后,重新整理单链表} //处理给定行的边链表//建立指定行的边链表public void makeActiveEdgeList(activeEdgeList list,int y) { if(activeHeader==null)activeHeader=list; //指定为当前单链表else//把list与当前单链表合并activeHeader=activeHeader.merge(list,y);}//填充何种图案public boolean isTilePattern(int i,int j) {if( i%8==0 || j%8==0 || i%8==1 || j%8==1 ||i%8==2 || j%8==2 ) return true; // 填充图案1 return false; // 填充图案2}//设定像素颜色public void putPixel(int i,int j) {int r,g,b;if(isTilePattern(i,j))r=g=b=0; //黑色else {r=(int)(Math.random()*255); //0到255之间的随机数g=(int)(Math.random()*255);b=(int)(Math.random()*255);}int a=0xff000000|(r<<16)|(g<<8)|b; //像素颜色/*图像的i行j列处的颜色*/pixel[(pixelHeight-1-(j-yoffset))*pixelWidth+(i-xoffset)]=a;}//填充指定行的像素列public void fillScanline(int xleft,int xright,int y) {for(int x=xleft;x<=xright;x++)putPixel(x,y); //调用putPixel(),设定改点像素颜色}//------多边形填充总控程序----public void scanLineFillPolygon() {registerActiveEdgeEntry(); //生成边结点bucketSort(); //单链表数组bucketd初始化scanPolygon(); //多边形的扫描转换repaint(); //paint绘图}//响应鼠标击键事件public void mousePressed(MouseEvent e) {int ix=e.getX(); //鼠标的Jawa AWT坐标int iy=e.getY();if(isPolygonMode) { //正在进行多边形填充double x,y; //鼠标的当前位置if(e.getClickCount()>=2) { //双击,添加多边形的顶点isDoubleClicked=true; //标记/* 给光栅向量表增加一行,这个向量成员方法导致编译要加上参数-Xlint,会出现警告错误*/lines.addElement(new Line(lastx,lasty,x0,y0));isPolygonMode=false;scanLineFillPolygon(); //多边形填充总控程序if(numEdge>2) { //内存图像生成mis=new MemoryImageSource(pixelWidth,pixelHeight,pixel,0,pixelWidth);image=createImage(mis);}}else { //单击,准备开始if(isFirstClicked) { //初次点击isFirstClicked=false;/*先按照Jawa AWT坐标ix iy得到视图索引号,再由视图反向转换得到用户坐标系下的lastx lasty*/lastx=x0=m.getUserX(ix,m.getViewport(ix,iy));lasty=y0=m.getUserY(iy,m.getViewport(iy,iy));movingx=lastx; movingy=lasty;}else { //非初次点击/*ix iy所对应的用户坐标系下的坐标*/x=m.getUserX(ix,m.getViewport(ix,iy));y=m.getUserY(iy,m.getViewport(iy,iy));/* 给光栅向量表增加一行*/lines.addElement(new Line(lastx,lasty,x,y));lastx=x; lasty=y;}numPoints++;}repaint(); //paint绘图} //结束if(isPolygonMode)语句} //响应鼠标击键事件//响应鼠标移动事件public void mouseMoved(MouseEvent e) {if(!isFirstClicked && isPolygonMode) {int ix,iy;ix=e.getX(); //鼠标的Jawa AWT坐标iy=e.getY();/*ix iy所对应的用户坐标系下的坐标*/movingx=m.getUserX(ix,m.getViewport(ix,iy));movingy=m.getUserY(iy,m.getViewport(ix,iy));repaint();}} //响应鼠标移动事件//****java语法列出有关鼠标接口中的虚方法public void mouseClicked(MouseEvent e){}public void mouseEntered(MouseEvent e){}public void mouseExited(MouseEvent e){}public void mouseReleased(MouseEvent e){}public void mouseDragged(MouseEvent e){}} //结束多边形填充类运行结果:1 多边形的裁剪:2 多边形的填充:实验体会:通过这次的实习报告的编写,我学习了图形变换与裁剪的知识,学习了Sutherland-Cohen 算法的方法原理。