計算幾何學概述

1.基本知識
(1)線段兩端點P (x1, y1),Q(x2, y2),線段上任意一點表示爲:

x = λx1 + (1-λ)x2
y = λy1 + (1-λ)y2
0 <= λ <= 1
與原點O結合起來便有矢量OPOQ,得到PQ
(2)矢量加減法
P
+Q= (x1+x2, y1+y2)   滿足交換律P+Q=Q+P
P-Q= (x1-x2, y1-y2)     P-Q = -(Q-P)
(3)矢量叉積
P×Q = x1 × y2 - x2 × y1 ;結果是一個標量,可以用行列式表示
P×Q = -(Q×P)
P×(-Q) = -(P×Q)
P×Q>0 Q在P的逆時針方向;<0在順時針方向;=0共線,同向或反向。

更一般的情況:
A(x0, y0) B(x1, y1) C(x2, y2)三點構成ABAC:

typedef struct point
{
double x;
double y;
}POINT;

double CrossProd(POINT A, POINT B, POINT C)
{
return (B.x - A.x) * (C.y - A.y) - (C.x - A.x) * (B.y - A.y);
}
(4)折線段拐向判斷
根據前面(3)的判斷方向,如果我們是判斷AB和BC的方向只需要判斷AB×AC即可:
>0,BC在AB的左側;<0,BC在AB的右側;=0三點共線
(5)判斷點是否在線段上
這個可以根據前面的共線條件=0判斷,但是在這裏要解決是否在延長線或者反延長線上。ON-SEGMENT(POINT P1,POINT P2, POINT Q)
{
if (((min(P1.x, P2.x) <= Q.x)&&(Q.x <= max(P1.x, P2.x)))&&
  ((min(P1.y, P2.y) <= Q.y) &&(Q.y <= max(P1.y, P2.y))))
{
  return true;
}
return false;
}

(6)跨立實驗與判斷兩線段是否相交
快速排斥實驗:
以兩個向量分辨作爲矩形的對角線,判斷這兩個矩形是否相交。
跨立實驗:如果PQ與MN相交則,P和Q分別在MN兩側。

跨立的相交條件是:
(MP×MN)×(MN×MQ)>=0
(PM×PQ)×(PQ×PN)>=0

(7)整數點與Pick定理
1899年,Pick定理:設以整數點爲頂點的多邊形的面積爲S,多邊形內部的整數點爲N,多邊形邊界上的整數點數爲L,則N +(1/2)L - 1  = S

感興趣者參考維基百科:http://zh.wikipedia.org/zh-cn/%E7%9A%AE%E5%85%8B%E5%AE%9A%E7%90%86

// 求a,b最大公因數 大數在a上
int gcd(int a, int b)
{
if (b == 0) return a;
else return gcd(b, a%b);
}

int OnEdge(int n, POINT *p)
{
int i, ret = 0;
for (i = 0; i < n; i++)
{
  ret += gcd(fabs(p[i].x-p[(i+1)%n].x), fabs(p[i].y-p[(i+1)%n].y));
}
return ret;
}

int InSide(int n, POINT *p)
{
int i, area = 0;
for (i = 0; i < n; i++)
{
  area += p[(i+1)%n].y * (p[i].x - p[(i+2)%n].x);
}
return fabs(area) - OnEdge(n, p)/2 + 1;
}

2. 基本算法
(1)判斷線段和直線是否相交

跨立實驗
(2)判斷矩形是否包含該點
判斷該點的橫座標和縱座標是否夾在矩形的左右邊和上下邊之間。
(3)判斷線段、折線、多邊形是否在矩形中
矩形是凸集,只需要判斷所有點是否在矩形中
(4)判斷矩形是否在矩形中
同(3)
(5)判斷圓是否在矩形中
圓心在矩形中,且圓心到矩形最短距離小於半徑
(6)判斷點是否在多邊形中
引申該點到多邊形外產生射線,如果該線與多邊形邊的交點爲偶數則在多邊形外,奇數則在多邊形內。
特殊情況:該點在多邊形邊上(只要判斷一下即可);射線上含有多邊形頂點(重新隨機選擇)

