计算机图形学のY-X扫描线算法

本文介绍了使用Y-X扫描线算法进行边界填充,并通过该算法实现了AMD Logo的绘制。代码中详细展示了如何建立边表、活动边表,以及如何进行扫描线填充。算法首先对点按y坐标排序,然后逐行处理,更新活动边表并填充像素。最终展示的运行效果成功地绘制出了AMD的图形。
摘要由CSDN通过智能技术生成

Y-X扫描线算法:

今日白嫖1/∞ 🤣

直接上源码

后序在补上详解

#define NDEBUG
#ifndef GLUT_DISABLE_ATEXIT_HACK
#define GLUT_DISABLE_ATEXIT_HACK
#endif
#include <windows.h>
#include <gl/glut.h>
#include <cmath>
#include <vector>
#include <algorithm>
#include <list>

using namespace std;

//设置像素点
void setPixel(int xCoord, int yCoord){
	glBegin (GL_POINTS);{
		glVertex2i (xCoord, yCoord);
	}
	glEnd();
}

//设置窗口大小
const int windowHeight=400;
const int windowWidge=400;

//点类
struct Point{
	int x, y;
	Point():x(0), y(0){}
	Point(int nx, int ny):x(nx), y(ny){}
};

//边类
struct Edge{
	float x;
	float dx;
	int yMax;
	Edge():x(0),dx(0),yMax(0){}
	Edge(float nx, float ndx, int nyMax):x(nx),dx(ndx),yMax(nyMax){}

};

//sort的cmp函数
bool cmp_x(Point p1, Point p2){		//x升序排列
	return p1.x<p2.x;
}
bool cmp_y(Point p1, Point p2){
	return p1.y<p2.y;
}

//Y-X扫描行
void boundaryFill(vector <Point> pointList){
	int pointNum=pointList.size ();

	//计算maxY & minY
	vector<Point> pointListCopy(pointList);
	sort(pointListCopy.begin (), pointListCopy.end (), cmp_y);
	int yMin=pointListCopy.at (0).y;
	int yMax=pointListCopy.at (pointNum-1).y;

	//初始化边表ET & 活动边表AET
	list<Edge> ET[windowHeight];
	list<Edge> AET;
	//加入头结点, 以简化计算
	AET.push_back (Edge());

	//建立边表ET
	for(int i=0;i<pointNum;++i){
		int x0=pointList.at (i).x;
		int y0=pointList.at (i).y;
		int x1=pointList.at ((i+1)%pointNum).x;
		int y1=pointList.at ((i+1)%pointNum).y;

		//舍弃与扫描线水平的线
		if(y0==y1){
			continue;
		}
		//计算ET边的各个参数:
		int yMinNow=min(y0,y1);
		int yMaxNow=max(y0,y1);
		float x=y0<y1?x0:x1;
		float dx=(x0-x1)*1.0/(y0-y1);

		ET[yMinNow].push_back (Edge(x,dx,yMaxNow));
	}

	//建立活动边表AET
	for(int i=yMin;i<yMax;++i){
		//按递增顺序建立AET
		for(auto j=ET[i].begin ();j!=ET[i].end ();){
			auto pAET=AET.begin ();
			auto end=AET.end ();
			for(;pAET!=end;++pAET){
				if(pAET->x >j->x){
					break;
				}
				if(j->x == pAET->x && j->dx < pAET->dx){
					break;
				}
			}
			AET.insert (pAET,*j);
			j=ET[i].erase (j);
		}

		//删除当前AET中y=ymax的边
		//先删除再填色, 否则在交点处会出现奇数点出错导致没填色
		for(auto temp=AET.begin ();temp!=AET.end ();){
			if(temp->yMax==i){
				temp=AET.erase (temp);
			}else{
				temp++;
			}
		}

		//填色
		//一次选取两个点组成填色区间
		auto pAET=AET.begin ();
		pAET++;	//跳过头结点
		auto pAET_next=pAET;
		pAET_next++;
		int count=AET.size ()-1;
		while(count>=2){
			for(int x=pAET->x;x<pAET_next->x;++x){
				setPixel (x,i);
			}
			pAET++;
			pAET++;
			pAET_next=pAET;
			pAET_next++;
			count-=2;
		}

		//更新AET中x的值
		for(auto &temp:AET){
			temp.x+=temp.dx;
		}
	}
	return ;
}





