Language agnostic 点与线段之间的最短距离
我需要一个基本函数来找到点和线段之间的最短距离。可以随意用任何语言编写解决方案;我可以将其转换为我正在使用的JavascriptLanguage 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的直线之间的交点;如果该点位于线段的两个点之间,
编辑:我的线段由两个端点定义。所以我的线段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|