前段时间做东西的时候,因为要用鼠标拾取线段,涉及到了求一个点到线段最短距离的算法问题。在网上找了半天,主要提供了两种算法,一种是经典型的,直接用高中的几何知道求,当然算法也巨复杂,要是用几万条线段的话可能计算起来够呛;另一种用矢量的方法,算法很简略,但是如果高数学得不好可能很难理解。后来我想能不能找一种又省时又易懂的算法,自己想了半天,竟然给我想到了一种,算法比矢量的还快,并且相当易懂~
下面把这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;//返回高长度
下面把这3种算法贴出来,大家以后要是遇到这种问题可以参考一下:
//小虫原创,转载请说明出处:yjukh.blogcn.com
1.经典算法:
//
//
//
//
//
//
//
double
const
{
doubledbLen1,
doubledbAng,
dbLen1
if
return
dbLen2
if
return
dbLen3=
if
return
if
{
if
{
if
dbAng1
else
dbAng1
}
else
{
dbAng1
if
dbAng1
}
dbAng2
if
dbAng
if
dbAng
if
dbAng
if
return
else
} //小虫原创,转载请说明出处:yjukh.blogcn.com
else
{
if
{
if
dbAng1
else
dbAng1
}
else
{
dbAng1
if
dbAng1
}
dbAng2
if
dbAng2
dbAng
if
dbAng
if
dbAng
if
return
else
return
}
}
这种算法是直接用解析几何求的,光从程序上看就可以知道很复杂,所以不推荐。
2.矢量算法:
float
{
}
float
{
Point
Point
float
if
float
if
Point
}
这个算法也算简单,但是要理解这个算法必须有高数的矢量图形知识,里面包含了点乘和叉乘的算法,要自己写重载函数或自己把它展开。如果对矢量算法不懂的可以到网上搜索一下,或是参考以下网页:
参考资料1
3.我的算法:
float
{
return
}
//小虫原创,转载请说明出处:yjukh.blogcn.com
float
{ //小虫原创,转载请说明出处:yjukh.blogcn.com
float
a=Distance(P2,P3);
if(a<=0.00001)//P3和P2重合,直接返回0
return
b=Distance(P1,P3);
if(b<=0.00001))//P3和P1重合,直接返回0
return
c=Distance(P1,P2);
if(c<=0.00001)//如果P1和P2坐标相同,即线段长度为0,直接返回距离
return
//小虫原创,转载请说明出处:yjukh.blogcn.com
if(a*a>=b*b+c*c)//若角A为钝角,即P3点的投影不在线段上,只需返回b即可
return
if(b*b>=a*a+c*c)//若角B为钝角,即P3点的投影不在线段上,只需返回a即可
return
float
s=(a+b+c)/2;//否则P3点的投影必在线段上,只需返回高即可
s=sqrt(s*(s-a)*(s-b)*(s-c));//海伦公式,求出面积 //小虫原创,转载请说明出处:yjukh.blogcn.com
return