计算几何模板
来源:互联网 发布:淘宝的聚划算怎么抢 编辑:程序博客网 时间:2024/06/05 05:17
二维几何
坐标运算
点和向量共用的坐标Coord运算,使用stl中的容器complex实现(参考地址)。
复数相乘的几何意义为长度相乘,极角相加。
const double EPS=1e-9,PI=acos(-1);typedef complex<double> Coord;Coord(double x=0,double y=0);//构造函数double real(Coord a);//a的实部(复平面的横坐标),也可写作a.real()double imag(Coord a);//a的虚部(复平面的纵坐标),也可写作a.imag()double abs(Coord a);//向量a的模长,或是点a到原点的距离double norm(Coord a);//abs的平方,比abs快,但是要注意浮点数精度溢出double arg(Coord a);//a的幅角,与atan2(a.real(),a.imag())等价Coord polar(double r,double t);//极坐标生成方式,r为幅值,t为幅角//运算符重载,<< >>运算符输出括号形式的坐标+、-、*、/(以及对应的赋值运算,但是赋值运算不能写在表达式中,详见参考地址)、<<、>>int sgn(double x)//浮点数判断符号 { return fabs(x)<EPS?0: x<0?-1:1;}bool operator!=(Coord a,Coord b)//不等运算符,涉及到浮点数比较要重写{ return sgn(a.real()-b.real())||sgn(a.imag()-b.imag());}bool operator==(Coord a,Coord b){ return !(a!=b);}
点和向量
typedef Coord Point;typedef Coord Vec;double Dot(Vec A,Vec B)//点乘{ return A.real()*B.real()+A.imag()*B.imag();}double Cross(Vec A,Vec B)//叉乘 { return A.real()*B.imag()-A.imag()*B.real();}Vec Rotate(Vec A,double rad)//向量逆时针旋转rad度(弧度){ return A*polar(1.0,rad);}double Angle(Vec A,Vec B)//向量夹角{ return acos(Dot(A,B)/abs(A)/abs(B));}double Area2(Point A,Point B,Point C)//向量AB、向量AC三角形有向面积的二倍{ return Cross(B-A,C-A);}Vec Normal(Vec A)//计算非零向量A的单位法向量,左转90°,把长度归一{ return Vec(-A.imag(),A.real())/abs(A);}
直线和线段
用直线上的一点p和方向向量v表示一条经过p的直线,直线上的所有点q满足q=p+t*v,其中t是参数;当限制t≥0时,该参数方程表示射线;限制0≤t≤1时,该参数方程表示线段。
此外,如果已知线段端点a1和a2,可以通过Line(a1,a2-a1)来得到对应的参数形式。
struct Line{ Point p; Vec v; Line() {} Line(Point p,Vec v):p(p),v(v) {} Point point(double t)//线上一点 { return p+t*v; }};bool Onleft(Point P,Line L)//p是否在有向直线L左侧,不含线上{ return Cross(L.v,P-L.p)>0;}double DistanceToLine(Point P,Line L)//点到直线距离(有向){ return Cross(L.v,P-L.p)/abs(L.v);}double DistanceToSegmentS(Point P,Point A,Point B)//点到线段的距离(无向){ if(A==B)return abs(P-A); Vec v1=B-A,v2=P-A,v3=P-B; if(sgn(Dot(v1,v2))<0)return abs(v2); if(sgn(Dot(v1,v3))>0)return abs(v3); return fabs(DistanceToLine(P,Line(A,B-A)));}Point GetLineProjection(Point P,Line L)//点在直线上的投影{ return L.point(Dot(L.v,P-L.p)/norm(L.v));}Point GetLineIntersection(Line L1,Line L2)//直线交点,须确保两直线相交。{ return L1.point(Cross(L2.v,L1.p-L2.p)/Cross(L1.v,L2.v));}bool SegmentProperIntersection(Point a1,Point a2,Point b1,Point b2)//线段相交判定,交点不在一条线段的端点{ double c1=Cross(a2-a1,b1-a1),c2=Cross(a2-a1,b2-a1), c3=Cross(b2-b1,a1-b1),c4=Cross(b2-b1,a2-b1); return sgn(c1)*sgn(c2)<0&&sgn(c3)*sgn(c4)<0;}bool OnSegment(Point P,Point a1,Point a2)//判断点是否在线段上,不包含端点{ return sgn(Dot(a1-P,a2-P))<0&&!sgn(Cross(a1-P,a2-P));}
多边形和凸包
Morley定理:三角形每个内角的三等分线相交成等边三角形。
欧拉定理:平面图的点数V、边数E和面数F满足V+F-E=2。
typedef vector<Point> Polygon;//表示一个点集、多边形或凸包int isPointInPolygon(Point p,Polygon poly)//点在多边形内的判定,转角法,正值为内部,0为外部,-1在边界上{ int ans=0; for(int i=0,k,d1,d2,n=poly.size(); i!=n; ++i) { if(OnSegment(p,poly[i],poly[(i+1)%n]))return -1;//在边界上 k=sgn(Cross(poly[(i+1)%n]-poly[i],p-poly[i])); d1=sgn(poly[i].imag()-p.imag()); d2=sgn(poly[(i+1)%n].imag()-p.imag()); if(k>0&&d1<=0&&d2>0)++ans; if(k<0&&d2<=0&&d1>0)++ans; } return ans;}double PolygonArea(Polygon p)//计算多边形的有向面积,凸多边形即为面积{ double area=0; for(int i=1,n=p.size()-1; i<n; ++i) area+=Area2(p[0],p[i],p[i+1]); return area/2;}struct PointCmp//先比横坐标再比纵坐标的比较类{ bool operator()(Point a,Point b) { return sgn(a.real()-b.real())? a.real()<b.real(): a.imag()<b.imag(); }};int ConvexHull(Polygon p,Polygon &sol)//获得凸包;不希望凸包的边上有输入点,把两个<=改成<{ sort(p.begin(),p.end(),PointCmp());//先比横坐标再比纵坐标 for(int i=0; i!=p.size(); ++i) { while(sol.size()>1&& Area2(sol[sol.size()-2],sol[sol.size()-1],p[i])<=0) sol.pop_back(); sol.push_back(p[i]); } for(int i=sol.size()-2,k=sol.size(); i>=0; --i) { while(sol.size()>k&& Area2(sol[sol.size()-2],sol[sol.size()-1],p[i])<=0) sol.pop_back(); sol.push_back(p[i]); } if(p.size()>1)sol.pop_back(); return sol.size();}Polygon CutPolygon(Polygon poly,Point A,Point B)//用有向直线A->B切割多边形poly, 返回“左侧”。 如果退化,可能会返回一个单点或者线段,复杂度O(n2){ Polygon newpoly; for(int i=0,n=poly.size(); i!=n; ++i) { Point C=poly[i],D=poly[(i+1)%n]; if(sgn(Cross(B-A,C-A))>=0)newpoly.push_back(C); if(!sgn(Cross(B-A, C-D))) { Point ip=GetLineIntersection(Line(A,B-A),Line(C,D-C)); if(OnSegment(ip,C,D))newpoly.push_back(ip); } } return newpoly;}struct LineCmp//比较极角的比较类{ bool operator()(Line a,Line b) { return arg(a.v)<arg(b.v); }};Polygon HalfplaneIntersection(vector<Line> L)//半平面交{ sort(L.begin(),L.end(),LineCmp());//按极角排序 int first,last;//双端队列的第一个元素和最后一个元素 vector<Point> p(L.size(),Point()); //p[i]为q[i]和q[i+1]的交点 vector<Line> q(L.size(),Line());//双端队列 q[first=last=0]=L[0]; //队列初始化为只有一个半平面L[0] for(int i=0,n=L.size(); i!=n; ++i) { while(first<last&&!Onleft(L[i],p[last-1])) --last; while(first<last&&!Onleft(L[i],p[first])) ++first; q[++last]=L[i]; if(!sgn(Cross(q[last].v,q[last-1].v))) { --last; if(Onleft(L[i].p,q[last])) q[last]=L[i]; } if(first<last) p[last-1]=GetLineIntersection(q[last-1], q[last]); } while(first<last&&!Onleft(p[last-1],q[first])) --last;//删除无用平面 if(last-first<=1)return Polygon();//空集 p[last]=GetLineIntersection(q[last],q[first]); return Polygon(p.begin()+first,p.begin()+last+1);//从deque复制到输出中}
圆
struct Circle{ Point c; double r; Circle(Point c,double r):c(c),r(r) {} Point point(double t)//通过圆心角t确定圆上坐标 { return c+polar(r,t); }};Line getTangent(Point C,Point P)//圆心C,圆上一点P处切线{ return Line(P,Normal(C-P));}int getLineCircleIntersecion(Line L,Circle C,Polygon &sol)//直线和圆的交点,返回交点数量,sol保存解{ double a=L.v.real(),b=L.p.real()-C.c.real(), c=L.v.imag(),d=L.p.imag()-C.c.imag(); double e=a*a+c*c,f=2*(a*b+c*d),g=b*b+d*d-C.r*C.r; double delta=f*f-4*e*g; if(sgn(delta)<0)return 0;//相离 if(sgn(delta)==0) { double t=-f/2/e; sol.push_back(L.point(t)); return 1; } double t1=(-f-sqrt(delta))/2/e,t2=(-f+sqrt(delta))/2/e; sol.push_back(L.point(t1)); sol.push_back(L.point(t2)); return 2;}int getCircleCircleIntersection(Circle C1,Circle C2,Polygon &sol)//两圆相交,返回值为-1时表示重合,其余同上{ double d=abs(C1.c - C2.c); if(sgn(d)==0&&sgn(C1.r-C2.r)==0)return -1;//同心 if(C1.r+C2.r<d||d<fabs(C1.r-C2.r))return 0;//相离或内含 double a=arg(C2.c - C1.c),//向量C1C2的极角 da=acos((d*d+C1.r*C1.r-C2.r*C2.r)/(2*d*C1.r));//C1C2到C1P1的角 if(sgn(da)==0)//相切 { sol.push_back(C1.point(a)); return 1; } sol.push_back(C1.point(a-da)); sol.push_back(C1.point(a+da)); return 2;}int getTangents(Point p,Circle C,Polygon &sol)//过点p做圆C的切线,返回切线个数{ Vec u=p-C.c; double d=abs(u); if(d<C.r)return 0;//点在圆内 else if(sgn(d-C.r)==0)//点在圆上 { sol.push_back(p); return 1; } double base=arg(u),ang=acos(C.r/d); sol.push_back(C.point(base+ang)); sol.push_back(C.point(base-ang)); return 2;}int getTangents(Circle A, Circle B,Polygon &a,Polygon &b)//两圆的公切线,返回切线的个数,-1表示有无数条公切线,a[i]、b[i]表示第i条切线在圆A,圆B上的切点{ int d=abs(A.c-B.c),rdiff =fabs(A.r-B.r),rsum =A.r+B.r; if(sgn(d)==0&&sgn(rdiff)==0)return -1;//重合 if(d<rdiff)return 0;//内含 double base=arg(B.c-A.c); if(sgn(d-rdiff)==0)//内切 { a.push_back(A.point(base)); b.push_back(B.point(base)); return 1; } //有外共切线,两条夹住两圆的切线 int cnt=2; double ang=acos(rdiff/d); a.push_back(A.point(base+ang)); a.push_back(A.point(base-ang)); b.push_back(B.point(base+ang)); b.push_back(B.point(base+ang)); if(sgn(d-rsum)==0)//外切 { ++cnt; a.push_back(A.point(base)); b.push_back(B.point(PI+base)); } else if(d>rsum)//外离,再加两条 { cnt+=2; double ang = acos(rsum/d); a.push_back(A.point(base+ang)); a.push_back(A.point(base-ang)); b.push_back(B.point(PI+base+ang)); b.push_back(B.point(PI+base-ang)); } return cnt;}
三维几何
坐标运算
增加了一个add_noise增加扰动函数,可以让坐标发生微小偏移。
struct Coord3{ double x,y,z; Coord3(double x=0,double y=0,double z=0):x(x),y(y),z(z) {} Coord3& operator+=(const Coord3 &a){return x+=a.x,y+=a.y,z+=a.z,*this;} Coord3& operator-=(const Coord3 &a){return x-=a.x,y-=a.y,z-=a.z,*this;} Coord3& operator*=(const double &d){return x*=d,y*=d,z*=d,*this;} Coord3& operator/=(const double &d){return x/=d,y/=d,z/=d,*this;} double randeps(){return (rand()/(double)RAND_MAX-0.5)*EPS;} void add_noise(){*this+=Coord3(randeps(),randeps(),randeps());}};bool operator!=(const Coord3 &a,const Coord3 &b)//不等运算符,涉及到浮点数比较要重写{ return sgn(a.x-b.x)||sgn(a.y-b.y)||sgn(a.z-b.z);}bool operator==(const Coord3 &a,const Coord3 &b){ return !(a!=b);}Coord3 operator+(Coord3 a,const Coord3 &b){ return a+=b;}Coord3 operator-(Coord3 a,const Coord3 &b){ return a-=b;}Coord3 operator*(Coord3 a,const double &d){ return a*=d;}Coord3 operator/(Coord3 a,const double &d){ return a/=d;}
点和向量
typedef Coord3 Point3;typedef Coord3 Vec3;double Dot(const Vec3& A,const Vec3& B){ return A.x*B.x+A.y*B.y+A.z*B.z;}Vec3 Cross(const Vec3& A, const Vec3& B){ return Vec3(A.y*B.z - A.z*B.y, A.z*B.x - A.x*B.z, A.x*B.y - A.y*B.x);}double abs(const Vec3& A){ return sqrt(Dot(A,A));}double Angle(const Vec3& A,const Vec3& B){ return acos(Dot(A,B)/abs(A)/abs(B));}double Area2(Point3 A,Point3 B, Point3 C){ return abs(Cross(B-A,C-A));}double Volume6(Point3 A, Point3 B, Point3 C, Point3 D)//四面体体积{ return Dot(D-A,Cross(B-A, C-A));}Point3 Centroid(Point3 A, Point3 B, Point3 C, Point3 D)//四面体的重心{ return (A+B+C+D)/4.0;}
线和平面
double DistanceToPlane(Point3 p, Point3 p0, const Vec3& n)// 点p到平面p0-n的有向距离。n必须为单位向量{ return Dot(p-p0,n);}Point3 GetPlaneProjection(Point3 p, Point3 p0, const Vec3& n)// 点p在平面p0-n上的投影。n必须为单位向量{ return p-n*Dot(p-p0,n);}Point3 LinePlaneIntersection(Point3 p1, Point3 p2, Point3 p0, Vec3 n)//直线p1-p2 与平面p0-n的交点,假设交点唯一存在{ Vec3 v = p2-p1; double t = Dot(n, p0-p1) / Dot(n, p2-p1);//分母为0,直线与平面平行或在平面上 return p1 + v*t; //如果是线段 判断t是否在0~1之间}double DistanceToLine(Point3 P,Point3 A,Point3 B)// 点P到直线AB的距离{ Vec3 v1=B-A,v2=P-A; return abs(Cross(v1, v2))/abs(v1);}double DistanceToSeg(Point3 P, Point3 A, Point3 B)//点到线段的距离{ if(A==B)return abs(P-A); Vec3 v1=B-A,v2=P-A,v3=P-B; if(sgn(Dot(v1,v2))<0)return abs(v2); if(sgn(Dot(v1,v3))>0)return abs(v3); return fabs(DistanceToLine(P,A,B));}bool LineDistance3D(Point3 p1, Vec3 u, Point3 p2, Vec3 v, double& s)//求异面直线 p1+s*u与p2+t*v的公垂线对应的s,如果平行|重合,返回0{ double b = Dot(u, u) * Dot(v, v) - Dot(u, v) * Dot(u, v); if(sgn(b) == 0) return 0; double a = Dot(u, v) * Dot(v, p1-p2) - Dot(v, v) * Dot(u, p1-p2); s = a/b; return 1;}bool SameSide(Point3 p1, Point3 p2, Point3 a, Point3 b)// p1和p2是否在线段a-b的同侧{ return sgn(Dot(Cross(b-a, p1-a), Cross(b-a, p2-a))) >= 0;}bool PointInTri(Point3 PP, Point3 *P)// 点P在三角形P0, P1, P2中{ return SameSide(PP,P[0],P[1],P[2])&& SameSide(PP,P[1],P[0],P[2])&& SameSide(PP,P[2],P[0],P[1]);}bool TriSegIntersection(Point3* P,Point3 A,Point3 B,Point3& PP)// 三角形P0P1P2是否和线段AB相交{ Vec3 n = Cross(P[1]-P[0], P[2]-P[0]); if(sgn(Dot(n, B-A)) == 0) return false; // 线段A-B和平面P0P1P2平行或共面 double t = Dot(n, P[0]-A) / Dot(n, B-A);// 平面A和直线P1-P2有惟一交点 if(sgn(t) < 0 || sgn(t-1) > 0) return false; // 不在线段AB上 PP = A + (B-A)*t; // 交点 return PointInTri(PP, P);}bool TriTriIntersection(Point3* T1, Point3* T2)//空间两三角形是否相交{ Point3 P; for(int i = 0; i < 3; i++) { if(TriSegIntersection(T1, T2[i], T2[(i+1)%3], P)|| TriSegIntersection(T2, T1[i], T1[(i+1)%3], P)) return true; } return false;}double SegSegDistance(Point3 a1, Point3 b1, Point3 a2, Point3 b2,Point3 &ans1,Point3 &ans2)//空间两直线上最近点对 返回最近距离 点对保存在ans1 ans2中{ Vec3 v1 = (a1-b1), v2 = (a2-b2); Vec3 N = Cross(v1, v2); Vec3 ab = (a1-a2); double ans = Dot(N, ab) / abs(N); Point3 p1 = a1, p2 = a2; Vec3 d1 = b1-a1, d2 = b2-a2; double t1, t2; t1 = Dot((Cross(p2-p1, d2)), Cross(d1, d2)); t2 = Dot((Cross(p2-p1, d1)), Cross(d1, d2)); double dd = abs((Cross(d1, d2))); t1 /= dd*dd; t2 /= dd*dd; ans1 = (a1 + (b1-a1)*t1); ans2 = (a2 + (b2-a2)*t2); return fabs(ans);}bool InsideWithMinDistance(Point3 PP, Point3 *P, double mindist)// 判断PP是否在三角形P中,并且到三条边的距离都至少为mindist。保证P, A, B, C共面{ return PointInTri(PP,P)&& DistanceToLine(PP,P[0],P[1])>=mindist|| DistanceToLine(PP,P[1],P[2])>=mindist|| DistanceToLine(PP,P[2],P[0])>=mindist;}
空间多边形和凸包
struct ConvexPolyhedron//凸包问题{ struct Face { int v[3]; Face(int a, int b, int c) { v[0] = a,v[1] = b,v[2] = c; } Vec3 Normal(const vector<Point3>& P) const { return Cross(P[v[1]]-P[v[0]], P[v[2]]-P[v[0]]); } bool CanSee(const vector<Point3>& P, int i) const// f是否能看见P[i] { return Dot(P[i]-P[v[0]], Normal(P)) > 0; } }; vector<Point3> P; vector<Face> faces; void read(vector<Point3> P2) { P=P2; for(int i = 0; i < P2.size(); i++) P2[i].add_noise(); faces = CH3D(P2); } vector<Face> CH3D(const vector<Point3>& P)//增量法求三维凸包,P已经过扰动,没有考虑各种特殊情况(如四点共面) { int n = P.size(); vector<vector<int> > vis(n); for(int i = 0; i < n; i++) vis[i].resize(n); vector<Face> cur; cur.push_back(Face(0, 1, 2)); // 由于已经进行扰动,前三个点不共线 cur.push_back(Face(2, 1, 0)); for(int i = 3; i < n; i++) { vector<Face> next; for(int j = 0; j < cur.size(); j++)// 计算每条边的“左面”的可见性 { Face& f = cur[j]; int res = f.CanSee(P, i); if(!res) next.push_back(f); for(int k = 0; k < 3; k++) vis[f.v[k]][f.v[(k+1)%3]] = res; } for(int j = 0; j < cur.size(); j++) for(int k = 0; k < 3; k++) { int a = cur[j].v[k], b = cur[j].v[(k+1)%3]; if(vis[a][b] != vis[b][a] && vis[a][b]) // (a,b)是分界线,左边对P[i]可见 next.push_back(Face(a, b, i)); } cur = next; } return cur; } Point3 centroid()//三维凸包重心 { Point3 C = P[0],tot(0,0,0); double totv = 0; for(int i = 0; i < faces.size(); i++) { Point3 p1 = P[faces[i].v[0]], p2 = P[faces[i].v[1]], p3 = P[faces[i].v[2]]; double v = -Volume6(p1, p2, p3, C); totv += v; tot = tot + Centroid(p1, p2, p3, C)*v; } return tot / totv; } double mindist(Point3 C)//凸包重心到表面最近距离 { double ans = 1e30;//INF for(int i = 0; i < faces.size(); i++) { Point3 p1 = P[faces[i].v[0]], p2 = P[faces[i].v[1]], p3 = P[faces[i].v[2]]; ans = min(ans, fabs(-Volume6(p1, p2, p3, C) / Area2(p1, p2, p3))); } return ans; }};
阅读全文
0 0
- [模板]计算几何模板
- 经典计算几何模板
- 计算几何模板2
- 计算几何 模板
- 计算几何模板
- 计算几何经典模板
- 计算几何模板
- ACM计算几何模板
- 计算几何模板
- 计算几何模板
- 计算几何模板
- 计算几何 模板
- 计算几何初步模板
- 计算几何三维模板
- 二维计算几何模板
- 计算几何模板
- 计算几何模板
- 计算几何的模板
- iptables
- Linux awk使用示例
- 王一三学习笔记 | 理解Java垃圾回收
- 数据结构与算法之LinkedList源码分析
- LeetCode-008 String to Integer (atoi)
- 计算几何模板
- [AGC005F]Many Easy Problems-FFT-容斥原理
- 圆周率 π 的求法(二)
- websocket,无法建立到 ws://... 服务器的连接
- 大数据量查询优化——数据库设计、SQL语句、JAVA编码
- C/C++遇到的问题及分析
- order by与group by与时间同时存在问题
- LeetCode-009 Palindrome Number
- junit并发访问数据库引发的问题