计算几何:模板

叉积:两个向量的叉积是一个标量,a×b的几何意义为他们所形成的平行四边形的有向面积

凸包:把给定点包围在内部,面积最小的凸多边形

半平面交:一个半平面指的是由满足ax+by+c>ax+by+c>=0的点集组成的二维区域。

一般来说在写代码的时候,我们可以把一个半平面想象成一个向量所在的直线右面的一片区域 

半平面的交可能是一个封闭图形也可能是没有边界的区域或者为空

对踵点对:如果两个点 p 和 q (属于 P) 在两条平行切线上, 那么他们就形成了一个对踵点对

旋转卡壳: Shamos提出了一个简单的 O(n) 时间的算法来确定一个凸 n 角形的对踵点对

Shamos的算法就像绕着多边形旋转一对卡壳。 因此就有了术语“旋转卡壳”

pick公式:顶点坐标均是整点的简单多边形:面积=内部格点数目+边上格点数目/2-1

   1 /*
   2 计算几何
   3 目录 
   4 ㈠ 点的基本运算 
   5 1. 平面上两点之间距离 1 
   6 2. 判断两点是否重合 1 
   7 3. 矢量叉乘 1 
   8 4. 矢量点乘 2 
   9 5. 判断点是否在线段上 2 
  10 6. 求一点饶某点旋转后的坐标 2 
  11 7. 求矢量夹角 2 
  12 ㈡ 线段及直线的基本运算 
  13 1. 点与线段的关系 3 
  14 2. 求点到线段所在直线垂线的垂足 4 
  15 3. 点到线段的最近点 4 
  16 4. 点到线段所在直线的距离 4 
  17 5. 点到折线集的最近距离 4 
  18 6. 判断圆是否在多边形内 5 
  19 7. 求矢量夹角余弦 5 
  20 8. 求线段之间的夹角 5 
  21 9. 判断线段是否相交 6 
  22 10.判断线段是否相交但不交在端点处 6 
  23 11.求线段所在直线的方程 6 
  24 12.求直线的斜率 7 
  25 13.求直线的倾斜角 7 
  26 14.求点关于某直线的对称点 7 
  27 15.判断两条直线是否相交及求直线交点 7 
  28 16.判断线段是否相交,如果相交返回交点 7 
  29 ㈢ 多边形常用算法模块 
  30 1. 判断多边形是否简单多边形 8 
  31 2. 检查多边形顶点的凸凹性 9 
  32 3. 判断多边形是否凸多边形 9 
  33 4. 求多边形面积 9 
  34 5. 判断多边形顶点的排列方向,方法一 10 
  35 6. 判断多边形顶点的排列方向,方法二 10 
  36 7. 射线法判断点是否在多边形内 10 
  37 8. 判断点是否在凸多边形内 11 
  38 9. 寻找点集的graham算法 12 
  39 10.寻找点集凸包的卷包裹法 13 
  40 11.判断线段是否在多边形内 14 
  41 12.求简单多边形的重心 15 
  42 13.求凸多边形的重心 17 
  43 14.求肯定在给定多边形内的一个点 17 
  44 15.求从多边形外一点出发到该多边形的切线 18 
  45 16.判断多边形的核是否存在 19 
  46 ㈣ 圆的基本运算 
  47 1 .点是否在圆内 20 
  48 2 .求不共线的三点所确定的圆 21 
  49 ㈤ 矩形的基本运算 
  50 1.已知矩形三点坐标,求第4点坐标 22 
  51 ㈥ 常用算法的描述 22 
  52 ㈦ 补充 
  53 1.两圆关系: 24 
  54 2.判断圆是否在矩形内: 24 
  55 3.点到平面的距离: 25 
  56 4.点是否在直线同侧: 25 
  57 5.镜面反射线: 25 
  58 6.矩形包含: 26 
  59 7.两圆交点: 27 
  60 8.两圆公共面积: 28 
  61 9. 圆和直线关系: 29 
  62 10. 内切圆: 30 
  63 11. 求切点: 31 
  64 12. 线段的左右旋: 31 
  65 13.公式: 32 
  66 */
  67 /* 需要包含的头文件 */ 
  68 #include <cmath > 
  69  
  70 /* 常用的常量定义 */ 
  71 const double    INF        = 1E200    
  72 const double    EP        = 1E-10 
  73 const int        MAXV    = 300 
  74 const double    PI        = 3.14159265 
  75  
  76 /* 基本几何结构 */ 
  77 struct POINT 
  78 { 
  79     double x; 
  80     double y; 
  81     POINT(double a=0, double b=0) { x=a; y=b;} //constructor 
  82 }; 
  83 struct LINESEG 
  84 { 
  85     POINT s; 
  86     POINT e; 
  87     LINESEG(POINT a, POINT b) { s=a; e=b;} 
  88     LINESEG() { } 
  89 }; 
  90 struct LINE           // 直线的解析方程 a*x+b*y+c=0  为统一表示,约定 a >= 0 
  91 { 
  92    double a; 
  93    double b; 
  94    double c; 
  95    LINE(double d1=1, double d2=-1, double d3=0) {a=d1; b=d2; c=d3;} 
  96 }; 
  97  
  98 /**********************
  99  *                    * 
 100  *   点的基本运算     * 
 101  *                    * 
 102  **********************/ 
 103  
 104 double dist(POINT p1,POINT p2)                // 返回两点之间欧氏距离 
 105 { 
 106     return( sqrt( (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y) ) ); 
 107 } 
 108 bool equal_point(POINT p1,POINT p2)           // 判断两个点是否重合  
 109 { 
 110     return ( (abs(p1.x-p2.x)<EP)&&(abs(p1.y-p2.y)<EP) ); 
 111 } 
 112 /****************************************************************************** 
 113 r=multiply(sp,ep,op),得到(sp-op)和(ep-op)的叉积 
 114 r>0:ep在矢量opsp的逆时针方向; 
 115 r=0:opspep三点共线; 
 116 r<0:ep在矢量opsp的顺时针方向 
 117 *******************************************************************************/ 
 118 double multiply(POINT sp,POINT ep,POINT op) 
 119 { 
 120     return((sp.x-op.x)*(ep.y-op.y)-(ep.x-op.x)*(sp.y-op.y)); 
 121 } 
 122 /* 
 123 r=dotmultiply(p1,p2,op),得到矢量(p1-op)和(p2-op)的点积,如果两个矢量都非零矢量 
 124 r<0:两矢量夹角为锐角;
 125 r=0:两矢量夹角为直角;
 126 r>0:两矢量夹角为钝角 
 127 *******************************************************************************/ 
 128 double dotmultiply(POINT p1,POINT p2,POINT p0) 
 129 { 
 130     return ((p1.x-p0.x)*(p2.x-p0.x)+(p1.y-p0.y)*(p2.y-p0.y)); 
 131 } 
 132 /****************************************************************************** 
 133 判断点p是否在线段l上
 134 条件:(p在线段l所在的直线上) && (点p在以线段l为对角线的矩形内)
 135 *******************************************************************************/ 
 136 bool online(LINESEG l,POINT p) 
 137 { 
 138     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 ) ) ); 
 139 } 
 140 // 返回点p以点o为圆心逆时针旋转alpha(单位:弧度)后所在的位置 
 141 POINT rotate(POINT o,double alpha,POINT p) 
 142 { 
 143     POINT tp; 
 144     p.x-=o.x; 
 145     p.y-=o.y; 
 146     tp.x=p.x*cos(alpha)-p.y*sin(alpha)+o.x; 
 147     tp.y=p.y*cos(alpha)+p.x*sin(alpha)+o.y; 
 148     return tp; 
 149 } 
 150 /* 返回顶角在o点,起始边为os,终止边为oe的夹角(单位:弧度) 
 151     角度小于pi,返回正值 
 152     角度大于pi,返回负值 
 153     可以用于求线段之间的夹角 
 154 原理:
 155     r = dotmultiply(s,e,o) / (dist(o,s)*dist(o,e))
 156     r'= multiply(s,e,o)
 157     r >= 1    angle = 0;
 158     r <= -1    angle = -PI
 159     -1<r<1 && r'>0    angle = arccos(r)
 160     -1<r<1 && r'<=0    angle = -arccos(r)
 161 */ 
 162 double angle(POINT o,POINT s,POINT e) 
 163 { 
 164     double cosfi,fi,norm; 
 165     double dsx = s.x - o.x; 
 166     double dsy = s.y - o.y; 
 167     double dex = e.x - o.x; 
 168     double dey = e.y - o.y; 
 169  
 170     cosfi=dsx*dex+dsy*dey; 
 171     norm=(dsx*dsx+dsy*dsy)*(dex*dex+dey*dey); 
 172     cosfi /= sqrt( norm ); 
 173  
 174     if (cosfi >=  1.0 ) return 0; 
 175     if (cosfi <= -1.0 ) return -3.1415926; 
 176  
 177     fi=acos(cosfi); 
 178     if (dsx*dey-dsy*dex>0) return fi;      // 说明矢量os 在矢量 oe的顺时针方向 
 179     return -fi; 
 180 } 
 181   /***************************** 
 182   *                             * 
 183   *      线段及直线的基本运算   * 
 184   *                             * 
 185   *****************************/ 
 186  
 187 /* 判断点与线段的关系,用途很广泛 
 188 本函数是根据下面的公式写的,P是点C到线段AB所在直线的垂足 
 189                 AC dot AB 
 190         r =     --------- 
 191                  ||AB||^2 
 192              (Cx-Ax)(Bx-Ax) + (Cy-Ay)(By-Ay) 
 193           = ------------------------------- 
 194                           L^2 
 195     r has the following meaning: 
 196         r=0      P = A 
 197         r=1      P = B 
 198         r<0         P is on the backward extension of AB 
 199         r>1      P is on the forward extension of AB 
 200         0<r<1     P is interior to AB 
 201 */ 
 202 double relation(POINT p,LINESEG l) 
 203 { 
 204     LINESEG tl; 
 205     tl.s=l.s; 
 206     tl.e=p; 
 207     return dotmultiply(tl.e,l.e,l.s)/(dist(l.s,l.e)*dist(l.s,l.e)); 
 208 } 
 209 // 求点C到线段AB所在直线的垂足 P 
 210 POINT perpendicular(POINT p,LINESEG l) 
 211 { 
 212     double r=relation(p,l); 
 213     POINT tp; 
 214     tp.x=l.s.x+r*(l.e.x-l.s.x); 
 215     tp.y=l.s.y+r*(l.e.y-l.s.y); 
 216     return tp; 
 217 } 
 218 /* 求点p到线段l的最短距离,并返回线段上距该点最近的点np 
 219 注意:np是线段l上到点p最近的点,不一定是垂足 */ 
 220 double ptolinesegdist(POINT p,LINESEG l,POINT &np) 
 221 { 
 222     double r=relation(p,l); 
 223     if(r<0) 
 224     { 
 225         np=l.s; 
 226         return dist(p,l.s); 
 227     } 
 228     if(r>1) 
 229     { 
 230         np=l.e; 
 231         return dist(p,l.e); 
 232     } 
 233     np=perpendicular(p,l); 
 234     return dist(p,np); 
 235 } 
 236 // 求点p到线段l所在直线的距离,请注意本函数与上个函数的区别  
 237 double ptoldist(POINT p,LINESEG l) 
 238 { 
 239     return abs(multiply(p,l.e,l.s))/dist(l.s,l.e); 
 240 } 
 241 /* 计算点到折线集的最近距离,并返回最近点. 
 242 注意:调用的是ptolineseg()函数 */ 
 243 double ptopointset(int vcount,POINT pointset[],POINT p,POINT &q) 
 244 { 
 245     int i; 
 246     double cd=double(INF),td; 
 247     LINESEG l; 
 248     POINT tq,cq; 
 249  
 250     for(i=0;i<vcount-1;i++) 
 251     { 
 252         l.s=pointset[i]; 
 253  
 254         l.e=pointset[i+1]; 
 255         td=ptolinesegdist(p,l,tq); 
 256         if(td<cd) 
 257         { 
 258             cd=td; 
 259             cq=tq; 
 260         } 
 261     } 
 262     q=cq; 
 263     return cd; 
 264 } 
 265 /* 判断圆是否在多边形内.ptolineseg()函数的应用2 */ 
 266 bool CircleInsidePolygon(int vcount,POINT center,double radius,POINT polygon[]) 
 267 { 
 268     POINT q; 
 269     double d; 
 270     q.x=0; 
 271     q.y=0; 
 272     d=ptopointset(vcount,polygon,center,q); 
 273     if(d<radius||fabs(d-radius)<EP) 
 274         return true; 
 275     else 
 276         return false; 
 277 } 
 278 /* 返回两个矢量l1和l2的夹角的余弦(-1 --- 1)注意:如果想从余弦求夹角的话,注意反余弦函数的定义域是从 0到pi */ 
 279 double cosine(LINESEG l1,LINESEG l2) 
 280 { 
 281     return (((l1.e.x-l1.s.x)*(l2.e.x-l2.s.x) + 
 282     (l1.e.y-l1.s.y)*(l2.e.y-l2.s.y))/(dist(l1.e,l1.s)*dist(l2.e,l2.s))) ); 
 283 } 
 284 // 返回线段l1与l2之间的夹角 单位:弧度 范围(-pi,pi) 
 285 double lsangle(LINESEG l1,LINESEG l2) 
 286 { 
 287     POINT o,s,e; 
 288     o.x=o.y=0; 
 289     s.x=l1.e.x-l1.s.x; 
 290     s.y=l1.e.y-l1.s.y; 
 291     e.x=l2.e.x-l2.s.x; 
 292     e.y=l2.e.y-l2.s.y; 
 293     return angle(o,s,e); 
 294 } 
 295 // 如果线段u和v相交(包括相交在端点处)时,返回true 
 296 //
 297 //判断P1P2跨立Q1Q2的依据是:( P1 - Q1 ) × ( Q2 - Q1 ) * ( Q2 - Q1 ) × ( P2 - Q1 ) >= 0。
 298 //判断Q1Q2跨立P1P2的依据是:( Q1 - P1 ) × ( P2 - P1 ) * ( P2 - P1 ) × ( Q2 - P1 ) >= 0。
 299 bool intersect(LINESEG u,LINESEG v) 
 300 { 
 301     return( (max(u.s.x,u.e.x)>=min(v.s.x,v.e.x))&&                     //排斥实验 
 302             (max(v.s.x,v.e.x)>=min(u.s.x,u.e.x))&& 
 303             (max(u.s.y,u.e.y)>=min(v.s.y,v.e.y))&& 
 304             (max(v.s.y,v.e.y)>=min(u.s.y,u.e.y))&& 
 305             (multiply(v.s,u.e,u.s)*multiply(u.e,v.e,u.s)>=0)&&         //跨立实验 
 306             (multiply(u.s,v.e,v.s)*multiply(v.e,u.e,v.s)>=0)); 
 307 } 
 308 //  (线段u和v相交)&&(交点不是双方的端点) 时返回true    
 309 bool intersect_A(LINESEG u,LINESEG v) 
 310 { 
 311     return    ((intersect(u,v))&& 
 312             (!online(u,v.s))&& 
 313             (!online(u,v.e))&& 
 314             (!online(v,u.e))&& 
 315             (!online(v,u.s))); 
 316 } 
 317 // 线段v所在直线与线段u相交时返回true;方法:判断线段u是否跨立线段v  
 318 bool intersect_l(LINESEG u,LINESEG v) 
 319 { 
 320     return multiply(u.s,v.e,v.s)*multiply(v.e,u.e,v.s)>=0; 
 321 } 
 322 // 根据已知两点坐标,求过这两点的直线解析方程: a*x+b*y+c = 0  (a >= 0)  
 323 LINE makeline(POINT p1,POINT p2) 
 324 { 
 325     LINE tl; 
 326     int sign = 1; 
 327     tl.a=p2.y-p1.y; 
 328     if(tl.a<0) 
 329     { 
 330         sign = -1; 
 331         tl.a=sign*tl.a; 
 332     } 
 333     tl.b=sign*(p1.x-p2.x); 
 334     tl.c=sign*(p1.y*p2.x-p1.x*p2.y); 
 335     return tl; 
 336 } 
 337 // 根据直线解析方程返回直线的斜率k,水平线返回 0,竖直线返回 1e200 
 338 double slope(LINE l) 
 339 { 
 340     if(abs(l.a) < 1e-20)
 341         return 0; 
 342     if(abs(l.b) < 1e-20)
 343         return INF; 
 344     return -(l.a/l.b); 
 345 } 
 346 // 返回直线的倾斜角alpha ( 0 - pi) 
 347 double alpha(LINE l) 
 348 { 
 349     if(abs(l.a)< EP)
 350         return 0; 
 351     if(abs(l.b)< EP)
 352         return PI/2; 
 353     double k=slope(l); 
 354     if(k>0) 
 355         return atan(k); 
 356     else 
 357         return PI+atan(k); 
 358 } 
 359 // 求点p关于直线l的对称点  
 360 POINT symmetry(LINE l,POINT p) 
 361 { 
 362    POINT tp; 
 363    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); 
 364    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); 
 365    return tp; 
 366 } 
 367 // 如果两条直线 l1(a1*x+b1*y+c1 = 0), l2(a2*x+b2*y+c2 = 0)相交,返回true,且返回交点p  
 368 bool lineintersect(LINE l1,LINE l2,POINT &p) // 是 L1,L2 
 369 { 
 370     double d=l1.a*l2.b-l2.a*l1.b; 
 371     if(abs(d)<EP) // 不相交 
 372         return false; 
 373     p.x = (l2.c*l1.b-l1.c*l2.b)/d; 
 374     p.y = (l2.a*l1.c-l1.a*l2.c)/d; 
 375     return true; 
 376 } 
 377 // 如果线段l1和l2相交,返回true且交点由(inter)返回,否则返回false 
 378 bool intersection(LINESEG l1,LINESEG l2,POINT &inter) 
 379 { 
 380     LINE ll1,ll2; 
 381     ll1=makeline(l1.s,l1.e); 
 382     ll2=makeline(l2.s,l2.e); 
 383     if(lineintersect(ll1,ll2,inter)) 
 384         return online(l1,inter); 
 385     else 
 386         return false; 
 387 } 
 388  
 389 /****************************** 
 390 *                              * 
 391 * 多边形常用算法模块          * 
 392 *                              * 
 393 ******************************/ 
 394  
 395 // 如果无特别说明,输入多边形顶点要求按逆时针排列 
 396  
 397 /* 
 398 返回值:输入的多边形是简单多边形,返回true 
 399 要 求:输入顶点序列按逆时针排序 
 400 说 明:简单多边形定义: 
 401 1:循环排序中相邻线段对的交是他们之间共有的单个点 
 402 2:不相邻的线段不相交 
 403 本程序默认第一个条件已经满足 
 404 */ 
 405 bool issimple(int vcount,POINT polygon[]) 
 406 { 
 407     int i,cn; 
 408     LINESEG l1,l2; 
 409     for(i=0;i<vcount;i++) 
 410     { 
 411         l1.s=polygon[i]; 
 412         l1.e=polygon[(i+1)%vcount]; 
 413         cn=vcount-3; 
 414         while(cn) 
 415         { 
 416             l2.s=polygon[(i+2)%vcount]; 
 417             l2.e=polygon[(i+3)%vcount]; 
 418             if(intersect(l1,l2)) 
 419                 break; 
 420             cn--; 
 421         } 
 422         if(cn) 
 423             return false; 
 424     } 
 425     return true; 
 426 } 
 427 // 返回值:按输入顺序返回多边形顶点的凸凹性判断,bc[i]=1,iff:第i个顶点是凸顶点 
 428 void checkconvex(int vcount,POINT polygon[],bool bc[]) 
 429 { 
 430     int i,index=0; 
 431     POINT tp=polygon[0]; 
 432     for(i=1;i<vcount;i++) // 寻找第一个凸顶点 
 433     { 
 434         if(polygon[i].y<tp.y||(polygon[i].y == tp.y&&polygon[i].x<tp.x)) 
 435         { 
 436             tp=polygon[i]; 
 437             index=i; 
 438         } 
 439     } 
 440     int count=vcount-1; 
 441     bc[index]=1; 
 442     while(count) // 判断凸凹性 
 443     { 
 444         if(multiply(polygon[(index+1)%vcount],polygon[(index+2)%vcount],polygon[index])>=0 ) 
 445             bc[(index+1)%vcount]=1; 
 446         else 
 447             bc[(index+1)%vcount]=0; 
 448         index++; 
 449         count--; 
 450     } 
 451 }
 452 // 返回值:多边形polygon是凸多边形时,返回true  
 453 bool isconvex(int vcount,POINT polygon[]) 
 454 { 
 455     bool bc[MAXV]; 
 456     checkconvex(vcount,polygon,bc); 
 457     for(int i=0;i<vcount;i++) // 逐一检查顶点,是否全部是凸顶点 
 458         if(!bc[i]) 
 459             return false; 
 460     return true; 
 461 } 
 462 // 返回多边形面积(signed);输入顶点按逆时针排列时,返回正值;否则返回负值 
 463 double area_of_polygon(int vcount,POINT polygon[]) 
 464 { 
 465     int i; 
 466     double s; 
 467     if (vcount<3) 
 468         return 0; 
 469     s=polygon[0].y*(polygon[vcount-1].x-polygon[1].x); 
 470     for (i=1;i<vcount;i++) 
 471         s+=polygon[i].y*(polygon[(i-1)].x-polygon[(i+1)%vcount].x); 
 472     return s/2; 
 473 } 
 474 // 如果输入顶点按逆时针排列,返回true 
 475 bool isconterclock(int vcount,POINT polygon[]) 
 476 { 
 477     return area_of_polygon(vcount,polygon)>0; 
 478 } 
 479 // 另一种判断多边形顶点排列方向的方法  
 480 bool isccwize(int vcount,POINT polygon[]) 
 481 { 
 482     int i,index; 
 483     POINT a,b,v; 
 484     v=polygon[0]; 
 485     index=0; 
 486     for(i=1;i<vcount;i++) // 找到最低且最左顶点,肯定是凸顶点 
 487     { 
 488         if(polygon[i].y<v.y||polygon[i].y == v.y && polygon[i].x<v.x) 
 489         { 
 490             index=i; 
 491         } 
 492     } 
 493     a=polygon[(index-1+vcount)%vcount]; // 顶点v的前一顶点 
 494     b=polygon[(index+1)%vcount]; // 顶点v的后一顶点 
 495     return multiply(v,b,a)>0; 
 496 } 
 497 /********************************************************************************************   
 498 射线法判断点q与多边形polygon的位置关系,要求polygon为简单多边形,顶点逆时针排列 
 499    如果点在多边形内:   返回0 
 500    如果点在多边形边上: 返回1 
 501    如果点在多边形外:    返回2 
 502 *********************************************************************************************/ 
 503 int insidepolygon(int vcount,POINT Polygon[],POINT q) 
 504 { 
 505     int c=0,i,n; 
 506     LINESEG l1,l2; 
 507     bool bintersect_a,bonline1,bonline2,bonline3; 
 508     double r1,r2; 
 509  
 510     l1.s=q; 
 511     l1.e=q; 
 512     l1.e.x=double(INF); 
 513     n=vcount; 
 514     for (i=0;i<vcount;i++) 
 515     { 
 516         l2.s=Polygon[i]; 
 517         l2.e=Polygon[(i+1)%n]; 
 518         if(online(l2,q))
 519             return 1; // 如果点在边上,返回1 
 520         if ( (bintersect_a=intersect_A(l1,l2))|| // 相交且不在端点 
 521         ( (bonline1=online(l1,Polygon[(i+1)%n]))&& // 第二个端点在射线上 
 522         ( (!(bonline2=online(l1,Polygon[(i+2)%n])))&& /* 前一个端点和后一个端点在射线两侧 */ 
 523         ((r1=multiply(Polygon[i],Polygon[(i+1)%n],l1.s)*multiply(Polygon[(i+1)%n],Polygon[(i+2)%n],l1.s))>0) ||    
 524         (bonline3=online(l1,Polygon[(i+2)%n]))&&     /* 下一条边是水平线,前一个端点和后一个端点在射线两侧  */ 
 525             ((r2=multiply(Polygon[i],Polygon[(i+2)%n],l1.s)*multiply(Polygon[(i+2)%n], 
 526         Polygon[(i+3)%n],l1.s))>0) 
 527                 ) 
 528             ) 
 529         ) c++; 
 530     } 
 531     if(c%2 == 1) 
 532         return 0; 
 533     else 
 534         return 2; 
 535 } 
 536 //点q是凸多边形polygon内时,返回true;注意:多边形polygon一定要是凸多边形  
 537 bool InsideConvexPolygon(int vcount,POINT polygon[],POINT q) // 可用于三角形! 
 538 { 
 539     POINT p; 
 540     LINESEG l; 
 541     int i; 
 542     p.x=0;p.y=0; 
 543     for(i=0;i<vcount;i++) // 寻找一个肯定在多边形polygon内的点p:多边形顶点平均值 
 544     { 
 545         p.x+=polygon[i].x; 
 546         p.y+=polygon[i].y; 
 547     } 
 548     p.x /= vcount; 
 549     p.y /= vcount; 
 550  
 551     for(i=0;i<vcount;i++) 
 552     { 
 553         l.s=polygon[i];l.e=polygon[(i+1)%vcount]; 
 554         if(multiply(p,l.e,l.s)*multiply(q,l.e,l.s)<0) /* 点p和点q在边l的两侧,说明点q肯定在多边形外 */ 
 555         break; 
 556     } 
 557     return (i==vcount); 
 558 } 
 559 /********************************************** 
 560 寻找凸包的graham 扫描法 
 561 PointSet为输入的点集; 
 562 ch为输出的凸包上的点集,按照逆时针方向排列; 
 563 n为PointSet中的点的数目 
 564 len为输出的凸包上的点的个数 
 565 **********************************************/ 
 566 void Graham_scan(POINT PointSet[],POINT ch[],int n,int &len) 
 567 { 
 568     int i,j,k=0,top=2; 
 569     POINT tmp; 
 570     // 选取PointSet中y坐标最小的点PointSet[k],如果这样的点有多个,则取最左边的一个 
 571     for(i=1;i<n;i++) 
 572         if ( PointSet[i].y<PointSet[k].y || (PointSet[i].y==PointSet[k].y) && (PointSet[i].x<PointSet[k].x) ) 
 573             k=i; 
 574     tmp=PointSet[0]; 
 575     PointSet[0]=PointSet[k]; 
 576     PointSet[k]=tmp; // 现在PointSet中y坐标最小的点在PointSet[0] 
 577     for (i=1;i<n-1;i++) /* 对顶点按照相对PointSet[0]的极角从小到大进行排序,极角相同的按照距离PointSet[0]从近到远进行排序 */ 
 578     { 
 579         k=i; 
 580         for (j=i+1;j<n;j++) 
 581             if ( multiply(PointSet[j],PointSet[k],PointSet[0])>0 ||  // 极角更小    
 582                 (multiply(PointSet[j],PointSet[k],PointSet[0])==0) && /* 极角相等,距离更短 */        
 583                 dist(PointSet[0],PointSet[j])<dist(PointSet[0],PointSet[k])
 584                ) 
 585                 k=j; 
 586         tmp=PointSet[i]; 
 587         PointSet[i]=PointSet[k]; 
 588         PointSet[k]=tmp; 
 589     } 
 590     ch[0]=PointSet[0]; 
 591     ch[1]=PointSet[1]; 
 592     ch[2]=PointSet[2]; 
 593     for (i=3;i<n;i++) 
 594     { 
 595         while (multiply(PointSet[i],ch[top],ch[top-1])>=0) 
 596             top--; 
 597         ch[++top]=PointSet[i]; 
 598     } 
 599     len=top+1; 
 600 } 
 601 // 卷包裹法求点集凸壳,参数说明同graham算法    
 602 void ConvexClosure(POINT PointSet[],POINT ch[],int n,int &len) 
 603 { 
 604     int top=0,i,index,first; 
 605     double curmax,curcos,curdis; 
 606     POINT tmp; 
 607     LINESEG l1,l2; 
 608     bool use[MAXV]; 
 609     tmp=PointSet[0]; 
 610     index=0; 
 611     // 选取y最小点,如果多于一个,则选取最左点 
 612     for(i=1;i<n;i++) 
 613     { 
 614         if(PointSet[i].y<tmp.y||PointSet[i].y == tmp.y&&PointSet[i].x<tmp.x) 
 615         { 
 616             index=i; 
 617         } 
 618         use[i]=false; 
 619     } 
 620     tmp=PointSet[index]; 
 621     first=index; 
 622     use[index]=true; 
 623  
 624     index=-1; 
 625     ch[top++]=tmp; 
 626     tmp.x-=100; 
 627     l1.s=tmp; 
 628     l1.e=ch[0]; 
 629     l2.s=ch[0]; 
 630  
 631     while(index!=first) 
 632     { 
 633         curmax=-100; 
 634         curdis=0; 
 635         // 选取与最后一条确定边夹角最小的点,即余弦值最大者 
 636         for(i=0;i<n;i++) 
 637         { 
 638             if(use[i])continue; 
 639             l2.e=PointSet[i]; 
 640             curcos=cosine(l1,l2); // 根据cos值求夹角余弦,范围在 (-1 -- 1 ) 
 641             if(curcos>curmax || fabs(curcos-curmax)<1e-6 && dist(l2.s,l2.e)>curdis) 
 642             { 
 643                 curmax=curcos; 
 644                 index=i; 
 645                 curdis=dist(l2.s,l2.e); 
 646             } 
 647         } 
 648         use[first]=false;            //清空第first个顶点标志,使最后能形成封闭的hull 
 649         use[index]=true; 
 650         ch[top++]=PointSet[index]; 
 651         l1.s=ch[top-2]; 
 652         l1.e=ch[top-1]; 
 653         l2.s=ch[top-1]; 
 654     } 
 655     len=top-1; 
 656 } 
 657 /*********************************************************************************************  
 658     判断线段是否在简单多边形内(注意:如果多边形是凸多边形,下面的算法可以化简) 
 659        必要条件一:线段的两个端点都在多边形内; 
 660     必要条件二:线段和多边形的所有边都不内交; 
 661     用途:    1. 判断折线是否在简单多边形内 
 662             2. 判断简单多边形是否在另一个简单多边形内 
 663 **********************************************************************************************/ 
 664 bool LinesegInsidePolygon(int vcount,POINT polygon[],LINESEG l) 
 665 { 
 666     // 判断线端l的端点是否不都在多边形内 
 667     if(!insidepolygon(vcount,polygon,l.s)||!insidepolygon(vcount,polygon,l.e)) 
 668         return false; 
 669     int top=0,i,j; 
 670     POINT PointSet[MAXV],tmp; 
 671     LINESEG s; 
 672  
 673     for(i=0;i<vcount;i++) 
 674     { 
 675         s.s=polygon[i]; 
 676         s.e=polygon[(i+1)%vcount]; 
 677         if(online(s,l.s)) //线段l的起始端点在线段s上 
 678             PointSet[top++]=l.s; 
 679         else if(online(s,l.e)) //线段l的终止端点在线段s上 
 680             PointSet[top++]=l.e; 
 681         else 
 682         { 
 683             if(online(l,s.s)) //线段s的起始端点在线段l上 
 684                 PointSet[top++]=s.s; 
 685             else if(online(l,s.e)) // 线段s的终止端点在线段l上 
 686                 PointSet[top++]=s.e; 
 687             else 
 688             { 
 689                 if(intersect(l,s)) // 这个时候如果相交,肯定是内交,返回false 
 690                 return false; 
 691             } 
 692         } 
 693     } 
 694  
 695     for(i=0;i<top-1;i++) /* 冒泡排序,x坐标小的排在前面;x坐标相同者,y坐标小的排在前面 */ 
 696     { 
 697         for(j=i+1;j<top;j++) 
 698         { 
 699             if( PointSet[i].x>PointSet[j].x || fabs(PointSet[i].x-PointSet[j].x)<EP && PointSet[i].y>PointSet[j].y ) 
 700             { 
 701                 tmp=PointSet[i]; 
 702                 PointSet[i]=PointSet[j]; 
 703                 PointSet[j]=tmp; 
 704             } 
 705         } 
 706     } 
 707  
 708     for(i=0;i<top-1;i++) 
 709     { 
 710         tmp.x=(PointSet[i].x+PointSet[i+1].x)/2; //得到两个相邻交点的中点 
 711         tmp.y=(PointSet[i].y+PointSet[i+1].y)/2; 
 712         if(!insidepolygon(vcount,polygon,tmp)) 
 713             return false; 
 714     } 
 715     return true; 
 716 } 
 717 /*********************************************************************************************  
 718 求任意简单多边形polygon的重心 
 719 需要调用下面几个函数: 
 720     void AddPosPart(); 增加右边区域的面积 
 721     void AddNegPart(); 增加左边区域的面积 
 722     void AddRegion(); 增加区域面积 
 723 在使用该程序时,如果把xtr,ytr,wtr,xtl,ytl,wtl设成全局变量就可以使这些函数的形式得到化简,
 724 但要注意函数的声明和调用要做相应变化 
 725 **********************************************************************************************/ 
 726 void AddPosPart(double x, double y, double w, double &xtr, double &ytr, double &wtr) 
 727 { 
 728     if (abs(wtr + w)<1e-10 ) return; // detect zero regions 
 729     xtr = ( wtr*xtr + w*x ) / ( wtr + w ); 
 730     ytr = ( wtr*ytr + w*y ) / ( wtr + w ); 
 731     wtr = w + wtr; 
 732     return; 
 733 } 
 734 void AddNegPart(double x, ouble y, double w, double &xtl, double &ytl, double &wtl) 
 735 { 
 736     if ( abs(wtl + w)<1e-10 ) 
 737         return; // detect zero regions 
 738  
 739     xtl = ( wtl*xtl + w*x ) / ( wtl + w ); 
 740     ytl = ( wtl*ytl + w*y ) / ( wtl + w ); 
 741     wtl = w + wtl; 
 742     return; 
 743 } 
 744 void AddRegion ( double x1, double y1, double x2, double y2, double &xtr, double &ytr, 
 745         double &wtr, double &xtl, double &ytl, double &wtl ) 
 746 { 
 747     if ( abs (x1 - x2)< 1e-10 ) 
 748         return; 
 749  
 750     if ( x2 > x1 ) 
 751     { 
 752         AddPosPart ((x2+x1)/2, y1/2, (x2-x1) * y1,xtr,ytr,wtr); /* rectangle 全局变量变化处 */ 
 753         AddPosPart ((x1+x2+x2)/3, (y1+y1+y2)/3, (x2-x1)*(y2-y1)/2,xtr,ytr,wtr);    
 754         // triangle 全局变量变化处 
 755     } 
 756     else 
 757     { 
 758         AddNegPart ((x2+x1)/2, y1/2, (x2-x1) * y1,xtl,ytl,wtl);  
 759         // rectangle 全局变量变化处 
 760         AddNegPart ((x1+x2+x2)/3, (y1+y1+y2)/3, (x2-x1)*(y2-y1)/2,xtl,ytl,wtl);  
 761         // triangle  全局变量变化处 
 762     } 
 763 } 
 764 POINT cg_simple(int vcount,POINT polygon[]) 
 765 { 
 766     double xtr,ytr,wtr,xtl,ytl,wtl;        
 767     //注意: 如果把xtr,ytr,wtr,xtl,ytl,wtl改成全局变量后这里要删去 
 768     POINT p1,p2,tp; 
 769     xtr = ytr = wtr = 0.0; 
 770     xtl = ytl = wtl = 0.0; 
 771     for(int i=0;i<vcount;i++) 
 772     { 
 773         p1=polygon[i]; 
 774         p2=polygon[(i+1)%vcount]; 
 775         AddRegion(p1.x,p1.y,p2.x,p2.y,xtr,ytr,wtr,xtl,ytl,wtl); //全局变量变化处 
 776     } 
 777     tp.x = (wtr*xtr + wtl*xtl) / (wtr + wtl); 
 778     tp.y = (wtr*ytr + wtl*ytl) / (wtr + wtl); 
 779     return tp; 
 780 } 
 781 // 求凸多边形的重心,要求输入多边形按逆时针排序 
 782 POINT gravitycenter(int vcount,POINT polygon[]) 
 783 { 
 784     POINT tp; 
 785     double x,y,s,x0,y0,cs,k; 
 786     x=0;y=0;s=0; 
 787     for(int i=1;i<vcount-1;i++) 
 788     { 
 789         x0=(polygon[0].x+polygon[i].x+polygon[i+1].x)/3; 
 790         y0=(polygon[0].y+polygon[i].y+polygon[i+1].y)/3; //求当前三角形的重心 
 791         cs=multiply(polygon[i],polygon[i+1],polygon[0])/2; 
 792         //三角形面积可以直接利用该公式求解 
 793         if(abs(s)<1e-20) 
 794         { 
 795             x=x0;y=y0;s+=cs;continue; 
 796         } 
 797         k=cs/s; //求面积比例 
 798         x=(x+k*x0)/(1+k); 
 799         y=(y+k*y0)/(1+k); 
 800         s += cs; 
 801     } 
 802     tp.x=x; 
 803     tp.y=y; 
 804     return tp; 
 805 } 
 806  
 807 /************************************************
 808 给定一简单多边形,找出一个肯定在该多边形内的点 
 809 定理1    :每个多边形至少有一个凸顶点 
 810 定理2    :顶点数>=4的简单多边形至少有一条对角线 
 811 结论    : x坐标最大,最小的点肯定是凸顶点 
 812     y坐标最大,最小的点肯定是凸顶点            
 813 ************************************************/ 
 814 POINT a_point_insidepoly(int vcount,POINT polygon[]) 
 815 { 
 816     POINT v,a,b,r; 
 817     int i,index; 
 818     v=polygon[0]; 
 819     index=0; 
 820     for(i=1;i<vcount;i++) //寻找一个凸顶点 
 821     { 
 822         if(polygon[i].y<v.y) 
 823         { 
 824             v=polygon[i]; 
 825             index=i; 
 826         } 
 827     } 
 828     a=polygon[(index-1+vcount)%vcount]; //得到v的前一个顶点 
 829     b=polygon[(index+1)%vcount]; //得到v的后一个顶点 
 830     POINT tri[3],q; 
 831     tri[0]=a;tri[1]=v;tri[2]=b; 
 832     double md=INF; 
 833     int in1=index; 
 834     bool bin=false; 
 835     for(i=0;i<vcount;i++) //寻找在三角形avb内且离顶点v最近的顶点q 
 836     { 
 837         if(i == index)continue; 
 838         if(i == (index-1+vcount)%vcount)continue; 
 839         if(i == (index+1)%vcount)continue; 
 840         if(!InsideConvexPolygon(3,tri,polygon[i]))continue; 
 841         bin=true; 
 842         if(dist(v,polygon[i])<md) 
 843         { 
 844             q=polygon[i]; 
 845             md=dist(v,q); 
 846         } 
 847     } 
 848     if(!bin) //没有顶点在三角形avb内,返回线段ab中点 
 849     { 
 850         r.x=(a.x+b.x)/2; 
 851         r.y=(a.y+b.y)/2; 
 852         return r; 
 853     } 
 854     r.x=(v.x+q.x)/2; //返回线段vq的中点 
 855     r.y=(v.y+q.y)/2; 
 856     return r; 
 857 } 
 858 /***********************************************************************************************
 859 求从多边形外一点p出发到一个简单多边形的切线,如果存在返回切点,其中rp点是右切点,lp是左切点 
 860 注意:p点一定要在多边形外 ,输入顶点序列是逆时针排列 
 861 原 理:    如果点在多边形内肯定无切线;凸多边形有唯一的两个切点,凹多边形就可能有多于两个的切点; 
 862         如果polygon是凸多边形,切点只有两个只要找到就可以,可以化简此算法 
 863         如果是凹多边形还有一种算法可以求解:先求凹多边形的凸包,然后求凸包的切线 
 864 /***********************************************************************************************/ 
 865 void pointtangentpoly(int vcount,POINT polygon[],POINT p,POINT &rp,POINT &lp) 
 866 { 
 867     LINESEG ep,en; 
 868     bool blp,bln; 
 869     rp=polygon[0]; 
 870     lp=polygon[0]; 
 871     for(int i=1;i<vcount;i++) 
 872     { 
 873         ep.s=polygon[(i+vcount-1)%vcount]; 
 874         ep.e=polygon[i]; 
 875         en.s=polygon[i]; 
 876         en.e=polygon[(i+1)%vcount]; 
 877         blp=multiply(ep.e,p,ep.s)>=0;                // p is to the left of pre edge 
 878         bln=multiply(en.e,p,en.s)>=0;                // p is to the left of next edge 
 879         if(!blp&&bln) 
 880         { 
 881             if(multiply(polygon[i],rp,p)>0)           // polygon[i] is above rp 
 882             rp=polygon[i]; 
 883         } 
 884         if(blp&&!bln) 
 885         { 
 886             if(multiply(lp,polygon[i],p)>0)           // polygon[i] is below lp 
 887             lp=polygon[i]; 
 888         } 
 889     } 
 890     return ; 
 891 } 
 892 // 如果多边形polygon的核存在,返回true,返回核上的一点p.顶点按逆时针方向输入  
 893 bool core_exist(int vcount,POINT polygon[],POINT &p) 
 894 { 
 895     int i,j,k; 
 896     LINESEG l; 
 897     LINE lineset[MAXV]; 
 898     for(i=0;i<vcount;i++) 
 899     { 
 900         lineset[i]=makeline(polygon[i],polygon[(i+1)%vcount]); 
 901     } 
 902     for(i=0;i<vcount;i++) 
 903     { 
 904         for(j=0;j<vcount;j++) 
 905         { 
 906             if(i == j)continue; 
 907             if(lineintersect(lineset[i],lineset[j],p)) 
 908             { 
 909                 for(k=0;k<vcount;k++) 
 910                 { 
 911                     l.s=polygon[k]; 
 912                     l.e=polygon[(k+1)%vcount]; 
 913                     if(multiply(p,l.e,l.s)>0)      
 914                     //多边形顶点按逆时针方向排列,核肯定在每条边的左侧或边上 
 915                     break; 
 916                 } 
 917                 if(k == vcount)             //找到了一个核上的点 
 918                 break; 
 919             } 
 920         } 
 921         if(j<vcount) break; 
 922     } 
 923     if(i<vcount) 
 924         return true; 
 925     else 
 926         return false; 
 927 } 
 928 /************************* 
 929 *                         * 
 930 * 圆的基本运算           * 
 931 *                         * 
 932 *************************/ 
 933 /******************************************************************************
 934 返回值    : 点p在圆内(包括边界)时,返回true 
 935 用途    : 因为圆为凸集,所以判断点集,折线,多边形是否在圆内时,
 936     只需要逐一判断点是否在圆内即可。 
 937 *******************************************************************************/ 
 938 bool point_in_circle(POINT o,double r,POINT p) 
 939 { 
 940     double d2=(p.x-o.x)*(p.x-o.x)+(p.y-o.y)*(p.y-o.y); 
 941     double r2=r*r; 
 942     return d2<r2||abs(d2-r2)<EP; 
 943 } 
 944 /******************************************************************************
 945 用 途    :求不共线的三点确定一个圆 
 946 输 入    :三个点p1,p2,p3 
 947 返回值    :如果三点共线,返回false;反之,返回true。圆心由q返回,半径由r返回 
 948 *******************************************************************************/ 
 949 bool cocircle(POINT p1,POINT p2,POINT p3,POINT &q,double &r) 
 950 { 
 951     double x12=p2.x-p1.x; 
 952     double y12=p2.y-p1.y; 
 953     double x13=p3.x-p1.x; 
 954     double y13=p3.y-p1.y; 
 955     double z2=x12*(p1.x+p2.x)+y12*(p1.y+p2.y); 
 956     double z3=x13*(p1.x+p3.x)+y13*(p1.y+p3.y); 
 957     double d=2.0*(x12*(p3.y-p2.y)-y12*(p3.x-p2.x)); 
 958     if(abs(d)<EP) //共线,圆不存在 
 959         return false; 
 960     q.x=(y13*z2-y12*z3)/d; 
 961     q.y=(x12*z3-x13*z2)/d; 
 962     r=dist(p1,q); 
 963     return true; 
 964 } 
 965 int line_circle(LINE l,POINT o,double r,POINT &p1,POINT &p2) 
 966 { 
 967     return true; 
 968 } 
 969  
 970 /************************** 
 971 *                          * 
 972 * 矩形的基本运算          * 
 973 *                         * 
 974 **************************/ 
 975 /* 
 976 说明:因为矩形的特殊性,常用算法可以化简: 
 977 1.判断矩形是否包含点 
 978 只要判断该点的横坐标和纵坐标是否夹在矩形的左右边和上下边之间。 
 979 2.判断线段、折线、多边形是否在矩形中 
 980 因为矩形是个凸集,所以只要判断所有端点是否都在矩形中就可以了。 
 981 3.判断圆是否在矩形中 
 982 圆在矩形中的充要条件是:圆心在矩形中且圆的半径小于等于圆心到矩形四边的距离的最小值。 
 983 */ 
 984 // 已知矩形的三个顶点(a,b,c),计算第四个顶点d的坐标. 注意:已知的三个顶点可以是无序的 
 985 POINT rect4th(POINT a,POINT b,POINT c) 
 986 { 
 987     POINT d; 
 988     if(abs(dotmultiply(a,b,c))<EP) // 说明c点是直角拐角处 
 989     { 
 990         d.x=a.x+b.x-c.x; 
 991         d.y=a.y+b.y-c.y; 
 992     } 
 993     if(abs(dotmultiply(a,c,b))<EP) // 说明b点是直角拐角处 
 994     { 
 995         d.x=a.x+c.x-b.x; 
 996         d.y=a.y+c.y-b.x; 
 997     } 
 998     if(abs(dotmultiply(c,b,a))<EP) // 说明a点是直角拐角处 
 999     { 
1000         d.x=c.x+b.x-a.x; 
1001         d.y=c.y+b.y-a.y; 
1002     } 
1003     return d; 
1004 } 
1005  
1006 /************************* 
1007 *                        * 
1008 * 常用算法的描述        * 
1009 *                        * 
1010 *************************/ 
1011 /* 
1012 尚未实现的算法: 
1013 1. 求包含点集的最小圆 
1014 2. 求多边形的交 
1015 3. 简单多边形的三角剖分 
1016 4. 寻找包含点集的最小矩形 
1017 5. 折线的化简 
1018 6. 判断矩形是否在矩形中 
1019 7. 判断矩形能否放在矩形中 
1020 8. 矩形并的面积与周长 
1021 9. 矩形并的轮廓 
1022 10.矩形并的闭包 
1023 11.矩形的交 
1024 12.点集中的最近点对 
1025 13.多边形的并 
1026 14.圆的交与并 
1027 15.直线与圆的关系 
1028 16.线段与圆的关系 
1029 17.求多边形的核监视摄象机 
1030 18.求点集中不相交点对 railwai 
1031 *//* 
1032 寻找包含点集的最小矩形 
1033 原理:该矩形至少一条边与点集的凸壳的某条边共线 
1034 First take the convex hull of the points. Let the resulting convex 
1035 polygon be P. It has been known for some time that the minimum 
1036 area rectangle enclosing P must have one rectangle side flush with 
1037 (i.e., collinear with and overlapping) one edge of P. This geometric 
1038 fact was used by Godfried Toussaint to develop the "rotating calipers" 
1039 algorithm in a hard-to-find 1983 paper, "Solving Geometric Problems 
1040 with the Rotating Calipers" (Proc. IEEE MELECON). The algorithm 
1041 rotates a surrounding rectangle from one flush edge to the next, 
1042 keeping track of the minimum area for each edge. It achieves O(n) 
1043 time (after hull computation). See the "Rotating Calipers Homepage" 
1044 http://www.cs.mcgill.ca/~orm/rotcal.frame.html for a description 
1045 and applet. 
1046 *//* 
1047 折线的化简 伪码如下: 
1048 Input: tol = the approximation tolerance 
1049 L = {V0,V1,...,Vn-1} is any n-vertex polyline 
1050 Set start = 0; 
1051 Set k = 0; 
1052 Set W0 = V0; 
1053 for each vertex Vi (i=1,n-1) 
1054 { 
1055 if Vi is within tol from Vstart 
1056 then ignore it, and continue with the next vertex 
1057 Vi is further than tol away from Vstart 
1058 so add it as a new vertex of the reduced polyline 
1059 Increment k++; 
1060 Set Wk = Vi; 
1061 Set start = i; as the new initial vertex 
1062 } 
1063 Output: W = {W0,W1,...,Wk-1} = the k-vertex simplified polyline 
1064 */ 
1065 /******************** 
1066 *                    * 
1067 * 补充                * 
1068 *                    * 
1069 ********************/ 
1070  
1071 //两圆关系: 
1072 /* 两圆: 
1073 相离: return 1; 
1074 外切: return 2; 
1075 相交: return 3; 
1076 内切: return 4; 
1077 内含: return 5; 
1078 */ 
1079 int CircleRelation(POINT p1, double r1, POINT p2, double r2) 
1080 { 
1081     double d = sqrt( (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y) ); 
1082  
1083     if( fabs(d-r1-r2) < EP ) // 必须保证前两个if先被判定! 
1084         return 2; 
1085     if( fabs(d-fabs(r1-r2)) < EP ) 
1086         return 4; 
1087     if( d > r1+r2 ) 
1088         return 1; 
1089     if( d < fabs(r1-r2) ) 
1090         return 5; 
1091     if( fabs(r1-r2) < d && d < r1+r2 ) 
1092         return 3; 
1093     return 0; // indicate an error! 
1094 } 
1095 //判断圆是否在矩形内:
1096 // 判定圆是否在矩形内,是就返回true(设矩形水平,且其四个顶点由左上开始按顺时针排列) 
1097 // 调用ptoldist函数,在第4页 
1098 bool CircleRecRelation(POINT pc, double r, POINT pr1, POINT pr2, POINT pr3, POINT pr4) 
1099 { 
1100     if( pr1.x < pc.x && pc.x < pr2.x && pr3.y < pc.y && pc.y < pr2.y ) 
1101     { 
1102         LINESEG line1(pr1, pr2); 
1103         LINESEG line2(pr2, pr3); 
1104         LINESEG line3(pr3, pr4); 
1105         LINESEG line4(pr4, pr1); 
1106         if( r<ptoldist(pc,line1) && r<ptoldist(pc,line2) &&    r<ptoldist(pc,line3) && r<ptoldist(pc,line4) ) 
1107             return true; 
1108     } 
1109     return false; 
1110 } 
1111 //点到平面的距离: 
1112 //点到平面的距离,平面用一般式表示ax+by+cz+d=0 
1113 double P2planeDist(double x, double y, double z, double a, double b, double c, double d) 
1114 { 
1115     return fabs(a*x+b*y+c*z+d) / sqrt(a*a+b*b+c*c); 
1116 } 
1117 //点是否在直线同侧:
1118 //两个点是否在直线同侧,是则返回true 
1119 bool SameSide(POINT p1, POINT p2, LINE line) 
1120 { 
1121     return (line.a * p1.x + line.b * p1.y + line.c) * 
1122     (line.a * p2.x + line.b * p2.y + line.c) > 0; 
1123 }
1124 //镜面反射线:
1125 // 已知入射线、镜面,求反射线。 
1126 // a1,b1,c1为镜面直线方程(a1 x + b1 y + c1 = 0 ,下同)系数;  
1127 //a2,b2,c2为入射光直线方程系数;  
1128 //a,b,c为反射光直线方程系数. 
1129 // 光是有方向的,使用时注意:入射光向量:<-b2,a2>;反射光向量:<b,-a>. 
1130 // 不要忘记结果中可能会有"negative zeros" 
1131 void reflect(double a1,double b1,double c1,double a2,double b2,double c2,double &a,double &b,double &c) 
1132 { 
1133     double n,m; 
1134     double tpb,tpa; 
1135     tpb=b1*b2+a1*a2; 
1136     tpa=a2*b1-a1*b2; 
1137     m=(tpb*b1+tpa*a1)/(b1*b1+a1*a1); 
1138     n=(tpa*b1-tpb*a1)/(b1*b1+a1*a1); 
1139     if(fabs(a1*b2-a2*b1)<1e-20) 
1140     { 
1141         a=a2;b=b2;c=c2; 
1142         return; 
1143     } 
1144     double xx,yy; //(xx,yy)是入射线与镜面的交点。 
1145     xx=(b1*c2-b2*c1)/(a1*b2-a2*b1); 
1146     yy=(a2*c1-a1*c2)/(a1*b2-a2*b1); 
1147     a=n; 
1148     b=-m; 
1149     c=m*yy-xx*n; 
1150 } 
1151 //矩形包含: 
1152 // 矩形2(C,D)是否在1(A,B)内
1153 bool r2inr1(double A,double B,double C,double D)  
1154 { 
1155     double X,Y,L,K,DMax; 
1156     if (A < B) 
1157     { 
1158         double tmp = A; 
1159         A = B; 
1160         B = tmp; 
1161     } 
1162     if (C < D) 
1163     { 
1164         double tmp = C; 
1165         C = D; 
1166         D = tmp; 
1167     } 
1168     if (A > C && B > D)                 // trivial case  
1169         return true; 
1170     else 
1171         if (D >= B) 
1172             return false; 
1173         else 
1174         { 
1175             X = sqrt(A * A + B * B);         // outer rectangle's diagonal  
1176             Y = sqrt(C * C + D * D);         // inner rectangle's diagonal  
1177             if (Y < B) // check for marginal conditions 
1178                 return true; // the inner rectangle can freely rotate inside 
1179             else 
1180                 if (Y > X) 
1181                     return false; 
1182                 else 
1183                 { 
1184                     L = (B - sqrt(Y * Y - A * A)) / 2; 
1185                     K = (A - sqrt(Y * Y - B * B)) / 2; 
1186                     DMax = sqrt(L * L + K * K); 
1187                     if (D >= DMax) 
1188                     return false; 
1189                     else 
1190                     return true; 
1191                 } 
1192         } 
1193 } 
1194 //两圆交点: 
1195 // 两圆已经相交(相切) 
1196 void  c2point(POINT p1,double r1,POINT p2,double r2,POINT &rp1,POINT &rp2) 
1197 { 
1198     double a,b,r; 
1199     a=p2.x-p1.x; 
1200     b=p2.y-p1.y; 
1201     r=(a*a+b*b+r1*r1-r2*r2)/2; 
1202     if(a==0&&b!=0) 
1203     { 
1204         rp1.y=rp2.y=r/b; 
1205         rp1.x=sqrt(r1*r1-rp1.y*rp1.y); 
1206         rp2.x=-rp1.x; 
1207     } 
1208     else if(a!=0&&b==0) 
1209     { 
1210         rp1.x=rp2.x=r/a; 
1211         rp1.y=sqrt(r1*r1-rp1.x*rp2.x); 
1212         rp2.y=-rp1.y; 
1213     } 
1214     else if(a!=0&&b!=0) 
1215     { 
1216         double delta; 
1217         delta=b*b*r*r-(a*a+b*b)*(r*r-r1*r1*a*a); 
1218         rp1.y=(b*r+sqrt(delta))/(a*a+b*b); 
1219         rp2.y=(b*r-sqrt(delta))/(a*a+b*b); 
1220         rp1.x=(r-b*rp1.y)/a; 
1221         rp2.x=(r-b*rp2.y)/a; 
1222     } 
1223  
1224     rp1.x+=p1.x; 
1225     rp1.y+=p1.y; 
1226     rp2.x+=p1.x; 
1227     rp2.y+=p1.y; 
1228 } 
1229 //两圆公共面积:
1230 // 必须保证相交 
1231 double c2area(POINT p1,double r1,POINT p2,double r2) 
1232 { 
1233     POINT rp1,rp2; 
1234     c2point(p1,r1,p2,r2,rp1,rp2); 
1235  
1236     if(r1>r2) //保证r2>r1 
1237     { 
1238         swap(p1,p2); 
1239         swap(r1,r2); 
1240     } 
1241     double a,b,rr; 
1242     a=p1.x-p2.x; 
1243     b=p1.y-p2.y; 
1244     rr=sqrt(a*a+b*b); 
1245  
1246     double dx1,dy1,dx2,dy2; 
1247     double sita1,sita2; 
1248     dx1=rp1.x-p1.x; 
1249     dy1=rp1.y-p1.y; 
1250     dx2=rp2.x-p1.x; 
1251     dy2=rp2.y-p1.y; 
1252     sita1=acos((dx1*dx2+dy1*dy2)/r1/r1); 
1253  
1254     dx1=rp1.x-p2.x; 
1255     dy1=rp1.y-p2.y; 
1256     dx2=rp2.x-p2.x; 
1257     dy2=rp2.y-p2.y; 
1258     sita2=acos((dx1*dx2+dy1*dy2)/r2/r2); 
1259     double s=0; 
1260     if(rr<r2)//相交弧为优弧 
1261         s=r1*r1*(PI-sita1/2+sin(sita1)/2)+r2*r2*(sita2-sin(sita2))/2; 
1262     else//相交弧为劣弧 
1263         s=(r1*r1*(sita1-sin(sita1))+r2*r2*(sita2-sin(sita2)))/2; 
1264  
1265     return s; 
1266 } 
1267 //圆和直线关系: 
1268 //0----相离 1----相切 2----相交 
1269 int clpoint(POINT p,double r,double a,double b,double c,POINT &rp1,POINT &rp2) 
1270 { 
1271     int res=0; 
1272  
1273     c=c+a*p.x+b*p.y; 
1274     double tmp; 
1275     if(a==0&&b!=0) 
1276     { 
1277         tmp=-c/b; 
1278         if(r*r<tmp*tmp) 
1279             res=0; 
1280         else if(r*r==tmp*tmp) 
1281         { 
1282             res=1; 
1283             rp1.y=tmp; 
1284             rp1.x=0; 
1285         } 
1286         else 
1287         { 
1288             res=2; 
1289             rp1.y=rp2.y=tmp; 
1290             rp1.x=sqrt(r*r-tmp*tmp); 
1291             rp2.x=-rp1.x; 
1292         } 
1293     } 
1294     else if(a!=0&&b==0) 
1295     { 
1296         tmp=-c/a; 
1297         if(r*r<tmp*tmp) 
1298             res=0; 
1299         else if(r*r==tmp*tmp) 
1300         { 
1301             res=1; 
1302             rp1.x=tmp; 
1303             rp1.y=0; 
1304         } 
1305         else 
1306         { 
1307             res=2; 
1308             rp1.x=rp2.x=tmp; 
1309             rp1.y=sqrt(r*r-tmp*tmp); 
1310             rp2.y=-rp1.y; 
1311         } 
1312     } 
1313     else if(a!=0&&b!=0) 
1314     { 
1315         double delta; 
1316         delta=b*b*c*c-(a*a+b*b)*(c*c-a*a*r*r); 
1317         if(delta<0) 
1318             res=0; 
1319         else if(delta==0) 
1320         { 
1321             res=1; 
1322             rp1.y=-b*c/(a*a+b*b); 
1323             rp1.x=(-c-b*rp1.y)/a; 
1324         } 
1325         else 
1326         { 
1327             res=2; 
1328             rp1.y=(-b*c+sqrt(delta))/(a*a+b*b); 
1329             rp2.y=(-b*c-sqrt(delta))/(a*a+b*b); 
1330             rp1.x=(-c-b*rp1.y)/a; 
1331             rp2.x=(-c-b*rp2.y)/a; 
1332         } 
1333     } 
1334     rp1.x+=p.x; 
1335     rp1.y+=p.y; 
1336     rp2.x+=p.x; 
1337     rp2.y+=p.y; 
1338     return res; 
1339 } 
1340 //内切圆: 
1341 void incircle(POINT p1,POINT p2,POINT p3,POINT &rp,double &r) 
1342 { 
1343     double dx31,dy31,dx21,dy21,d31,d21,a1,b1,c1; 
1344     dx31=p3.x-p1.x; 
1345     dy31=p3.y-p1.y; 
1346     dx21=p2.x-p1.x; 
1347     dy21=p2.y-p1.y; 
1348  
1349     d31=sqrt(dx31*dx31+dy31*dy31); 
1350     d21=sqrt(dx21*dx21+dy21*dy21); 
1351     a1=dx31*d21-dx21*d31; 
1352     b1=dy31*d21-dy21*d31; 
1353     c1=a1*p1.x+b1*p1.y; 
1354  
1355     double dx32,dy32,dx12,dy12,d32,d12,a2,b2,c2; 
1356     dx32=p3.x-p2.x; 
1357     dy32=p3.y-p2.y; 
1358     dx12=-dx21; 
1359     dy12=-dy21; 
1360  
1361     d32=sqrt(dx32*dx32+dy32*dy32); 
1362     d12=d21; 
1363     a2=dx12*d32-dx32*d12; 
1364     b2=dy12*d32-dy32*d12; 
1365     c2=a2*p2.x+b2*p2.y; 
1366  
1367     rp.x=(c1*b2-c2*b1)/(a1*b2-a2*b1); 
1368     rp.y=(c2*a1-c1*a2)/(a1*b2-a2*b1); 
1369     r=fabs(dy21*rp.x-dx21*rp.y+dx21*p1.y-dy21*p1.x)/d21; 
1370 } 
1371 //求切点: 
1372 // p---圆心坐标, r---圆半径, sp---圆外一点, rp1,rp2---切点坐标 
1373 void cutpoint(POINT p,double r,POINT sp,POINT &rp1,POINT &rp2) 
1374 { 
1375     POINT p2; 
1376     p2.x=(p.x+sp.x)/2; 
1377     p2.y=(p.y+sp.y)/2; 
1378  
1379     double dx2,dy2,r2; 
1380     dx2=p2.x-p.x; 
1381     dy2=p2.y-p.y; 
1382     r2=sqrt(dx2*dx2+dy2*dy2); 
1383     c2point(p,r,p2,r2,rp1,rp2); 
1384 } 
1385 //线段的左右旋: 
1386 /* l2在l1的左/右方向(l1为基准线) 
1387 返回    0    : 重合; 
1388 返回    1    : 右旋; 
1389 返回    –1 : 左旋; 
1390 */ 
1391 int rotat(LINESEG l1,LINESEG l2) 
1392 { 
1393     double dx1,dx2,dy1,dy2; 
1394     dx1=l1.s.x-l1.e.x; 
1395     dy1=l1.s.y-l1.e.y; 
1396     dx2=l2.s.x-l2.e.x; 
1397     dy2=l2.s.y-l2.e.y; 
1398  
1399     double d; 
1400     d=dx1*dy2-dx2*dy1; 
1401     if(d==0) 
1402         return 0; 
1403     else if(d>0) 
1404         return -1; 
1405     else 
1406         return 1; 
1407 } 
1408 /*
1409 公式: 
1410 球坐标公式: 
1411 直角坐标为 P(x, y, z) 时,对应的球坐标是(rsinφcosθ, rsinφsinθ, rcosφ),其中φ是向量OP与Z轴的夹角,范围[0,π];是OP在XOY面上的投影到X轴的旋角,范围[0,2π]  
1412 直线的一般方程转化成向量方程: 
1413 ax+by+c=0 
1414 x-x0     y-y0 
1415    ------ = ------- // (x0,y0)为直线上一点,m,n为向量 
1416 m        n 
1417 转换关系: 
1418 a=n;b=-m;c=m·y0-n·x0; 
1419 m=-b; n=a; 
1420 三点平面方程: 
1421 三点为P1,P2,P3 
1422 设向量  M1=P2-P1; M2=P3-P1; 
1423 平面法向量:  M=M1 x M2 () 
1424 平面方程:    M.i(x-P1.x)+M.j(y-P1.y)+M.k(z-P1.z)=0 
1425 */
原文地址:https://www.cnblogs.com/aininot260/p/9637013.html