实验报告5 多边形裁剪与填充
- 格式:doc
- 大小:197.50 KB
- 文档页数:17
多边形裁剪报告范文多边形裁剪是计算机图形学中的一项重要技术,用于确定多边形在一个给定的裁剪窗口内的可见部分。
多边形裁剪在许多应用中都有广泛的应用,例如计算机辅助设计、游戏开发和计算机动画等。
本报告将介绍多边形裁剪的原理、算法和优化技术等内容。
多边形裁剪的基本原理是确定多边形在裁剪窗口的哪一部分是可见的,然后通过一些几何运算来截取多边形的可见部分。
多边形裁剪有两个主要的方法:线段裁剪和多边形裁剪。
线段裁剪是指对多边形的边进行裁剪,而多边形裁剪是指对整个多边形进行裁剪。
多边形裁剪的算法有许多种,其中比较常用的有Cohen-Sutherland裁剪算法和Liang-Barsky裁剪算法。
Cohen-Sutherland算法是一种用于线段裁剪的算法,它通过判断线段与裁剪窗口的位置关系来确定线段是否需要裁剪。
Liang-Barsky算法是一种用于多边形裁剪的算法,它通过计算多边形边界与裁剪窗口的交点来确定多边形需要裁剪的部分。
除了基本的多边形裁剪算法外,还有一些优化技术可以提高多边形裁剪的效率。
其中之一是使用边界框来提前判断多边形是否与裁剪窗口相交,从而减少不必要的计算量。
另一个优化技术是使用扫描线算法来处理多边形内部的裁剪,以提高裁剪效率。
此外,还可以利用并行计算来加速多边形裁剪的过程。
多边形裁剪的应用非常广泛。
在计算机辅助设计中,多边形裁剪可以用于剪裁复杂的几何图形,以提高绘制的效果和性能。
在游戏开发中,多边形裁剪可以用于处理游戏中的可见性问题,从而提高游戏的渲染效果和帧率。
在计算机动画中,多边形裁剪可以用于对动画中的对象进行裁剪,以实现更逼真的效果。
综上所述,多边形裁剪是计算机图形学中的一个重要技术,用于确定多边形在给定的裁剪窗口内的可见部分。
它有多种算法和优化技术可供选择,可以应用于各种领域,如计算机辅助设计、游戏开发和计算机动画等。
多边形裁剪的研究和应用将为计算机图形学的发展提供重要支持。
试验实验一:图形的区域填充一、实验目的区域填充是指先将区域内的一点(常称为种子点)赋予给定颜色,然后将这种颜色扩展到整个区域内的过程。
区域填充技术广泛应用于交互式图形、动画和美术画的计算机辅助制作中。
本实验采用递归填充算法或打描线算法实现对光栅图形的区域填充。
通过本实验,可以掌握光栅图形编程的基本原理和方法。
实验内容掌握光栅图形的表示方法,实现种子算法或扫描线算法。
通过程序设计实现上述算法。
建议采用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 曲]内的给定区域的区段。
实验三多边形的有效边表填充算法一、实验目的与要求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表,填充扫描线和多边形相交的区间。
天津理工大学计算机科学与技术学院实验报告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); 然后利用循环进行裁剪。
#include <graphics.h>#include <conio.h>#include<stdio.h>#define EdgeNum 7#define MAX 2*EdgeNumtypedef struct{float x;float y;}Vertex;typedef Vertex Edge[2];typedef Vertex VertexArray[MAX]; VertexArray inVertexArray; VertexArray outVertexArray; Edge clipBoundary;int inLength=0;int outLength;int count;struct Seed //记录种子点{int x;int y;}stack[100];int top; //栈顶指针void setstackempty(){int i;for(i=0; i<=top; i++){stack[i].x=0;stack[i].y=0;}top=0;}int isstackempty(){if (top>0)return 1;elsereturn 0;}void stackpush(Seed p_xy){top+=1;stack[top]=p_xy;}Seed stackpop(){Seed val;val.x=stack[top].x;val.y=stack[top].y;top=top-1;return val;}void floodfill4(int x, int y, COLORREF oldcolor, COLORREF newcolor) {//第一次进入wwint xl,xr,i;bool spanNeedFill;Seed pt;setstackempty();//初始化pt.x=x;pt.y=y;stackpush(pt); //将前面生产的区段压入堆栈while(isstackempty()){pt=stackpop();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(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(pt);spanNeedFill=FALSE;}while(getpixel(x,y)!=oldcolor&&x<=xr)x++;}}}void Output(Vertex *newVertex,VertexArray outVertexArray){//将newVertex加入到结果多边形顶点表outVertexArray中outVertexArray[outLength].x = newVertex->x;outVertexArray[outLength].y = newVertex->y;outLength++;}void Intersect(Vertex *s, Vertex *p, Edge clipBoundary, Vertex *I)//3.求多边形的边与剪裁边的交点I{if(clipBoundary[0].y==clipBoundary[1].y)//水平剪裁边{I->y=clipBoundary[0].y;I->x=s->x+(clipBoundary[0].y - s->y)*(p->x - s->x)/(p->y - s->y);}else//竖直边剪裁{I->x = clipBoundary[0].x;I->y = s->y+(clipBoundary[0].x - s->x)*(p->y - s->y)/(p->x - s->x);}}bool Inside(Vertex *testVertex, Edge clipBoundary)//2.判断点是否在剪裁边的可见侧{if(clipBoundary[1].x > clipBoundary[0].x)//剪裁边为窗口下边{if(testVertex->y >= clipBoundary[0].y)return true;}else if(clipBoundary[1].x < clipBoundary[0].x)//剪裁边为窗口上边{if(testVertex->y <= clipBoundary[0].y)return true;}else if(clipBoundary[1].y > clipBoundary[0].y)//剪裁边为窗口右边{if(testVertex->x <= clipBoundary[0].x)return true;}else if(clipBoundary[1].y < clipBoundary[0].y)//剪裁边为窗口左边{if(testVertex->x >= clipBoundary[0].x)return true;}return false;}void SutherlandHodgmanClip(VertexArray inVertexArray,VertexArray outVertexArray)//1 {Vertex *s,*p,I;int j;s=&(inVertexArray[inLength-1]);for(j=0;j<inLength;j++){p=&(inVertexArray[j]);if( Inside(p,clipBoundary)){if(Inside(s,clipBoundary))Output(p,outVertexArray);//情况1else{Intersect(s,p,clipBoundary,&I);//情况4Output(&I,outVertexArray);Output(p,outVertexArray);}}else if(Inside(s,clipBoundary)){Intersect(s,p,clipBoundary,&I);//情况2Output(&I,outVertexArray);}//情况3无输出s=p;}}void StartSutherlandHodgmanClip()int i;//剪裁左边clipBoundary[0].x=100;clipBoundary[0].y=200;clipBoundary[1].x=100;clipBoundary[1].y=50;outLength = 0;for(i=0;i<MAX;i++){outVertexArray[i].x=0;outVertexArray[i].y=0;}SutherlandHodgmanClip(inVertexArray,outVertexArray);//首尾相同时,裁剪顶点集个数减1if(outVertexArray[outLength-1].x==outVertexArray[0].x&&outVertexArray[outLength-1].y= =outVertexArray[0].y) outLength--;//inVertexArray为最后的输出多变形//剪裁下边,注意此时输出多边形变为输入多边形!!clipBoundary[0].x=100;clipBoundary[0].y=50;clipBoundary[1].x=400;clipBoundary[1].y=50;inLength=outLength;outLength=0;//初始化输出点列表for(i=0;i<MAX;i++){inVertexArray[i].x=0;inVertexArray[i].y=0;}SutherlandHodgmanClip(outVertexArray,inVertexArray);if(inVertexArray[outLength-1].x==inVertexArray[0].x&&inVertexArray[outLength-1].y==in VertexArray[0].y) outLength--;//inVertexArray为最后的输出多变形//剪裁右边clipBoundary[0].x=400;clipBoundary[0].y=50;clipBoundary[1].x=400;clipBoundary[1].y=200;inLength=outLength;outLength=0;//初始化输出点列表for(i=0;i<MAX;i++){outVertexArray[i].x=0;outVertexArray[i].y=0;}SutherlandHodgmanClip(inVertexArray,outVertexArray);if(outVertexArray[outLength-1].x==outVertexArray[0].x&&outVertexArray[outLength-1].y= =outVertexArray[0].y) outLength--;clipBoundary[0].x=400;clipBoundary[0].y=200;clipBoundary[1].x=100;clipBoundary[1].y=200;inLength=outLength;outLength=0;//初始化输出点列表for( i=0;i<MAX;i++){inVertexArray[i].x=0;inVertexArray[i].y=0;}SutherlandHodgmanClip(outVertexArray,inVertexArray);//扫描的结果setcolor(GREEN);//inVertexArray为最后的输出多变形for(i=1;inVertexArray[i].x!=0 &&inVertexArray[i].y!=0;i++){moveto(inVertexArray[i-1].x,inVertexArray[i-1].y);lineto(inVertexArray[i].x,inVertexArray[i].y);}moveto(inVertexArray[i-1].x,inVertexArray[i-1].y);lineto(inVertexArray[0].x,inVertexArray[0].y);if(inVertexArray[outLength-1].x!=inVertexArray[0].x||inVertexArray[outLength-1].y!=inVert exArray[0].y){inVertexArray[outLength].x=inVertexArray[0].x;inVertexArray[outLength].y=inVertexArray[0].y;}}void AddPoint(int array[],int n){int i=0;while(i<n-1){inVertexArray[inLength].x=array[i];inVertexArray[inLength].y=array[i+1];i=i+2;inLength++;count++;}}void main(){initgraph(640, 480); // 打开图形窗口setcolor(RED);int point1[]={100,50,100,200,400,200,400,50,100,50};drawpoly(5,point1);int x1,y1,x2,y2;setcolor(BLUE);int point[]={80,56,110,90,178,150,324,178,460,150,369,89,100,60,80,56};drawpoly(8,point);AddPoint(point,MAX);printf("按任意键开始裁剪\n");getchar();StartSutherlandHodgmanClip();printf("按任意键开始填充\n");getchar();cleardevice(); //清屏int k=0,i=0;int points[100];while(i<=outLength){points[k]=inVertexArray[i].x;points[k+1]=inVertexArray[i].y;i++;k+=2;}drawpoly(outLength+1,points);//画出裁剪的结果COLORREF oldcolor=RGB(0,0,0);COLORREF newcolor=RGB(255,0,0);int x=300,y=100;//确定种子floodfill4(x,y,oldcolor,newcolor); //填充while(!kbhit()){}closegraph(); // 关闭图形窗口}。
多边形的偏移填充算法多边形偏移(polygon offset)算法可能我们印象不深,不过用过autoCAD的同学也印象autoCAD 上面也还是有这个功能的。
我们可以用autoCAD上的“正多边形”功能画一个多边形,然后用修改工具中“偏移”按钮,对多边形进行偏移,见图1,从外面的一个大的5边形按照边偏移至里面小的5边形,其中相应边偏移的距离定义为offset值。
图1 AutoCAD中的多边形偏移效果图当然,这只是简单的情况,复杂的情况可能是有多个多边形,其中1个outer多边形,多个inner 多边形,然后offset的时候应该是outer多边形向内offset,inner多边形向外offset。
当一个多边形(特别是凹多边形)初步offset时,可能会发生自交;然后多边形之间也可能会发生相交。
大概思路:这里就需要首先将自交的多边形分裂出来,并选择正确的多边形;然后将选择出来的多边形进行求交计算,再一次将有相交的多边形合并分裂出来,并且选择正确的多边形,这个时候得到的全部多边形就是一次offset出来的结果。
1、为了保证outer多边形能向内offset,inner多边形能向外offset,这里需要保证outer多边形是逆时针方向旋转的,inner多边形是顺时针方向旋转的。
1.1 这里就稍稍讲下多边形的顺逆判断。
在多边形是简单多边形的前提下,其实还是挺简单的,只要找出多边形左下角的一个顶点,然后判断与这个顶点相连的两条边的叉积是否大于0就行了;如果多边形不是简单多边形,比如有自相交,有顶点夹角为0的情况等等,这个时候多边形就不应该有顺逆这种属性吧2、对单个多边形,根据角平分线初步偏移得到角点对于一个角点,可以设这个顶点为curPoint,相连的前一个点为prePoint,下一个点为nexPoint,于是可以得到两个向量a = prePoint – curPoint,b=nexPoint – curPoint。
《计算机图形学》实验报告《裁剪算法实验》姓名闫学森学号3013216087专业计算机班级3班天津大学计算机科学与技术学院2015年12 月1日一、实验目的实现Sutherland-Hodgman多边形裁剪算法二、实验内容自定义裁剪窗口和待裁剪直线段(或多边形),采用不同颜色突出显示裁剪结果三、实验结果四、实验分析和总结Sutherland-Hodgman多边形裁剪算法是将原多边形进行左右下上四次裁剪。
其中进行两次分解? 第一次分解:将多边形关于矩形窗口的裁剪分解为它关于窗口四条边所在直线的裁剪;? 第二次分解:将多边形关于一条直线的裁剪分解为多边形各边关于该直线的裁剪。
四次裁剪相似,只要修改部分变量即可。
但是第一次修改时没有完全改掉,出来的图像不正确。
通过这次试验使我了解到如何运用计算机程序对窗口进行剪裁,了解到编码剪裁算法直观方便,速度较快,中点分割剪裁算法不用进行乘除运算,剪裁效率高, Sutherland-Hodgman 直线裁剪算法更快。
五、源代码// PolygonClipDemo.cpp : Defines the class behaviors for the application.//#include "stdafx.h"#include "PolygonClipDemo.h"#include "MainFrm.h"#ifdef _DEBUG#define new DEBUG_NEW#endif// CPolygonClipDemoAppBEGIN_MESSAGE_MAP(CPolygonClipDemoApp, CWinApp)ON_COMMAND(ID_APP_ABOUT, OnAppAbout)END_MESSAGE_MAP()// CPolygonClipDemoApp constructionCPolygonClipDemoApp::CPolygonClipDemoApp(){// TODO: add construction code here,// Place all significant initialization in InitInstance}// The one and only CPolygonClipDemoApp objectCPolygonClipDemoApp theApp;// CPolygonClipDemoApp initializationBOOL CPolygonClipDemoApp::InitInstance(){// InitCommonControls() is required on Windows XP if an application// manifest specifies use of ComCtl32.dll version 6 or later to enable// visual styles. Otherwise, any window creation will fail.InitCommonControls();CWinApp::InitInstance();// Standard initialization// If you are not using these features and wish to reduce the size// of your final executable, you should remove from the following// the specific initialization routines you do not need// Change the registry key under which our settings are stored// TODO: You should modify this string to be something appropriate// such as the name of your company or organizationSetRegistryKey(_T("Local AppWizard-Generated Applications"));// To create the main window, this code creates a new frame window// object and then sets it as the application's main window objectCMainFrame* pFrame = new CMainFrame;m_pMainWnd = pFrame;// create and load the frame with its resourcespFrame->LoadFrame(IDR_MAINFRAME,WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL,NULL);// The one and only window has been initialized, so show and update itpFrame->ShowWindow(SW_SHOW);pFrame->UpdateWindow();// call DragAcceptFiles only if there's a suffix// In an SDI app, this should occur after ProcessShellCommandreturn TRUE;}// CPolygonClipDemoApp message handlers// CAboutDlg dialog used for App Aboutclass CAboutDlg : public CDialog{public:CAboutDlg();// Dialog Dataenum { IDD = IDD_ABOUTBOX };protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support// Implementationprotected:DECLARE_MESSAGE_MAP()};CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) {}void CAboutDlg::DoDataExchange(CDataExchange* pDX) {CDialog::DoDataExchange(pDX);}BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)END_MESSAGE_MAP()// App command to run the dialogvoid CPolygonClipDemoApp::OnAppAbout(){CAboutDlg aboutDlg;aboutDlg.DoModal();}// CPolygonClipDemoApp message handlers。
大学实验报告学院:计算机科学与信息学院专业:软件工程班级:102班学号实验组实验时间指导教师成绩实验项目名称实验五直线和多边形的裁剪实验目的掌握直线段的裁剪算法以及多边形的裁剪算法实验要求熟练掌握直线段的裁剪算法以及多边形的裁剪算法的基本原理,并编写测试代码进行实验。
实验原理Cohen-Sutherland直线剪裁算法以区域编码为基础,将窗口及其周围的,8个方向以4 bit的二进制数进行编码。
右图所示的编码方法将窗口及其邻域分为5个区域:⑴域:区域(0000)。
⑵上域:区域(1001, 1000, 1010)。
⑶下域:区域(0101, 0100, 0110)。
⑷左域:区域(1001, 0001, 0101)。
⑸右域:区域(1010, 0010, 0110)。
当线段的两个端点的编码的逻辑“与”非零时,线段为显然不可见的,对某线段的两个端点的区号进行位与运算,可知这两个端点是否同在视区的上、下、左、右;Cohen-Sutherland直线剪裁算法的算法思想是:对于每条线段P1P2分为三种情况处理。
(1)若P1P2完全在窗口,则显示该线段P1P2简称“取”之。
(2)若P1P2明显在窗口外,则丢弃该线段,简称“弃”之。
(3)若线段既不满足“取”的条件,也不满足“弃”的条件,则在交点处把线段分为两段。
其中while (code1 != 0 || code2 != 0) {if ((code1 & code2) != 0) {// 两端点的编码相与不为0,表示直线在窗口外return;}if (code1 != 0) {code = code1;} else {code = code2;}if ((LEFT & code) != 0) {// 直线的端点与矩形窗口的左边编码相与!=0 x = XL;y = y1 + (y2 - y1) * (XL - x1) / (x2 - x1);// 求直线与矩形窗口的左边界的交点} else if ((RIGHT & code) != 0) {// 直线的端点与矩形窗口的右边编码相与!=0x = XR;y = y1 + (y2 - y1) * (XR - x1) / (x2 - x1);// 求直线与矩形窗口的右边界的交点} else if ((BOTTOM & code) != 0) {// 直线的端点与矩形窗口的下边编码相与!=0y = YB;x = x1 + (x2 - x1) * (YB - y1) / (y2 - y1);// 求直线与矩形窗口的下边界的交点} else if ((TOP & code) != 0) {// 直线的端点与矩形窗口的上边编码相与!=0y = YT;x = x1 + (x2 - x1) * (YT - y1) / (y2 - y1);// 直线的端点与矩形窗口的上// 边编码相与!=0}if (code == code1) {x1 = x;y1 = y;code1 = encode(x, y);} else {x2 = x;y2 = y;code2 = encode(x, y);}}g.drawLine((int) (x1 + 0.5), (int) (y1 + 0.5), (int) (x2 + 0.5),(int) (y2 + 0.5));}二、多边形裁剪的核心代码为:通过点集画直线或者多边形:private void draw() {//通过点集画直线或者多边形for (int i = 1; i < points.size(); i++) {Point p1 = new Point();p1 = points.get(i);int x1 = (int) p1.getX();int y1 = (int) p1.getY();Point p2 = new Point();p2 = points.get(i - 1);int x2 = (int) p2.getX();int y2 = (int) p2.getY();g.drawLine(x1, y1, x2, y2);}}多边形的裁剪函数:private Point[] cutPicture(Point[] point, Point[] edge) {// 剪裁函数,参数为(点集,边)Point[] intersectPoint = new Point[20];//存放交点的集合for (int j = 0; j < 20; j++) {intersectPoint[j] = new Point();}Point s = new Point();Point p = new Point();Point t = new Point();int i = 0;int length = point.length;s = point[length - 1];for (int j = 0; j < length; j++) {p = point[j];if (inside(p, edge)) {// sp在窗口,情况1if (inside(s, edge)) {intersectPoint[i] = p;i += 1;} else {// s在窗口外,情况4t = intersect(s, p, edge);intersectPoint[i] = t;i += 1;intersectPoint[i] = p;i += 1;}} else if (inside(s, edge)) {// s在窗口,p在窗口外,情况3t = intersect(s, p, edge);intersectPoint[i] = t;i += 1;}// 情况2没有输出s = p;}List<Point> tempList = new ArrayList<Point>();for (int k = 0; k < i; k++) {if (intersectPoint[k] != null) {Point pt = intersectPoint[k];tempList.add(pt);}}Point[] temp = new Point[tempList.size()];for (int j = 0; j < tempList.size(); j++) {temp[j] = new Point();temp[j] = tempList.get(j);}intersectPoint = temp;return intersectPoint;}判断点是否在裁剪边的可见侧:private boolean inside(Point point, Point[] edge) {//判断点是否在裁剪边的可见侧// 裁剪边为窗口下边if ((edge[0].y == edge[1].y) && (edge[0].x < edge[1].x)) {if (point.y >= edge[0].y) {return true;}}// 裁剪边为窗口上边if ((edge[0].y == edge[1].y) && (edge[0].x > edge[1].x)) {if (point.y <= edge[0].y) {return true;}}// 裁剪边为窗口右边if ((edge[0].x == edge[1].x) && (edge[0].y < edge[1].y)) {if (point.x <= edge[0].x) {return true;}}// 裁剪边为窗口左边if ((edge[0].x == edge[1].x) && (edge[0].y > edge[1].y)) {if (point.x >= edge[0].x) {return true;}}return false;}直线段与窗口边界求交:private Point intersect(Point s, Point p, Point[] edge) {//直线段与窗口边界求交,并返回交点Point t = new Point();if (edge[0].y == edge[1].y) {// 水平裁剪边t.y = edge[0].y;t.x = s.x + (edge[0].y - s.y) * (p.x - s.x) / (p.y - s.y);} else if (edge[0].x == edge[1].x) {// 垂直裁剪边t.x = edge[0].x;t.y = s.y + (edge[0].x - s.x) * (p.y - s.y) / (p.x - s.x);}return t;}鼠标的监听类(部类):class MouseMonitor extends MouseAdapter {//通过鼠标的单击获取点,并画出直线或者多边形public void mouseClicked(MouseEvent e) {points.add(e.getPoint());if (points.size() > 1) {draw();}}}键盘的监听类(部类):class KeyMonitor extends KeyAdapter {// 键盘控制public void keyPressed(KeyEvent e) {switch (e.getKeyCode()) {case KeyEvent.VK_R:// 清空画布和点集panel.repaint();points.removeAll(points);break;case KeyEvent.VK_W://对裁剪窗口的处理g.setColor(Color.RED);g.drawRect(XL, YB, XR - XL, YT - YB);//存放裁剪窗口的边top = new Point[2];// 存放裁剪窗口的上边top[0] = new Point(XL, YB);top[1] = new Point(XR, YB);right = new Point[2];//存放裁剪窗口的右边right[0] = new Point(XR, YB);right[1] = new Point(XR, YT);bottom = new Point[2];//存放裁剪窗口的下边bottom[0] = new Point(XR, YT);bottom[1] = new Point(XL, YT);left = new Point[2];//存放裁剪窗口的左边left[0] = new Point(XL, YT);left[1] = new Point(XL, YB);break;case KeyEvent.VK_A://对直线段进行裁剪g.setColor(Color.GREEN);Point p1 = points.get(0);Point p2 = points.get(1);lineCut(p1.getX(), p1.getY(), p2.getX(), p2.getY());break;case KeyEvent.VK_B://对多边形进行裁剪source = new Point[points.size()];//得到多边形的点for (int i = 0; i < points.size(); i++) {source[i] = points.get(i);}g.setColor(Color.GREEN);wT = cutPicture(source, top);//得到多边形与裁剪窗口上边的交点wR = cutPicture(wT, right);//得到多边形与裁剪窗口右边的交点wB = cutPicture(wR, bottom);//得到多边形与裁剪窗口下边的交点wL = cutPicture(wB, left);//得到多边形与裁剪窗口左边的交点第二种情况:线段在裁剪窗口的部,线段完全可见。
《计算机图形学》实验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)点阵表示是用位于多边形内的象素的集合来刻划多边形,该方法虽然没有多边形的几何信息,是面着色所需要的图像表示形式。
《计算机图形学》实验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 算法的方法原理。