【计算机图形学 】SutherlandHodgmanClip 多边形裁剪算法及其改进 | OpenGL+鼠标交互

其他计算机图形学实验

传送门

前言

利用SutherlandHodgmanClip 多边形裁剪算法,实现多边形的裁剪,并和鼠标进行交互。
并且对该算法进行了改进,使之同样适用于凹多边形。(本来Weiler-Athenton算法 更加万能,但确实找了很久资料也不知道怎么写,就此放弃,望大佬赐教
具体原理略过,会贴上完整代码及注释,可直接运行。

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

要点:
1.多边形的裁剪过程
2.改变鼠标点击和键盘的响应事件。

参考代码:
传送门

最终效果:

  1. 鼠标左键单击得到多边形的各个顶点,右键单击连接多边形,按空格键对多边形进行裁剪
  2. 对应控制台会输出顶点坐标

改进前(SutherlandHodgmanClip 多边形裁剪算法):
在这里插入图片描述
在这里插入图片描述

改进后:
在这里插入图片描述

改进前(SutherlandHodgmanClip 多边形裁剪算法)

1.新建结构体:点,线,裁剪窗口

struct point
{
	float x, y;
	point() {}
	point(float xx, float yy)
		:x(xx), y(yy) {}

	bool operator < (const point& a)const //重载运算符
	{
		return x < a.x;
	}
};
struct EDGE//Edge边
{
	float bx, by, ex, ey;
	EDGE() {}
	EDGE(float bxx, float byy, float exx, float eyy)
		:bx(bxx), by(byy), ex(exx), ey(eyy) {}
};
struct Window {
public:
	int x, y;
	int width;
	int height;
	int l, r, t, b; //边界:左右上下
	Window(int x, int y, int width, int height) {
		this->x = x;
		this->y = y;
		this->width = width;
		this->height = height;
		l = x;
		r = x + width;
		t = y + height;
		b = y;
	}
};
Window myWindow(200, 200, 400, 250);

2.用于画点和画裁剪窗口的函数

//画点函数
void draw_a_point(float x, float y, float color[])
{
	glPointSize(5.0f);
	glBegin(GL_POINTS);
	glColor3fv(color);
	glVertex2f(x, y);
	glEnd();
	glFlush();
}

//画裁剪窗口
void draw_window() {
	glBegin(GL_LINE_LOOP);
	glVertex2i(myWindow.l, myWindow.b);
	glVertex2i(myWindow.l, myWindow.t);
	glVertex2i(myWindow.r, myWindow.t);
	glVertex2i(myWindow.r, myWindow.y);
	glEnd();
}

3.创建裁剪窗口的四条边

注意,是由四个有向线段逆时针组成的长方形。 方便后面判断点的位置

EDGE left(200, 450, 200, 200);
EDGE bottom(200, 200, 600, 200);
EDGE right(600, 200, 600, 450);
EDGE top(600, 450, 200, 450);

4.判断点的位置

两个参数:给定一个点和要判断的它与哪个边界的位置关系
返回结果:在这个边界内部,即在裁剪窗口里面,就返回true,否则返回false。

bool inside(point& pt, EDGE ClipBoundary)//判断点是否可见
{
	if (ClipBoundary.ex > ClipBoundary.bx)
	{
		if (pt.y >= ClipBoundary.by)//裁剪边为窗口下边沿
			return true;
	}
	else if (ClipBoundary.ex < ClipBoundary.bx)
	{
		if (pt.y <= ClipBoundary.by)//裁剪边为窗口上边沿
			return true;
	}
	else if (ClipBoundary.ey > ClipBoundary.by)//裁剪边为窗口右边沿
	{
		if (pt.x <= ClipBoundary.bx)
			return true;
	}
	else if (ClipBoundary.ey < ClipBoundary.by)//裁剪边为窗口左边沿
	{
		if (pt.x >= ClipBoundary.bx)
			return true;
	}
	return false;
}

5.求直线与边界的交点

四个参数:(起点)s点,(终点)p点,某条边界,交点的名称

//直线段SP和边界求交,返回交点
void intersect(point& s, point& p, EDGE ClipBoundary, point& intersect_pt)
{
	if (ClipBoundary.by == ClipBoundary.ey)//水平裁剪边界
	{
		intersect_pt.y = ClipBoundary.by;
		//x=起点的横坐标+dy/sp斜率
		intersect_pt.x = s.x + (ClipBoundary.by - s.y) * (p.x - s.x) / (p.y - s.y);
	}
	else//垂直裁剪边界
	{
		intersect_pt.x = ClipBoundary.bx;
		intersect_pt.y = s.y + (ClipBoundary.bx - s.x) * (p.y - s.y) / (p.x - s.x);
	}
}

6.SutherlandHodgmanClip 多边形裁剪算法

void SutherlandHodgmanClip(EDGE ClipBoundary)
{
	point s, p, ip;
	output_vertice.clear();
	s = input_vertice[input_vertice.size() - 1];//先从最后一个点指向第一个点的线段开始检验

	for (int j = 0; j < input_vertice.size(); j++)
	{
		p = input_vertice[j];
		if (inside(p, ClipBoundary))//p在内
		{
			if (inside(s, ClipBoundary))//sp都在窗口内
			{
				output_vertice.push_back(p);
			}
			else//p在里面 s不在
			{
				intersect(s, p, ClipBoundary, ip);
				output_vertice.push_back(ip);
				output_vertice.push_back(p);
			}
		}
		else//p在外面
		{
			if (inside(s, ClipBoundary))//s在窗口内p在窗口外
			{
				intersect(s, p, ClipBoundary, ip);
				output_vertice.push_back(ip);	
			}
			//sp都在外面则无输出
		}
		s = p;
	}
	input_vertice = output_vertice;//这次的输出作为下一次的输入,input_vertice和output_vertice是全局变量
}

7.鼠标响应事件

通过鼠标点击,输入要裁剪的多边形的顶点信息,并在控制台输出相应坐标。

void mymouse(int button, int state, int x, int y)
{
	glClearColor(1, 1, 1, 1);
	//bool flag = 0;

	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
	{
		draw_a_point(x, window_height - y, polygon_point_color);
		point p(x, window_height - y);
		input_vertice.push_back(p);
		cout << "多边形顶点" << input_vertice.size() << ":(" << x << ", " << window_height - y << ")" << endl;
	}

	if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
	{
		glLineWidth(2.0f);
		glBegin(GL_LINES);
		glColor3fv(polygon_point_color);
		//绘制多边形
		for (int i = 0; i < input_vertice.size(); i++)
		{
			if (i == input_vertice.size() - 1)
			{
				glVertex2f(input_vertice[0].x, input_vertice[0].y);
				glVertex2f(input_vertice[i].x, input_vertice[i].y);
			}
			else
			{
				glVertex2f(input_vertice[i].x, input_vertice[i].y);
				glVertex2f(input_vertice[i + 1].x, input_vertice[i + 1].y);
			}
		}
		glEnd();
		glFlush();
	}
}

8.键盘响应事件

裁剪多边形。以顺时针的顺序,从最左边的边界开始,对每条边进行裁剪,得到输出坐标,并绘制裁剪后的多边形。

void keyboard(unsigned char key, int x, int y)
{
	if (key == 32)
	{
		//顺时针
		SutherlandHodgmanClip(::left);
		SutherlandHodgmanClip(bottom);
		SutherlandHodgmanClip(::right);
		SutherlandHodgmanClip(top);

		glLineWidth(4.0f);
		glBegin(GL_LINE_LOOP);
		glColor3fv(intersect_point_color);
		for (int i = 0; i < output_vertice.size(); i++)
		{//draw_a_point(output_vertice[i].x, output_vertice[i].y, intersect_point_color);
			glVertex2f(output_vertice[i].x, output_vertice[i].y);
		}
		glEnd();
		glFlush();

	}
}

完整代码

#include<gl/glut.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<time.h>
using namespace std;
const int window_width = 800, window_height = 600;
struct point
{
	float x, y;
	point() {}
	point(float xx, float yy)
		:x(xx), y(yy) {}

	bool operator < (const point& a)const //重载运算符
	{
		return x < a.x;
	}
};
struct EDGE//Edge边
{
	float bx, by, ex, ey;
	EDGE() {}
	EDGE(float bxx, float byy, float exx, float eyy)
		:bx(bxx), by(byy), ex(exx), ey(eyy) {}
};
struct Window {
public:
	int x, y;
	int width;
	int height;
	int l, r, t, b; //边界:左右上下
	Window(int x, int y, int width, int height) {
		this->x = x;
		this->y = y;
		this->width = width;
		this->height = height;
		l = x;
		r = x + width;
		t = y + height;
		b = y;
	}
};
Window myWindow(200, 200, 400, 250);

//map<point, int> vis;
vector<point> input_vertice; //输入
vector<point> output_vertice; //输出
float intersect_point_color[3] = { 1,0,0 };
float polygon_point_color[3] = { 0,0,1 };
EDGE left(200, 450, 200, 200);
EDGE bottom(200, 200, 600, 200);
EDGE right(600, 200, 600, 450);
EDGE top(600, 450, 200, 450);

void draw_a_point(float x, float y, float color[]);
void draw_window();
bool inside(point& pt, EDGE ClipBoundary);//判断点是否可见
void intersect(point& s, point& p, EDGE ClipBoundary, point& intersect_pt);//直线段SP和边界求交,返回交点
void SutherlandHodgmanClip(EDGE ClipBoundary, int in_len, int& out_len);
void mymouse(int button, int state, int x, int y);
void keyboard(unsigned char key, int x, int y);
void display();

int main(int argc, char* argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RED);
	glutInitWindowPosition(300, 150);
	glutInitWindowSize(window_width, window_height);
	glutCreateWindow("固定矩形窗口裁剪交互式多边形");
	cout << "左键画点\n右键连接成窗口\n按空格裁剪\n" << endl;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0, window_width, 0, window_height);
	glClearColor(1, 1, 1, 1);
	glClear(GL_COLOR_BUFFER_BIT);

	glutDisplayFunc(&display);
	glutMouseFunc(&mymouse);
	glutKeyboardFunc(&keyboard);

	glutMainLoop();
	return 0;
}


