/*
计算几何
目录
㈠ 点的基本运算
1. 平面上两点之间距离 1
2. 判断两点是否重合 1
3. 矢量叉乘 1
4. 矢量点乘 2
5. 判断点是否在线段上 2
6. 求一点饶某点旋转后的坐标 2
7. 求矢量夹角 2
㈡ 线段及直线的基本运算
1. 点与线段的关系 3
2. 求点到线段所在直线垂线的垂足 4
3. 点到线段的最近点 4
4. 点到线段所在直线的距离 4
5. 点到折线集的最近距离 4
6. 判断圆是否在多边形内 5
7. 求矢量夹角余弦 5
8. 求线段之间的夹角 5
9. 判断线段是否相交 6
10.判断线段是否相交但不交在端点处 6
11.求线段所在直线的方程 6
12.求直线的斜率 7
13.求直线的倾斜角 7
14.求点关于某直线的对称点 7
15.判断两条直线是否相交及求直线交点 7
16.判断线段是否相交,如果相交返回交点 7
㈢ 多边形常用算法模块
1. 判断多边形是否简单多边形 8
2. 检查多边形顶点的凸凹性 9
3. 判断多边形是否凸多边形 9
4. 求多边形面积 9
5. 判断多边形顶点的排列方向,方法一 10
6. 判断多边形顶点的排列方向,方法二 10
7. 射线法判断点是否在多边形内 10
8. 判断点是否在凸多边形内 11
9. 寻找点集的graham算法 12
10.寻找点集凸包的卷包裹法 13
11.判断线段是否在多边形内 14
12.求简单多边形的重心 15
13.求凸多边形的重心 17
14.求肯定在给定多边形内的一个点 17
15.求从多边形外一点出发到该多边形的切线 18
16.判断多边形的核是否存在 19
㈣ 圆的基本运算
1 .点是否在圆内 20
2 .求不共线的三点所确定的圆 21
㈤ 矩形的基本运算
1.已知矩形三点坐标,求第4点坐标 22
㈥ 常用算法的描述 22
㈦ 补充
1.两圆关系: 24
2.判断圆是否在矩形内: 24
3.点到平面的距离: 25
4.点是否在直线同侧: 25
5.镜面反射线: 25
6.矩形包含: 26
7.两圆交点: 27
8.两圆公共面积: 28
9. 圆和直线关系: 29
10. 内切圆: 30
11. 求切点: 31
12. 线段的左右旋: 31
13.公式: 32
*/
/* 需要包含的头文件 */
#include
/* 常用的常量定义 */
const double INF = 1E200
const double EP = 1E-10
const int MAXV = 300
const double PI = 3.14159265
/* 基本几何结构 */
struct POINT
{
double x;
double y;
POINT(double a=0, double b=0) { x=a; y=b;} //constructor
};
struct LINESEG
{
POINT s;
POINT e;
LINESEG(POINT a, POINT b) { s=a; e=b;}
LINESEG() { }
};
struct LINE // 直线的解析方程 a*x+b*y+c=0 为统一表示,约定 a >= 0
{
double a;
double b;
double c;
LINE(double d1=1, double d2=-1, double d3=0) {a=d1; b=d2; c=d3;}
};
/**********************
* *
* 点的基本运算 *
* *
**********************/
double dist(POINT p1,POINT p2) // 返回两点之间欧氏距离
{
return( sqrt( (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y) ) );
}
bool equal_point(POINT p1,POINT p2) // 判断两个点是否重合
{
return ( (abs(p1.x-p2.x)0:ep在矢量opsp的逆时针方向;
r=0:opspep三点共线;
r<0:ep在矢量opsp的顺时针方向
*******************************************************************************/
double multiply(POINT sp,POINT ep,POINT op)
{
return((sp.x-op.x)*(ep.y-op.y)-(ep.x-op.x)*(sp.y-op.y));
}
/*
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));
}
/******************************************************************************
判断点p是否在线段l上
条件:(p在线段l所在的直线上) && (点p在以线段l为对角线的矩形内)
*******************************************************************************/
bool online(LINESEG l,POINT p)
{
return( (multiply(l.e,p,l.s)==0) &&( ( (p.x-l.s.x)*(p.x-l.e.x)<=0 )&&( (p.y-l.s.y)*(p.y-l.e.y)<=0 ) ) );
}
// 返回点p以点o为圆心逆时针旋转alpha(单位:弧度)后所在的位置
POINT rotate(POINT o,double alpha,POINT p)
{
POINT tp;
p.x-=o.x;
p.y-=o.y;
tp.x=p.x*cos(alpha)-p.y*sin(alpha)+o.x;
tp.y=p.y*cos(alpha)+p.x*sin(alpha)+o.y;
return tp;
}
/* 返回顶角在o点,起始边为os,终止边为oe的夹角(单位:弧度)
角度小于pi,返回正值
角度大于pi,返回负值
可以用于求线段之间的夹角
原理:
r = dotmultiply(s,e,o) / (dist(o,s)*dist(o,e))
r'= multiply(s,e,o)
r >= 1 angle = 0;
r <= -1 angle = -PI
-10 angle = arccos(r)
-1= 1.0 ) return 0;
if (cosfi <= -1.0 ) return -3.1415926;
fi=acos(cosfi);
if (dsx*dey-dsy*dex>0) return fi; // 说明矢量os 在矢量 oe的顺时针方向
return -fi;
}
/*****************************\
* *
* 线段及直线的基本运算 *
* *
\*****************************/
/* 判断点与线段的关系,用途很广泛
本函数是根据下面的公式写的,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
01)
{
np=l.e;
return dist(p,l.e);
}
np=perpendicular(p,l);
return dist(p,np);
}
// 求点p到线段l所在直线的距离,请注意本函数与上个函数的区别
double ptoldist(POINT p,LINESEG l)
{
return abs(multiply(p,l.e,l.s))/dist(l.s,l.e);
}
/* 计算点到折线集的最近距离,并返回最近点.
注意:调用的是ptolineseg()函数 */
double ptopointset(int vcount,POINT pointset[],POINT p,POINT &q)
{
int i;
double cd=double(INF),td;
LINESEG l;
POINT tq,cq;
for(i=0;i= 0。
//判断Q1Q2跨立P1P2的依据是:( Q1 - P1 ) × ( P2 - P1 ) * ( P2 - P1 ) × ( Q2 - P1 ) >= 0。
bool intersect(LINESEG u,LINESEG v)
{
return( (max(u.s.x,u.e.x)>=min(v.s.x,v.e.x))&& //排斥实验
(max(v.s.x,v.e.x)>=min(u.s.x,u.e.x))&&
(max(u.s.y,u.e.y)>=min(v.s.y,v.e.y))&&
(max(v.s.y,v.e.y)>=min(u.s.y,u.e.y))&&
(multiply(v.s,u.e,u.s)*multiply(u.e,v.e,u.s)>=0)&& //跨立实验
(multiply(u.s,v.e,v.s)*multiply(v.e,u.e,v.s)>=0));
}
// (线段u和v相交)&&(交点不是双方的端点) 时返回true
bool intersect_A(LINESEG u,LINESEG v)
{
return ((intersect(u,v))&&
(!online(u,v.s))&&
(!online(u,v.e))&&
(!online(v,u.e))&&
(!online(v,u.s)));
}
// 线段v所在直线与线段u相交时返回true;方法:判断线段u是否跨立线段v
bool intersect_l(LINESEG u,LINESEG v)
{
return multiply(u.s,v.e,v.s)*multiply(v.e,u.e,v.s)>=0;
}
// 根据已知两点坐标,求过这两点的直线解析方程: a*x+b*y+c = 0 (a >= 0)
LINE makeline(POINT p1,POINT p2)
{
LINE tl;
int sign = 1;
tl.a=p2.y-p1.y;
if(tl.a<0)
{
sign = -1;
tl.a=sign*tl.a;
}
tl.b=sign*(p1.x-p2.x);
tl.c=sign*(p1.y*p2.x-p1.x*p2.y);
return tl;
}
// 根据直线解析方程返回直线的斜率k,水平线返回 0,竖直线返回 1e200
double slope(LINE l)
{
if(abs(l.a) < 1e-20)
return 0;
if(abs(l.b) < 1e-20)
return INF;
return -(l.a/l.b);
}
// 返回直线的倾斜角alpha ( 0 - pi)
double alpha(LINE l)
{
if(abs(l.a)< EP)
return 0;
if(abs(l.b)< EP)
return PI/2;
double k=slope(l);
if(k>0)
return atan(k);
else
return PI+atan(k);
}
// 求点p关于直线l的对称点
POINT symmetry(LINE l,POINT p)
{
POINT tp;
tp.x=((l.b*l.b-l.a*l.a)*p.x-2*l.a*l.b*p.y-2*l.a*l.c)/(l.a*l.a+l.b*l.b);
tp.y=((l.a*l.a-l.b*l.b)*p.y-2*l.a*l.b*p.x-2*l.b*l.c)/(l.a*l.a+l.b*l.b);
return tp;
}
// 如果两条直线 l1(a1*x+b1*y+c1 = 0), l2(a2*x+b2*y+c2 = 0)相交,返回true,且返回交点p
bool lineintersect(LINE l1,LINE l2,POINT &p) // 是 L1,L2
{
double d=l1.a*l2.b-l2.a*l1.b;
if(abs(d)=0 )
bc[(index+1)%vcount]=1;
else
bc[(index+1)%vcount]=0;
index++;
count--;
}
}
// 返回值:多边形polygon是凸多边形时,返回true
bool isconvex(int vcount,POINT polygon[])
{
bool bc[MAXV];
checkconvex(vcount,polygon,bc);
for(int i=0;i0;
}
// 另一种判断多边形顶点排列方向的方法
bool isccwize(int vcount,POINT polygon[])
{
int i,index;
POINT a,b,v;
v=polygon[0];
index=0;
for(i=1;i0;
}
/********************************************************************************************
射线法判断点q与多边形polygon的位置关系,要求polygon为简单多边形,顶点逆时针排列
如果点在多边形内: 返回0
如果点在多边形边上: 返回1
如果点在多边形外: 返回2
*********************************************************************************************/
int insidepolygon(int vcount,POINT Polygon[],POINT q)
{
int c=0,i,n;
LINESEG l1,l2;
bool bintersect_a,bonline1,bonline2,bonline3;
double r1,r2;
l1.s=q;
l1.e=q;
l1.e.x=double(INF);
n=vcount;
for (i=0;i0) ||
(bonline3=online(l1,Polygon[(i+2)%n]))&& /* 下一条边是水平线,前一个端点和后一个端点在射线两侧 */
((r2=multiply(Polygon[i],Polygon[(i+2)%n],l1.s)*multiply(Polygon[(i+2)%n],
Polygon[(i+3)%n],l1.s))>0)
)
)
) c++;
}
if(c%2 == 1)
return 0;
else
return 2;
}
//点q是凸多边形polygon内时,返回true;注意:多边形polygon一定要是凸多边形
bool InsideConvexPolygon(int vcount,POINT polygon[],POINT q) // 可用于三角形!
{
POINT p;
LINESEG l;
int i;
p.x=0;p.y=0;
for(i=0;i0 || // 极角更小
(multiply(PointSet[j],PointSet[k],PointSet[0])==0) && /* 极角相等,距离更短 */
dist(PointSet[0],PointSet[j])=0)
top--;
ch[++top]=PointSet[i];
}
len=top+1;
}
// 卷包裹法求点集凸壳,参数说明同graham算法
void ConvexClosure(POINT PointSet[],POINT ch[],int n,int &len)
{
int top=0,i,index,first;
double curmax,curcos,curdis;
POINT tmp;
LINESEG l1,l2;
bool use[MAXV];
tmp=PointSet[0];
index=0;
// 选取y最小点,如果多于一个,则选取最左点
for(i=1;icurmax || fabs(curcos-curmax)<1e-6 && dist(l2.s,l2.e)>curdis)
{
curmax=curcos;
index=i;
curdis=dist(l2.s,l2.e);
}
}
use[first]=false; //清空第first个顶点标志,使最后能形成封闭的hull
use[index]=true;
ch[top++]=PointSet[index];
l1.s=ch[top-2];
l1.e=ch[top-1];
l2.s=ch[top-1];
}
len=top-1;
}
/*********************************************************************************************
判断线段是否在简单多边形内(注意:如果多边形是凸多边形,下面的算法可以化简)
必要条件一:线段的两个端点都在多边形内;
必要条件二:线段和多边形的所有边都不内交;
用途: 1. 判断折线是否在简单多边形内
2. 判断简单多边形是否在另一个简单多边形内
**********************************************************************************************/
bool LinesegInsidePolygon(int vcount,POINT polygon[],LINESEG l)
{
// 判断线端l的端点是否不都在多边形内
if(!insidepolygon(vcount,polygon,l.s)||!insidepolygon(vcount,polygon,l.e))
return false;
int top=0,i,j;
POINT PointSet[MAXV],tmp;
LINESEG s;
for(i=0;iPointSet[j].x || fabs(PointSet[i].x-PointSet[j].x)PointSet[j].y )
{
tmp=PointSet[i];
PointSet[i]=PointSet[j];
PointSet[j]=tmp;
}
}
}
for(i=0;i x1 )
{
AddPosPart ((x2+x1)/2, y1/2, (x2-x1) * y1,xtr,ytr,wtr); /* rectangle 全局变量变化处 */
AddPosPart ((x1+x2+x2)/3, (y1+y1+y2)/3, (x2-x1)*(y2-y1)/2,xtr,ytr,wtr);
// triangle 全局变量变化处
}
else
{
AddNegPart ((x2+x1)/2, y1/2, (x2-x1) * y1,xtl,ytl,wtl);
// rectangle 全局变量变化处
AddNegPart ((x1+x2+x2)/3, (y1+y1+y2)/3, (x2-x1)*(y2-y1)/2,xtl,ytl,wtl);
// triangle 全局变量变化处
}
}
POINT cg_simple(int vcount,POINT polygon[])
{
double xtr,ytr,wtr,xtl,ytl,wtl;
//注意: 如果把xtr,ytr,wtr,xtl,ytl,wtl改成全局变量后这里要删去
POINT p1,p2,tp;
xtr = ytr = wtr = 0.0;
xtl = ytl = wtl = 0.0;
for(int i=0;i=4的简单多边形至少有一条对角线
结论 : x坐标最大,最小的点肯定是凸顶点
y坐标最大,最小的点肯定是凸顶点
************************************************/
POINT a_point_insidepoly(int vcount,POINT polygon[])
{
POINT v,a,b,r;
int i,index;
v=polygon[0];
index=0;
for(i=1;i=0; // p is to the left of pre edge
bln=multiply(en.e,p,en.s)>=0; // p is to the left of next edge
if(!blp&&bln)
{
if(multiply(polygon[i],rp,p)>0) // polygon[i] is above rp
rp=polygon[i];
}
if(blp&&!bln)
{
if(multiply(lp,polygon[i],p)>0) // polygon[i] is below lp
lp=polygon[i];
}
}
return ;
}
// 如果多边形polygon的核存在,返回true,返回核上的一点p.顶点按逆时针方向输入
bool core_exist(int vcount,POINT polygon[],POINT &p)
{
int i,j,k;
LINESEG l;
LINE lineset[MAXV];
for(i=0;i0)
//多边形顶点按逆时针方向排列,核肯定在每条边的左侧或边上
break;
}
if(k == vcount) //找到了一个核上的点
break;
}
}
if(j r1+r2 )
return 1;
if( d < fabs(r1-r2) )
return 5;
if( fabs(r1-r2) < d && d < r1+r2 )
return 3;
return 0; // indicate an error!
}
//判断圆是否在矩形内:
// 判定圆是否在矩形内,是就返回true(设矩形水平,且其四个顶点由左上开始按顺时针排列)
// 调用ptoldist函数,在第4页
bool CircleRecRelation(POINT pc, double r, POINT pr1, POINT pr2, POINT pr3, POINT pr4)
{
if( pr1.x < pc.x && pc.x < pr2.x && pr3.y < pc.y && pc.y < pr2.y )
{
LINESEG line1(pr1, pr2);
LINESEG line2(pr2, pr3);
LINESEG line3(pr3, pr4);
LINESEG line4(pr4, pr1);
if( r 0;
}
//镜面反射线:
// 已知入射线、镜面,求反射线。
// a1,b1,c1为镜面直线方程(a1 x + b1 y + c1 = 0 ,下同)系数;
//a2,b2,c2为入射光直线方程系数;
//a,b,c为反射光直线方程系数.
// 光是有方向的,使用时注意:入射光向量:<-b2,a2>;反射光向量:.
// 不要忘记结果中可能会有"negative zeros"
void reflect(double a1,double b1,double c1,double a2,double b2,double c2,double &a,double &b,double &c)
{
double n,m;
double tpb,tpa;
tpb=b1*b2+a1*a2;
tpa=a2*b1-a1*b2;
m=(tpb*b1+tpa*a1)/(b1*b1+a1*a1);
n=(tpa*b1-tpb*a1)/(b1*b1+a1*a1);
if(fabs(a1*b2-a2*b1)<1e-20)
{
a=a2;b=b2;c=c2;
return;
}
double xx,yy; //(xx,yy)是入射线与镜面的交点。
xx=(b1*c2-b2*c1)/(a1*b2-a2*b1);
yy=(a2*c1-a1*c2)/(a1*b2-a2*b1);
a=n;
b=-m;
c=m*yy-xx*n;
}
//矩形包含:
// 矩形2(C,D)是否在1(A,B)内
bool r2inr1(double A,double B,double C,double D)
{
double X,Y,L,K,DMax;
if (A < B)
{
double tmp = A;
A = B;
B = tmp;
}
if (C < D)
{
double tmp = C;
C = D;
D = tmp;
}
if (A > C && B > D) // trivial case
return true;
else
if (D >= B)
return false;
else
{
X = sqrt(A * A + B * B); // outer rectangle's diagonal
Y = sqrt(C * C + D * D); // inner rectangle's diagonal
if (Y < B) // check for marginal conditions
return true; // the inner rectangle can freely rotate inside
else
if (Y > X)
return false;
else
{
L = (B - sqrt(Y * Y - A * A)) / 2;
K = (A - sqrt(Y * Y - B * B)) / 2;
DMax = sqrt(L * L + K * K);
if (D >= DMax)
return false;
else
return true;
}
}
}
//两圆交点:
// 两圆已经相交(相切)
void c2point(POINT p1,double r1,POINT p2,double r2,POINT &rp1,POINT &rp2)
{
double a,b,r;
a=p2.x-p1.x;
b=p2.y-p1.y;
r=(a*a+b*b+r1*r1-r2*r2)/2;
if(a==0&&b!=0)
{
rp1.y=rp2.y=r/b;
rp1.x=sqrt(r1*r1-rp1.y*rp1.y);
rp2.x=-rp1.x;
}
else if(a!=0&&b==0)
{
rp1.x=rp2.x=r/a;
rp1.y=sqrt(r1*r1-rp1.x*rp2.x);
rp2.y=-rp1.y;
}
else if(a!=0&&b!=0)
{
double delta;
delta=b*b*r*r-(a*a+b*b)*(r*r-r1*r1*a*a);
rp1.y=(b*r+sqrt(delta))/(a*a+b*b);
rp2.y=(b*r-sqrt(delta))/(a*a+b*b);
rp1.x=(r-b*rp1.y)/a;
rp2.x=(r-b*rp2.y)/a;
}
rp1.x+=p1.x;
rp1.y+=p1.y;
rp2.x+=p1.x;
rp2.y+=p1.y;
}
//两圆公共面积:
// 必须保证相交
double c2area(POINT p1,double r1,POINT p2,double r2)
{
POINT rp1,rp2;
c2point(p1,r1,p2,r2,rp1,rp2);
if(r1>r2) //保证r2>r1
{
swap(p1,p2);
swap(r1,r2);
}
double a,b,rr;
a=p1.x-p2.x;
b=p1.y-p2.y;
rr=sqrt(a*a+b*b);
double dx1,dy1,dx2,dy2;
double sita1,sita2;
dx1=rp1.x-p1.x;
dy1=rp1.y-p1.y;
dx2=rp2.x-p1.x;
dy2=rp2.y-p1.y;
sita1=acos((dx1*dx2+dy1*dy2)/r1/r1);
dx1=rp1.x-p2.x;
dy1=rp1.y-p2.y;
dx2=rp2.x-p2.x;
dy2=rp2.y-p2.y;
sita2=acos((dx1*dx2+dy1*dy2)/r2/r2);
double s=0;
if(rr0)
return -1;
else
return 1;
}
/*
公式:
球坐标公式:
直角坐标为 P(x, y, z) 时,对应的球坐标是(rsinφcosθ, rsinφsinθ, rcosφ),其中φ是向量OP与Z轴的夹角,范围[0,π];是OP在XOY面上的投影到X轴的旋角,范围[0,2π]
直线的一般方程转化成向量方程:
ax+by+c=0
x-x0 y-y0
------ = ------- // (x0,y0)为直线上一点,m,n为向量
m n
转换关系:
a=n;b=-m;c=m·y0-n·x0;
m=-b; n=a;
三点平面方程:
三点为P1,P2,P3
设向量 M1=P2-P1; M2=P3-P1;
平面法向量: M=M1 x M2 ()
平面方程: M.i(x-P1.x)+M.j(y-P1.y)+M.k(z-P1.z)=0
*/