计算几何 模板

#include<iostream> #include<algorithm> #include<utility> #include<cstdio> #include<cstring> #include<cstdlib> #include<sstream> #include<cmath> #include<ctime> #include<vector> #include<string> #include<map> #include<set> #include<queue> #include<stack> #include<numeric> #include<limits> using namespace std; const double eps= 1e-10; const int maxn = 1024; const int inf =1012345678; /* 判断线段相交 极角排序 凸包模板 求国这两点的直线解析方程 如果两条直线相交,并且返回交点 如果线段seg1和seg2相交,返回true且交点由(inter)返回,否则返回false 判断点p是否在线段l上 返回多边形面积(signed)此函数同样适用于非凸多边形 多边形polygon是凸多边形时,返回true 点q是凸多边形polygon内时,返回true; 判断点与线段的关系 求点C到线段AB所在直线的垂足 P 求点p到线段l的最短距离,并返回线段上距该点最近的点np 计算点到折线集的最近距离,并返回最近点. 只能判断圆是否与多边形是否相交和圆心在多边形内部 对于已经排序(可以是顺序也可以是逆序)的多边形(可以是凸也可以是凹)求多边形的重心 将直线或者线段向左手方向推进一定长度 用直线切凸包,半平面交 //判断一个凸多边形(已经按照顺或者逆时针排序)是顺时针还是逆时针,并把顺时针改成逆时针 //旋转卡壳求凸包(逆时针排序)的直径,性质:凸包直径一定是凸包的顶点 */ struct POINT { double x,y; void read(){scanf("%lf%lf", &x,&y);} POINT(){} POINT(double a,double b){x=a; y=b;} }; bool operator ==(const POINT &a,const POINT &b) { if(a.x==b.x && b.y==a.y) return true; else false; } struct SEG { POINT p1,p2; SEG(){} SEG(POINT a,POINT b){p1=a; p2=b;} void read(){p1.read();p2.read();} }; struct LINE { double a,b,c; LINE(){} LINE(double aa,double bb,double cc) { a=aa; b=bb; c=cc; } }; int dblcmp(double t) { if(fabs(t)<eps) return 0; return t<0?-1:1; } double cross(POINT p2,POINT p3,POINT p1)//L12*L13 { //大于0则向量p1p2(p2-p1)在向量p1p3(p3-p1)顺时针方向 //小于0则向量p1p2(p2-p1) 在向量p1p3(p3-p1)逆时针方向 //等于0则在同一直线上 return (p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y); } int onseg1(POINT p1,POINT p2,POINT p3)//p3 在p1 和p2中间 { if(dblcmp(min(p1.x,p2.x)-p3.x)<=0 && dblcmp(p3.x-max(p1.x,p2.x))<=0 && dblcmp(min(p1.y,p2.y)-p3.y)<=0 && dblcmp(p3.y-max(p1.y,p2.y))<=0 ) return 1; return 0; } //点积 /*点积 r=dotmultiply(p1,p2,op),得到矢量(p1-op)和(p2-op)的点积,如果两个矢量都非零矢量 r<0:两矢量夹角为钝角;r=0:两矢量夹角为直角;r>0:两矢量夹角为锐角 */ double dotmultiply(POINT p1,POINT p2,POINT p0) { return ((p1.x-p0.x)*(p2.x-p0.x)+ (p1.y-p0.y)*(p2.y-p0.y)); } //距离 double dist(POINT p1,POINT p2) { double d1=p1.x-p2.x; double d2=p1.y-p2.y; return sqrt(d1*d1+d2*d2); } int onseg2(POINT p1,POINT p2,POINT p3) { if(dblcmp(p2.x- p1.x) ==0 )//在统一竖直线上 { if(dblcmp(min(p1.x,p2.x)-p3.x)==0 && dblcmp(min(p1.y,p2.y)-p3.y)<0 && dblcmp(p3.y-max(p1.y,p2.y))<0 ) return 1; return 0; } if(dblcmp(p1.y-p2.y)==0 )//统一水平线上 { if(dblcmp(min(p1.x,p2.x)-p3.x)<0 && dblcmp(p3.x-max(p1.x,p2.x))<0 && dblcmp(min(p1.y,p2.y)-p3.y)==0) return 1; return 0; } if(dblcmp(min(p1.x,p2.x)- p3.x)<0 &&dblcmp( p3.x-max(p1.x,p2.x))<0 && dblcmp(min(p1.y,p2.y)-p3.y)<0 &&dblcmp( p3.y-max(p1.y,p2.y))<0 ) return 1; return 0; } //********判断线段相交****************/ int seginsec(SEG a ,SEG b) { //-1无交点 //0 平行无交点 //1 完全重合包括相等 //2 平行但是有无穷交点 //3 有端点交点 //4 有非端点交点 double d1,d2,d3,d4; d1= cross(a.p2,b.p1,a.p1); d2= cross(a.p2,b.p2,a.p1); d3= cross (b.p2,a.p1,b.p1); d4= cross(b.p2,a.p2,b.p1); if(dblcmp(d1*d2)<0 && dblcmp(d3*d4)<0) return 4; if(dblcmp(d1) ==0 && dblcmp(d2)==0 && dblcmp(d3)==0 && dblcmp(d3) ==0 && dblcmp(d4) ==0) { if(a.p1==b.p1 &&!onseg1(a.p1,a.p2,b.p2)) return 3; if(a.p1==b.p2 &&!onseg1(a.p1,a.p2,b.p1) ) return 3; if(a.p2==b.p1 &&!onseg1(a.p1,a.p2,b.p2)) return 3; if(a.p2==b.p2 &&!onseg1(a.p1,a.p2,b.p1) ) return 3; if(onseg2(a.p1,a.p2,b.p1) && onseg2(a.p1,a.p2,b.p2)) return 1; if(onseg1(a.p1,a.p2,b.p1) || onseg1(a.p1,a.p2,b.p2)) return 2; if(onseg2(b.p1,b.p2,a.p1) && onseg2(b.p1,b.p2,a.p2)) return 1; if(onseg1(b.p1,b.p2,a.p1) || onseg1(b.p1,b.p2,a.p2)) return 2; return 0; } if(dblcmp(d1)==0 && onseg1(a.p1,a.p2,b.p1)) return 3; if(dblcmp(d2)==0 && onseg1(a.p1,a.p2,b.p2)) return 3; if(dblcmp(d3)==0 && onseg1(b.p1,b.p2,a.p1)) return 3; if(dblcmp(d4)==0 && onseg1(b.p1,b.p2,a.p2)) return 3; return -1; } double DIS(const POINT p1,const POINT p2) { double x=p1.x-p2.x;double y=p1.y-p2.y;return sqrt(x*x+y*y); } POINT pangle;//极角排序 pangle是定位点 /*bool cmpangle(const node &r,const node &t) { POINT a=r.p; POINT b=t.p; POINT c=pangle.p; int res=cross(a,b,c); if(res>0) return true; if(res<0) return false; double q=DIS(c,a),p=DIS(c,b); return q<p; }*/ /************凸包模板*************************/ //注意求凸包并不能保证求出的是凸多边形,有可能所有点都在一条直线上,这样可以用top指针《=2 来判断是否所有点在一条直线上 int top; //0到top-1是凸包点 ,第top个和第0个是相同的点 int cmp(POINT a,POINT b) { if(dblcmp(a.y-b.y)==0) return dblcmp(a.x-b.x)<0; return dblcmp(a.y-b.y)<0; } //特别注意的是传进来的 点p经过了排序处理,顺序和以前不一样了 void graham(int n,POINT p[],POINT polygon[])//polygon是返回的凸包 { int i,j,temtop; if(n<=1) { polygon[0]=polygon[1]=p[0];return; } sort(p,p+n,cmp); top=-1; polygon[++top]=p[0]; polygon[++top]=p[1]; for(i=2; i<n; ++i) { while(top && dblcmp( cross(p[i],polygon[top],polygon[top-1] ))>=0) top--; polygon[++top]=p[i]; } temtop=top; polygon[++top]=p[n-2]; for(i=n-3;i>=0; i--) { while(top>=temtop+1 && dblcmp(cross(p[i],polygon[top], polygon[top-1]))>=0) top--; polygon[++top]=p[i]; } } /************凸包模板*************************/ //已知两点坐标,求国这两点的直线解析方程 //a*x+b*y+c=0 (a>=0) LINE makeline(POINT p1,POINT p2) { LINE t; int sign=1; t.a=p2.y-p1.y; if(t.a<0) { sign=-1; t.a=sign*t.a; } t.b=sign*(p1.x-p2.x); t.c=sign*(p1.y*p2.x-p1.x*p2.y); return t; } //如果两条直线l1(a1*x+b1*y+c1=0) ,l2(a2*x+b2*y+c2=0)相交,并且返回交点 //-1 表示平行,0表示是重合的一条直线,1表示相交 int lineinsec(LINE l1,LINE l2,POINT &p) { double d= l1.a*l2.b-l1.b*l2.a; if(dblcmp(d)==0) { double d1=l1.a*l2.c-l1.c*l2.a; if(dblcmp(d)==0) return 0; return -1; } p.x=(l2.c*l1.b-l1.c*l2.b)/d; p.y=(l2.a*l1.c-l1.a*l2.c)/d; return 1; } // 判断点p是否在线段l上 //条件:(p在线段l所在的直线上)&& (点p在以线段l为对角线的矩形内) bool onseg(SEG l,POINT p) {//如果不能在线段端点上,则要把<=改成<; return (dblcmp(cross(p,l.p2,l.p1))==0 && (onseg1(l.p1,l.p2,p))); } //如果线段seg1和seg2相交,返回true且交点由(inter)返回,否则返回false bool seginsec(SEG seg1,SEG seg2,POINT &inter) { LINE l1,l2; l1= makeline(seg1.p1,seg1.p2); l2= makeline(seg2.p1,seg2.p2); if(lineinsec(l1,l2,inter)==1) { return onseg(seg1,inter); } else return false; } //返回多边形面积(signed) //输入顶点按逆时针排列时,返回正值;否则返回负值 //返回浮点值==0.0时,说明不是多边形,没有面积。此函数同样适用于非凸多边形(wangkun的添加) double area_polygon(int vcount ,POINT polygon[]) { int i; double s; if(vcount<3) return 0; s=polygon[0].y*(polygon[vcount-1].x-polygon[1].x); for(i=1; i<vcount; i++) s+=polygon[i].y*(polygon[i-1].x-polygon[(i+1)%vcount].x); return s/2; } //必须是逆时针的凸包方向输入顺序返回多边形顶点的凸凹性判断 //返回值:,bc[i]=1,iff:第i个顶点是凸顶点 void checkconvex(int vcount ,POINT polygon[],bool bc[]) { int i,index=0; POINT tp=polygon[0]; for(i=1; i<vcount ; i++)//寻找第一个凸点 { if(polygon[i].y< tp.y || (polygon[i].y == tp.y && polygon[i].x< tp.x)) { tp=polygon[i]; index=i; } } double s; int count =vcount -1; bc[index]=true; while(count ) { if (dblcmp(( cross(polygon[(index+1)%vcount], polygon[(index+2)%vcount] ,polygon[index]))>=0)) bc[(index+1)%vcount]=true; else bc[(index+1)%vcount]=false; index=(index+1)%vcount; count--; } } //判断的时候必须保证凸包是顺时针 //返回值:多边形polygon是凸多边形时,返回true bool isconvex(int vcount ,POINT polygon[]) { bool bc[maxn]; checkconvex(vcount ,polygon,bc); for(int i=0; i<vcount ;i++)// 逐一检查顶点,是否全部是凸顶点 if(!bc[i]) return false; return true; } //点q是凸多边形polygon内时,返回true; //注意:多边形polygon一定要是凸多边形 (因为要求多边形内部一点) bool InsideConvexPolygon(int vcount ,POINT polygon[],POINT q) { POINT p; SEG l; int i; p.x=0;p.y=0; for(i=0; i<vcount ;i++)// 寻找一个肯定在多边形polygon内的点p:多边形顶点平均值 { p.x+=polygon[i].x; p.y+=polygon[i].y; } p.x/=vcount; p.y/=vcount; for(i=0; i<vcount ; i++) { l.p1= polygon[i]; l.p2=polygon[(i+1)%vcount]; if(dblcmp(cross(p,l.p2,l.p1)* cross(q,l.p2,l.p1)) <0) break; } return (i==vcount); } /* 线段及直线的基本运算 判断点与线段的关系, 用途很广泛 本函数是根据下面的公式写的,P是点C到线段AB所在直线的垂足 AC dot AB r = --------- ||AB||^2 (Cx-Ax)(Bx-Ax) + (Cy-Ay)(By-Ay) = ------------------------------- L^2 r has the following meaning: r=0 P = A r=1 P = B r<0 P is on the backward extension of AB r>1 P is on the forward extension of AB 0<r<1 P is interior to AB */ double relation(POINT p,SEG l) { SEG tl; tl.p1=l.p1; tl.p2=p; return dotmultiply(tl.p2,l.p2,l.p1)/(dist(l.p1,l.p2)*dist(l.p1,l.p2)); } //求点C到线段AB所在直线的垂足 P POINT perpendicular(POINT p,SEG l,double r) { //double r = relation(p,l); POINT tp; tp.x= l.p1.x+r*(l.p2.x-l.p1.x); tp.y= l.p1.y +r*(l.p2.y-l.p1.y); return tp; } //求点p到线段l的最短距离,并返回线段上距该点最近的点np //注意:np是线段l上到点p最近的点,不一定是垂足 double ptolinesegdist(POINT p,SEG l,POINT &np) { double r=relation(p,l); if(r<0) { np= l.p1; return dist(p,l.p1); } if(r>1) { np = l.p2; return dist(p,l.p2); } np = perpendicular(p,l,r); return dist(p,np); } //计算点到折线集的最近距离,并返回最近点. //注意:调用的是 ptolinesegdist()函数 double ptopointset(int vcount,POINT pointset[],POINT p,POINT &q) { int i; double cd=double(inf),td; SEG l; POINT tq,cq; for(i=0; i<vcount-1; i++) { l.p1=pointset[i]; l.p2=pointset[i+1]; td=ptolinesegdist(p,l,tq); if(td<cd) { cd=td; cq=tq; } } q=cq; return cd; } // 判断圆是否在多边形内. //只能判断圆是否与多边形是否相交和圆心在多边形内部 bool CircleInsidePolygon(int vcount ,POINT center,double radius,POINT polygon[]) { if(!InsideConvexPolygon(vcount ,polygon,center)) return false; POINT q; double d; q.x=0;q.y=0; d=ptopointset(vcount,polygon,center,q); if( dblcmp(d-radius)>=0 )//注意相切的情况 return true; else return false; } /*对于已经排序(可以是顺序也可以是逆序)的多边形(可以是凸也可以是凹)求多边形的重心*/ POINT gravitycenter(int vcount ,POINT polygon[]) { POINT tp;int i; double x,y,s,x0,y0,cs,k; x=0; y=0; s=0; for(i=1; i<vcount-1; i++) { x0=(polygon[0].x+polygon[i].x+polygon[i+1].x)/3; y0=(polygon[0].y+polygon[i].y+polygon[i+1].y)/3; cs=cross(polygon[i],polygon[i+1],polygon[0])/2; s+=cs; x+=cs*x0; y+=cs*y0; } tp.x=x/s; tp.y=y/s; return tp; } inline void tuijin(POINT &p1,POINT &p2,double d)//逆时针的两个点想凸包内推进,顺时针向外 //向向量的左手方向推进 { // printf("d=%lf %lf %lf %lf %lf/n",d,p1.x,p1.y,p2.x,p2.y); double px,py,l; px=-p2.y+p1.y; py=p2.x-p1.x; l=sqrt(px*px+py*py); px=d*px/l; py=d*py/l; p1.x+=px; p2.x+=px; p1.y+=py; p2.y+=py; // printf("d=%lf %lf %lf %lf %lf/n",d,p1.x,p1.y,p2.x,p2.y); } inline int cutpolygon(POINT polygon[],int vcount,POINT p1,POINT p2)//凸包(多边形不行)必须逆时针方向,直线p1p2也是按照逆时针(左手)的,也就是和多边形的方向是相同的 //返回被切后多边形的点数(如果只剩一条线返回的点数也可能是正的) { POINT pp[maxn]; double now[maxn]; int a=0,b=0; int i,j; int nn; for (i=0; i<vcount; i++) { now[i]=cross(p2,polygon[i],p1); if (now[i]>0) a++;//左边 要保留的点 if (now[i]<0) b++;//右边 要删除的点 } if (a==0 && b==vcount) return 0; else if (a==0 && b!=vcount) //处理只剩下一条线的情况 { nn=0; for (i=0; i<vcount; i++) if (now[i]==0)//now[i]==0; polygon[nn++]=polygon[i]; polygon[nn]=polygon[0]; return nn; } if (b==0) return vcount; else if (b!=0)//处理切割的情况 { nn=2; polygon[vcount]=polygon[0]; now[vcount]=now[0]; i=0; for (;;) { if (now[i]>0 && now[i+1]<=0) break; i=(i+1)%vcount; } //定比分点法求坐标 pp[0].x=(now[i]*polygon[i+1].x-now[i+1]*polygon[i].x)/(now[i]-now[i+1]); pp[0].y=(now[i]*polygon[i+1].y-now[i+1]*polygon[i].y)/(now[i]-now[i+1]); j=(i+1)%vcount; for (;;) { if (now[j+1]>0) break; j=(j+1)%vcount; } pp[1].x=(now[j]*polygon[j+1].x-now[j+1]*polygon[j].x)/(now[j]-now[j+1]); pp[1].y=(now[j]*polygon[j+1].y-now[j+1]*polygon[j].y)/(now[j]-now[j+1]); j=(j+1)%vcount; for(;;j=(j+1)%vcount) { pp[nn++]=polygon[j]; if (i==j) break; } for (i=0; i<nn; i++) polygon[i]=pp[i]; polygon[nn]=polygon[0]; return nn; } } //判断一个凸多边形(已经按照顺或者逆时针排序)是顺时针还是逆时针,并把顺时针改成逆时针 void anticlockwise(POINT *polygon,int len)//传入凸包和点数 { for(int i=0;i<len-2;i++) { double tmp=cross(polygon[i+1],polygon[i+2],polygon[i]); if(dblcmp(tmp)>0) return; else if(dblcmp(tmp)<0) { reverse(polygon,polygon+len); return; } } } //旋转卡壳求凸包(逆时针排序)的直径 //性质:凸包直径一定是凸包的顶点 double rotatecaliper(POINT *polygon,int n) { int q=1; double ans=0; polygon[n]=polygon[0]; for(int p=0;p<n;p++) { //下面这一步实际上就是找最远点 while(cross(polygon[p+1],polygon[q+1],polygon[p])>cross(polygon[p+1],polygon[q],polygon[p])) q=(q+1)%n; ans=max(ans,max(dist(polygon[p],polygon[q]),dist(polygon[p+1],polygon[q+1]))); } return ans; }  

你可能感兴趣的:(c,struct,extension)