bool inside(point& pt, EDGE ClipBoundary)//判断点是否可见
{
	if (ClipBoundary.ex > ClipBoundary.bx)
	{
		if (pt.y >= ClipBoundary.by)//裁剪边为窗口下边沿
			return true;
	}
	else if (ClipBoundary.ex < ClipBoundary.bx)
	{
		if (pt.y <= ClipBoundary.by)//裁剪边为窗口上边沿
			return true;
	}
	else if (ClipBoundary.ey > ClipBoundary.by)//裁剪边为窗口右边沿
	{
		if (pt.x <= ClipBoundary.bx)
			return true;
	}
	else if (ClipBoundary.ey < ClipBoundary.by)//裁剪边为窗口左边沿
	{
		if (pt.x >= ClipBoundary.bx)
			return true;
	}
	return false;
}

//直线段SP和边界求交,返回交点
void intersect(point& s, point& p, EDGE ClipBoundary, point& intersect_pt)
{
	if (ClipBoundary.by == ClipBoundary.ey)//水平裁剪边界
	{
		intersect_pt.y = ClipBoundary.by;
		//x=起点的横坐标+dy/sp斜率
		intersect_pt.x = s.x + (ClipBoundary.by - s.y) * (p.x - s.x) / (p.y - s.y);
	}
	else//垂直裁剪边界
	{
		intersect_pt.x = ClipBoundary.bx;
		intersect_pt.y = s.y + (ClipBoundary.bx - s.x) * (p.y - s.y) / (p.x - s.x);
	}
}

