【计算机图形学 】多边形种子填充算法 | OpenGL+鼠标交互

本文介绍了如何利用鼠标操作在OpenGL环境中实现多边形的4连通和扫描线填充,通过队列和栈数据结构的切换,展示了不同填充效果。通过点击和拖拽鼠标来绘制顶点,并实时填充区域。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

其他计算机图形学实验汇总

传送门

前言

实现多边形种子填充算法,分为四连通填充和扫描线填充,(分别由队列和栈实现的)并和鼠标进行交互。
具体原理略过,最后会贴上完整代码,可直接运行。

环境:
vs2019,OpenGL的库(可以搜索如何用vs使用OpenGL的库,可以使用vs自带的插件或者其他方法,很方便)

要点:
1.根据点的存储问题改变多边形填充算法:栈?队列?
2.改变鼠标点击和鼠标拖拽的响应事件。

最终效果:
用鼠标随意画顶点:点击鼠标左键画点,点击鼠标右键结束画点,形成多边形,点击鼠标中键确定区域填充种子点位置。
然后展示填充过程。同时,对应控制台会输出顶点坐标和个数

代码借鉴:
文章传送门

步骤

1.点的结构体

struct point
{
	int x, y;
	point(){}
	point(int xx, int yy):
		x(xx), y(yy) {}
};
vector<point> vertice; //顶点

2.确定线的颜色

float newcolor[3] = { 0, 1, 1 }; //填充颜色
float boundarycolor[3] = { 0, 0, 0 };  //边界颜色

3.设计画点函数(用于填充颜色的点)

//画点函数
void draw_a_point(int x, int y)
{
	glBegin(GL_POINTS);
	glColor3fv(newcolor);
	glVertex2f(x, y);
	glEnd();
	glFlush();
}

4.判断是否为边界的函数

根据颜色是否一致来判断,参数是颜色的RGB值

//判断函数
bool is_equal(float* a, float* b)
{
	return a[0] == b[0] && a[1] == b[1] && a[2] == b[2];
}

5. 扫描线算法实现

用队列queue实现的 4连通区域的填充算法

因为点是以队列形式进行存储,先进先出,则呈现的效果就是4连通区域的填充;
如果改成用栈存储点,先进后出,呈现的效果就是扫描线形式的填充(如下文)

//用队列queue实现的 4连通区域的填充算法
void BoundaryFill4(int x, int y)
{
	queue<point> q;
	q.push(point(x, y));
	while (!q.empty())
	{
		point now = q.front();
		q.pop();
		int nowx = now.x, nowy = now.y;
		float color[3];
		glReadPixels(nowx, nowy, 1, 1, GL_RGB, GL_FLOAT, color); //读取像素颜色
		if (!is_equal(color, newcolor) && !is_equal(color, boundarycolor))
		{
			draw_a_point(nowx, nowy);
			q.push(point(nowx, nowy + 1)); //下方
			q.push(point(nowx, nowy - 1));
			q.push(point(nowx + 1, nowy));
			q.push(point(nowx - 1, nowy)); //左方
		}
	}
}

用栈实现的 扫描线形式的填充算法

//用栈实现的 扫描线形式的填充算法
void BoundaryFill4_Stack(int x, int y)
{
	stack <point> q;
	q.push(point(x, y));
	while (!q.empty())
	{
		point now = q.top();
		q.pop();
		int nowx = now.x, nowy = now.y;
		float color[3];
		glReadPixels(nowx, nowy, 1, 1, GL_RGB, GL_FLOAT, color);
		if (!is_equal(color, newcolor) && !is_equal(color, boundarycolor))
		{
			draw_a_point(nowx, nowy);
			q.push(point(nowx, nowy + 1));
			q.push(point(nowx, nowy - 1));
			q.push(point(nowx + 1, nowy));
			q.push(point(nowx - 1, nowy));
		}
	}
}

6. 鼠标响应事件

想要更换不同的多边形填充算法,就更换最后一个鼠标中键的响应事件

void mymouse(int button, int state, int x, int y)
{
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
	{
		draw_a_point(x, window_height - y);

		point p(x, window_height - y);
		vertice.push_back(p);
		cout << "顶点" << vertice.size() << ": (" << x << ", " << y << ")" << endl;
	}

	if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
	{
		glClearColor(1, 1, 1, 1);//设置绘制窗口颜色为白色
		glColor3fv(boundarycolor);

		glBegin(GL_LINES);
		for (int i = 0; i < vertice.size(); i++)
		{
			if (i == vertice.size() - 1)//画完最后一个点,使其闭合
			{
				glVertex2f(vertice[0].x, vertice[0].y);
				glVertex2f(vertice[i].x, vertice[i].y);
			}
			else
			{
				glVertex2f(vertice[i].x, vertice[i].y);
				glVertex2f(vertice[i + 1].x, vertice[i + 1].y);
			}
		}
		glEnd();
		glFlush();
		vertice.clear();
	}

	if (button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN)
	{
		cout << "center: (" << x << ", " << y << ")" << endl;
		//想要更换不同的多边形填充算法,就更换最后一个鼠标中键的响应事件
		//BoundaryFill4(x, window_height - y);
		BoundaryFill4_Stack(x, window_height - y);
	}
}

完整代码

//系统递归栈太慢了,用stack或者queue会好一点,本质一样,都是bfs

#include<iostream>
#include<vector>
#include<queue>
#include<stack>
#include<GL/glut.h>
using namespace std;
int window_width = 800, window_height = 600;

struct point
{
	int x, y;
	point(){}
	point(int xx, int yy):
		x(xx), y(yy) {}
};
vector<point> vertice; //顶点

