通过opengl实现凸多边形的交

时间:2022-11-12 09:24:50
 

 

多边形求交

1. 动画的实现

使用glutTimerFunc函数,反复回调刷新,产生动画效果。
 

2. 点在多边形内部判断

使用多边形的性质,多边形内部的点起始的射线与多边形有奇数个交点,外部的点起始的射线有偶数个交点。
所以,我写算法的依据就是从一个点做水平的射线,计算其与各个边有没有交点
 

3. 求交点

简单的二维几何学
 


4. 交集的点序:

当我们得到了交点和型内点后,下一个是核心问题,点序。
我们定义红色的多边形是P,白色的是Q,交点是M,等待排序的点如标注
 


首先我们将点存储成两组:
组1:M1P2M2M3M4
组2:Q1M1M2M3M4Q5
在每个有序组内,都按照顺时针存储。这里要注意一个问题,如果一个边上有两个交点,需要使用顶点与交点的距离来判定点序。
然后,将组2插入组1:
我们知道,要插入的Q点,一定在两个交点M之间。
所以,我们先把组2顺时针移动变成:M4Q5Q1M1M2M3
这时候,在组1中找到M4和M1
M1P2M2M3M4
将Q5Q1按顺序插入:
M1P2M2M3M4 Q5Q1
这样,就得到了相交的图像。


同理,并运算也是这样进行,不过就是把型内点变成型外点。


5. 数据结构

这里只用了堆栈,但是后来发现,使用链表更具优越性,但尚未修改。


6. 改进方式

如果想在凹多边形使用这套算法,必须先进行凹多边形三角划分,再套用方法。

7. 算法框图

  通过opengl实现凸多边形的交
引用了:黄俊华, 闫遂军, 朱小龙,等. 一种凸多边形的交、并求解算法[J]. 桂林理工大学学报, 2007, 27(4):589-592.


效果及实现

 
通过opengl实现凸多边形的交

#include <stdio.h>  
#include <stdlib.h>  
#include <conio.h>
#include <math.h>
#include "list.h" 
#include "gl/glut.h"  
#include "stack.h"  

const GLfloat Pi = 3.1415926536f;
GLfloat
POLYGON_A[5][2] = { { 0, 0 },{ -0.1f, 0.5f },{ 0, 0.9f },{ 0.4f, 0.7f },{ 0.5f, 0.2f } };
GLfloat
//POLYGON_B[5][2] = { { -0.4f, 0.1 },{ -0.6, 0.6f },{ -0.5f, 0.7f },{ 0.1f, 0.71f },{ -0.2f, 0.21f } };
POLYGON_B[5][2] = { { -0.4f, 0.1 },{ -0.6, 0.5f },{ -0.5f, 0.7f },{ 0.1f, 0.51f },{ -0.2f, 0.21f } };
//POLYGON_B[5][2] = { { -0.5f, 0.1 },{ -0.6, 0.8f },{ -0.5f, 0.9f },{ 0.1f, 0.71f },{ -0.2f, 0.21f } };
GLfloat movetmp = 0.00301f;
int num = 5;
GLfloat label=1;
int jb = 1;//取1为交,取0为并


GLfloat max(GLfloat a, GLfloat b) {
	if (a > b) return a;
	else return b;

}

GLfloat min(GLfloat a, GLfloat b) {
	if( a < b) return a;
	else return b;

}

int next(int a) {
	if (a == (num - 1)) return 0;
	else return a + 1;

}
int last(int a) {
	if (a == 0) return num-1;
	else return a - 1;

}


int PtInPolygon(GLfloat p[2], GLfloat POLYGON[5][2], int nCount)
{
	int nCross = 0;
	GLfloat x;
	for (int i = 0; i < nCount; i++)    
	{
		GLfloat p1[2]; 
		GLfloat p2[2];
		p1[0]= POLYGON[i][0];
		p1[1] = POLYGON[i][1];
		p2[0] = POLYGON[next(i)][0];
		p2[1] = POLYGON[next(i)][1];

		// 求解 y=p.y 与 p1p2 的交点
		if (p1[1] == p2[1]) // p1p2 与 y=p0.y平行 
			continue;
		if (p[1] < min(p1[1], p2[1])) // 交点在p1p2延长线上 
			continue;
		if (p[1] >= max(p1[1], p2[1])) // 交点在p1p2延长线上 
			continue;
		// 求交点的 X 坐标 -------------------------------------------------------------- 
		 x = (p[1] - p1[1]) * (p2[0] - p1[0]) /(p2[1] - p1[1]) + p1[0];
		if (x >p[0])
			nCross++; // 只统计单边交点 
	}
	// 单边交点为偶数,点在多边形之外 --- 
	return (nCross % 2 == 1);
}

