2. 直线与线段
2.0 预备函数
// 结构定义与宏定义 #include<stdio.h> #include<string.h> #include<stdlib.h> #include <math.h> #define eps 1e-8 #define zero(x) (((x)>0?(x):-(x))<eps) struct point { double x,y; }; struct line { point a,b; }; // 计算 s cross t product (P1-P0)x(P2-P0) double xmult(point p1,point p2,point p0) { return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y); } double xmult(double x1,double y1,double x2,double y2,double x0,double y0) { return (x1-x0)*(y2-y0)-(x2-x0)*(y1-y0); } // 计算 t dot t product (P1-P0).(P2-P0) double dmult(point p1,point p2,point p0) { return (p1.x-p0.x)*(p2.x-p0.x)+(p1.y-p0.y)*(p2.y-p0.y); } double dmult(double x1,double y1,double x2,double y2,double x0,double y0) { return (x1-x0)*(x2-x0)+(y1-y0)*(y2-y0); } // 两点距离 double distance(point p1,point p2) { return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); } double distance(double x1,double y1,double x2,double y2) { return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); }
2.1 判三点是否共线
int dots_inline(point p1,point p2,point p3) { return zero(xmult(p1,p2,p3)); }
2.2 判点是否在线段上
// 判点是否在线段上, , , , 包括端点(下面为两种接口模式) int dot_online_in(point p,line l) { return zero(xmult(p,l.a,l.b))&&(l.a.x-p.x)*(l.b.x-p.x)<eps&&(l.a.y-p.y)*(l.b.y-p.y)<eps; } int dot_online_in(point p,point l1,point l2) { return zero(xmult(p,l1,l2))&&(l1.x-p.x)*(l2.x-p.x)<eps&&(l1.y-p.y)*(l2.y-p.y)<eps; } // 判点是否在线段上, , , , 不包括端点 int dot_online_ex(point p,line l) { return dot_online_in(p,l)&&(!zero(p.x-l.a.x)||!zero(p.y-l.a.y)) &&(!zero(p.x-l.b.x)||!zero(p.y-l.b.y)); }
2.3 判断两点在线段的同一侧
// 判两点在线段同侧, , , , 点在线段上返回 0 0 0 0 int same_side(point p1,point p2,line l) { return xmult(l.a,p1,l.b)*xmult(l.a,p2,l.b)>eps; } int same_side(point p1,point p2,point l1,point l2) { return xmult(l1,p1,l2)*xmult(l1,p2,l2)>eps; }
2.4 判断两点是否在线段的异侧
// 判两点在线段异侧,点在线段上返回 0 int opposite_side(point p1,point p2,line l) { return xmult(l.a,p1,l.b)*xmult(l.a,p2,l.b)<-eps; } int opposite_side(point p1,point p2,point l1,point l2) { return xmult(l1,p1,l2)*xmult(l1,p2,l2)<-eps; }
2.5 求点关于直线的对称点
// 点 点关于直线的对称点 / / / // y by lyt // 缺点:用了斜率 // 也可以利用" " " " 点到直线上的最近点" " " " 来做,避免使用斜率。 point symmetric_point(point p1, point l1, point l2) { point ret; if (l1.x > l2.x - eps && l1.x < l2.x + eps) { ret.x = (2 * l1.x - p1.x); ret.y = p1.y; } else { double k = (l1.y - l2.y ) / (l1.x - l2.x); ret.x = (2*k*k*l1.x + 2*k*p1.y - 2*k*l1.y - k*k*p1.x + p1.x) / (1 + k*k); ret.y = p1.y - (ret.x - p1.x ) / k; } return ret; }
2.7 判断两线段是否相交
2.7.1 常用版
//定义点 struct Point { double x; double y; }; typedef struct Point point; //叉积 double multi(point p0, point p1, point p2) { return ( p1.x - p0.x )*( p2.y - p0.y )-( p2.x - p0.x )*( p1.y - p0.y ); } //相交返回 true,否则为 false, 接口为两线段的端点 bool isIntersected(point s1,point e1, point s2,point e2) { return (max(s1.x,e1.x) >= min(s2.x,e2.x)) && (max(s2.x,e2.x) >= min(s1.x,e1.x)) && (max(s1.y,e1.y) >= min(s2.y,e2.y)) && (max(s2.y,e2.y) >= min(s1.y,e1.y)) && (multi(s1,s2,e1)*multi(s1,e1,e2)>0) && (multi(s2,s1,e2)*multi(s2,e2,e1)>0); }
2.7.2 不常用版
// 判两线段相交,包括端点和部分重合 int intersect_in(line u,line v) { if (!dots_inline(u.a,u.b,v.a)||!dots_inline(u.a,u.b,v.b)) return !same_side(u.a,u.b,v)&&!same_side(v.a,v.b,u); return dot_online_in(u.a,v)||dot_online_in(u.b,v)||dot_online_in(v.a,u)||dot_online_in(v.b,u); } int intersect_in(point u1,point u2,point v1,point v2) { if (!dots_inline(u1,u2,v1)||!dots_inline(u1,u2,v2)) return !same_side(u1,u2,v1,v2)&&!same_side(v1,v2,u1,u2); return dot_online_in(u1,v1,v2)||dot_online_in(u2,v1,v2)||dot_online_in(v1,u1,u2)||dot_online_in(v2,u1,u 2); } // 判两线段相交,不包括端点和部分重合 int intersect_ex(line u,line v) { return opposite_side(u.a,u.b,v)&&opposite_side(v.a,v.b,u); } int intersect_ex(point u1,point u2,point v1,point v2) { return opposite_side(u1,u2,v1,v2)&&opposite_side(v1,v2,u1,u2); }
2.8 求两条直线的交点
// 计算两直线交点, , , , 注意事先判断直线是否平行 ! ! ! ! // 线段交点请另外判线段相交( ( ( ( 同时还是要判断是否平行) !) point intersection(point u1,point u2,point v1,point v2) { point ret=u1; double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x)) /((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x)); ret.x+=(u2.x-u1.x)*t; ret.y+=(u2.y-u1.y)*t; return ret; }
2.9 点到直线的最近距离
point ptoline(point p,point l1,point l2) { point t=p; t.x+=l1.y-l2.y,t.y+=l2.x-l1.x; return intersection(p,t,l1,l2); }
2.10 点到线段的最近距离
point ptoseg(point p,point l1,point l2) { point t=p; t.x+=l1.y-l2.y,t.y+=l2.x-l1.x; if (xmult(l1,t,p)*xmult(l2,t,p)>eps) return distance(p,l1)<distance(p,l2)?l1:l2; return intersection(p,t,l1,l2); }
3. 多边形
3.0 预备浮点函数
#include <stdlib.h> #include<stdio.h> #include<string.h> #include <math.h> #define MAXN 1000 //offset 为多变形坐标的最大绝对值 #define offset 10000 #define eps 1e-8 // 浮点数判 0 0 0 0 #define zero(x) (((x)>0?(x):-(x))<eps) // 浮点数判断符 #define _sign(x) ((x)>eps?1:((x)<-eps?2:0)) // 定义点 struct point { double x,y; } pt[MAXN ]; // 定义线段 struct line { point a,b; }; // 叉积 double xmult(point p1,point p2,point p0) { return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y); }
3.1 判定是否是凸多边形
// 判定凸多边形,顶点按顺时针或逆时针给出, 允许相邻边共线, 是凸多边形返回 1 否则返回 0 int is_convex(int n,point* p) { int i,s[3]= {1,1,1}; for (i=0; i<n&&s[1]|s[2]; i++) s[_sign(xmult(p[(i+1)%n],p[(i+2)%n],p[i]))]=0; return s[1]|s[2]; } // 判凸行,顶点按顺时针或逆时针给出, 不允许相邻边共线, 是凸多边形返回 1 , 否则返回 0 int is_convex_v2(int n,point* p) { int i,s[3]= {1,1,1}; for (i=0; i<n&&s[0]&&s[1]|s[2]; i++) s[_sign(xmult(p[(i+1)%n],p[(i+2)%n],p[i]))]=0; return s[0]&&s[1]|s[2]; }
3.2 判定点是否在多边形内
// 判点在凸多边形内或多边形边上时返回 1 1 1 1 ,严格在凸多边形外返回 0 0 0 0 int inside_convex(point q,int n,point* p) { int i,s[3]= {1,1,1}; for (i=0; i<n&&s[1]|s[2]; i++) s[_sign(xmult(p[(i+1)%n],q,p[i]))]=0; return s[1]|s[2]; } / / / // 判点严格在凸多边形内返回 , 1, 在边上或者严格在外返回 0 0 0 0 int inside_convex_v2(point q,int n,point* p) { int i,s[3]= {1,1,1}; for (i=0; i<n&&s[0]&&s[1]|s[2]; i++) s[_sign(xmult(p[(i+1)%n],q,p[i]))]=0; return s[0]&&s[1]|s[2]; } // 判点在任意多边形内, 顶点按顺时针或逆时针给出 //on_edge 表示点在多边形边上时的返回值,offset 为多边形坐标上限,严格在内返回1 ,严格在外返回0 int inside_polygon(point q,int n,point* p,int on_edge=2) { point q2; int i=0,count; while (i<n) for (count=i=0,q2.x=rand()+offset,q2.y=rand()+offset; i<n; i++) { if (zero(xmult(q,p[i],p[(i+1)%n]))&&(p[i].x-q.x)*(p[(i+1)%n].x-q.x)<eps &&(p[i].y-q.y)*(p[(i+1)%n].y-q.y)<eps) return on_edge; else if (zero(xmult(q,q2,p[i]))) break; else if (xmult(q,p[i],q2)*xmult(q,p[(i+1)%n],q2)<-eps&& xmult(p[i],q,p[(i+1)%n])*xmult(p[i],q2,p[(i+1)%n])<-eps) count++; } return count&1; }
3.3 判定一条线段是否在一个任意多边形内
// 预备函数 inline int opposite_side(point p1,point p2,point l1,point l2) { return xmult(l1,p1,l2)*xmult(l1,p2,l2)<-eps; } inline int dot_online_in(point p,point l1,point l2) { return zero(xmult(p,l1,l2))&&(l1.x-p.x)*(l2.x-p.x)<eps&&(l1.y-p.y)*(l2.y-p.y)<eps; } //判线段在任意多边形内,顶点按顺时针或逆时针给出,与边界相交返回1 int inside_polygon(point l1,point l2,int n,point* p) { point t[MAXN],tt; int i,j,k=0; if (!inside_polygon(l1,n,p)||!inside_polygon(l2,n,p)) return 0; for (i=0; i<n; i++) { if (opposite_side(l1,l2,p[i],p[(i+1)%n])&&opposite_side(p[i],p[(i+1)%n],l1,l2)) return 0; else if (dot_online_in(l1,p[i],p[(i+1)%n])) t[k++]=l1; else if (dot_online_in(l2,p[i],p[(i+1)%n])) t[k++]=l2; else if (dot_online_in(p[i],l1,l2)) t[k++]=p[i]; } for (i=0; i<k; i++) for (j=i+1; j<k; j++) { tt.x=(t[i].x+t[j].x)/2; tt.y=(t[i].y+t[j].y)/2; if (!inside_polygon(tt,n,p)) return 0; } return 1; }
. 4. 三角形
4.0 预备函数
#include <math.h> #include <string.h> #include <stdlib.h> #include<stdio.h> / / / // 定义点 struct point { double x,y; }; typedef struct point point; // 定义直线 struct line { point a,b; }; typedef struct line line; // 两点距离 double distance(point p1,point p2) { return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); } // 两直线求交点 point intersection(line u,line v) { point ret=u.a; double t=((u.a.x-v.a.x)*(v.a.y-v.b.y)-(u.a.y-v.a.y)*(v.a.x-v.b.x)) /((u.a.x-u.b.x)*(v.a.y-v.b.y)-(u.a.y-u.b.y)*(v.a.x-v.b.x)); ret.x+=(u.b.x-u.a.x)*t; ret.y+=(u.b.y-u.a.y)*t; return ret; }
4.1 求三角形的外心
point circumcenter(point a,point b,point c) { line u,v; u.a.x=(a.x+b.x)/2; u.a.y=(a.y+b.y)/2; u.b.x=u.a.x-a.y+b.y; u.b.y=u.a.y+a.x-b.x; v.a.x=(a.x+c.x)/2; v.a.y=(a.y+c.y)/2; v.b.x=v.a.x-a.y+c.y; v.b.y=v.a.y+a.x-c.x; return intersection(u,v); }
4.2 求三角形内心
point incenter(point a,point b,point c) { line u,v; double m,n; u.a=a; m=atan2(b.y-a.y,b.x-a.x); n=atan2(c.y-a.y,c.x-a.x); u.b.x=u.a.x+cos((m+n)/2); u.b.y=u.a.y+sin((m+n)/2); v.a=b; m=atan2(a.y-b.y,a.x-b.x); n=atan2(c.y-b.y,c.x-b.x); v.b.x=v.a.x+cos((m+n)/2); v.b.y=v.a.y+sin((m+n)/2); return intersection(u,v); }
4.3 求三角形垂心
point perpencenter(point a,point b,point c) { line u,v; u.a=c; u.b.x=u.a.x-a.y+b.y; u.b.y=u.a.y+a.x-b.x; v.a=b; v.b.x=v.a.x-a.y+c.y; v.b.y=v.a.y+a.x-c.x; return intersection(u,v); }
. 5. 圆
5.0 预备函数
#include <math.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #define eps 1e-8 struct point { double x,y; }; typedef struct point point; double xmult(point p1,point p2,point p0) { return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y); } double distance(point p1,point p2) { return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); } // 点到直线的距离 double disptoline(point p,point l1,point l2) { return fabs(xmult(p,l1,l2))/distance(l1,l2); } // 求两直线交点 point intersection(point u1,point u2,point v1,point v2) { point ret=u1; double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x)) /((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x)); ret.x+=(u2.x-u1.x)*t; ret.y+=(u2.y-u1.y)*t; return ret; }
5.1 判定直线是否与圆相交
//判直线和圆相交,包括相切 int intersect_line_circle(point c,double r,point l1,point l2) { return disptoline(c,l1,l2)<r+eps; }
5.2 判定线段与圆相交
int intersect_seg_circle(point c,double r, point l1,point l2) { double t1=distance(c,l1)-r,t2=distance(c,l2)-r; point t=c; if (t1<eps||t2<eps) return t1>-eps||t2>-eps; t.x+=l1.y-l2.y; t.y+=l2.x-l1.x; return xmult(l1,c,t)*xmult(l2,c,t)<eps&&disptoline(c,l1,l2)-r<eps; }
5.3 判圆和圆相交
int intersect_circle_circle(point c1,double r1,point c2,double r2) { return distance(c1,c2)<r1+r2+eps&&distance(c1,c2)>fabs(r1-r2)-eps; }
5.4 计算圆上到点p最近点
//当p为圆心时,返回圆心本身 point dot_to_circle(point c,double r,point p) { point u,v; if (distance(p,c)<eps) return p; u.x=c.x+r*fabs(c.x-p.x)/distance(c,p); u.y=c.y+r*fabs(c.y-p.y)/distance(c,p)*((c.x-p.x)*(c.y-p.y)<0?-1:1); v.x=c.x-r*fabs(c.x-p.x)/distance(c,p); v.y=c.y-r*fabs(c.y-p.y)/distance(c,p)*((c.x-p.x)*(c.y-p.y)<0?-1:1); return distance(u,p)<distance(v,p)?u:v; }
5.5 计算直线与圆的交点
// 计算直线与圆的交点,保证直线与圆有交点 // 计算线段与圆的交点可用这个函数后判点是否在线段上 void intersection_line_circle(point c,double r,point l1,point l2,point& p1,point& p2) { point p=c; double t; p.x+=l1.y-l2.y; p.y+=l2.x-l1.x; p=intersection(p,c,l1,l2); t=sqrt(r*r-distance(p,c)*distance(p,c))/distance(l1,l2); p1.x=p.x+(l2.x-l1.x)*t; p1.y=p.y+(l2.y-l1.y)*t; p2.x=p.x-(l2.x-l1.x)*t; p2.y=p.y-(l2.y-l1.y)*t; }
5.6 计算两个圆的交点
// 计算圆与圆的交点,保证圆与圆有交点,圆心不重合 void intersection_circle_circle(point c1,double r1,point c2,double r2,point& p1,point& p2) { point u,v; double t; t=(1+(r1*r1-r2*r2)/distance(c1,c2)/distance(c1,c2))/2; u.x=c1.x+(c2.x-c1.x)*t; u.y=c1.y+(c2.y-c1.y)*t; v.x=u.x+c1.y-c2.y; v.y=u.y-c1.x+c2.x; intersection_line_circle(c1,r1,u,v,p1,p2); }
. 6. 球面
6.0 给出地球经度纬度,计算圆心角
#include <math.h> const double pi=acos(-1); // 计算圆心角lat表示纬度,-90<=w<=90,lng 表示经度 // 返回两点所在大圆劣弧对应圆心角 ,0<=angle<=pi double angle(double lng1,double lat1,double lng2,double lat2) { double dlng=fabs(lng1-lng2)*pi/180; while (dlng>=pi+pi) dlng-=pi+pi; if (dlng>pi) dlng=pi+pi-dlng; lat1*=pi/180,lat2*=pi/180; return acos(cos(lat1)*cos(lat2)*cos(dlng)+sin(lat1)*sin(lat2)); }
6.1 已知经纬度,计算地球上两点直线距离
// 计算距离,r为球半径 double line_dist(double r,double lng1,double lat1,double lng2,double lat2) { double dlng=fabs(lng1-lng2)*pi/180; while (dlng>=pi+pi) dlng-=pi+pi; if (dlng>pi) dlng=pi+pi-dlng; lat1*=pi/180,lat2*=pi/180; return r*sqrt(2-2*(cos(lat1)*cos(lat2)*cos(dlng)+sin(lat1)*sin(lat2))); }
6.2 已知经纬度,计算地球上两点球面距离
// 计算球面距离,r为球半径 inline double sphere_dist(double r,double lng1,double lat1,double lng2,double lat2) { return r*angle(lng1,lat1,lng2,lat2); }