void SutherlandHodgmanClip(EDGE ClipBoundary)
{
	point s, p, ip;
	output_vertice.clear();
	s = input_vertice[input_vertice.size() - 1];//先从最后一个点指向第一个点的线段开始检验

	for (int j = 0; j < input_vertice.size(); j++)
	{
		p = input_vertice[j];
		if (inside(p, ClipBoundary))//p在内
		{
			if (inside(s, ClipBoundary))//sp都在窗口内
			{
				output_vertice.push_back(p);
			}
			else//p在里面 s不在
			{
				intersect(s, p, ClipBoundary, ip);
				output_vertice.push_back(ip);
				output_vertice.push_back(p);
			}
		}
		else//p在外面
		{
			if (inside(s, ClipBoundary))//s在窗口内p在窗口外
			{
				intersect(s, p, ClipBoundary, ip);
				output_vertice.push_back(ip);
			}
			//sp都在外面则无输出
		}
		s = p;
	}
	input_vertice = output_vertice;//这次的输出作为下一次的输入,input_vertice和output_vertice是全局变量
}

//画点函数
void draw_a_point(float x, float y, float color[])
{
	glPointSize(5.0f);
	glBegin(GL_POINTS);
	glColor3fv(color);
	glVertex2f(x, y);
	glEnd();
	glFlush();
}

