在二维/三维图形学系统当中,线段的拾取是一个经常使用的功能
如何根据鼠标点来判断线段是否被选择了,最主要的方法之一是通过点到线段的最小距离来判定的
无论二维还是三维情况下,使用点到直线的距离公式似乎是最直接的选择
但是不要忘记点到直线公式计算的是点和直线之间的关系,而不是点和线段之间的关系
需要额外的判断,点在线段上的投影点是否位于线段内部,是才可以采用点到直线的公式,否则就不能
算法如下
class Point
float Distance(Point p1, Point p2); // 返回两点之间的距离
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(a, c);
f = f/d;
Point D = a + f *ab; // c在ab线段上的投影点
return Distance(a, D);
}
这个算法计算量很少,在现在流行配置的计算机基本上在当中1秒钟可以检测出上百万个线段
这个是检测一个点和一个线段之间的最短距离的
对于一个系统当中存在大量的线段需要检测的时候,应该把线段按照区域划分成组,建立一个包围盒
先检测点是否在包围盒内部,然后再进入组内检测具体的线段。
///
c语言版
//网上搜的一个算法,自己改了改。激动的是终于看到了点乘的用途。
#include <cstdio>
#include <cmath>
struct POINT
{
double x,y;
void Offset(POINT A) //相对A偏移
{
x=x+A.x;
y=y+A.y;
}
POINT operator - ( POINT &A )
{
POINT X;
X.x = x-A.x;
X.y = y-A.y;
return X;
}
};
double Dist(POINT A, POINT B,bool sqr=1)
{
double x = A.x - B.x;
double y = A.y - B.y;
if(sqr)
return sqrt(x*x+y*y);
else
return x*x + y*y;
}
double Pnt2SegmentDist(POINT A, POINT B, POINT C)
{
POINT AB = B - A; //向量AB
POINT AC = C - A;
double r = AB.x * AC.x + AB.y * AC.y;//AB与AC的点乘积
r /= Dist(A,B,0);//AC在AB上的投影比上AB。调用Dist(),不开方
//若C的投影在AB外
if(r < 0)
return Dist(A, C);//调用Dist(),开方
if(r > 1)
return Dist(B, C);
//若C的投影在AB之间
POINT D = AB;
D.x *= r;
D.y *= r;//因为AB是向量,所以可以这样做。得到AC在AB上的投影向量。
D.Offset(A);//点D的绝对坐标
return Dist(C, D);
}
int main()
{
// freopen("a.txt","r",stdin);
POINT A,B,C;
while(scanf("%lf%lf%lf%lf%lf%lf",&A.x,&A.y,&B.x,&B.y,&C.x,&C.y)!=EOF)
{
printf("%lf/n",Pnt2LineDist(A,B,C));
}
return 0;
}