计算几何学概述

来源:互联网 发布:用itunes安装软件 编辑:程序博客网 时间:2024/04/30 10:48

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入栈,对其他的点依据相对于栈顶元素的最小极角,并距离最远的点入栈。
左链:类之。

原创粉丝点击