# 点到线段的距离

# 两种情况

首先要判断点PP 到直线ABAB 的投影是否落在线段ABAB 上。

若落在线段内,即A\angle AB\angle B 为锐角,则距离为点到直线ABAB 的距离
垂足落在线段内

若落在线段外,即A\angle AB\angle B 为钝角,则距离为点到线段ABAB 的最近端点的距离
垂足落在线段外

# 如何判断垂足是否落在线段上?

有两种做法。

第一种根据角度判断,若A\angle AB\angle B 均为锐角,则为情况一,否则为情况二。
这里可以利用向量的点积运算。
我们知道

ab=abcosa,b\boldsymbol a \cdot \boldsymbol b=|\boldsymbol a||\boldsymbol b|\cos \langle \boldsymbol a,\boldsymbol b \rangle

那么根据ABAP\overrightarrow{AB}\cdot\overrightarrow{AP}BABP\overrightarrow{BA}\cdot\overrightarrow{BP}的正负即可判断是否锐角。

第二种根据投影长度判断。若投影ADAD 与线段ABAB 的长度之比在[0,1][0,1] 之间,表明垂足落在线段ABAB 内。
这里依然利用点积运算的式子

cosa,b=abab\cos \langle \boldsymbol a,\boldsymbol b \rangle=\frac{\boldsymbol a \cdot \boldsymbol b}{|\boldsymbol a||\boldsymbol b|}

AD 与 AB 之比即为

APcosAAB=ABAPAB2\frac{|AP|\cos A}{|AB|}=\frac{\overrightarrow{AB}\cdot\overrightarrow{AP}}{|AB|^2}

我们检验这个值是否落在[0,1][0,1] 即可。

# 计算距离

对于情况一,点到直线的距离,可以使用三角形面积推算。

PD=AB×APAB|PD|=\frac{|\overrightarrow{AB}\times\overrightarrow{AP}|}{|AB|}

对于情况二,简单对 P 到 AB 两点距离取最小值即可。

dis(P,AB)=min(dis(P,A),dis(P,B))dis(P,AB)=\min(dis(P,A),dis(P,B))

# 附代码

inline double sqr(const double &x){// 平方
    return x*x;
}
struct P{
    double x,y;
    double len()const{// 模长
        return sqrt(x*x+y*y);
    }
    double dot(const P &rv)const{// 点乘
        return x*rv.x+y*rv.y;
    }
    double cross(const P &rv)const{// 叉乘
        return x*rv.y-y*rv.x;
    }
    double dis(const P &rv)const{// 两点距离
        return sqrt(sqr(x-rv.x)+sqr(y-rv.y));
    }
    P operator - (const P &rv)const{// 取向量
        return {x-rv.x,y-rv.y};
    }
};
struct Seg{
    P a,b;
    P vec;
    Seg()=default;
    Seg(const P &a,const P &b):a(a),b(b),vec(b-a){}
    double dis(const P &p){// 点到线段的距离
        double d=(p-a).dot(vec)/sqr(vec.len());
        if(d>0 && d<1)
            return fabs((p-a).cross(vec)/vec.len());
        return std::min(p.dis(a),p.dis(b));
    }
};
更新于 阅读次数