計算幾何:模板

Claire_ljy發表於2020-04-04

叉積:兩個向量的叉積是一個標量,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

相關文章