#define eps 0.000001
#define zero(x) (((x)>0?(x):-(x))<eps)

int inside_polygon(int n, POINT p, POINT *pt)
{
POINT p1;
int i = 0, count;
while (i < n) //隨機取一個足夠遠的p1
{
  p1.x = rand() + offset, p1.y = rand() + offset;
  count = 0;
  for (i = 0; i < n; i++) // 依次對多邊形的每條邊s = pt[i]pt[i+1]進行考察
  {
   if (zero(CrossProd(p, pt[i], pt[(i+1)%n])) && (pt[i].x - p.x) * (pt[(i+1)%n].x - p.x) < eps \
    && (pt[i].y - p.y) * (pt[(i+1)%n].y - p.y) < eps)
   {
    return 0; // p 在多邊形邊上
   }
   else if (zero(CrossProd(p, p1, pt[i])))
   {
    break; // 點在射線上
   }
   else if (CrossProd(p, pt[i], p1) * CrossProd(p, p1, pt[(i+1)%n]) > eps &&\
    CrossProd(pt[i], p, pt[(i+1)%n]) * CrossProd(pt[i], pt[(i+1)%n], p1) > eps)
   {
    count ++; // 射線與多邊形邊相交,統計交點數
   }
  }
}
}

(7)判斷線段是否在多邊形內
只需要檢查該線段兩個端點是否在多邊形內,且是否與多邊形相交(凹多邊形),如果相交,求出交點,並計算兩個相鄰交點的中點是否在多邊形內,如果在,則該線段在多邊形內。
if (線段PQ的端點不都在多邊形內)

return false;

else
{

點集pointSet初始化爲空;

for (多邊形的每條邊s)

if (線段的某個端點在s上)

將該點加入pointSet;
else

{

   if (s的某個端點在PQ上)
      將該端點加入pointSet;

   else if (s和線段PQ相交)
      return false;

}

將pointSet中的點按照X-Y座標排序;

  for(pointSet[i],pointSet[i+1])

if (兩個相鄰的點不在多邊形內)

   return false;
  return true;

}

(8)判斷折線是否在多邊形內
判斷折線的每條線段是否在多邊形內
(9)判斷多邊形是否在多邊形內
判斷多邊形的每條邊是否在多邊形內
(10)判斷矩形是否在多邊形內
同上
(11)判斷圓是否在多邊形內
判斷圓的半徑是否小於等於圓心到多邊形邊最短距離
(12)判斷點是否在圓內
計算圓心到該點距離,判斷是否小於等於半徑
(13)判斷線段、折線、矩形、多邊形是否在圓內
圓是凸集,只要判斷每個頂點是否在圓內
(14)判斷圓是否在圓內
計算要判斷的圓的半徑是否小於該圓半徑,如果否則不在圓內,若是則計算兩個圓心之間的距離,如果小於兩個半徑之差則在圓內,否則不在。

3.凸包

將點集包含的最小凸邊形,退化情況爲一條線段或一個點
凸包求法:
(1)Graham掃描法
void graham( int n )

{

令P爲點集中y座標最小的點;

  m =n -1;

利用叉積將剩餘的點逆時針排序;

設定棧S;

將P壓入棧,並依次壓入排序好的第一個和第二個點;

棧頂位置top = 2;

  for(i = 3; i<=m; i++)

{

    //向左轉利用叉積,如果下面的叉積小於0,則不是左轉

    While(計算次棧頂元素,棧頂元素以及第i個元素和棧頂元素所形成的角不是向左轉)

       將棧頂元素出棧;

   將第i個元素壓棧;

}

輸出棧中元素;//即凸包的頂點

}
(2)Jarvis步進法
點集Q處於最低最左邊位置(y座標值最小)的一個點M是凸包的一個頂點,最高最右邊位置(y座標值最大)的點N是凸包上的點。
這樣有一條MN線,如此在外圍拉線,包住各個點,外圍的點就是凸包上的點。
確定右鏈和左鏈:
右鏈:設定一個棧,先將M入棧,對其他的點依據相對於棧頂元素的最小極角,並距離最遠的點入棧。
左鏈:類之。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章