3种求点到线段最短距离的算法

来源:互联网 发布:淘宝网二手家具沙发 编辑:程序博客网 时间:2024/05/02 02:34

3种求点到线段最短距离的算法 [原]

(2006-08-05 13:31:00)
http://blog.sina.com.cn/s/blog_4dacef8501000foz.html

  前段时间做东西的时候,因为要用鼠标拾取线段,涉及到了求一个点到线段最短距离的算法问题。在网上找了半天,主要提供了两种算法,一种是经典型的,直接用高中的几何知道求,当然算法也巨复杂,要是用几万条线段的话可能计算起来够呛;另一种用矢量的方法,算法很简略,但是如果高数学得不好可能很难理解。后来我想能不能找一种又省时又易懂的算法,自己想了半天,竟然给我想到了一种,算法比矢量的还快,并且相当易懂~
下面把这3种算法贴出来,大家以后要是遇到这种问题可以参考一下:
//小虫原创,转载请说明出处:yjukh.blogcn.com
1.经典算法:
////////////////////////////////////////////////////////////////
//
// 方法名:PointSegment
// 方法说明:求一点到一线段距离
// 参数:x1, y1,x2, y2 线段两端点坐标
// x, y 所求点坐标
// 返回值:dbLength 两点间距
//
////////////////////////////////////////////////////////////////

double PointSegment(const double x1, const double y1,const double x2,
const double y2, const double x, const double y)
{
doubledbLen1, dbLen2, dbLen3;
doubledbAng, dbAng1, dbAng2;

dbLen1 = Distance(x, y, x1, y1);
if (EQ(0.0, dbLen1))
return 0.0;
dbLen2 = Distance(x, y, x2, y2);
if (EQ(0.0, dbLen2))
return 0.0;
dbLen3= Distance(x1, y1, x2, y2);
if (EQ(0.0, dbLen3))
return dbLen1;
if (dbLen1 < dbLen2)
{
if (EQ(y1, y2))
{
if (x1 < x2)
dbAng1 = 0.0;
else
dbAng1 = PI;
}
else
{
dbAng1 = acos((x2 - x1) / dbLen3);
if (y1 > y2)
dbAng1 = 2 * PI - dbAng1;
}
dbAng2 = acos((x - x1) / dbLen1);
if (y1 > y) dbAng2 = 2 * PI - dbAng2;
dbAng = dbAng2 - dbAng1;
if (dbAng < 0.0)
dbAng = - dbAng;
if (dbAng > PI)
dbAng = 2 * PI - dbAng;
if (dbAng > PI / 2)
return dbLen1;
else return (dbLen1 * sin(dbAng));
}//小虫原创,转载请说明出处:yjukh.blogcn.com
else
{
if (EQ(y1, y2))
{
if (x1 < x2)
dbAng1 = PI;
else
dbAng1 = 0.0;
}
else
{
dbAng1 = acos((x1 - x2) / dbLen3);
if (y2 > y1)
dbAng1 = 2 * PI - dbAng1;
}
dbAng2 = acos((x - x2) / dbLen2);
if (y2 > y)
dbAng2 = 2 * PI - dbAng2;
dbAng = dbAng2 - dbAng1;
if (dbAng < 0.0)
dbAng = - dbAng;
if (dbAng > PI)
dbAng = 2 * PI - dbAng;
if (dbAng > PI / 2)
return dbLen2;
else
return (dbLen2 * sin(dbAng));
}
}
  这种算法是直接用解析几何求的,光从程序上看就可以知道很复杂,所以不推荐。

2.矢量算法:
float Distance(Point p1, Point p2) // 返回两点之间的距离
{
return (float)sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}

float DistanceLine(Point a, Point b, Point c) // a和b是线段的两个端点, c是检测点
{
Point ab = b - a;
Point ac = c-a;
float f = ab * ac;
if (f<0) return Distance(a, c);
float d = ab * ab;
if ( f>d) return Distance(b, c);
f = f/d;
Point D = a + f *ab; // c在ab线段上的投影点
return Distance(a, D);
}

  这个算法也算简单,但是要理解这个算法必须有高数的矢量图形知识,里面包含了点乘和叉乘的算法,要自己写重载函数或自己把它展开。如果对矢量算法不懂的可以到网上搜索一下,或是参考以下网页:
  参考资料1
参考资料2

3.我的算法:
float Distance(Point p1, Point p2) // 返回两点之间的距离
{
return (float)sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
//小虫原创,转载请说明出处:yjukh.blogcn.com
float GetDistance(Point P1, Point P2, Point P3)
{//小虫原创,转载请说明出处:yjukh.blogcn.com
float a,b,c;
a=Distance(P2,P3);
if(a<=0.00001)//P3和P2重合,直接返回0
return 0.0f;
b=Distance(P1,P3);
if(b<=0.00001))//P3和P1重合,直接返回0
return 0.0f;
c=Distance(P1,P2);
if(c<=0.00001)//如果P1和P2坐标相同,即线段长度为0,直接返回距离
return a;
//小虫原创,转载请说明出处:yjukh.blogcn.com
if(a*a>=b*b+c*c)//若角A为钝角,即P3点的投影不在线段上,只需返回b即可
return b;
if(b*b>=a*a+c*c)//若角B为钝角,即P3点的投影不在线段上,只需返回a即可
return a;
float s;
s=(a+b+c)/2;//否则P3点的投影必在线段上,只需返回高即可
s=sqrt(s*(s-a)*(s-b)*(s-c));//海伦公式,求出面积//小虫原创,转载请说明出处:yjukh.blogcn.com
return 2*s/c;//返回高长度
原创粉丝点击