float newcolor[3] = { 0, 1, 1 }; //填充颜色
float boundarycolor[3] = { 0, 0, 0 };  //边界颜色

void draw_a_point(int x, int y);
bool is_equal(float* a, float* b);
void BoundaryFill4(int x, int y);
void BoundaryFill4_Stack(int x, int y);
void mymouse(int button, int state, int x, int y);
void KeyBoards(unsigned char key, int x, int y);


//画点函数
void draw_a_point(int x, int y)
{
	glBegin(GL_POINTS);
	glColor3fv(newcolor);
	glVertex2f(x, y);
	glEnd();
	glFlush();
}

//判断函数
bool is_equal(float* a, float* b)
{
	return a[0] == b[0] && a[1] == b[1] && a[2] == b[2];
}

//4连通区域的递归填充算法
//void BoundaryFill4(int x, int y)
//{
//	float color[3];
//	glReadPixels(x, y, 1, 1, GL_RGB, GL_FLOAT, color);
//	//cout << x << "," << y << ":" << color[0] << color[1] << color[2] << endl;
//	if (!is_equal(color, newcolor) && !is_equal(color, boundarycolor))
//	{
//		draw_a_point(x, y);
//		BoundaryFill4(x, y + 1);
//		BoundaryFill4(x, y - 1);
//		BoundaryFill4(x + 1, y);
//		BoundaryFill4(x - 1, y);
//	}
//}

//用队列queue实现的 4连通区域的填充算法
void BoundaryFill4(int x, int y)
{
	queue<point> q;
	q.push(point(x, y));
	while (!q.empty())
	{
		point now = q.front();
		q.pop();
		int nowx = now.x, nowy = now.y;
		float color[3];
		glReadPixels(nowx, nowy, 1, 1, GL_RGB, GL_FLOAT, color); //读取像素颜色
		if (!is_equal(color, newcolor) && !is_equal(color, boundarycolor))
		{
			draw_a_point(nowx, nowy);
			q.push(point(nowx, nowy + 1)); //下方
			q.push(point(nowx, nowy - 1));
			q.push(point(nowx + 1, nowy));
			q.push(point(nowx - 1, nowy)); //左方
		}
	}
}

//用栈实现的 扫描线形式的填充算法
void BoundaryFill4_Stack(int x, int y)
{
	stack <point> q;
	q.push(point(x, y));
	while (!q.empty())
	{
		point now = q.top();
		q.pop();
		int nowx = now.x, nowy = now.y;
		float color[3];
		glReadPixels(nowx, nowy, 1, 1, GL_RGB, GL_FLOAT, color);
		if (!is_equal(color, newcolor) && !is_equal(color, boundarycolor))
		{
			draw_a_point(nowx, nowy);
			q.push(point(nowx, nowy + 1));
			q.push(point(nowx, nowy - 1));
			q.push(point(nowx + 1, nowy));
			q.push(point(nowx - 1, nowy));
		}
	}
}

void mymouse(int button, int state, int x, int y)
{
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
	{
		draw_a_point(x, window_height - y);

		point p(x, window_height - y);
		vertice.push_back(p);
		cout << "顶点" << vertice.size() << ": (" << x << ", " << y << ")" << endl;
	}

	if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
	{
		glClearColor(1, 1, 1, 1);//设置绘制窗口颜色为白色
		glColor3fv(boundarycolor);

		glBegin(GL_LINES);
		for (int i = 0; i < vertice.size(); i++)
		{
			if (i == vertice.size() - 1)//画完最后一个点,使其闭合
			{
				glVertex2f(vertice[0].x, vertice[0].y);
				glVertex2f(vertice[i].x, vertice[i].y);
			}
			else
			{
				glVertex2f(vertice[i].x, vertice[i].y);
				glVertex2f(vertice[i + 1].x, vertice[i + 1].y);
			}
		}
		glEnd();
		glFlush();
		vertice.clear();
	}

	if (button == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN)
	{
		cout << "center: (" << x << ", " << y << ")" << endl;
		//BoundaryFill4(x, window_height - y);
		BoundaryFill4_Stack(x, window_height - y);
	}
}
void KeyBoards(unsigned char key, int x, int y)
{
	if (key == 32)
	{
		BoundaryFill4(x, window_height - y);
		glFlush();
	}
}

void display()
{
	glClear(GL_COLOR_BUFFER_BIT);
	glColor3f(0.0, 0.4, 0.2);
	glPointSize(1);
	glBegin(GL_POINTS);
	//BoundaryFill4(m, window_height - n);
	glEnd();
	glFlush();
}

int main(int argc, char* argv[])
{
	cout << "点击鼠标左键画点;" << endl << "点击鼠标右键结束画点,形成多边形;" << endl << "点击鼠标中键确定区域填充种子点位置。" << endl;
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowPosition(400, 150);
	glutInitWindowSize(window_width, window_height);
	glutCreateWindow("区域种子填充");
	glMatrixMode(GL_PROJECTION);/*设置为投影类型模式和其他观察参数*/
	//glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();/*设置为投影类型模式和其他观察参数*/
	//gluOrtho2D(0, window_width, window_height, 0);/*设置为投影类型模式和其他观察参数,观察窗口的大小要与画布大小一致,所以直接设置全局变量即可*/
	gluOrtho2D(0, window_width, 0, window_height); //上面的不可以!!!

	glClearColor(1, 1, 1, 1);//设置绘制窗口颜色为白色
	glClear(GL_COLOR_BUFFER_BIT);

	glutDisplayFunc(&display);//自己加的,回调函数
	glutMouseFunc(&mymouse);
	//glutKeyboardFunc(&KeyBoards);
	glutMainLoop();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值