//画裁剪窗口
void draw_window() {
	glBegin(GL_LINE_LOOP);
	glVertex2i(myWindow.l, myWindow.b);
	glVertex2i(myWindow.l, myWindow.t);
	glVertex2i(myWindow.r, myWindow.t);
	glVertex2i(myWindow.r, myWindow.y);
	glEnd();
}

void mymouse(int button, int state, int x, int y)
{
	glClearColor(1, 1, 1, 1);
	//bool flag = 0;

	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
	{
		draw_a_point(x, window_height - y, polygon_point_color);
		point p(x, window_height - y);
		input_vertice.push_back(p);
		cout << "多边形顶点" << input_vertice.size() << ":(" << x << ", " << window_height - y << ")" << endl;
	}

	if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
	{
		glLineWidth(2.0f);
		glBegin(GL_LINES);
		glColor3fv(polygon_point_color);
		//绘制多边形
		for (int i = 0; i < input_vertice.size(); i++)
		{
			if (i == input_vertice.size() - 1)
			{
				glVertex2f(input_vertice[0].x, input_vertice[0].y);
				glVertex2f(input_vertice[i].x, input_vertice[i].y);
			}
			else
			{
				glVertex2f(input_vertice[i].x, input_vertice[i].y);
				glVertex2f(input_vertice[i + 1].x, input_vertice[i + 1].y);
			}
		}
		glEnd();
		glFlush();
	}
}

void keyboard(unsigned char key, int x, int y)
{
	if (key == 32)
	{
		//顺时针
		SutherlandHodgmanClip(::left);
		SutherlandHodgmanClip(bottom);
		SutherlandHodgmanClip(::right);
		SutherlandHodgmanClip(top);

		glLineWidth(4.0f);
		glBegin(GL_LINE_LOOP);
		glColor3fv(intersect_point_color);
		for (int i = 0; i < output_vertice.size(); i++)
		{//draw_a_point(output_vertice[i].x, output_vertice[i].y, intersect_point_color);
			glVertex2f(output_vertice[i].x, output_vertice[i].y);
		}
		glEnd();
		glFlush();

	}
}

void display()
{
	glClear(GL_COLOR_BUFFER_BIT);
	glColor3f(0.0f, 0.0f, 0.0f);
	glLineWidth(3.0);

	draw_window();
	/*SutherlandHodgmanClip(::left);
	SutherlandHodgmanClip(bottom);
	SutherlandHodgmanClip(::right);
	SutherlandHodgmanClip(top);*/

	//glEnd();
	glFlush();
}

改进后

改进思路

在之前代码的基础上,新增了一个数组,用来记录绘制过的点。一个点每被绘制一次,那么他的visit标志就加一。然后改变了画线的方法,用bresenham算法画直线。当该点visit访问次数是单数时,就用裁剪专用的颜色画出来(我选的是红色);当该点visit访问次数是双数时,就用边框的颜色画出来(我选的是黑色)
那么为什么要这样做呢?
因为我们想要把重复画过的线变成没有画过的线,所以要手动改变画线函数。
什么意思呢? 根据观察可以知道,SutherlandHodgmanClip 算法的缺陷在于他在画凹多边形时会多绘制一些其实本应该被裁剪掉的线。而这种线有一个特征,就是这种线其实都被我们重复绘制过。于是只要想办法把这个重复绘制的线消除,就可以解决SutherlandHodgmanClip 算法的缺陷。

1.新增的数组

map<float, map<float, int> > vis;

2.新增的画线函数

//画线函数
void draw_line_point(float x, float y)
{
	vis[x][y]++;
	glPointSize(2.5f);
	glBegin(GL_POINTS);
	if (vis[x][y] % 2 == 0)//访问偶数次,与边框颜色相同,涂黑色
	{
		glColor3f(0, 0, 0);
		glVertex2f(x, y);
	}
	else{
		glColor3f(1, 0, 0);//奇数次,涂红色
		glVertex2f(x, y);
	}
	glEnd();
	glFlush();
}

3.bresenham画线算法

