多边形求交
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. 算法框图
引用了:黄俊华, 闫遂军, 朱小龙,等. 一种凸多边形的交、并求解算法[J]. 桂林理工大学学报, 2007, 27(4):589-592.
效果及实现
#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; }