GLfloat getX(GLfloat pointA[2], GLfloat pointB[2], GLfloat pointC[2], GLfloat pointD[2],int nt) {
	GLfloat X[2];
	GLfloat  m, n, a, b, c, d;//ax+by=m 	cx + dy = n
	b = -1; d = -1;
	a = (pointA[1] - pointB[1]) / (pointA[0] - pointB[0]);
	m = (pointA[1] - pointB[1]) / (pointA[0] - pointB[0])*pointA[0] - pointA[1];
	c=  (pointC[1] - pointD[1]) / (pointC[0] - pointD[0]);
	n = (pointC[1] - pointD[1]) / (pointC[0] - pointD[0])*pointC[0] - pointC[1];

	X[0]= (m*d - b*n) / (a*d - b*c);
	X[1]= (m*c - a*n) / (b*c - a*d);
	if ((X[0] <= max(pointA[0], pointB[0])) && (X[0] >= min(pointA[0], pointB[0])) && (X[0] <= max(pointC[0], pointD[0])) && (X[0] >= min(pointC[0], pointD[0])) && (X[1] <= max(pointA[1], pointB[1])) && (X[1] >= min(pointA[1], pointB[1])) && (X[1] <= max(pointC[1], pointD[1])) && (X[1] >= min(pointC[1], pointD[1])))
	{
		if (nt == 1)	return X[1];
		else return X[0];
	}
	else return 10086;
}