void bresenham1(GLint x1, GLint y1, GLint x2, GLint y2)
{
	//glClear(GL_COLOR_BUFFER_BIT);

	draw_line_point(x1, y1);

	int dx = abs(x1 - x2);
	int dy = abs(y1 - y2);
	int flag = 0;//表示当前斜率k的绝对值是否大于1
	if (dx == 0 && dy == 0)
		return;

	if (dy > dx)//斜率绝对值大于1
	{
		flag = 1;
		//横纵坐标轴互换,将x视作y,将y视作x,所有坐标都需要互换
		swap(x1, y1);
		swap(x2, y2);
		swap(dx, dy);
	}

	//确定步长tx,ty,斜率为正或为负,且只考虑左/右上or左/右下,左/右边的情况不加步长即可
	int tx = x2 > x1 ? 1 : -1;
	int ty = y2 > y1 ? 1 : -1;

	//确定下一个点的坐标x,y
	int x = x1 + 1;
	int y = y1;

	//dt和ds由迭代公式推出。dt是右上的点,ds是右边的点
	int dt = 2 * (dy - dx);
	int ds = 2 * dy;

	//判别式的值d=2k*dx-dx = 2*dy-dx
	int d = 2 * dy - dx;

	while (x != x2)
	{
		if (d >= 0)//选T点(右上的点
		{
			//d(i+1) = d(i) + 2(dy - dx)
			d += dt;
			y += ty;
		}
		else//选S点,y方向不加步长
		{
			//d(i+1) = d(i) + 2*dy
			d += ds;
		}

		if (flag)//斜率大于1
		{
			draw_line_point(y, x);
		}
		else
		{
			draw_line_point(x, y);
		}
		x += tx;//x增加步长
	}
}

4.改变的键盘响应事件

void keyboard(unsigned char key, int x, int y)
{
	if (key == 32)
	{
		//顺时针
		SutherlandHodgmanClip(::left);
		SutherlandHodgmanClip(bottom);
		SutherlandHodgmanClip(::right);
		SutherlandHodgmanClip(top);

		glLineWidth(4.0f);
		glBegin(GL_LINE_LOOP);
		glColor3fv(intersect_point_color);
		//画出裁剪后的多边形
		for (int i = 0; i < output_vertice.size() - 1; i++)
		{
			bresenham1(output_vertice[i].x, output_vertice[i].y, output_vertice[i + 1].x, output_vertice[i + 1].y);
		}
		bresenham1(output_vertice[0].x, output_vertice[0].y, output_vertice[output_vertice.size() - 1].x, output_vertice[output_vertice.size() - 1].y);

		glEnd();
		glFlush();

	}
}

完整代码

#include<gl/glut.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<time.h>
using namespace std;
const int window_width = 800, window_height = 600;
struct point
{
	float x, y;
	point() {}
	point(float xx, float yy)
		:x(xx), y(yy) {}

	bool operator < (const point& a)const //运算符重载
	{
		return x < a.x;
	}
};
struct EDGE//Edge边
{
	float bx, by, ex, ey;
	EDGE() {}
	EDGE(float bxx, float byy, float exx, float eyy)
		:bx(bxx), by(byy), ex(exx), ey(eyy) {}
};

//窗口
struct Window {
public:
	int x, y;
	int width;
	int height;
	int l, r, t, b; //边界:左右上下
	Window(int x, int y, int width, int height) {
		this->x = x;
		this->y = y;
		this->width = width;
		this->height = height;
		l = x;
		r = x + width;
		t = y + height;
		b = y;
	}
};
Window myWindow(200, 200, 400, 250);

map<float, map<float, int> > vis;
vector<point> input_vertice;  //输入
vector<point> output_vertice; //输出
float intersect_point_color[3] = { 1,0,0 };
float polygon_point_color[3] = { 0,0,1 };
EDGE left(200, 450, 200, 200);
EDGE bottom(200, 200, 600, 200);
EDGE right(600, 200, 600, 450);
EDGE top(600, 450, 200, 450);

