在编写绘制、编辑导线功能的时候, 为了使得用户能够选取导线, 需要判断点到线段的距离是否在一个合适的范围内。
如图所示, 点与线短的关系有这么四种。
1、2、3都很好处理, 就是4。
这里, 可以使用向量的方式, 先求出∠A的余弦值cosA, 然后计算∠A的正弦值sinA, 最后AC*sinA为所求。
注意 $$\sin^2A =1-\cos^2A$$
而 $$\cos^2A = ( \frac{ \overrightarrow{AC} \cdot \overrightarrow{AB} }{ | \overrightarrow{AC} | \dot | \overrightarrow{AB} | } )^2 = \frac{ ( \overrightarrow{AC} \cdot \overrightarrow{AB} )^2 }{ | \overrightarrow{AC} |^2 \dot | \overrightarrow{AB} |^2}$$
由于我可以直接判断距离平方的数值, 于是就根本不需要根号了。
代码如下, 代码中的p, 为C点:
Public Class MyMath Public Shared Function Distance2(p As Point) As Integer Return p.X * p.X + p.Y * p.Y End Function Public Shared Function Distance2(a As Point, b As Point) As Integer Dim delta = a - b Return delta.X * delta.X + delta.Y * delta.Y End Function ''' <summary> ''' 点到线段的距离的平方 ''' </summary> ''' <param name="a">线段一个端点</param> ''' <param name="b">线段一个端点</param> ''' <param name="p">待测点</param> ''' <returns>距离的平方</returns> ''' <remarks></remarks> Public Shared Function PointToLine2(a As Point, b As Point, p As Point) As Double Dim AP = p - a Dim d2AP = Distance2(AP) If d2AP = 0 Then Return 0.0 End If Dim BP = p - b Dim d2BP = Distance2(BP) If d2BP = 0 Then Return 0.0 End If Dim AB = b - a Dim d2AB = Distance2(AB) If d2AB = 0 Then '3 Return d2AP End If If d2BP > d2AB + d2AP Then '1 Return d2AP End If If d2AP > d2AB + d2BP Then '2 Return d2BP End If Dim APdotAB As Double = AP.X * AB.X + AP.Y * AB.Y Return d2AP - (APdotAB * APdotAB) / d2AB End Function ''' <summary> ''' 点到线段的距离 ''' </summary> ''' <param name="a">线段一个端点</param> ''' <param name="b">线段一个端点</param> ''' <param name="p">待测点</param> ''' <returns>距离</returns> ''' <remarks></remarks> Public Shared Function PointToLine(a As Point, b As Point, p As Point) As Double Return Math.Sqrt(PointToLine2(a, b, p)) End Function Public Shared Function PointToLine_way2(a As Point, b As Point, p As Point) As Double Dim AP = p - a Dim d2AP = Distance2(AP) If d2AP = 0 Then Return 0.0 End If Dim BP = p - b Dim d2BP = Distance2(BP) If d2BP = 0 Then Return 0.0 End If Dim AB = b - a Dim d2AB = Distance2(AB) If d2AB = 0 Then '3 Return Math.Sqrt(d2AP) End If If d2BP > d2AB + d2AP Then '1 Return Math.Sqrt(d2AP) End If If d2AP > d2AB + d2BP Then '2 Return Math.Sqrt(d2BP) End If Dim x = Math.Sqrt(d2AP), y = Math.Sqrt(d2BP), z = Math.Sqrt(d2AB) Dim l = (x + y + z) / 2 Dim s = Math.Sqrt(l * (l - x) * (l - y) * (l - z)) Return 2 * s / z End Function Public Shared Sub test() Dim a As New Point(4, 2) Dim b As New Point(2, 4) Dim c As New Point(3, 4) Dim ans As Double Dim sw As New Stopwatch() Dim count = 10000000 sw.Reset() sw.Start() For i = 1 To count ans = PointToLine(a, b, c) Next sw.Stop() Debug.Print(ans) Debug.Print(sw.ElapsedMilliseconds.ToString() + "ms") sw.Reset() sw.Start() For i = 1 To count ans = PointToLine_way2(a, b, c) Next sw.Stop() Debug.Print(ans) Debug.Print(sw.ElapsedMilliseconds.ToString() + "ms") End Sub End Class
way2为参考文献(http://blog.csdn.net/yjukh/article/details/5213577)的方法, 还求了个面积, 然后求高, 十分奇妙。里面需要多次开方, 我认为性能损失很大。经过简单的测试, 性能似乎确实没有我的好, 除了1、2、3三种情况(然而这三种情况的代码是一样的啊喂 (((摔>_< )。
听说还有一种非常好的矢量算法, 不会。
你居然用vb