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

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

传送门

前言

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

环境:
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;
}

  • 7
    点赞
  • 94
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于 OpenGL种子填充算法实现代码: ```cpp #include <iostream> #include <GL/glut.h> const int WIDTH = 600; const int HEIGHT = 400; void init() { glClearColor(0.0, 0.0, 0.0, 0.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, WIDTH, 0.0, HEIGHT); glMatrixMode(GL_MODELVIEW); } void display() { glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0, 1.0, 1.0); // 选择一个种子点 int seedX = 300; int seedY = 200; // 开始填充 glPointSize(1.0); glBegin(GL_POINTS); glColor3f(1.0, 0.0, 0.0); glVertex2i(seedX, seedY); glEnd(); glFlush(); // 记录已经填充的点 bool filled[WIDTH][HEIGHT] = { false }; // 添加种子点到队列 int queue[WIDTH * HEIGHT][2]; int queueStart = 0; int queueEnd = 0; queue[queueEnd][0] = seedX; queue[queueEnd][1] = seedY; queueEnd++; // 开始种子填充算法 while (queueStart != queueEnd) { // 取出队列头部的点 int x = queue[queueStart][0]; int y = queue[queueStart][1]; queueStart++; // 向上填充 int upX = x; int upY = y + 1; if (upY < HEIGHT && !filled[upX][upY]) { filled[upX][upY] = true; glBegin(GL_POINTS); glVertex2i(upX, upY); glEnd(); queue[queueEnd][0] = upX; queue[queueEnd][1] = upY; queueEnd++; } // 向下填充 int downX = x; int downY = y - 1; if (downY >= 0 && !filled[downX][downY]) { filled[downX][downY] = true; glBegin(GL_POINTS); glVertex2i(downX, downY); glEnd(); queue[queueEnd][0] = downX; queue[queueEnd][1] = downY; queueEnd++; } // 向左填充 int leftX = x - 1; int leftY = y; if (leftX >= 0 && !filled[leftX][leftY]) { filled[leftX][leftY] = true; glBegin(GL_POINTS); glVertex2i(leftX, leftY); glEnd(); queue[queueEnd][0] = leftX; queue[queueEnd][1] = leftY; queueEnd++; } // 向右填充 int rightX = x + 1; int rightY = y; if (rightX < WIDTH && !filled[rightX][rightY]) { filled[rightX][rightY] = true; glBegin(GL_POINTS); glVertex2i(rightX, rightY); glEnd(); queue[queueEnd][0] = rightX; queue[queueEnd][1] = rightY; queueEnd++; } glFlush(); } } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(WIDTH, HEIGHT); glutInitWindowPosition(100, 100); glutCreateWindow("Seed Fill Algorithm"); init(); glutDisplayFunc(display); glutMainLoop(); return 0; } ``` 在这个实现中,我们首先选择了一个种子点,然后在 OpenGL 中将其绘制出来。接着,我们使用一个二维布尔数组 `filled` 来记录哪些点已经被填充了。我们使用一个队列来保存待填充的点,初始时只有种子点在队列中。在每次循环中,我们从队列头部取出一个点,向上下左右四个方向检查是否需要填充,如果需要填充,就在 OpenGL 中绘制该点,并将其加入队列。我们不断重复这个过程,直到队列为空,即所有需要填充的点都已经填充完毕。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值