void draw_a_point(float x, float y, float color[]);
void draw_window();
bool inside(point& pt, EDGE ClipBoundary);//判断点是否可见
void intersect(point& s, point& p, EDGE ClipBoundary, point& intersect_pt);//直线段SP和边界求交,返回交点
void SutherlandHodgmanClip(EDGE ClipBoundary, int in_len, int& out_len);
void mymouse(int button, int state, int x, int y);
void keyboard(unsigned char key, int x, int y);
void draw_line_point(float x, float y);
void bresenham1(GLint x1, GLint y1, GLint x2, GLint y2);
void mydisplay(void);
int main(int argc, char* argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RED);
	glutInitWindowPosition(300, 150);
	glutInitWindowSize(window_width, window_height);
	glutCreateWindow("固定矩形窗口裁剪交互式多边形");
	cout << "左键画点\n右键连接成窗口\n按空格裁剪\n" << endl;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0, window_width, 0, window_height);
	glClearColor(1, 1, 1, 1);
	glClear(GL_COLOR_BUFFER_BIT);

	glutMouseFunc(&mymouse);
	glutKeyboardFunc(&keyboard);
	glutDisplayFunc(&mydisplay);

	glutMainLoop();
	return 0;
}

bool inside(point& pt, EDGE ClipBoundary)//判断点是否可见
{
	if (ClipBoundary.ex > ClipBoundary.bx)
	{
		if (pt.y >= ClipBoundary.by)//裁剪边为窗口下边沿
			return true;
	}
	else if (ClipBoundary.ex < ClipBoundary.bx)
	{
		if (pt.y <= ClipBoundary.by)//裁剪边为窗口上边沿
			return true;
	}
	else if (ClipBoundary.ey > ClipBoundary.by)//裁剪边为窗口右边沿
	{
		if (pt.x <= ClipBoundary.bx)
			return true;
	}
	else if (ClipBoundary.ey < ClipBoundary.by)//裁剪边为窗口左边沿
	{
		if (pt.x >= ClipBoundary.bx)
			return true;
	}
	return false;
}

//直线段SP和边界求交,返回交点
void intersect(point& s, point& p, EDGE ClipBoundary, point& intersect_pt)//直线段SP和边界求交,返回交点
{
	if (ClipBoundary.by == ClipBoundary.ey)//水平裁剪边界
	{
		intersect_pt.y = ClipBoundary.by;
		//x=起点的横坐标+dy/sp斜率
		intersect_pt.x = s.x + (ClipBoundary.by - s.y) * (p.x - s.x) / (p.y - s.y);
	}
	else//垂直裁剪边界
	{
		intersect_pt.x = ClipBoundary.bx;
		intersect_pt.y = s.y + (ClipBoundary.bx - s.x) * (p.y - s.y) / (p.x - s.x);
	}
}

void bresenham1(GLint x1, GLint y1, GLint x2, GLint y2)
{
	//glClear(GL_COLOR_BUFFER_BIT);

	draw_line_point(x1, y1);

	int dx = abs(x1 - x2);
	int dy = abs(y1 - y2);
	int flag = 0;//表示当前斜率k的绝对值是否大于1
	if (dx == 0 && dy == 0)
		return;

	if (dy > dx)//斜率绝对值大于1
	{
		flag = 1;
		//横纵坐标轴互换,将x视作y,将y视作x,所有坐标都需要互换
		swap(x1, y1);
		swap(x2, y2);
		swap(dx, dy);
	}

	//确定步长tx,ty,斜率为正或为负,且只考虑左/右上or左/右下,左/右边的情况不加步长即可
	int tx = x2 > x1 ? 1 : -1;
	int ty = y2 > y1 ? 1 : -1;

	//确定下一个点的坐标x,y
	int x = x1 + 1;
	int y = y1;

	//dt和ds由迭代公式推出。dt是右上的点,ds是右边的点
	int dt = 2 * (dy - dx);
	int ds = 2 * dy;

	//判别式的值d=2k*dx-dx = 2*dy-dx
	int d = 2 * dy - dx;

	while (x != x2)
	{
		if (d >= 0)//选T点(右上的点
		{
			//d(i+1) = d(i) + 2(dy - dx)
			d += dt;
			y += ty;
		}
		else//选S点,y方向不加步长
		{
			//d(i+1) = d(i) + 2*dy
			d += ds;
		}

		if (flag)//斜率大于1
		{
			draw_line_point(y, x);
		}
		else
		{
			draw_line_point(x, y);
		}
		x += tx;//x增加步长
	}
}

