二维几何-点和直线

版权声明:转载请注明出处。 https://blog.csdn.net/baidu_38304645/article/details/83243745

这篇文章讲的是二维几何中点和直线的相关问题。

直线的参数表示。直线可以用直线上的一点P0和方向向量v表示(虽然这个向量的大小没什么用处)。直线上所有点P满足P=P0+tv。其中t称为参数。如果已知直线上的两个不同点A和B,则方向向量为B-A,所以参数方程为A+(B-A)t。

参数方程最方便的地方在于直线、射线和线段的方程形式是一样的。区别仅仅在于参数。直线的t没有范围限制。射线的t>0,线段的t在0~1之间,这样,很多对于直线适用的公式可以很方便的用在射线和线段上。

有向直线的存储结构定义:

//有向直线。它的左边就是对应的半平面
struct Line
{
    Point p;                        //直线上任意一点
    Vector v;                      // 方向向量,左边就是对应的半平面
    double ang;                     //极角

    Line(){}
    Line(Point P, Vector v):p(p), v(v){ang = angle(v);}
    //根据参数,求直线上的点
    Point point(double t)
    {
        return p + v*t;
    }
    //直线的移动 方向是向量的左方向,即上方
    Line move(double d)
    {
        return Line(p + Normal(v)*d, v);
    }

    bool operator < (const Line &L) const   //排序用的比较运算符
    {
        return ang < L.ang;
    }
};

直线交点:

设直线分别为P+tv和Q+tw,设向量u=P-Q,交点在第一条直线的参数为t1,第二条直线上的参数为t2,则x和y坐标可以列出一个方程解得:

t1=cross(w,u) / cross(v,w)

t2 = cross(v,u) / cross(v,w)

调用前请确保两条直线P+tv和Q+tw有唯一交点。当且仅当Cross(v,w)非0

如果P,v,Q,w的各个分量均为有理数,则交点坐标也是有理数。在精度要求极高的情况下,可以考虑自定义分数类。

Point GetLineIntersection(Point P, Vector v, Point Q, Vector w)
{
    Vector u;
    double t;

    u = P - Q;
    t = Cross(w, u) / Cross(v, w);
    return P + v * t;
}

点到直线的距离:

点到直线的距离是一个常用的函数。可以用叉积算出。即用平行四边形的面积除以底。

double DistanceToLine(Point P, Point A, Point B)
{
    //如果A==B 
    if(A == B)
         return Length(P-A);
    Vector v1, v2;

    v1 = B - A;
    v2 = P - A;
    return fabs(Cross(v1, v2)) / Length(v1);   //如果不取绝对值,得到的是有向距离
}

点到线段的距离。

设投影点为Q,如果Q在线段AB上,则所求距离就是P点到直线AB的距离;如果Q在射线BA上,则所求距离为PA距离;否则为PB距离,判断Q的位置可以用点积进行(参见上篇 :两个向量的为位置关系)。

double DistanceToSegment(Point P, Point A, Point B)
{
    Vector v1, v2, v3;

    v1 = B - A;
    v2 = P - A;
    v3 = P - B;
    if(A == B)                                 //A点等于B点
        return Length(v2);
    if(dcmp(Dot(v1, v2)) < 0)                  //Q在射线BA上
        return Length(v2);
    else if(dcmp(Dot(v1, v3)) > 0)             //Q在射线AB上
        return Length(v3);
    else
        return fabs(Cross(v1, v2)) / Length(v1);
}

点在直线上的投影。

我们把直线AB写成参数式A+tv(v为向量AB),并且设Q的参数为t0,那么Q=A+t0v,根据PQ垂直与AB,两个向量的点积应该为0。

因此Dot(v,P-(A+t0v))=0.根据分配率,有Dot(v,P-A)-t0*Dot(v,v)=0,这样就可以解出t0,从而得到Q点。

Point GetLineProjection(Point P, Point A, Point B)
{
    Vector v;

    v = B - A;
    return A + v * (Dot(v, P-A) / Dot(v, v));
}

.线段相交判定。给定两条线段,判断是否相交。我们定义规范相交为两线段恰好有一个公共点,且不在任何一条线段的端点。线段规范相交的充要条件是:每条线段的两个端点都在另一条线段的两侧(这里的两侧是指叉积的符号不同)

bool SegmentProperIntersection(Point a1, Point a2, Point b1, Point b2)
{
    double c1, c2, c3, c4;

    c1 = Cross(a2-a1, b1-a1);
    c2 = Cross(a2-a1, b2-a1);
    c3 = Cross(b2-b1, a1-b1);
    c4 = Cross(b2-b1, a2-b1);

    return (dcmp(c1) * dcmp(c2) < 0) && (dcmp(c3) * dcmp(c4) < 0);
}

如果允许在端点处相交,情况就比较复杂了。如果c1和c2都是0,表示两线段共线,这时可能会有部分重叠的情况。如果c1和c2不都是0,则只有一种相交方法,即某个端点在另外一条线段上,为了判断上述情况是否发生,还需要判断一下点是否在一条线段(不包含端点)。

bool OnSegment(Point P, Point a1, Point a2)
{
    return (dcmp(Cross(a1-P, a2-P)) == 0) && (dcmp(Dot(a1-P, a2-P)) < 0);
}

判断一个点是否在一条线段上.首先判断是否是端点,不是的话判断是否在线段上。

bool isPointOnSegment(Point p, Point a1, Point a2)
{
    return (p==a1) || (p== a2) || OnSegment(p, a1, a2);
}

猜你喜欢

转载自blog.csdn.net/baidu_38304645/article/details/83243745
今日推荐