关于计算点到线段的距离

已知线段AB,求某点到该线段的距离。有三种情况,如图:图中只画出两种,其实垂足在另一侧也是一样的。还有就是垂足与A或B共点,这两种情形可以按照前述两种情况任意之一处理。

当垂足在线段上时(包括垂足与A或B共点)

以A为起点B为终点构建向量u,以A为起点C为终点构建向量v

由向量叉积的定义容易得出:以向量u为底、向量v为侧边的平行四边形的面积。则

S_{\triangle ABC}= \frac{\left | \vec{u} \wedge \vec{v} \right |}{2}

翻译成程序员的大白话就是三角形abc的面积=abs((Bx-Ax)*(Cy-Ay)-(By-Ay)*(Cx-Ax))/2

那么点到线段的距离就是

L_{CD}= \frac{\left | \vec{u} \wedge \vec{v} \right |}{\left | \vec{u} \right |}

翻译下就是线段CD的长度=abs((Bx-Ax)*(Cy-Ay)-(By-Ay)*(Cx-Ax))/sqrt((Ax-Bx)^2+(Ay-By)^2)

程序中如果线段是固定位置的则应预先计算长度避免重复计算开方,进一步的,可以预先计算(1/sqrt((Ax-Bx)^2+(Ay-By)^2))用一式与之相乘避免除法运算减少性能损失。

当垂足不在线段上时

这种情形比较简单,只要确定在哪个端点一侧,计算该点到最近一侧端点的距离即可。

最重要的是如何区分属于哪种情形呢?

由向量点积的性质可知,将两个向量的起点对齐,如果构成的角度小于90度,点积大于0、角度大于90度时点积小于0。那么以B为起点C为终点,作向量w。注意是计算C到线段AB的距离,A为向量u的起点,B为终点。上图u·v>0,u·w<0。

此时u·v<0,u·w<0

此时u·v>0,u·w>0

为了照顾程序员朋友给下公式吧

u·v=(Bx-Ax)*(Cx-Ax)+(By-Ay)*(Cy-Ay)

u·w=(Bx-Ax)*(Cx-Bx)+(By-Ay)*(Cy-By)

结论

当u·v与u·w不同号时垂足在线段上,使用u与v的叉积除以u的长度得出距离。(点积为0时也可使用这个方法计算距离,使用端点计算亦可)

当u·v与u·w同时小于0时垂足在起点一侧,计算起点与该点的距离即可。

当u·v与u·w同时大于0时垂足在终点一侧,计算终点与该点的距离即可。

代码如下:(哪种语言也不能直接运行,姑且认为是java或者C++吧,都需要自己定义Point类和abs、sqrt方法,C++注意改成引用参数)

float pointToLine(Point s, Point e, Point p) {
	float ux = e.x - s.x;
	float uy = e.y - s.y;
	float vx = p.x - s.x;
	float vy = p.y - s.y;
	float wx = p.x - e.x;
	float wy = p.y - e.y;
	float umv = ux * vx + uy * vy;
	float umw = ux * wx + uy * wy;
	if (umv * umw > 0) {
		// 点的垂足不在线段上
		if (umv > 0) {
			// 垂足在终点一侧
			return sqrt(wx * wx + wy * wy);
		} else {
			// 垂足在起点一侧
			return sqrt(vx * vx + vy * vy);
		}
	} else {
		// 点的垂足在线段上
		return abs(ux * vy - uy * vx) / sqrt(ux * ux + uy * uy);
	}
}

注意,如果线段长度是固定的,就应该将sqrt(ux*ux+uy*uy)缓存起来,它只表示长度,与位置无关。如果要计算距离平方可以将结果平方一下(好像有什么不对劲,不过已经少算一次开方了,乘一下还是可以接受的)。

另外由于浮点数精度的问题,算术运算的次数越多偏差越大,尤其是除法,所以想减少偏差就尽量少做除法。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值