
计算目录
已知,线段的起点和终点,以及点的坐标。求点到线的最短距离

一、原理分析
线段的起点为 (
 ,
, ) ,终点为(
) ,终点为(
 ,
, ),点的坐标为(
),点的坐标为(
 ,
, )
)
求垂点坐标:( ,
, )
)
解答如下:
1.向量相乘为0,即向量垂直
由向量公式可以知道,两个向量相乘为0表示垂直。
假设线段向量为 ,点和垂点向量为
,点和垂点向量为
  *
* =0
=0
=>
2.垂点在线段上
由于 ( ,
, )经过线段因此假设线段为y=ax+b,得到一下
)经过线段因此假设线段为y=ax+b,得到一下
由1和2得到 ===>
将4带入1 ===>
将4和5带入3 ===>
由( 7` ) ===>
由 (6`)和(8`) ===>

===>
将 带入(8`)得到最后结果
带入(8`)得到最后结果
计算好垂点,那么我们就要计算他们于起始点的位置。
判断垂点是否在线段中,如果不在,那么需要计算他距离最近的点。
3.垂点范围
 的范围
 的范围
 
如果:  ==>
 ==>
那么满足  ,表示
,表示 在线段上
在线段上
如果:
那么满足  ,表示
,表示 在线段上
在线段上
  的范围
 的范围
 
如果:  ==>
 ==>
那么满足  ,表示
,表示 在线段上
在线段上
如果:
那么满足  ,表示
,表示 在线段上
在线段上
如果垂点不在线段上,那么点 到定点的距离最短。
到定点的距离最短。
二、代码实现
线段的数据结构
struct LineSegment {
    QPointF startPoint;
    QPointF endPoint;
    LineSegment(QPointF a, QPointF b)
    {
        startPoint = a;
        endPoint = b;
    }
    LineSegment() {}
};计算垂点代码如下:
 /**
     * @brief 获取点到在线段上的线的垂点
     * @param pt 点
     * @param seg 线段
     * @return
     */
QPointF QDrawingPaperView::getPointToLineVerticalpoint(QPointF pt,
                                                       LineSegment seg)
{
    QPointF np;
    double x_se = seg.startPoint.x() - seg.endPoint.x();
    double y_se = seg.startPoint.y() - seg.endPoint.y();
    double x_se_2 = x_se * x_se;
    double y_se_2 = y_se * y_se;
    double x = (x_se_2 * pt.x() + (pt.y() - seg.startPoint.y()) * y_se * x_se +
                seg.startPoint.x() * y_se_2) /
               (x_se_2 + y_se_2);
    double y = pt.y() + x_se * (pt.x() - x) / y_se;
    np.setX(x);
    np.setY(y);
    return np;
}
计算两点之间的距离如下:
// 计算两点之间的距离
double QDrawingPaperView::distance(QPointF startPoint, QPointF endPoint)
{
    double dis = 0;
    double width = startPoint.x() - endPoint.x();
    double height = startPoint.y() - endPoint.y();
    dis = qSqrt(width * width + height * height);
    return dis;
}判断垂点是否在线段上
bool QDrawingPaperView::verticalPointIsOnLine(QPoint np, LineSegment seg)
{
    bool isOnX = false;  // 垂点的x坐标是否在线段上
    bool isOnY = false;  // 垂点的y坐标是否在线段上
    if (seg.startPoint.x() > seg.endPoint.x()) {
        if (np.x() < seg.startPoint.x() && np.x() > seg.endPoint.x()) {
            isOnX = true;
        }
    } else {
        if (np.x() < seg.endPoint.x() && np.x() > seg.startPoint.x()) {
            isOnX = true;
        }
    }
    if (seg.startPoint.y() > seg.endPoint.y()) {
        if (np.y() < seg.startPoint.y() && np.y() > seg.endPoint.y()) {
            isOnY = true;
        }
    } else {
        if (np.y() < seg.endPoint.y() && np.y() > seg.startPoint.y()) {
            isOnY = true;
        }
    }
    
    if (isOnX && isOnY) {
        return true;
    }
    return false;
}点计算距离线段距离
double QDrawingPaperView::pointToSegmentDis(QPointF pt, LineSegment seg)
{
    // 1. 获取垂点
    QPointF Verticalpoint;
    Verticalpoint = getPointToLineVerticalpoint(pt, seg);
    // 2. 判断垂点是否在线段上
    bool isOnSeg = verticalPointIsOnLine(Verticalpoint, seg);
    if (isOnSeg) {
        // 如果在线段上那么垂点到点的距离最短
        return distance(pt, Verticalpoint);
    } else {
        // 如果不在点段上那么到两点的距离最短。
        double startDistance = distance(pt, seg.startPoint);
        double endDistance = distance(pt, seg.endPoint);
        return startDistance < endDistance ? startDistance : endDistance;
    }
}
git传送门










