计算几何(学习)模板
点结构:
struct point { double x,y; point(double a = 0,double b = 0): x(a),y(b){} };
浮点误差处理:
int dblcmp(double x) { if(fabs(x) < eps) return 0; return x > 0 ? 1:-1; } 或者 int dblcmp(double x) { if (x > eps) return 1; else if (x < -eps) return -1; else return 0; }
判断线段是否相交并求交点(规范相交)
double det(double x1, double y1, double x2, double y2) { //求叉积 return x1*y2 - x2*y1; } double cross(Point a, Point b, Point c) { //向量ab,和向量ac return det(b.x - a.x, b.y - a.y, c.x - a.x, c.y - a.y); } double dotdet(double x1, double y1, double x2, double y2) { //点积 return x1*x2 + y1*y2; } double dot(Point a, Point b, Point c) { return dotdet(b.x - a.x, b.y - a.y, c.x - a.x, c.y - a.y); } int betweenCmp(Point a, Point b, Point c) { //判断a是不是在bc范围内 return dbcmp(dot(a, b, c)); } bool segcross(Point a, Point b, Point c, Point d) { double s1, s2; int d1, d2, d3, d4; d1 = dbcmp(s1 = cross(a, b, c)); d2 = dbcmp(s2 = cross(a, b, d)); d3 = dbcmp(cross(c, d, a)); d4 = dbcmp(cross(c, d, b)); if((d1^d2) == -2 && (d3^d4) == -2) { //规范相交 return true; } //非规范相交 if((d1 == 0 && betweenCmp(c, a, b) <= 0) || (d2 == 0 && betweenCmp(d, a, b) <= 0) || (d3 == 0 && betweenCmp(a, c, d) <= 0) || (d4 == 0 && betweenCmp(b, c, d) <= 0)) return true; return false; }
叉积求多边形面积
double area(point p[],int n) { //这里是相对于原点(0, 0),也可以在多边形上找一个点作为向量的起点 double s = 0; int i; p[n].x = p[0].x; p[n].y = p[0].y; for(i = 0; i < n; ++i) s += det(p[i].x, p[i].y, p[i+1].x, p[i+1].y); return fabs(s / 2.0); }
三角形面积:
1. 海伦公式
p = (a+b+c)/2; S = sqrt(p*(p-a)*(p-b)*(p-c));
2:叉积求解:一直三点:
A(x1,y1),B(x2,y2),C(x3,y3); S = fabs(-x2 * y1 + x3*y1+x1*y2-x3*y2-x1*y3+x2*y3) / 2.0;
判断多边形是否为凸多边形
输入p[1],p[2] ...p[n]。 令p[0] = p[n] p[n + 1] = p[1]; bool isconvexpg() { int dir = 0; for (int i = 0; i <= n - 1; ++i) { int temp = dblcmp(cross(p[i],p[i + 1],p[i + 2])); if (!dir) dir = temp; if (dir*temp < 0) return false; } return true; }
判断一点是否在多边形内,环顾法:
double getdis(point a,point b) { double x = a.x - b.x; double y = a.y - b.y; return sqrt(x*x + y*y); } //利用点积求角度 double getangle(point a,point b,point c) { double dj = dotdet(b.x - a.x,b.y - a.y,c.x - a.x,c.y - a.y); double dis = getdis(a,b)*getdis(a,c); double tmp = dj/dis; return acos(tmp); } //判断是否在多边形内 bool IsIn() { int i; double angle = 0.0; for (i = 1; i <= n; ++i) { if (dblcmp(cross(cir,p[i],p[i + 1])) >= 0) angle += getangle(cir,p[i],p[i + 1]); else angle -= getangle(cir,p[i],p[i + 1]); } //printf("angle == %lf\n",angle); if (dblcmp(angle) == 0) return false;//在外边 else if (dblcmp(angle - pi) == 0 || dblcmp(angle + pi) == 0)//在边上 { if (dblcmp(r) == 0) return true; } else if (dblcmp(angle - 2.0*pi) == 0 || dblcmp(angle + 2.0*pi) == 0)//在里面 { return true; } else //在多边行顶点 { if (dblcmp(r) == 0) return true; } return false; }
求凸包的graham_scan算法模板
//这里起点与终点肯定在凸包上,不知道怎么证明 int graham(point *p,int len) { top = 0; int i; //先排序,lrj黑书上的排序方法 sort(p,p + len,cmp); stack[top++] = p[0]; stack[top++] = p[1]; //求右链 for (i = 2; i < len; ++i) { while (top > 1 && dblcmp(cross(stack[top - 2],stack[top - 1],p[i])) <= 0) top--; stack[top++] = p[i]; } //求左链 int tmp = top; for (i =len - 2; i >= 0; --i) { while (top > tmp && dblcmp(cross(stack[top - 2],stack[top - 1],p[i]))<= 0) top--; stack[top++] = p[i]; } return top - 1;//起点两次进栈 - 1 }
利用凸包求最远点距离——旋转卡壳法:
int rotaing(point *p,int len) { p[len] = p[0]; int ans = 0,q = 1; for (int i = 0; i < len; ++i) { while (cross(p[i],p[i + 1],p[q + 1]) > cross(p[i],p[i + 1],p[q])) q = (q + 1)%len; ans = max(ans,max(dis2(p[i],p[q]),dis2(p[i + 1],p[q + 1]))); //这里之所以计算i+1与q+1的距离是考虑到在凸多边形中存在平行边的问题 } return ans; }