Language agnostic 点与线段之间的最短距离

Language agnostic 点与线段之间的最短距离,language-agnostic,geometry,distance,line-segment,Language Agnostic,Geometry,Distance,Line Segment,我需要一个基本函数来找到点和线段之间的最短距离。可以随意用任何语言编写解决方案;我可以将其转换为我正在使用的Javascript 编辑:我的线段由两个端点定义。所以我的线段AB由两点A x1,y1和B x2,y2定义。我想找出这条线段和点cx3,y3之间的距离。我的几何技能已经过时了,所以我所看到的例子令人困惑,我很抱歉地承认。我假设你想找到点和线段之间的最短距离;为此,需要找到与穿过点的线段lineB垂直的直线A,确定该直线A和穿过线段lineB的直线之间的交点;如果该点位于线段的两个点之间,

我需要一个基本函数来找到点和线段之间的最短距离。可以随意用任何语言编写解决方案;我可以将其转换为我正在使用的Javascript


编辑:我的线段由两个端点定义。所以我的线段AB由两点A x1,y1和B x2,y2定义。我想找出这条线段和点cx3,y3之间的距离。我的几何技能已经过时了,所以我所看到的例子令人困惑,我很抱歉地承认。

我假设你想找到点和线段之间的最短距离;为此,需要找到与穿过点的线段lineB垂直的直线A,确定该直线A和穿过线段lineB的直线之间的交点;如果该点位于线段的两个点之间,则距离是该点与刚找到的点之间的距离,该点是直线A和直线B的交点;如果该点不在线段的两点之间,则需要获得该点与线段两端较近点之间的距离;这可以通过采用平方距离来轻松实现,以避免直线段的点和两点之间的平方根;无论哪一个更接近,取这个的平方根。

嘿,我昨天刚刚写了这个。它在ActionScript3.0中,基本上是Javascript,尽管您可能没有相同的Point类

//st = start of line segment
//b = the line segment (as in: st + b = end of line segment)
//pt = point to test
//Returns distance from point to line segment.  
//Note: nearest point on the segment to the test point is right there if we ever need it
public static function linePointDist( st:Point, b:Point, pt:Point ):Number
{
    var nearestPt:Point; //closest point on seqment to pt

    var keyDot:Number = dot( b, pt.subtract( st ) ); //key dot product
    var bLenSq:Number = dot( b, b ); //Segment length squared

    if( keyDot <= 0 )  //pt is "behind" st, use st
    {
        nearestPt = st  
    }
    else if( keyDot >= bLenSq ) //pt is "past" end of segment, use end (notice we are saving twin sqrts here cuz)
    {
        nearestPt = st.add(b);
    }
    else //pt is inside segment, reuse keyDot and bLenSq to get percent of seqment to move in to find closest point
    {
        var keyDotToPctOfB:Number = keyDot/bLenSq; //REM dot product comes squared
        var partOfB:Point = new Point( b.x * keyDotToPctOfB, b.y * keyDotToPctOfB );
        nearestPt = st.add(partOfB);
    }

    var dist:Number = (pt.subtract(nearestPt)).length;

    return dist;
}

此外,这里有一个关于这个问题的完整且可读的讨论:

这是我最后编写的代码。这段代码假设一个点是以{x:5,y:7}的形式定义的。请注意,这不是绝对最有效的方法,但它是我能想到的最简单、最容易理解的代码

// a, b, and c in the code below are all points

function distance(a, b)
{
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return Math.sqrt(dx*dx + dy*dy);
}

function Segment(a, b)
{
    var ab = {
        x: b.x - a.x,
        y: b.y - a.y
    };
    var length = distance(a, b);

    function cross(c) {
        return ab.x * (c.y-a.y) - ab.y * (c.x-a.x);
    };

    this.distanceFrom = function(c) {
        return Math.min(distance(a,c),
                        distance(b,c),
                        Math.abs(cross(c) / length));
    };
}

忍不住用python编写代码:

来自math import sqrt,fabs def pdisa,b,c: t=b[0]-a[0],b[1]-a[1]向量ab dd=sqrtt[0]**2+t[1]**2 ab长度 t=t[0]/dd,t[1]/dd ab的单位向量 n=-t[1],t[0]到ab的法向单位向量 ac=c[0]-a[0],c[1]-a[1]矢量ac 返回fabsac[0]*n[0]+ac[1]*n[1]ac到n的最小距离投影 打印pdis1,1,2,2,0示例答案为1.414 fortran也一样:

实函数pdisa,b,c 实数,维数0:1,意图::a,b,c 实数,维数0:1::t,n,ac 雷亚尔::dd t=b-a!向量ab dd=sqrtt0**2+t1**2!ab长度 t=t/dd!ab的单位向量 n=/-t1,t0/!ab的法向单位向量 ac=c-a!矢量交流 pdis=absac0*n0+ac1*n1!ac到n的最小距离投影 终端功能PDI 程序测试 打印*,pdis/1.0,1.0/,/2.0,2.0/,/2.0,0.0/!示例答案是1.414 结束程序测试 伊莱,你确定的密码不正确。靠近线段所在直线但远离线段一端的点在线段附近会被错误判断。更新:提及的错误答案不再是可接受的答案

这里有一些正确的代码,在C++中。它假定了一个2D向量类vec2{float x,y;},本质上,该类包含用于加法、减法、缩放等的运算符,以及一个距离和点积函数,即x1 x2+y1 y2

