代码改变世界

边标志法填充多边形

2010-12-04 22:11  风恋残雪  阅读(...)  评论(... 编辑 收藏

这里不仔细讲原理,只是把我写的算法发出来,跟大家分享下,如果有错误的话,还请大家告诉我,如果写的不好,也请指出来,一起讨论进步。

基本思想: 先用一种特殊的颜色在帧缓冲器中将多边形的边界(水平边界除外)勾画出来,然后将着色的像素点依x坐标递增的顺序两两配对,再将每一对像素所构成的扫描线区间内的所有像素置为填充色。具体分为两个步骤;

(1) 打标记。对多边形的每条边进行直线扫描转换。

(2) 填充。 对每条与多边形相交的扫描线,依从左到右顺序,按“左闭右开”的原则对扫描线上的像素点进行填充。使用一个布尔量inside来指示当前点的状态,若点在多边形内,则inside为真,否则为假。inside初值为假,第当当前访问像素为被打上标记的点时就把inside取反。对未打标记的像素,inside不变。若访问当前像素时,对inside作必要操作后,inside为真,由把该像素置为填充色。

下面是程序:

/*
*	Date: 11/23/2010
*/
#include <GL/glew.h>
#include <GL/freeglut.h>
#include <vector>
#include <iostream>
#include <fstream>
using std::ofstream;
using std::ifstream;
using std::cout;
using std::endl;
ifstream cin ("polypoints.txt");
typedef struct _Point
{
	int x;
	int y;
}Point;



typedef struct _PolyPoints
{
	Point * pPoint;						// Pointer to points
	int		n;							// Number of points
	int		yMax;						// Max y of all points
	int		yMin;						// Min y of all points
	int		xMax;						// Max x of all points
	int		xMin;						// Min x of all points
}PolyPoints;


PolyPoints g_polyPoints;				// Storage for points of polygons
bool **	g_pbEdgeFlag;					// Edge flag
void inputPoints (void)
{
	int n;
	cin>>n;
	if (n < 3)
	{
		cout<<"number of points can not be less than 3"<<endl;
		exit (0);
	}
	g_polyPoints.n = n;
	g_polyPoints.pPoint = new Point[n];
	g_polyPoints.yMax = INT_MIN;
	g_polyPoints.yMin = INT_MAX;
	g_polyPoints.xMax = INT_MIN;
	g_polyPoints.xMin = INT_MAX;
	int x, y;
	for (int i = 0; i < n; ++i)
	{
		cin>>x>>y;
		g_polyPoints.pPoint[i].x = x;
		g_polyPoints.pPoint[i].y = y;
		if (g_polyPoints.yMax < y)
		{
			g_polyPoints.yMax = y;
		}
		if (g_polyPoints.yMin > y)
		{
			g_polyPoints.yMin = y;
		}
		if (g_polyPoints.xMax < x)
		{
			g_polyPoints.xMax = y;
		}
		if (g_polyPoints.xMin > x)
		{
			g_polyPoints.xMin = x;
		}
	}
	int xLen = g_polyPoints.xMax - g_polyPoints.xMin + 1;
	int yLen = g_polyPoints.yMax - g_polyPoints.yMin + 1;
	g_pbEdgeFlag = new bool *[yLen];
	for (int i = 0; i < yLen; ++i)
	{
		g_pbEdgeFlag[i] = new bool [xLen];
		memset (g_pbEdgeFlag[i], 0, sizeof (bool) * xLen);
	}
}
// Free the memory
void destroy (void)
{
	int yLen = g_polyPoints.yMax - g_polyPoints.yMin + 1;
	for (int i = 0; i < yLen; ++i)
	{
		delete g_pbEdgeFlag[i];
	}
	delete g_pbEdgeFlag;

}
void scanConvertLineAndFlag (const Point & p1, const Point & p2)
{
	int x1 = p1.x;
	int y1 = p1.y;
	int x2 = p2.x;
	int y2 = p2.y;
	int x, y, dx, dy, e;
	// k does not exist
	if (x1 == x2)
	{
		if (y1 < y2)
		{
			y = y1;
			glBegin (GL_POINTS);
			while (y <= y2)
			{
				glVertex2i (x1, y);
				// Flag current point
				bool bFlag = g_pbEdgeFlag[y-g_polyPoints.yMin][x1-g_polyPoints.xMin];
				g_pbEdgeFlag[y-g_polyPoints.yMin][x1-g_polyPoints.xMin] = !bFlag;
	
				++ y;
			}
			glEnd ();
		} // if (y1 < y2)
		else
		{
			y = y2;
			glBegin (GL_POINTS);
			while (y <= y1)
			{
				glVertex2i (x1, y);
				// Flag current point
				bool bFlag = g_pbEdgeFlag[y-g_polyPoints.yMin][x1-g_polyPoints.xMin];
				g_pbEdgeFlag[y-g_polyPoints.yMin][x1-g_polyPoints.xMin] = !bFlag;
	
				++ y;
			}
			glEnd ();
		}
	} // if (x1 == x2)
	else if (y1 == y2) // k = 0
	{
		if (x1 < x2)
		{
			glBegin (GL_POINTS);
			x = x1;
			while (x <= x2)
			{
			glVertex2i (x, y1);
				++ x;
			}
			glEnd ();
		} // if (x1 < x2)
		else 
		{
			x = x2;
			glBegin (GL_POINTS);
			while (x <= x1)
			{
				glVertex2i (x, y1);
				++ x;
			}
			glEnd ();
		}
	}
	else 
	{
		if (x1 > x2)
		{
			int temp = x1;
			x1 = x2;
			x2 = temp;
			temp = y1;
			y1 = y2;
			y2 = temp;
		}
		x = x1;
		y = y1;
		dx = x2 - x1;
		dy = y2 - y1;
		// k = 1
		if (dx == dy)
		{
			glBegin (GL_POINTS);
			while (x <= x2)
			{
				glVertex2i (x, y);
				// Flag current point
				bool bFlag = g_pbEdgeFlag[y-g_polyPoints.yMin][x-g_polyPoints.xMin];
				g_pbEdgeFlag[y-g_polyPoints.yMin][x-g_polyPoints.xMin] = !bFlag;
				++ x;
				++ y;
			}
			glEnd ();
		}
		else if (dx == -dy) // k = -1
		{
			glBegin (GL_POINTS);
			while (x <= x2)
			{
				glVertex2i (x, y);
				// Flag current point
				bool bFlag = g_pbEdgeFlag[y-g_polyPoints.yMin][x-g_polyPoints.xMin];
				g_pbEdgeFlag[y-g_polyPoints.yMin][x-g_polyPoints.xMin] = !bFlag;

				++ x;
				-- y;
			}
			glEnd ();
		}
		else if (dy > dx)	// k > 1
		{
			glBegin (GL_POINTS);
			dx <<= 1;
			e = - dy;
			dy <<= 1;
			y = y1 > y2 ? y2 : y1;
			int maxY = y1 > y2 ? y1 : y2;
			while (y <= maxY)
			{
				glVertex2i (x, y);
				// Flag current point
				bool bFlag = g_pbEdgeFlag[y-g_polyPoints.yMin][x-g_polyPoints.xMin];
				g_pbEdgeFlag[y-g_polyPoints.yMin][x-g_polyPoints.xMin] = !bFlag;

				++ y;
				e += dx;
				if (e > 0)
				{
					++ x;
					e -= dy;
				}
			}
			glEnd ();
		}
		else if (dy > 0)	// 0 < k < 1
		{
			e = -dx;
			dx <<= 1;
			dy <<= 1;
			glBegin (GL_POINTS);
			while (x <= x2)
			{
				glVertex2i (x, y);
				// Flag current point
				bool bFlag = g_pbEdgeFlag[y-g_polyPoints.yMin][x-g_polyPoints.xMin];
				g_pbEdgeFlag[y-g_polyPoints.yMin][x-g_polyPoints.xMin] = !bFlag;

				++ x;
				e += dy;
				if (e > 0)
				{
					e -= dx;
					++ y;
				}

			}
			glEnd ();
		}
		else if (-dy < dx)	// 0 > k > -1
		{
			e = -dx;
			dx <<= 1;
			dy <<= 1;
			glBegin (GL_POINTS);
			while (x <= x2)
			{
				glVertex2i (x, y);
				// Flag current point
				bool bFlag = g_pbEdgeFlag[y-g_polyPoints.yMin][x-g_polyPoints.xMin];
				g_pbEdgeFlag[y-g_polyPoints.yMin][x-g_polyPoints.xMin] = !bFlag;

				++ x;
				e += dy;
				if (e < 0)
				{
					-- y;
					e += dx;
				}
			}
			glEnd ();
		}
		else if (-dy > dx) // k < -1
		{
			e = dy;
			dx <<= 1;
			dy <<= 1;
			glBegin (GL_POINTS);
			y = y1 > y2 ? y1 : y2;
			int minY = y1 > y2 ? y2 : y1;
			while (y >= minY)
			{
				glVertex2i (x, y);
				// Flag current point
				bool bFlag = g_pbEdgeFlag[y-g_polyPoints.yMin][x-g_polyPoints.xMin];
				g_pbEdgeFlag[y-g_polyPoints.yMin][x-g_polyPoints.xMin] = !bFlag;
				e += dx;

				-- y;
				if (e > 0)
				{
					++ x;
					e += dy;
				}

			}
			glEnd ();
		}
	}
}
void init (void)
{
	glClearColor (0.0f, 0.0f, 0.0f, 1.0f);
}

void flagAllEdges (void)
{
	Point * pPoint = g_polyPoints.pPoint;
	int n = g_polyPoints.n;
	for (int i = 0; i < n; ++ i)
	{
		scanConvertLineAndFlag (pPoint[i], pPoint[(i+1)%n]);
	}
	for (int i = 0; i < n; ++i)
	{
		int j1 = (i+n-1)%n;
		int j2 = (i+1)%n;
		if (pPoint[j1].y > pPoint[i].y && pPoint[j2].y > pPoint[i].y ||
			pPoint[j1].y < pPoint[i].y && pPoint[j2].y < pPoint[i].y)
		{
			g_pbEdgeFlag[pPoint[i].y - g_polyPoints.yMin][pPoint[i].x - g_polyPoints.xMin] = false;
		}
		else
		{
			g_pbEdgeFlag[pPoint[i].y - g_polyPoints.yMin][pPoint[i].x - g_polyPoints.xMin] = true;

		}
	}
}

void edgeFlagFillPolygon (void)
{
	int xLen = g_polyPoints.xMax - g_polyPoints.xMin + 1;
	int yLen = g_polyPoints.yMax - g_polyPoints.yMin + 1;
	int nScanLine = g_polyPoints.yMin;
	glBegin (GL_POINTS);
	for (int i = 0; i < yLen; ++i, ++ nScanLine)
	{
		bool bFlag = false;
		int nHorizontal = g_polyPoints.xMin;
		for (int j = 0; j < xLen; ++j, ++ nHorizontal)
		{
			if (g_pbEdgeFlag[i][j])
			{
				bFlag = !bFlag;
			}
			while (bFlag)
			{
				glVertex2i (nHorizontal, nScanLine);
				++ j;
				++ nHorizontal;
				if (g_pbEdgeFlag[i][j])
				{
					bFlag = !bFlag;
				}
			}
		}
	}
	glEnd ();

}

void display (void)
{
	glClear (GL_COLOR_BUFFER_BIT);
	glLoadIdentity ();
	glColor3f (1.0f, 0.0f, 0.0f);
	//flagAllEdges ();
	// Fill a polygon
	edgeFlagFillPolygon ();
	glutSwapBuffers ();
}

void reshape (int w, int h)
{
	glViewport (0, 0, (GLsizei) w, (GLsizei) h);
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ();
	if (w <= h)
	{
		gluOrtho2D (-600.0, 600.0, -600.0 * (GLfloat) h / (GLfloat) w, 600.0 * (GLfloat) h / (GLfloat) w);
	}
	else
	{
		gluOrtho2D (-600.0 * (GLfloat) w / (GLfloat) h,600.0 * (GLfloat) w / (GLfloat) h, -600.0, 600.0);
	}
	glMatrixMode (GL_MODELVIEW);
	glLoadIdentity ();
}
void keyboard (unsigned char key, int x, int y)
{
	switch (key)
	{
	case 27: // 'VK_ESCAPE'
		exit (0);
		break;
	default:
		break;
	}
}
int main (int argc, char ** argv)
{
	glutInit (&argc, argv);
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
	glutInitWindowSize (600, 600);
	glutCreateWindow ("Bresenham line");
	init ();
	inputPoints ();
	flagAllEdges ();
	glutReshapeFunc (reshape);
	glutDisplayFunc (display);
	glutKeyboardFunc (keyboard);
	glutMainLoop ();
	destroy ();
	return 0;
}

polypoints.txt中的示例内容如下:
7
30 120
10 70
30 10
60 50
80 10
120 90
70 80


Locations of visitors to this page