//绘制程序
void display(){

	//AMD YES !!!

	vector <Point> pointList1;
	pointList1.push_back (Point(100,100));
	pointList1.push_back (Point(200,100));
	pointList1.push_back (Point(270,170));
	pointList1.push_back (Point(170,170));
	pointList1.push_back (Point(170,270));
	pointList1.push_back (Point(100,200));
	vector <Point> pointList2;
	pointList2.push_back (Point(170,285));
	pointList2.push_back (Point(280,285));
	pointList2.push_back (Point(280,170));
	pointList2.push_back (Point(340,100));
	pointList2.push_back (Point(340,340));
	pointList2.push_back (Point(100,340));

	glClear(GL_COLOR_BUFFER_BIT);//将屏幕设置为白色
	glColor3f(1.0,0.0,0.0);//设置当前颜色状态为绿色

	boundaryFill(pointList1);
	boundaryFill (pointList2);
	//	glFlush();//发送缓冲区
	glutSwapBuffers ();
	return ;
}

//初始化绘制
void init(){
	glClearColor(1.0,1.0,1.0,0.0);//清除颜色设置
	glMatrixMode(GL_PROJECTION);//设置投影方式
	gluOrtho2D (0.0,windowWidge*1.0,0.0,windowHeight*1.0);
	return ;
}

int main(int argc, char** argv){
	glutInit(&argc, argv);//初始化glut
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);//设置显示模式为单缓冲,RGB模式
	glutInitWindowPosition(0,0);//设置窗口位置
	glutInitWindowSize(windowWidge,windowHeight);//设置窗口大小
	glutCreateWindow("GLUT_Project_1");//设置窗口标题
	init();
	glutDisplayFunc(display);
	glutMainLoop();
	
	return 0;
}




运行效果:

绘制一个看起来还挺像的AMD Logo
在这里插入图片描述

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
用VC++6.0实现的扫描线填充,裁剪算法及画线方法 CPen pen(PS_SOLID,1,fillcolor); //设置扫描线所用笔的属性 CPen *old=pDC->SelectObject(&pen); int j,k,s=0; int p[9]; //每根扫描线交点 int pmin=1000; int pmax=0; for(int i=0;i<inLength;i++)//建立边表 { edge[i].dx=(float)(inVertexArray[i+1].x-inVertexArray[i].x)/(inVertexArray[i+1].y-inVertexArray[i].y); edge[i].num=i; if(inVertexArray[i].y<=inVertexArray[i+1].y) { edge[i].ymin=inVertexArray[i].y; edge[i].ymax=inVertexArray[i+1].y; edge[i].xmin=(float)inVertexArray[i].x; edge[i].xmax=(float)inVertexArray[i+1].x; } else{ edge[i].ymin=inVertexArray[i+1].y; edge[i].ymax=inVertexArray[i].y; edge[i].xmax=(float)inVertexArray[i].x; edge[i].xmin=(float)inVertexArray[i+1].x; } } //求多边形的最大最小值 for(int m=1;m<inLength;m++) { for(int n=0;n<inLength-m;n++) { if (pmax<inVertexArray[n].y) pmax = inVertexArray[n].y; if (pmin>inVertexArray[n].y) pmin=inVertexArray[n].y; } } for(int r=1;r<inLength;r++) //边表edge排序 { for(int q=0;q<inLength-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;scan>pmin;scan--) //扫描线遵守'“上开下闭”的原则 { int b=0; k=s; for(j=k;j<inLength;j++) { if((scan>=edge[j].ymin)&&(scan<=edge[j].ymax))//判断扫描线与线段是否相交于顶点 { int preNum = edge[j].num; int nextNum = edge[j].num+1; if (preNum==0) preNum = inLength - 1; else preNum = preNum -1; if (nextNum == inLength) nextNum = 0; if(scan==edge[j].ymax) //位于下顶点时,根据相临点的位置决定取几个点 { if(inVertexArray[nextNum].y<edge[j].ymax) { b++; p[b]=(int)edge[j].xmax; } if(inVertexArray[preNum].y<edge[j].ymax) { b++; p[b]=(int)edge[j].xmax; } } if(scan==edge[j].ymin) //位于上顶点时,取两个点 { b++; p[b]=(int)edge[j].xmin; b++; p[b]=(int)edge[j].xmin; } if((scan>edge[j].ymin)&&(scan<edge[j].ymax)) { b++; p[b]=(int)(edge[j].xmax+edge[j].dx*(scan-edge[j].ymax)); } } if(scan<edge[j].ymin) //建立新的活性边表 s=j; } if(b>1) { int tmpp = p[1]; for (int u = 1;u<=b;u++) //对交点排序 { for (int m = 1;m<=b-u;m++) { if (p[m]<p[m+1]) { tmpp = p[m]; p[m] = p[m+1]; p[m+1] = tmpp; } } } for(int n=1;n<b;n=n+2) { pDC->MoveTo(p[n],scan); pDC->LineTo(p[n+1],scan); } } }
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值