算法学习:计算几何基础

【定义】

 

【极角】极坐标系中的phi,对于直线来说可以直接看成斜率,因为斜率也可以表示为角度

 


 

计算几何是运用计算机中的值和代码去模拟真实的几何运算过程,所以需要先创建相对应的元素,而最基本的元素就是点,而点能够表示二维空间内的位置

同在二维空间,点和向量的储存方式类似(但是实际意义大不相同)

struct V
{
    double x, y;
    double ang;
    double angle()
    {//求取极角
        return atan2(y, x);
    }
    V(double X = 0, double Y = 0)
    {
        //初始化
        x = X, y = Y;
        ang = atan2(y, x);
    }
    bool operator ==(const V &b)
    {
        return cmp(x - b.x) && cmp(y - b.y);
    }

};

通过重命名令点和向量在某些用法上有所重合

typedef V  P;

定义运算(点和向量的个别运算又有所重合)

V operator +(V a, V b) { return V(a.x + b.x, a.y + b.y); }
V operator -(V a, V b) { return V(a.x - b.x, a.y - b.y); }
V operator *(V a, double b) { return V(a.x *b, a.y*b); }
V operator /(V a, double b) { return V(a.x / b, a.y / b); }
//叉积
double cross(V a, V b)
{
    return a.x*b.y - a.y*b.x;
}
//点积
double dot(V a, V b)
{
    return a.x*b.x + a.y*b.y;
}

 

而根据高中几何知识,我们知道两个点能够表示一条线段

所以有线段的定义

struct L
{
    P s, t;
    double ang;
    L(P X = V(), P Y = V())
    {
        s = X, t = Y, ang = (Y - X).angle();
    }
};

其相应运算都能够通过各种求交点,平行,叉积,点积求取完成

(证明都比较简单)

//通过斜率比较线段大小,极角排序
bool operator <(const L &a, const L &b)
{
    double r = a.ang - b.ang;
    if (cmp(r) != 0)    return cmp(r) == -1;
    //极角相同,默认偏下的更大
    return cmp(cross(a.t - a.s, b.t - a.s)) == -1;
}

//判断线段平行
bool is_parallel(L a, L b)
{
    return cmp(cross(a.t - a.s, b.t - b.s)) == 0;
}

//查找交点
P intersection(L a, L b)
{
    return a.s + (a.t - a.s)*(cross(b.t - b.s, a.s - b.s)) / cross(a.t - a.s, b.t - b.s);
}
bool is_right(L a, P b)
//判断点b是否在直线的右边(和起点连成的向量方向和直线向量方向相反)
{
    return cmp(cross(a.t - a.s, b - a.s)) < 0;
}

 

而面的确定可以有多个点或者多个线段确定

 

下面给出直线和线段的定义方法

struct L
{
   P s,t;
   L (){}
   L(P a,P b):s(a),t(b) {}
}
typedef S L;

注意!直线和线段的对待和处理方式区别很明显,一定要注意题目和题意所要使用的是直线还是线段

 

两向量是否相交

两线段是否相交

 

 

用高中几何知识来分析下为什么要这样写

前面的条件里面,能够看到,判断 a 的最左边是否小于 b 的最右端

这样就能够确定这两个线段的相交的范围一定是在两个线段的区域上的

底下的两条语句是在判定线段的点是否在另外一条线段的两边

 

这里注意下,取等于的情况,是两个线段刚好交于某个交点

所以在一些题目中,通过去掉等号,可以达到去除取到交点的情况

 

//求两线段是否相交
bool L_is_inter(L a, L b)
{
    return
        max(a.s.x, a.t.x) >= min(b.s.x, b.t.x) &&
        max(b.s.x, b.t.x) >= min(a.s.x, a.t.x) &&
        max(a.s.y, a.t.y) >= min(b.s.y, b.t.y) &&
        max(b.s.y, b.t.y) >= min(a.s.y, a.t.y) &&
        sgn((b.s - a.s) ^ (a.t - a.s))*sgn(b.t - a.s) ^ (a.t - a.s)) <= 0 &&
        sgn((a.s - b.s) ^ (b.t - a.s))*sgn((a.t - b.s) ^ (b.t - b.s)) <= 0;
}

 

 

 

【求多边形面积】

  叉积能够表示的是:两个线段围成的平行四边形面积

  然后通过逐个求取边和原点围成的多边形面积

  因为叉积有正有负,所以  

  通过这个原理就能够求得多边形面积

 

double area(P *p, int n)
{
    double res = 0;
    p[n + 1] = p[1];
    for (int i = 1; i <= n; i++)
        res += cross(p[i], p[i + 1]);
    return fabs(res / 2);
}

 

这些元素之间还有各种判断和操作

 


 

点是否在多边形内

通过计算点两边的边的个数是否相等,如果相等则说明点在多边形内

同时,计算点是否在直线上,查找点在直线上的情况

点是否在多边形内

 

 


 

 

通过以上两个知识点:

能够求两个多边形是否相交

两多边形是否相交

 

 


 

posted @ 2019-10-06 12:40  rentu  阅读(270)  评论(0编辑  收藏  举报