浮动最小距离vec2 v,vec2 w,vec2 p{ //返回线段vw和点p之间的最小距离 const float l2=长度_squaredv,w;//即| w-v | ^2-避免sqrt 如果l2==0.0,则返回distancep,v;//v==w大小写 /考虑扩展该段的行,参数化为V+T W-V。 //我们找到点p在直线上的投影。 //它落在t=[p-v.w-v]/| w-v | ^2的位置 //我们从[0,1]夹紧t以处理vw段外的点。 常量浮点t=max0,min1,dotp-v,w-v/l2; const vec2 projection=v+t*w-v;//投影落在段上 返回距离p,投影; } 编辑:我需要一个Javascript实现,所以在这里,它没有依赖项或注释,但它是上面的一个直接端口。点表示为具有x和y属性的对象

函数sqrx{返回x*x} 函数dist2v,w{返回sqrv.x-w.x+sqrv.y-w.y} 函数距离分段平方dp,v,w{ var l2=距离2V,w; 如果l2==0,则返回dist2p,v; var t=p.x-v.x*w.x-v.x+p.y-v.y*w.y-v.y/l2; t=Math.max0,Math.min1,t; 返回dist2p,{x:v.x+t*w.x-v.x, y:v.y+t*w.y-v.y}; } 函数distToSegmentp,v,w{返回Math.sqrtdistToSegmentSquaredp,v,w;} 编辑2:我需要一个Java版本,但更重要的是,我需要3d版本而不是2d版本

浮点数到浮点数的平方浮点数px、浮点数py、浮点数pz、浮点数lx1、浮点数ly1、浮点数lz1、浮点数lx2、浮点数ly2、浮点数lz2{ 佛罗里达州 oat line_dist=dist_sqlx1,ly1,lz1,lx2,ly2,lz2; 如果line_dist==0,则返回dist_sqpx、py、pz、lx1、ly1、lz1; 浮点数t=px-lx1*lx2-lx1+py-ly1*ly2-ly1+pz-lz1*lz2-lz1/直线距离; t=约束,0,1; 返回区sqpx,py,pz,lx1+t*lx2-lx1,ly1+t*ly2-ly1,lz1+t*lz2-lz1; }
这是一个针对有限线段的实现,而不是像这里的大多数其他函数一样的无限线段,这就是为什么我做了这个

Python:

def distx1、y1、x2、y2、x3、y3:x3、y3是点 px=x2-x1 py=y2-y1 标准=px*px+py*py u=x3-x1*px+y3-y1*py/norm 如果u>1: u=1 elif u<0: u=0 x=x1+u*px y=y1+u*py dx=x-x3 dy=y-y3 注:如果实际距离无关紧要, 如果你只想比较一下这个函数 返回此函数的其他结果,您可以 可以只返回平方距离 i、 e.移除sqrt以获得一点性能 dist=dx*dx+dy*dy**.5 返回区 AS3:

公共静态功能段DistTopointSega:Point、segB:Point、p:Point:Number { 变量p2:点=新点segB.x-segA.x,segB.y-segA.y; 变量:Number=p2.x*p2.x+p2.y*p2.y; 变量u:Number=p.x-segA.x*p2.x+p.y-segA.y*p2.y/某物; 如果u>1 u=1; 否则,如果u<0 u=0; 变量x:Number=segA.x+u*p2.x; 变量y:Number=segA.y+u*p2.y; 变量dx:Number=x-p.x; 变量dy:Number=y-p.y; var dist:Number=Math.sqrtdx*dx+dy*dy; 返回距离; } 爪哇

专用双最短距离浮点x1、浮点y1、浮点x2、浮点y2、浮点x3、浮点y3 { 浮点数px=x2-x1; 浮球py=y2-y1; 浮动温度=px*px+py*py; 浮子u=x3-x1*px+y3-y1*py/温度; ifu>1{ u=1; } 伊夫因数学 它使用线段的参数化描述,并将点投影到线段定义的直线上。当线段中的参数从0变为1时,如果投影超出此界限,则我们计算到相应点的距离,而不是垂直于线段的直线

清除[Global`*]; 距离[{起点},终点},终点]:= 模块[{param}, param=pt-start.end-start/Norm[end-start]^2;*参数。参数为。 这里是向量积* 哪个[ 参数<0,欧几里德距离[start,pt],*如果超出边界* 参数>1,欧几里德距离[结束,pt], 真,欧几里德距离[pt,开始+参数结束-开始]*正常距离* ] ]; 绘图结果:

Plot3D[distance[{{0, 0}, {1, 0}}, {xp, yp}], {xp, -1, 2}, {yp, -1, 2}]

绘制比截止距离更近的点:

等高线图:

在我自己的问题线索中,当我找到一个答案时,我被要求在这里放一个C答案:所以它在这里,修改自:

//计算点积AB.BC 私有双点产品双[]点A、双[]点B、双[]点C { double[]AB=新的double[2]; double[]BC=新的double[2]; AB[0]=点B[0]-点A[0]; AB[1]=点B[1]-点A[1]; BC[0]=点C[0]-点B[0]; BC[1]=点C[1]-点B[1]; 双点=AB[0]*BC[0]+AB[1]*BC[1]; 返回点; } //计算叉积AB x AC 私有双交叉产品双[]点A、双[]点B、双[]点C { double[]AB=新的double[2]; double[]AC=新的double[2]; AB[0]=点B[0]-点A[0]; AB[1]=点B[1]-点A[1]; AC[0]=pointC[0]-pointA[0]; AC[1]=点C[1]-点A[1]; 双交叉=AB[0]*AC[1]-AB[1]*AC[0]; 回程交叉; } //计算从A到B的距离 双距离双[]点A,双[]点B { 双d1=点A[0]-点B[0]; 双d2=点A[1]-点B[1]; 返回Math.Sqrtd1*d1+d2*d2; } //计算从AB到C的距离 //如果isSegment为true,则AB是一个段,而不是一条线。 double LineToPointDistance2Ddouble[]点A、double[]点B、double[]点C、, 布尔伊索节 { 双距离=交叉产品点A、点B、点C/距离点A、点B; 如果是分段 { double dot1=DotProductpointA,pointB,pointC; 如果dot1>0 返回距离点B,点C; double dot2=DotProductpointB,pointA,pointC; 如果dot2>0 返回距离点A,点C; } 返回Math.Absdist; }
我不是要回答而是要问问题,所以我希望我不会因为某些原因而获得百万的反对票,而只是在构建批评家。我只是想并且被鼓励分享其他人的想法,因为这条线索中的解决方案要么是使用某种外来语言Fortran、Mathematica,要么被某人标记为有缺陷。唯一有用的一个是Grumdrig f或者我是用C++编写的,没有人标记它有错误。但是它缺少被调用的方法点。 > x、 y是你的目标点,x1,y1到x2,y2是你的线段

更新:修复注释中的0长度行问题

函数pDistancex,y,x1,y1,x2,y2{ var A=x-x1; var B=y-y1; var C=x2-x1; var D=y2-y1; var dot=A*C+B*D; 变量len_sq=C*C+D*D; var参数=-1; 如果len_sq!=0//在0长度线的情况下 参数=点/透镜平方; 变量xx,yy; 如果参数<0{ xx=x1; yy=y1; } 否则,如果参数>1{ xx=x2; yy=y2; } 否则{ xx=x1+param*C; yy=y1+参数*D; } var dx=x-xx; var-dy=y-yy; 返回Math.sqrtdx*dx+dy*dy; }
Matlab代码,如果调用无参数函数,则具有内置自检功能:

function r = distPointToLineSegment( xy0, xy1, xyP )
% r = distPointToLineSegment( xy0, xy1, xyP )

if( nargin < 3 )
    selfTest();
    r=0;
else
    vx = xy0(1)-xyP(1);
    vy = xy0(2)-xyP(2);
    ux = xy1(1)-xy0(1);
    uy = xy1(2)-xy0(2);
    lenSqr= (ux*ux+uy*uy);
    detP= -vx*ux + -vy*uy;

    if( detP < 0 )
        r = norm(xy0-xyP,2);
    elseif( detP > lenSqr )
        r = norm(xy1-xyP,2);
    else
        r = abs(ux*vy-uy*vx)/sqrt(lenSqr);
    end
end


    function selfTest()
        %#ok<*NASGU>
        disp(['invalid args, distPointToLineSegment running (recursive)  self-test...']);

        ptA = [1;1]; ptB = [-1;-1];
        ptC = [1/2;1/2];  % on the line
        ptD = [-2;-1.5];  % too far from line segment
        ptE = [1/2;0];    % should be same as perpendicular distance to line
        ptF = [1.5;1.5];      % along the A-B but outside of the segment

        distCtoAB = distPointToLineSegment(ptA,ptB,ptC)
        distDtoAB = distPointToLineSegment(ptA,ptB,ptD)
        distEtoAB = distPointToLineSegment(ptA,ptB,ptE)
        distFtoAB = distPointToLineSegment(ptA,ptB,ptF)
        figure(1); clf;
        circle = @(x, y, r, c) rectangle('Position', [x-r, y-r, 2*r, 2*r], ...
            'Curvature', [1 1], 'EdgeColor', c);
        plot([ptA(1) ptB(1)],[ptA(2) ptB(2)],'r-x'); hold on;
        plot(ptC(1),ptC(2),'b+'); circle(ptC(1),ptC(2), 0.5e-1, 'b');
        plot(ptD(1),ptD(2),'g+'); circle(ptD(1),ptD(2), distDtoAB, 'g');
        plot(ptE(1),ptE(2),'k+'); circle(ptE(1),ptE(2), distEtoAB, 'k');
        plot(ptF(1),ptF(2),'m+'); circle(ptF(1),ptF(2), distFtoAB, 'm');
        hold off;
        axis([-3 3 -3 3]); axis equal;
    end

end

请参见以下网站中的Matlab几何工具箱:

ctrl+f并键入segment以查找与线段相关的函数。功能segment_point_dist_2d.m和segment_point_dist_3d.m是您所需要的


<>几何代码可在C版本和C++版本中使用,FORTRAN77版本和FORTRAN版本和Matlab版本。

,现在我的解决方案也是…… Javascript

它非常快,因为我试图避免任何Math.pow函数

如你所见,在函数的末尾,我有直线的距离

代码来自库


下面是对格鲁德利格解决方案的更完整的解释。此版本还返回最近的点本身

#include "stdio.h"
#include "math.h"

class Vec2
{
public:
    float _x;
    float _y;

    Vec2()
    {
        _x = 0;
        _y = 0;
    }

    Vec2( const float x, const float y )
    {
        _x = x;
        _y = y;
    }

    Vec2 operator+( const Vec2 &v ) const
    {
        return Vec2( this->_x + v._x, this->_y + v._y );
    }

    Vec2 operator-( const Vec2 &v ) const
    {
        return Vec2( this->_x - v._x, this->_y - v._y );
    }

    Vec2 operator*( const float f ) const
    {
        return Vec2( this->_x * f, this->_y * f );
    }

    float DistanceToSquared( const Vec2 p ) const
    {
        const float dX = p._x - this->_x;
        const float dY = p._y - this->_y;

        return dX * dX + dY * dY;
    }

    float DistanceTo( const Vec2 p ) const
    {
        return sqrt( this->DistanceToSquared( p ) );
    }

    float DotProduct( const Vec2 p ) const
    {
        return this->_x * p._x + this->_y * p._y;
    }
};

// return minimum distance between line segment vw and point p, and the closest point on the line segment, q
float DistanceFromLineSegmentToPoint( const Vec2 v, const Vec2 w, const Vec2 p, Vec2 * const q )
{
    const float distSq = v.DistanceToSquared( w ); // i.e. |w-v|^2 ... avoid a sqrt
    if ( distSq == 0.0 )
    {
        // v == w case
        (*q) = v;

        return v.DistanceTo( p );
    }

    // consider the line extending the segment, parameterized as v + t (w - v)
    // we find projection of point p onto the line
    // it falls where t = [(p-v) . (w-v)] / |w-v|^2

    const float t = ( p - v ).DotProduct( w - v ) / distSq;
    if ( t < 0.0 )
    {
        // beyond the v end of the segment
        (*q) = v;

        return v.DistanceTo( p );
    }
    else if ( t > 1.0 )
    {
        // beyond the w end of the segment
        (*q) = w;

        return w.DistanceTo( p );
    }

    // projection falls on the segment
    const Vec2 projection = v + ( ( w - v ) * t );

    (*q) = projection;

    return p.DistanceTo( projection );
}

float DistanceFromLineSegmentToPoint( float segmentX1, float segmentY1, float segmentX2, float segmentY2, float pX, float pY, float *qX, float *qY )
{
    Vec2 q;

    float distance = DistanceFromLineSegmentToPoint( Vec2( segmentX1, segmentY1 ), Vec2( segmentX2, segmentY2 ), Vec2( pX, pY ), &q );

    (*qX) = q._x;
    (*qY) = q._y;

    return distance;
}

void TestDistanceFromLineSegmentToPoint( float segmentX1, float segmentY1, float segmentX2, float segmentY2, float pX, float pY )
{
    float qX;
    float qY;
    float d = DistanceFromLineSegmentToPoint( segmentX1, segmentY1, segmentX2, segmentY2, pX, pY, &qX, &qY );
    printf( "line segment = ( ( %f, %f ), ( %f, %f ) ), p = ( %f, %f ), distance = %f, q = ( %f, %f )\n",
            segmentX1, segmentY1, segmentX2, segmentY2, pX, pY, d, qX, qY );
}

void TestDistanceFromLineSegmentToPoint()
{
    TestDistanceFromLineSegmentToPoint( 0, 0, 1, 1, 1, 0 );
    TestDistanceFromLineSegmentToPoint( 0, 0, 20, 10, 5, 4 );
    TestDistanceFromLineSegmentToPoint( 0, 0, 20, 10, 30, 15 );
    TestDistanceFromLineSegmentToPoint( 0, 0, 20, 10, -30, 15 );
    TestDistanceFromLineSegmentToPoint( 0, 0, 10, 0, 5, 1 );
    TestDistanceFromLineSegmentToPoint( 0, 0, 0, 10, 1, 5 );
}

对于任何感兴趣的人来说,这里是Joshua的Javascript代码到Objective-C的一个简单转换:

-doubledistanceToPoint:CGPointp fromLineSegmentBeween:CGPointl1和:CGPointl2 { 双A=p.x-l1.x; 双B=p.y-l1.y; 双C=l2.x-l1.x; 双D=l2.y-l1.y; 双点=A*C+B*D; 双透镜sq=C*C+D*D; 双参数=点/透镜平方; 双xx,yy; 如果参数<0 | | l1.x==l2.x&&l1.y==l2.y{ xx=l1.x; yy=l1.y; } 否则,如果参数>1{ xx=l2.x; yy=l2.y; } 否则{ xx=l1.x+参数*C; yy=l1.y+param*D; } 双dx=p.x-xx; 双dy=p.y-yy; 返回sqrtfdx*dx+dy*dy; } 我需要这个解决方案来与MKMapPoint一起工作,所以我将分享它,以防其他人需要它。只需进行一些小的更改,这将返回以米为单位的距离:

-doubledistanceToPoint:MKMapPointp fromLineSegmentBeween:MKMapPointl1和:MKMapPointl2 { 双A=p.x-l1.x; 双B=p.y-l1.y; 双C=l2.x-l1.x; 双D=l2.y-l1.y; 双点=A*C+B*D; 双透镜sq=C*C+D*D; 双参数=点/透镜平方; 双xx,yy; 如果参数<0 | | l1.x==l2.x&&l1.y==l2.y{ xx=l1.x; yy=l1.y; } 否则,如果参数>1{ xx=l2.x; yy=l2.y; } 否则{ xx=l1.x+参数*C; yy=l1.y+param*D; } 返回映射点SP之间的mkmeters,MKMapPointMakexx,yy; }
考虑对上面格鲁德里格回答的修改。很多时候,您会发现浮点不精确会导致问题。我在下面的版本中使用了double,但是您可以很容易地更改为float。重要的是,它使用ε来处理slop。此外,您可能会多次想知道十字路口发生在哪里,或者它是否发生过。如果返回的t<0.0或>1.0,则不会发生碰撞。但是,即使没有发生碰撞,很多时候你也会想知道线段上距离P最近的点在哪里,因此我使用qx和qy来返回这个位置

double PointSegmentDistanceSquared( double px, double py,
                                    double p1x, double p1y,
                                    double p2x, double p2y,
                                    double& t,
                                    double& qx, double& qy)
{
    static const double kMinSegmentLenSquared = 0.00000001;  // adjust to suit.  If you use float, you'll probably want something like 0.000001f
    static const double kEpsilon = 1.0E-14;  // adjust to suit.  If you use floats, you'll probably want something like 1E-7f
    double dx = p2x - p1x;
    double dy = p2y - p1y;
    double dp1x = px - p1x;
    double dp1y = py - p1y;
    const double segLenSquared = (dx * dx) + (dy * dy);
    if (segLenSquared >= -kMinSegmentLenSquared && segLenSquared <= kMinSegmentLenSquared)
    {
        // segment is a point.
        qx = p1x;
        qy = p1y;
        t = 0.0;
        return ((dp1x * dp1x) + (dp1y * dp1y));
    }
    else
    {
        // Project a line from p to the segment [p1,p2].  By considering the line
        // extending the segment, parameterized as p1 + (t * (p2 - p1)),
        // we find projection of point p onto the line. 
        // It falls where t = [(p - p1) . (p2 - p1)] / |p2 - p1|^2
        t = ((dp1x * dx) + (dp1y * dy)) / segLenSquared;
        if (t < kEpsilon)
        {
            // intersects at or to the "left" of first segment vertex (p1x, p1y).  If t is approximately 0.0, then
            // intersection is at p1.  If t is less than that, then there is no intersection (i.e. p is not within
            // the 'bounds' of the segment)
            if (t > -kEpsilon)
            {
                // intersects at 1st segment vertex
                t = 0.0;
            }
            // set our 'intersection' point to p1.
            qx = p1x;
            qy = p1y;
            // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if
            // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)).
        }
        else if (t > (1.0 - kEpsilon))
        {
            // intersects at or to the "right" of second segment vertex (p2x, p2y).  If t is approximately 1.0, then
            // intersection is at p2.  If t is greater than that, then there is no intersection (i.e. p is not within
            // the 'bounds' of the segment)
            if (t < (1.0 + kEpsilon))
            {
                // intersects at 2nd segment vertex
                t = 1.0;
            }
            // set our 'intersection' point to p2.
            qx = p2x;
            qy = p2y;
            // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if
            // we were doing PointLineDistanceSquared, then qx would be (p1x + (t * dx)) and qy would be (p1y + (t * dy)).
        }
        else
        {
            // The projection of the point to the point on the segment that is perpendicular succeeded and the point
            // is 'within' the bounds of the segment.  Set the intersection point as that projected point.
            qx = p1x + (t * dx);
            qy = p1y + (t * dy);
        }
        // return the squared distance from p to the intersection point.  Note that we return the squared distance
        // as an optimization because many times you just need to compare relative distances and the squared values
        // works fine for that.  If you want the ACTUAL distance, just take the square root of this value.
        double dpqx = px - qx;
        double dpqy = py - qy;
        return ((dpqx * dpqx) + (dpqy * dpqy));
    }
}

对于懒惰的人,这里是我上面@Grumdrig解决方案的Objective-C端口:

CGFloat sqr(CGFloat x) { return x*x; }
CGFloat dist2(CGPoint v, CGPoint w) { return sqr(v.x - w.x) + sqr(v.y - w.y); }
CGFloat distanceToSegmentSquared(CGPoint p, CGPoint v, CGPoint w)
{
    CGFloat l2 = dist2(v, w);
    if (l2 == 0.0f) return dist2(p, v);

    CGFloat t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
    if (t < 0.0f) return dist2(p, v);
    if (t > 1.0f) return dist2(p, w);
    return dist2(p, CGPointMake(v.x + t * (w.x - v.x), v.y + t * (w.y - v.y)));
}
CGFloat distanceToSegment(CGPoint point, CGPoint segmentPointV, CGPoint segmentPointW)
{
    return sqrtf(distanceToSegmentSquared(point, segmentPointV, segmentPointW));
}

基于Joshua Javascript的自动热键版本:

plDist(x, y, x1, y1, x2, y2) {
    A:= x - x1
    B:= y - y1
    C:= x2 - x1
    D:= y2 - y1

    dot:= A*C + B*D
    sqLen:= C*C + D*D
    param:= dot / sqLen

    if (param < 0 || ((x1 = x2) && (y1 = y2))) {
        xx:= x1
        yy:= y1
    } else if (param > 1) {
        xx:= x2
        yy:= y2
    } else {
        xx:= x1 + param*C
        yy:= y1 + param*D
    }

    dx:= x - xx
    dy:= y - yy

    return sqrt(dx*dx + dy*dy)
}
用t-sql编码

点是@px,@py,线段从@ax,@ay到@bx,@by

create function fn_sqr (@NumberToSquare decimal(18,10)) 
returns decimal(18,10)
as 
begin
    declare @Result decimal(18,10)
    set @Result = @NumberToSquare * @NumberToSquare
    return @Result
end
go

create function fn_Distance(@ax decimal (18,10) , @ay decimal (18,10), @bx decimal(18,10),  @by decimal(18,10)) 
returns decimal(18,10)
as
begin
    declare @Result decimal(18,10)
    set @Result = (select dbo.fn_sqr(@ax - @bx) + dbo.fn_sqr(@ay - @by) )
    return @Result
end
go

create function fn_DistanceToSegmentSquared(@px decimal(18,10), @py decimal(18,10), @ax decimal(18,10), @ay decimal(18,10), @bx decimal(18,10), @by decimal(18,10)) 
returns decimal(18,10)
as 
begin
    declare @l2 decimal(18,10)
    set @l2 = (select dbo.fn_Distance(@ax, @ay, @bx, @by))
    if @l2 = 0
        return dbo.fn_Distance(@px, @py, @ax, @ay)
    declare @t decimal(18,10)
    set @t = ((@px - @ax) * (@bx - @ax) + (@py - @ay) * (@by - @ay)) / @l2
    if (@t < 0) 
        return dbo.fn_Distance(@px, @py, @ax, @ay);
    if (@t > 1) 
        return dbo.fn_Distance(@px, @py, @bx, @by);
    return dbo.fn_Distance(@px, @py,  @ax + @t * (@bx - @ax),  @ay + @t * (@by - @ay))
end
go

create function fn_DistanceToSegment(@px decimal(18,10), @py decimal(18,10), @ax decimal(18,10), @ay decimal(18,10), @bx decimal(18,10), @by decimal(18,10)) 
returns decimal(18,10)
as 
begin
    return sqrt(dbo.fn_DistanceToSegmentSquared(@px, @py , @ax , @ay , @bx , @by ))
end
go

--example execution for distance from a point at (6,1) to line segment that runs from (4,2) to (2,1)
select dbo.fn_DistanceToSegment(6, 1, 4, 2, 2, 1) 
--result = 2.2360679775

--example execution for distance from a point at (-3,-2) to line segment that runs from (0,-2) to (-2,1)
select dbo.fn_DistanceToSegment(-3, -2, 0, -2, -2, 1) 
--result = 2.4961508830

--example execution for distance from a point at (0,-2) to line segment that runs from (0,-2) to (-2,1)
select dbo.fn_DistanceToSegment(0,-2, 0, -2, -2, 1) 
--result = 0.0000000000

看起来StackOverflow上几乎所有人都给出了23个答案,这是我对C的贡献。这主要是基于M.Katz的答案,而M.Katz的答案又是基于Grumdrig的答案

   public struct MyVector
   {
      private readonly double _x, _y;


      // Constructor
      public MyVector(double x, double y)
      {
         _x = x;
         _y = y;
      }


      // Distance from this point to another point, squared
      private double DistanceSquared(MyVector otherPoint)
      {
         double dx = otherPoint._x - this._x;
         double dy = otherPoint._y - this._y;
         return dx * dx + dy * dy;
      }


      // Find the distance from this point to a line segment (which is not the same as from this 
      //  point to anywhere on an infinite line). Also returns the closest point.
      public double DistanceToLineSegment(MyVector lineSegmentPoint1, MyVector lineSegmentPoint2,
                                          out MyVector closestPoint)
      {
         return Math.Sqrt(DistanceToLineSegmentSquared(lineSegmentPoint1, lineSegmentPoint2, 
                          out closestPoint));
      }


      // Same as above, but avoid using Sqrt(), saves a new nanoseconds in cases where you only want 
      //  to compare several distances to find the smallest or largest, but don't need the distance
      public double DistanceToLineSegmentSquared(MyVector lineSegmentPoint1, 
                                              MyVector lineSegmentPoint2, out MyVector closestPoint)
      {
         // Compute length of line segment (squared) and handle special case of coincident points
         double segmentLengthSquared = lineSegmentPoint1.DistanceSquared(lineSegmentPoint2);
         if (segmentLengthSquared < 1E-7f)  // Arbitrary "close enough for government work" value
         {
            closestPoint = lineSegmentPoint1;
            return this.DistanceSquared(closestPoint);
         }

         // Use the magic formula to compute the "projection" of this point on the infinite line
         MyVector lineSegment = lineSegmentPoint2 - lineSegmentPoint1;
         double t = (this - lineSegmentPoint1).DotProduct(lineSegment) / segmentLengthSquared;

         // Handle the two cases where the projection is not on the line segment, and the case where 
         //  the projection is on the segment
         if (t <= 0)
            closestPoint = lineSegmentPoint1;
         else if (t >= 1)
            closestPoint = lineSegmentPoint2;
         else 
            closestPoint = lineSegmentPoint1 + (lineSegment * t);
         return this.DistanceSquared(closestPoint);
      }


      public double DotProduct(MyVector otherVector)
      {
         return this._x * otherVector._x + this._y * otherVector._y;
      }

      public static MyVector operator +(MyVector leftVector, MyVector rightVector)
      {
         return new MyVector(leftVector._x + rightVector._x, leftVector._y + rightVector._y);
      }

      public static MyVector operator -(MyVector leftVector, MyVector rightVector)
      {
         return new MyVector(leftVector._x - rightVector._x, leftVector._y - rightVector._y);
      }

      public static MyVector operator *(MyVector aVector, double aScalar)
      {
         return new MyVector(aVector._x * aScalar, aVector._y * aScalar);
      }

      // Added using ReSharper due to CodeAnalysis nagging

      public bool Equals(MyVector other)
      {
         return _x.Equals(other._x) && _y.Equals(other._y);
      }

      public override bool Equals(object obj)
      {
         if (ReferenceEquals(null, obj)) return false;
         return obj is MyVector && Equals((MyVector) obj);
      }

      public override int GetHashCode()
      {
         unchecked
         {
            return (_x.GetHashCode()*397) ^ _y.GetHashCode();
         }
      }

      public static bool operator ==(MyVector left, MyVector right)
      {
         return left.Equals(right);
      }

      public static bool operator !=(MyVector left, MyVector right)
      {
         return !left.Equals(right);
      }
   }
这里有一个小测试程序

   public static class JustTesting
   {
      public static void Main()
      {
         Stopwatch stopwatch = new Stopwatch();
         stopwatch.Start();

         for (int i = 0; i < 10000000; i++)
         {
            TestIt(1, 0, 0, 0, 1, 1, 0.70710678118654757);
            TestIt(5, 4, 0, 0, 20, 10, 1.3416407864998738);
            TestIt(30, 15, 0, 0, 20, 10, 11.180339887498949);
            TestIt(-30, 15, 0, 0, 20, 10, 33.541019662496844);
            TestIt(5, 1, 0, 0, 10, 0, 1.0);
            TestIt(1, 5, 0, 0, 0, 10, 1.0);
         }

         stopwatch.Stop();
         TimeSpan timeSpan = stopwatch.Elapsed;
      }


      private static void TestIt(float aPointX, float aPointY, 
                                 float lineSegmentPoint1X, float lineSegmentPoint1Y, 
                                 float lineSegmentPoint2X, float lineSegmentPoint2Y, 
                                 double expectedAnswer)
      {
         // Katz
         double d1 = DistanceFromPointToLineSegment(new MyVector(aPointX, aPointY), 
                                              new MyVector(lineSegmentPoint1X, lineSegmentPoint1Y), 
                                              new MyVector(lineSegmentPoint2X, lineSegmentPoint2Y));
         Debug.Assert(d1 == expectedAnswer);

         /*
         // Katz using squared distance
         double d2 = DistanceFromPointToLineSegmentSquared(new MyVector(aPointX, aPointY), 
                                              new MyVector(lineSegmentPoint1X, lineSegmentPoint1Y), 
                                              new MyVector(lineSegmentPoint2X, lineSegmentPoint2Y));
         Debug.Assert(Math.Abs(d2 - expectedAnswer * expectedAnswer) < 1E-7f);
          */

         /*
         // Matti (optimized)
         double d3 = FloatVector.DistanceToLineSegment(new PointF(aPointX, aPointY), 
                                                new PointF(lineSegmentPoint1X, lineSegmentPoint1Y), 
                                                new PointF(lineSegmentPoint2X, lineSegmentPoint2Y));
         Debug.Assert(Math.Abs(d3 - expectedAnswer) < 1E-7f);
          */
      }

      private static double DistanceFromPointToLineSegment(MyVector aPoint, 
                                             MyVector lineSegmentPoint1, MyVector lineSegmentPoint2)
      {
         MyVector closestPoint;  // Not used
         return aPoint.DistanceToLineSegment(lineSegmentPoint1, lineSegmentPoint2, 
                                             out closestPoint);
      }

      private static double DistanceFromPointToLineSegmentSquared(MyVector aPoint, 
                                             MyVector lineSegmentPoint1, MyVector lineSegmentPoint2)
      {
         MyVector closestPoint;  // Not used
         return aPoint.DistanceToLineSegmentSquared(lineSegmentPoint1, lineSegmentPoint2, 
                                                    out closestPoint);
      }
   }
正如您所看到的,我试图衡量使用避免Sqrt方法的版本与正常版本之间的差异。我的测试表明,您可能可以节省大约2.5%,但我甚至不能确定这一点-不同测试运行中的变化是相同数量级的。我还试着测量Matti发布的版本,加上一个明显的优化,该版本似乎比基于Katz/Grumdrig代码的版本慢4%


编辑:顺便说一句,我也尝试过使用叉积和Sqrt测量到无限直线(而不是线段)的距离的方法,速度大约快32%。

公认的答案不起作用 e、 g.0,0和-10,2,10,2之间的距离应为2

下面是有效的代码:

   def dist2line2(x,y,line):
     x1,y1,x2,y2=line
     vx = x1 - x
     vy = y1 - y
     ux = x2-x1
     uy = y2-y1
     length = ux * ux + uy * uy
     det = (-vx * ux) + (-vy * uy) #//if this is < 0 or > length then its outside the line segment
     if det < 0:
       return (x1 - x)**2 + (y1 - y)**2
     if det > length:
       return (x2 - x)**2 + (y2 - y)**2
     det = ux * vy - uy * vx
     return det**2 / length
   def dist2line(x,y,line): return math.sqrt(dist2line2(x,y,line))

在F中,从点c到a和b之间线段的距离为g 受以下因素影响:

设点到线段距离a:向量,b:向量c:向量= 设d=b-a 设s=d.长度 设lambda=c-a*d/s 设p=lambda |>最大0.0 |>最小s*d/s a+p-c.长度 向量d沿线段从a指向b。d/s与c-a的点积给出了无穷直线与点c之间最接近点的参数。“最小”和“最大”函数用于将此参数钳制在0..s范围内,使点位于a和b之间。最后,a+p-c的长度是从c到线段上最近点的距离

示例用法:

pointToLineSegmentDistance (Vector(0.0, 0.0), Vector(1.0, 0.0)) (Vector(-1.0, 1.0))

使用圆弧切线的单线解决方案:

想法是将A移动到0,0,顺时针旋转三角形,使C位于X轴上, 当这种情况发生时,距离就是

a角度=a角度-Ay,Cx角度-Ax; b角度=AtanBy-Ay,Bx-Ax; AB长度=Sqrt Bx-Ax^2+By-Ay^2 By=单手镯-角度*长度 C

要转换为SQL的一行C

double distance = Math.Sin(Math.Atan2(b.Y - a.Y, b.X - a.X) - Math.Atan2(c.Y - a.Y, c.X - a.X)) * Math.Sqrt((b.X - a.X) * (b.X - a.X) + (b.Y - a.Y) * (b.Y - a.Y))

这里是devnullicus的C++版本转换为C。对于我的实现,我需要知道交叉点,找到了他工作的解决方案。< /P>

public static bool PointSegmentDistanceSquared(PointF point, PointF lineStart, PointF lineEnd, out double distance, out PointF intersectPoint)
{
    const double kMinSegmentLenSquared = 0.00000001; // adjust to suit.  If you use float, you'll probably want something like 0.000001f
    const double kEpsilon = 1.0E-14; // adjust to suit.  If you use floats, you'll probably want something like 1E-7f
    double dX = lineEnd.X - lineStart.X;
    double dY = lineEnd.Y - lineStart.Y;
    double dp1X = point.X - lineStart.X;
    double dp1Y = point.Y - lineStart.Y;
    double segLenSquared = (dX * dX) + (dY * dY);
    double t = 0.0;

    if (segLenSquared >= -kMinSegmentLenSquared && segLenSquared <= kMinSegmentLenSquared)
    {
        // segment is a point.
        intersectPoint = lineStart;
        t = 0.0;
        distance = ((dp1X * dp1X) + (dp1Y * dp1Y));
    }
    else
    {
        // Project a line from p to the segment [p1,p2].  By considering the line
        // extending the segment, parameterized as p1 + (t * (p2 - p1)),
        // we find projection of point p onto the line. 
        // It falls where t = [(p - p1) . (p2 - p1)] / |p2 - p1|^2
        t = ((dp1X * dX) + (dp1Y * dY)) / segLenSquared;
        if (t < kEpsilon)
        {
            // intersects at or to the "left" of first segment vertex (lineStart.X, lineStart.Y).  If t is approximately 0.0, then
            // intersection is at p1.  If t is less than that, then there is no intersection (i.e. p is not within
            // the 'bounds' of the segment)
            if (t > -kEpsilon)
            {
                // intersects at 1st segment vertex
                t = 0.0;
            }
            // set our 'intersection' point to p1.
            intersectPoint = lineStart;
            // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if
            // we were doing PointLineDistanceSquared, then intersectPoint.X would be (lineStart.X + (t * dx)) and intersectPoint.Y would be (lineStart.Y + (t * dy)).
        }
        else if (t > (1.0 - kEpsilon))
        {
            // intersects at or to the "right" of second segment vertex (lineEnd.X, lineEnd.Y).  If t is approximately 1.0, then
            // intersection is at p2.  If t is greater than that, then there is no intersection (i.e. p is not within
            // the 'bounds' of the segment)
            if (t < (1.0 + kEpsilon))
            {
                // intersects at 2nd segment vertex
                t = 1.0;
            }
            // set our 'intersection' point to p2.
            intersectPoint = lineEnd;
            // Note: If you wanted the ACTUAL intersection point of where the projected lines would intersect if
            // we were doing PointLineDistanceSquared, then intersectPoint.X would be (lineStart.X + (t * dx)) and intersectPoint.Y would be (lineStart.Y + (t * dy)).
        }
        else
        {
            // The projection of the point to the point on the segment that is perpendicular succeeded and the point
            // is 'within' the bounds of the segment.  Set the intersection point as that projected point.
            intersectPoint = new PointF((float)(lineStart.X + (t * dX)), (float)(lineStart.Y + (t * dY)));
        }
        // return the squared distance from p to the intersection point.  Note that we return the squared distance
        // as an optimization because many times you just need to compare relative distances and the squared values
        // works fine for that.  If you want the ACTUAL distance, just take the square root of this value.
        double dpqX = point.X - intersectPoint.X;
        double dpqY = point.Y - intersectPoint.Y;

        distance = ((dpqX * dpqX) + (dpqY * dpqY));
    }

    return true;
}

Grumdrig的C++/JavaScript实现对我非常有用,因此我提供了一个我正在使用的Python直接端口。完整的代码是

这里使用的是Swift

    /* Distance from a point (p1) to line l1 l2 */
func distanceFromPoint(p: CGPoint, toLineSegment l1: CGPoint, and l2: CGPoint) -> CGFloat {
    let A = p.x - l1.x
    let B = p.y - l1.y
    let C = l2.x - l1.x
    let D = l2.y - l1.y

    let dot = A * C + B * D
    let len_sq = C * C + D * D
    let param = dot / len_sq

    var xx, yy: CGFloat

    if param < 0 || (l1.x == l2.x && l1.y == l2.y) {
        xx = l1.x
        yy = l1.y
    } else if param > 1 {
        xx = l2.x
        yy = l2.y
    } else {
        xx = l1.x + param * C
        yy = l1.y + param * D
    }

    let dx = p.x - xx
    let dy = p.y - yy

    return sqrt(dx * dx + dy * dy)
}

这里没有看到Java实现,因此我将Javascript函数从公认的答案转换为Java代码:

static double sqr(double x) {
    return x * x;
}
static double dist2(DoublePoint v, DoublePoint w) {
    return sqr(v.x - w.x) + sqr(v.y - w.y);
}
static double distToSegmentSquared(DoublePoint p, DoublePoint v, DoublePoint w) {
    double l2 = dist2(v, w);
    if (l2 == 0) return dist2(p, v);
    double t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
    if (t < 0) return dist2(p, v);
    if (t > 1) return dist2(p, w);
    return dist2(p, new DoublePoint(
            v.x + t * (w.x - v.x),
            v.y + t * (w.y - v.y)
    ));
}
static double distToSegment(DoublePoint p, DoublePoint v, DoublePoint w) {
    return Math.sqrt(distToSegmentSquared(p, v, w));
}
static class DoublePoint {
    public double x;
    public double y;

    public DoublePoint(double x, double y) {
        this.x = x;
        this.y = y;
    }
}
WPF版本:

public class LineSegment
{
    private readonly Vector _offset;
    private readonly Vector _vector;

    public LineSegment(Point start, Point end)
    {
        _offset = (Vector)start;
        _vector = (Vector)(end - _offset);
    }

    public double DistanceTo(Point pt)
    {
        var v = (Vector)pt - _offset;

        // first, find a projection point on the segment in parametric form (0..1)
        var p = (v * _vector) / _vector.LengthSquared;

        // and limit it so it lays inside the segment
        p = Math.Min(Math.Max(p, 0), 1);

        // now, find the distance from that point to our point
        return (_vector * p - v).Length;
    }
}
C

改编自

公共静态双最小距离到线段此点p, 线路 { var v=直线起始点; var w=line.EndPoint; 双长度平方=距离平方dV,w; 如果长度平方==0.0 返回距离p,v; double t=Math.Max0,Math.Min1,DotProductp-v,w-v/长度平方; var投影=v+t*w-v; 返回距离p,投影; } 公共静态双距离点a、点b { 返回Math.SqrtDistanceSquareda,b; } 公共静态双距离平方点a点b点 { var d=a-b; 返回DotProductd,d; } 公共静态双点产品a点、b点 { 返回a.X*b.X+a.Y*b.Y; }
我制作了一个交互式Desmos图来演示如何实现这一点:

红色点是A,绿色点是B,C点是蓝色。 可以拖动图形中的点以查看值的更改。 在左侧,值“s”是线段的参数,即s=0表示点A,s=1表示点B。 值“d”是从第三个点到通过A和B的直线的距离

编辑:

有趣的小洞察:坐标s,d是坐标系中第三点C的坐标,其中AB是单位x轴,单位y轴垂直于AB。

二维和三维解决方案

考虑基准的变化,使线段变成0,0,0-d,0,0和点u,v,0。最短距离出现在该平面上,由

    u ≤ 0 -> d(A, C)
0 ≤ u ≤ d -> |v|
d ≤ u     -> d(B, C)
到其中一个端点或到支撑线的距离,取决于到该线的投影。等距轨迹由两个半圆和两条线段组成

在上面的表达式中,d是段AB的长度,u、v分别是AB/d单位向量在AB和AC方向上的叉积的标量积和模。因此,矢量

AB.AC ≤ 0             -> |AC|
    0 ≤ AB.AC ≤ AB²   -> |ABxAC|/|AB|
          AB² ≤ AB.AC -> |BC|

谢谢-这正是我想要的那种代码。我已经在下面发布了我自己的答案,因为我成功地将一些在当前时代浏览器Javascript中工作的东西组合在一起,但是我已经将您的答案标记为已接受,因为它简单、编写良好、易于理解,并且非常受欢迎。这不是缺少点方法吗?在任何情况下,都很容易计算:vec1.x*vec2.x+vec1.y*vec2.i这不是计算点到直线的距离而不是线段吗?这确实是到线段所在直线的距离,而不是到线段的距离。这似乎不起作用。如果你得到了一个0,0和5,0的段,并尝试点7,0,它将返回0,这不是真的。距离应该是2。他没有考虑到这个点投射到片段上的情况是从A到B的间隔。这可能是提问者想要的,但不是他问的。这不是最初的要求。这个代码有一个bug。靠近段所在直线但远离段一端的点将被错误地判断为靠近段。有趣的是,我将在下次编写此代码库时对此进行研究,以确认您的断言。谢谢你的提示。对不起,但是我试过了,它仍然会给我结果,就像直线延伸到无穷远一样。不过,我已经找到了格鲁迪格对工作的答案。在这种情况下,你用错了它,或者用非无限来表示别的意思。请看下面的代码示例:看起来像是代码中的错误或其他什么,我得到了与Frederik相同的结果/变量名的选择远远不够好

,something,u,…我尝试了函数的Python版本,发现如果参数是整数,它会显示不正确的结果。distAnother0,0,4,0,2,2给出的2.8284271247461903不正确。二者分别为0,0,4,0,2,2。给出2.0的正确答案。请注意这一点。我认为可以对代码进行改进,以便在某个地方进行浮点转换。谢谢,这段Matlab代码确实计算了到线段的最短距离,而不是到线段所在的无限长直线的距离。我添加了一个充实的版本作为单独的答案。谢谢@Grumdrig,您的javascript解决方案非常合适,而且节省了大量时间。我把你的解移植到Objective-C中,并添加到下面。我们只是想避免被零除。点p在直线上的投影就是直线上最接近点p的点。垂直于投影线的a将通过p。数字t是沿从v到w的线段投影下降的距离。所以如果t为0,投影正好落在v上;如果它是1,它在w上;例如,如果它是0.5,那么它是介于两者之间的一半。如果t小于0或大于1,则它落在经过线段一端或另一端的线上。在这种情况下,到段的距离将是到较近端的距离。哎呀-没有注意到有人提供了3D版本@RogiSolorzano,你需要先把纵横坐标转换成3-空间中的x,y,z坐标。谢谢你的发帖。但最后一种方法似乎有一个明显的优化:在确定需要dist之前不要计算dist。DotProduct上的评论说它是在计算AB.AC,但它是在计算AB.BC。根据定义,叉积返回一个向量,但在这里返回一个标量。感谢发布此消息。非常好的结构,注释和格式化,几乎让我忘记了我有多么讨厌C++。我用它制作了一个相应的C版本,我现在发布在这里。在我看到的所有解决这个问题的代码中,我最喜欢这个。它非常清晰易读。不过,它背后的数学原理有点神秘。例如,点积除以长度的平方表示什么?点积除以长度的平方表示从x1,y1的投影距离。这是点x,y最接近的直线的分数。请注意最后一个else子句,其中计算了xx,yy,这是点x,y在线段x1,y1-x2,y2上的投影。检查长度为0的线段在代码中太低了。”len_sq’将为零,在到达安全检查点之前,代码将除以0。它以米为单位返回。@nevermind,让我们调用点p0和定义线为p1和p2的点。然后得到向量A=p0-p1和B=p2-p1。Param是一个标量值,当乘以B时,该标量值为最接近p0的直线上的点。如果param=1,则最近的点为p1。如果它在0和1之间,它在p1和p2之间,所以我们插值。XX和YY是线段上最近的点,dx/dy是从p0到该点的向量,最后我们返回该向量的长度。我从这条直线返回'nan'。知道为什么吗?顺便说一下,谢谢你在Obj-C中输入这个!return dist2p,CGPointMakev.x+t*w.x-v.x,v.y+t*w.y-v.ysqrtf正在平方x,而不是平方root@Senseful不知道你的意思。sqrtf是平方根@awolf:看看上面的第一行代码。它定义了方法sqrtfx=x*x.@senseul谢谢,它的名称错误,而不是执行了错误的操作。这对我来说似乎很有效。感谢转换。值得注意的是,xx,yy是最近点的位置。我对您的代码进行了一些编辑,因此它会返回点和距离,并对名称进行了重构,以便它们描述内容,并在以下位置提供了示例:。我认为最后一行不正确,应该是:a+p-c.length这仍然不能完全解决问题。修正函数的一种方法是将lambda和p分别重新定义为let lambda=c-a*d/s*s和let p=a+lambda |>max 0.0 |>min 1.0*d。之后,该函数返回正确的距离,例如,对于a=0,1,b=1,0和c=1的情况,1.像一个符咒一样工作!!节省了我无数的时间。非常感谢!!尝试此代码后,似乎无法正常工作。我不知道你是如何表示线和点的,但这是你开始学习所需要的全部数学知识。应该不太难弄清楚你需要做什么。@Arthurkaliokoski:那个链接死了,但我找到了一个副本:@GuntherStruyf:那个链接也死了,但这个类似的链接也起作用:如果有人在寻找点和线之间的距离,而不是点和线段之间的距离,请检查这个链接:上面的链接死了。这里是伪代码和C++示例,解释和导出,如教科书所详述的,
public class LineSegment
{
    private readonly Vector _offset;
    private readonly Vector _vector;

    public LineSegment(Point start, Point end)
    {
        _offset = (Vector)start;
        _vector = (Vector)(end - _offset);
    }

    public double DistanceTo(Point pt)
    {
        var v = (Vector)pt - _offset;

        // first, find a projection point on the segment in parametric form (0..1)
        var p = (v * _vector) / _vector.LengthSquared;

        // and limit it so it lays inside the segment
        p = Math.Min(Math.Max(p, 0), 1);

        // now, find the distance from that point to our point
        return (_vector * p - v).Length;
    }
}
    u ≤ 0 -> d(A, C)
0 ≤ u ≤ d -> |v|
d ≤ u     -> d(B, C)
AB.AC ≤ 0             -> |AC|
    0 ≤ AB.AC ≤ AB²   -> |ABxAC|/|AB|
          AB² ≤ AB.AC -> |BC|