void myDisplay(void)
{
	int n = 1000;
	int a = 0;
	int c = 0;
	//int x;
	//int y;
	int number=0;
	//int labelinB[11];
	int part[5][5];                                                       
	int k = 0;
	GLfloat pointBtmp[3];
	GLfloat X[2],Xtmp[2];
	GLfloat Distance[2];
	GLfloat pointB[100][3];

	DuLinkList Listx;
	DuLinkList Listy;
	InitList(&Listx);
	InitList(&Listy);


	Stack mystackx;
	Stack mystacky;
	InitStack(&mystackx);
	InitStack(&mystacky);

	for (int i = 0; i < 100; i++) {
		for (int j = 0; j < 3; j++){
		 pointB[i][j]= -1;
		}
	}

	glClear(GL_COLOR_BUFFER_BIT);
	// 按照 A->C->E->B->D->A 的顺序,可以一笔将五角星画出
	glBegin(GL_LINE_LOOP);
	glColor3f(1.0f, 0.0f, 0.0f);
	glVertex2fv(POLYGON_A[0]);
	glVertex2fv(POLYGON_A[1]);
	glVertex2fv(POLYGON_A[2]);
	glVertex2fv(POLYGON_A[3]);
	glVertex2fv(POLYGON_A[4]);
	glEnd();

	glBegin(GL_LINE_LOOP);
	glColor3f(1.0f, 1.0f, 1.0f);
	glVertex2fv(POLYGON_B[0]);
	glVertex2fv(POLYGON_B[1]);
	glVertex2fv(POLYGON_B[2]);
	glVertex2fv(POLYGON_B[3]);
	glVertex2fv(POLYGON_B[4]);
	glEnd();

/*	number = 0;
	//In B polygon
	k = 0;
	x = 0;
	for (c = 0; c < num; c++) {
		if (PtInPolygon(POLYGON_B[c], POLYGON_A, num) == 1) {
			labelinB[k] = c;
			if ((labelinB[k] - labelinB[k - 1]) == 1) {
				y = y + 1;
				part[x][y + 1] = labelinB[k];
			}
			else {
				part[x][0] = labelinB[k];
				x = x + 1;
				y = 0;
			}
			k++;
			//Push(&mystackx, POLYGON_B[c][0]);
			//Push(&mystacky, POLYGON_B[c][1]);
			glBegin(GL_POLYGON);
			for (int i = 0; i < n; ++i)
				glVertex2f(POLYGON_B[c][0] + 0.02 * cos(2 * Pi / n*i), POLYGON_B[c][1] + 0.02 * sin(2 * Pi / n*i));
			glEnd();
		}

	}
	labelinB[k] = -1;

*/

	//求交点
	for (a = 0; a < num; a++) {
		number = 0;
		if (PtInPolygon(POLYGON_A[a], POLYGON_B, num) == jb) {
			Push(&mystackx, POLYGON_A[a][0]);
			Push(&mystacky, POLYGON_A[a][1]);
			glBegin(GL_POLYGON);
			for (int i = 0; i < n; ++i)
				glVertex2f(POLYGON_A[a][0] + 0.02 * cos(2 * Pi / n*i), POLYGON_A[a][1] + 0.02 * sin(2 * Pi / n*i));
			glEnd();
		}
			for (c = 0;c < num; c++) {
					X[0] = getX(POLYGON_A[a], POLYGON_A[next(a)], POLYGON_B[c], POLYGON_B[next(c)], 0);
					X[1] = getX(POLYGON_A[a], POLYGON_A[next(a)], POLYGON_B[c], POLYGON_B[next(c)], 1);

					if (X[0] != 10086) {
						number++;
						Distance[number-1]=sqrt((X[0] - POLYGON_A[a][0]) *(X[0] - POLYGON_A[a][0]) + (X[1] - POLYGON_A[a][1])*(X[1] - POLYGON_A[a][1]));
						Push(&mystackx, X[0]);
						Push(&mystacky, X[1]);						
						glBegin(GL_POLYGON);
						for (int i = 0; i < n; ++i)
							glVertex2f(X[0] + 0.02 * cos(2 * Pi / n*i), X[1] + 0.02 * sin(2 * Pi / n*i));
						glEnd();
						if (number == 2 && Distance[0]>Distance[1]) {
							Pop(&mystackx);
							Pop(&mystacky);
							Xtmp[0]=Pop(&mystackx);
							Xtmp[1]=Pop(&mystacky);
							Push(&mystackx, X[0]);
							Push(&mystacky, X[1]);
							Push(&mystackx, Xtmp[0]);
							Push(&mystacky, Xtmp[1]);

						}
					}					
		}
	}


	
	//插入分块




	
	//对偶交叉
	//求交点
	k = 0;
	for (a = 0; a < num; a++) {
		number = 0;
		if (PtInPolygon(POLYGON_B[a], POLYGON_A, num) == jb) {
			pointB[k][0] = POLYGON_B[a][0];
			pointB[k][1] = POLYGON_B[a][1];
			pointB[k][2] = 1;//1表示要插入的点
			k++;
			glBegin(GL_POLYGON);
			for (int i = 0; i < n; ++i)
				glVertex2f(POLYGON_B[a][0] + 0.02 * cos(2 * Pi / n*i), POLYGON_B[a][1] + 0.02 * sin(2 * Pi / n*i));
			glEnd();
		}
		for (c = 0; c < num; c++) {

			X[0] = getX(POLYGON_B[a], POLYGON_B[next(a)], POLYGON_A[c], POLYGON_A[next(c)], 0);
			X[1] = getX(POLYGON_B[a], POLYGON_B[next(a)], POLYGON_A[c], POLYGON_A[next(c)], 1);

			if (X[0] != 10086) {

				//

				//
				number++;
				Distance[number - 1] = sqrt((X[0] - POLYGON_B[a][0]) *(X[0] - POLYGON_B[a][0]) + (X[1] - POLYGON_B[a][1])*(X[1] - POLYGON_B[a][1]));
				pointB[k][0] = X[0];
				pointB[k][1] = X[1];
				pointB[k][2] = 0;//1表示要插入的点
				k++;
				glBegin(GL_POLYGON);
				for (int i = 0; i < n; ++i)
					glVertex2f(X[0] + 0.02 * cos(2 * Pi / n*i), X[1] + 0.02 * sin(2 * Pi / n*i));
				glEnd();
				if (number == 2 && Distance[0]>Distance[1]) {
					k--;
					pointB[k][0] = -1;
					pointB[k][1] =-1;
					pointB[k][2] = -1;//1表示要插入的点
					k--;
					Xtmp[0] = pointB[k][0];
					Xtmp[1] = pointB[k][1];
					pointB[k][0] = X[0];
					pointB[k][1] = X[1];
					pointB[k][2] = 0;//1表示要插入的点
					k++;
					pointB[k][0] = Xtmp[0];
					pointB[k][1] = Xtmp[1];
					pointB[k][2] = 0;//1表示要插入的点
					k++;

				}
			}
		}
	}

	//插入操作
	k--;
	a = k;
	while ((pointB[0][2] == 1 || pointB[k][2] == 1) && a>=0) {
		pointBtmp[0] = pointB[k][0];
		pointBtmp[1] = pointB[k][1];
		pointBtmp[2] = pointB[k][2];
		for (int i = 0; i < k; i++) {
			pointB[k - i][0] = pointB[k - i - 1][0];
			pointB[k - i][1] = pointB[k - i - 1][1];
			pointB[k - i][2] = pointB[k - i - 1][2];
		}
		pointB[0][0] = pointBtmp[0];
		pointB[0][1] = pointBtmp[1];
		pointB[0][2] = pointBtmp[2];
		a--;//判断并集时候的分离情况
		
	}

//绘制分离的两图
	if (a ==-1 || jb==0) {
		a = -1;
		glBegin(GL_POLYGON);
		glColor3f(1.0f, 1.0f, 0.0f);
		while (a<k) {
			a++;
			X[0] = pointB[a][0];
			X[1] = pointB[a][1];
			glVertex2fv(X);

		}
			glEnd();

			glBegin(GL_POLYGON);
			glColor3f(1.0f, 1.0f, 0.0f);
			while (!IsEmpty(&mystackx)) {
				X[0] = Pop(&mystackx);
				X[1] = Pop(&mystacky);
				glVertex2fv(X);
			}
			glEnd();
	}



	//涂色黄色
	if (a != -1) {
		glBegin(GL_POLYGON);
		glColor3f(1.0f, 1.0f, 0.0f);
		while (!IsEmpty(&mystackx)) {
			X[0] = Pop(&mystackx);
			X[1] = Pop(&mystacky);
			glVertex2fv(X);
			for (int i = k; i >= 0; i--) {
				if (X[0] == pointB[i][0] && X[1] == pointB[i][1] && pointB[i - 1][2] == 1 && jb==1) {
					while (pointB[i][2] != 1 && i >= 0) i--;//找到需要插入的开始点(只要不是1就跳过去)
					while (pointB[i][2] == 1 && i >= 0) {//对于所有的1来说,都进行这个操作
						Xtmp[0] = pointB[i][0];
						Xtmp[1] = pointB[i][1];
						glVertex2fv(Xtmp);
						i--;
					}
				}
			}

		}

		glEnd();
	}
	glFlush();//这个一定要在函数最后面

}




void drive(int data)
{
	if (_kbhit()) /* do nothing */ 	movetmp = 0;
	

	for (int i = 0; i < 5; i++) {
			 POLYGON_B[i][0] = POLYGON_B[i][0] + movetmp;
			
	}
	if ((POLYGON_B[0][0] < -0.8) || (POLYGON_B[0][0] > 1)) movetmp = -movetmp;
	glutTimerFunc(5, drive, -1);// call drive() again in 50 milliseconds								    /*callback function moves the car. .... move x, y, z, etc
						
	glutPostRedisplay();
}

int main(int argc, char *argv[])
{
	glutInit(&argc, argv);
	glColor3f(1.0, 0.0, 0.0);
	glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(500, 500);
	glutCreateWindow("第一次 OpenGL 作业");
	glutDisplayFunc(&myDisplay);
	glutTimerFunc(5, drive, -1); // physics, movement equations here
	glutMainLoop();

	return 0;
}