void SutherlandHodgmanClip(EDGE ClipBoundary)
{
	point s, p, ip;
	output_vertice.clear();
	s = input_vertice[input_vertice.size() - 1];//首尾

	for (int j = 0; j < input_vertice.size(); j++)
	{
		p = input_vertice[j];
		if (inside(p, ClipBoundary))//p在内
		{
			if (inside(s, ClipBoundary))//sp都在窗口内
			{
				output_vertice.push_back(p);
			}
			else//p在里面 s不在
			{
				intersect(s, p, ClipBoundary, ip);
				output_vertice.push_back(ip);
				output_vertice.push_back(p);

			}
		}
		else//p在外面
		{
			if (inside(s, ClipBoundary))//s在窗口内p在窗口外
			{
				intersect(s, p, ClipBoundary, ip);
				output_vertice.push_back(ip);
			}
			//sp都在外面则无输出
		}
		s = p;
	}
	input_vertice = output_vertice;//这次的输出作为下一次的输入,input_vertice和output_vertice是全局变量
}

//画点函数
void draw_a_point(float x, float y, float color[])
{
	glPointSize(5.0f);
	glBegin(GL_POINTS);
	glColor3fv(color);
	glVertex2f(x, y);
	glEnd();
	glFlush();
}

//画裁剪窗口
void draw_window() {
	glBegin(GL_LINE_LOOP);
	glVertex2i(myWindow.l, myWindow.b);
	glVertex2i(myWindow.l, myWindow.t);
	glVertex2i(myWindow.r, myWindow.t);
	glVertex2i(myWindow.r, myWindow.y);
	glEnd();
}

//画线函数
void draw_line_point(float x, float y)
{
	vis[x][y]++;
	glPointSize(2.5f);
	glBegin(GL_POINTS);
	if (vis[x][y] % 2 == 0)//访问偶数次,与边框颜色相同,涂黑色
	{
		glColor3f(0, 0, 0);
		glVertex2f(x, y);
	}
	else{
		glColor3f(1, 0, 0);//奇数次,涂红色
		glVertex2f(x, y);
	}
	glEnd();
	glFlush();
}

void mymouse(int button, int state, int x, int y)
{
	glClearColor(1, 1, 1, 1);

	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
	{
		draw_a_point(x, window_height - y, polygon_point_color);
		point p(x, window_height - y);
		input_vertice.push_back(p);
		cout << "多边形顶点" << input_vertice.size() << ":(" << x << ", " << window_height - y << ")" << endl;
	}

	if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
	{
		glLineWidth(2.0f);
		glBegin(GL_LINES);
		glColor3fv(polygon_point_color);
		//绘制多边形
		for (int i = 0; i < input_vertice.size(); i++)
		{
			if (i == input_vertice.size() - 1)
			{
				glVertex2f(input_vertice[0].x, input_vertice[0].y);
				glVertex2f(input_vertice[i].x, input_vertice[i].y);
			}
			else
			{
				glVertex2f(input_vertice[i].x, input_vertice[i].y);
				glVertex2f(input_vertice[i + 1].x, input_vertice[i + 1].y);
			}
		}
		glEnd();
		glFlush();
	}
}

void keyboard(unsigned char key, int x, int y)
{
	if (key == 32)
	{
		//顺时针
		SutherlandHodgmanClip(::left);
		SutherlandHodgmanClip(bottom);
		SutherlandHodgmanClip(::right);
		SutherlandHodgmanClip(top);

		glLineWidth(4.0f);
		glBegin(GL_LINE_LOOP);
		glColor3fv(intersect_point_color);
		//画出裁剪后的多边形
		for (int i = 0; i < output_vertice.size() - 1; i++)
		{
			bresenham1(output_vertice[i].x, output_vertice[i].y, output_vertice[i + 1].x, output_vertice[i + 1].y);
		}
		bresenham1(output_vertice[0].x, output_vertice[0].y, output_vertice[output_vertice.size() - 1].x, output_vertice[output_vertice.size() - 1].y);

		glEnd();
		glFlush();

	}
}

void mydisplay(void)
{
	glClear(GL_COLOR_BUFFER_BIT);  //clear the screen
	glColor3f(0.0f, 0.0f, 0.0f);
	glLineWidth(3.0);
	draw_window();
	glFlush();
}

总结

  1. 逻辑弄清楚很重要。其实这个算法的逻辑并不复杂,必要时可以手动模拟计算过程帮助理解
  2. 改进的思路很巧妙,再次感谢分享代码的大佬:)

这是最后一个实验作业了,总的来说计算机图形学还是很有趣的,也学到了很多,每次做完实验都感觉很快乐~~~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值