Reference:
点到线段最短距离的运算与点到直线的最短距离的运算二者之间存在一定的差别,即求点到线段最短距离时需要考虑参考点在沿线段方向的投影点是否在线段上,若在线段上才可采用点到直线距离公式,如下图所示,
- 左边的最短距离为点 P P P 与其在线段 A B AB AB 上投影 C C C 之间的线段 P C PC PC;
- 右边的最短距离为点 P P P 与端点 B B B 或 A A A 所构成的线段 P B PB PB 或 P A PA PA。
具体算法主要有以下三种:
1. 经典算法
该算法直接用高中时所学习到的解析几何知识对点到线段的距离进行求解。其基本思想是先判断点在线段端点、点在线上等等的特殊情况,逐步的由特殊到一般,当忽略点在线段上的特殊情况时,判断点到线段方向的垂线是否落在线段上的方法是通过比较横纵坐标的方式来判断,最后把不同的判断情况用不同的几何方式来进行处理计算得出结果。
由上面叙述的基本思路可以知道这种算法虽然很容易理解和接受,但从算法的实用性的角度分析还是有很大的缺点的,首先是算法复杂,计算量巨大,大量的比较判断、距离计算、角度计算等等,实际应用中往往是需要求由大量线段组成的折线到某点的最短距离,如此用这样的算法计算量是不能想象的。其次经典算法中使用的一些简化运算的函数不利于语言的重新包装,如果想换编程语言的话,就比较麻烦了。
2. 面积算法
该方法主要是先判断投影点是否在线段上,投影点在线段延长线上时,最短距离长度为点到端点的线段长度;当投影点在线段上时,先使用海伦公式计算三角形面积,再计算出三角形的高,即为最短距离。
运用面积算法求解点到线段最短距离思路很清晰,也很容易理解。从效率方面考虑,比如需要多次计算平方、根号,这对于大量数据进行运算是负担很重的。求面积就必须把三条边长全部求出,并且用到的海伦公式也需要进行开方运算,计算过程显得繁琐。
3. 矢量算法
矢量算法过程清晰,如果具有一定的空间几何基础,则是解决此类问题时应优先考虑的方法。当需要计算的数据量很大时,这种方式优势明显。
由于矢量具有方向性,故一些方向的判断直接根据其正负号就可以得知,使得其中的一些问题得以很简单的解决。
用此方法考虑,我们只需要找到向量
A
P
→
\overrightarrow{AP}
AP 在
A
B
→
\overrightarrow{AB}
AB 方向上的投影,具体如下:
A C → = ( A P → ⋅ A B → ) ∣ A B → ∣ 2 A B → = ( A P → ⋅ A B → ) ∣ A B → ∣ ⋅ A B → ∣ A B → ∣ \overrightarrow{A C}=\frac{(\overrightarrow{A P} \cdot \overrightarrow{A B})}{|\overrightarrow{A B}|^2} \overrightarrow{A B}=\frac{(\overrightarrow{A P} \cdot \overrightarrow{A B})}{|\overrightarrow{A B}|} \cdot \frac{\overrightarrow{A B}}{|\overrightarrow{A B}|} AC=∣AB∣2(AP⋅AB)AB=∣AB∣(AP⋅AB)⋅∣AB∣AB
上面的 A B → A B → \frac{\overrightarrow{A B}}{\overrightarrow{A B}} ABAB 是 A B → \overrightarrow{A B} AB 方向上的单位向量,其意义是给所求向量确定方向。 A P → ⋅ A B → \overrightarrow{A P} \cdot \overrightarrow{A B} AP⋅AB 是两个向量的内积,且 A P → ⋅ A B → = ∣ A P → ∣ ∣ A B → ∣ cos ( θ ) \overrightarrow{A P} \cdot \overrightarrow{A B} = |\overrightarrow{A P}| |\overrightarrow{A B}|\cos(\theta) AP⋅AB=∣AP∣∣AB∣cos(θ),其中 θ \theta θ 为向量 A P → \overrightarrow{A P} AP 与 A B → \overrightarrow{A B} AB 之间的夹角。 ∣ A B → ∣ |\overrightarrow{A B}| ∣AB∣ 是向量长度。
那么 ( A P → ⋅ A B → ) ∣ A B → ∣ = ∣ A P → ∣ ∣ A B → ∣ cos θ ∣ A B → ∣ = ∣ A P → ∣ cos θ \frac{(\overrightarrow{A P} \cdot \overrightarrow{A B})}{|\overrightarrow{A B}|}=\frac{|\overrightarrow{A P}||\overrightarrow{A B}| \cos \theta}{|\overrightarrow{A B}|}=|\overrightarrow{A P}| \cos \theta ∣AB∣(AP⋅AB)=∣AB∣∣AP∣∣AB∣cosθ=∣AP∣cosθ 即为上图中线段 A C AC AC 的长度值,不带有方向性。此数值加上方向 A B → A B → \frac{\overrightarrow{A B}}{\overrightarrow{A B}} ABAB 即可构成:有大小和方向的新向量 A C → \overrightarrow{A C} AC,即为 A P → \overrightarrow{A P} AP 在 A B → \overrightarrow{A B} AB 方向上的投影向量, C C C 为投影点。
根据得到的 r = ( A P → ⋅ A B → ) ∣ A B → ∣ 2 r=\frac{(\overrightarrow{A P} \cdot \overrightarrow{A B})}{|\overrightarrow{A B}|^{2}} r=∣AB∣2(AP⋅AB),由向量的方向性可知:
- 如果情况是上图 ( a ) (a) (a) 所示,那么 0 < r < 1 0<r<1 0<r<1;
- 如果情况是上图 ( b ) (b) (b) 所示,那么 r > = 1 r>=1 r>=1;
- 如果情况是上图 ( c ) (c) (c) 所示,那么 r < = 0 r<=0 r<=0。
特殊情况如点在线段上、点在端点、点在线段延长线上等等情况全部适用于此公式,只是作为特殊情况出现,无需另做讨论。这也是适量算法思想的优势所在。
故根据
r
r
r 值的不同,最短距离
d
d
d 可以计算为:
d
=
{
∣
A
P
→
∣
if
r
≤
0
∣
B
P
→
∣
if
r
≥
1
∣
C
P
→
∣
otherwise
d=\left\{\begin{array}{l}|\overrightarrow{A P}| \text { if } r \leq 0 \\ |\overrightarrow{B P}| \text { if } r \geq 1 \\ |\overrightarrow{C P} | \text { otherwise }\end{array}\right.
d=⎩⎪⎪⎨⎪⎪⎧∣AP∣ if r≤0∣BP∣ if r≥1∣CP∣ otherwise
C#代码为:
double PointToSegDist(double x, double y, double x1, double y1, double x2, double y2)
{
double cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
if (cross <= 0) return Math.Sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));
double d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
if (cross >= d2) return Math.Sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
double r = cross / d2;
double px = x1 + (x2 - x1) * r;
double py = y1 + (y2 - y1) * r;
return Math.Sqrt((x - px) * (x - px) + (y - py) * (y - py))
}