理解點線拓撲關係的計算原理

前序

由於業務需要,我學習了判斷點與點、點與線、線與線的關係的算法、理論,這裏彙總下,主要內容有:

  1. 點與點的關係
  2. 點與線的關係
  3. 線與線的關係

 

點與點

點與點關係相對最簡單,使用勾股定理即可:

這是怎樣計算兩個已知座標點之間的距離:

圖2點

 

把兩點名爲 A 和 B

 

 

圖2點

我們用從 A 畫的垂直線和從 B 畫的水平線,形成一個直角三角形

勾股定理告訴我們:

a2 + b2 = c2

 

 

圖2點

標出 A 和 B的座標

xA 代表 A 的 x座標
yA 代表 A 的 y座標

水平距離 a 是 (xA − xB)

垂直距離 b 是 (yA − yB)

 

我們現在可以解 c (兩點之間的距離):

開始:   c2 = a2 + b2
     
代進 a 和 b 的式:   c2 = (xA − xB)2 + (yA − yB)2
     
結果:   c = [(xA-xB)^2+(yA-yB)^2]的平方根

 

 

點與線的關係

這裏分爲:

點到線的最短距離,也叫點線距離或垂線長度, 這個不是今天的重點,細節移步到此

點在線的方位:在左側還是右側?

這裏不得不先講個東西:點乘, 也被稱爲“點積”、“標量積“、”內積“

對於向量a和向量b:                                                 

a和b的點積公式爲:

其中一維向量a和向量b的行列數相同。

點乘的幾何意義是可以用來表徵或計算兩個向量之間的夾角,以及在b向量在a向量方向上的投影,有公式:

 

推導過程如下,首先看一下向量組成:

 

 

定義向量:

 

 

根據三角形餘弦定理有:

根據關係c=a-b(a、b、c均爲向量)有:

即:

 

向量a,b的長度都是可以計算的已知量,從而有a和b間的夾角θ:

 

 

根據這個公式就可以計算向量a和向量b之間的夾角。從而就可以進一步判斷這兩個向量是否是同一方向,是否正交(也就是垂直)等方向關係,具體對應關係爲:


     a·b>0    方向基本相同,夾角在0°到90°之間

     a·b=0    正交,相互垂直  

     a·b<0    方向基本相反,夾角在90°到180°之間 

這樣就能判斷點在直線哪邊。

 

線與線的關係

常用問題:

線與線是否相交?

判斷兩條線段是否相交有兩步:
①快速排斥計算
②跨立計算

快速排斥
給出線條AB、CD,如果以AB、CD爲對角線的矩形不相交,那麼AB、CD也必不可能相交;如果矩形相交,那麼需要再通過跨立計算進行判斷。對於矩形不相交,有下面兩種情況:

對於上面兩種情況,可以分成四類來討論:
①AB兩座標中最大的x值 小於 CD兩座標中最小x值
②CD兩座標中最大的x值 小於 AB兩座標中最小x值
③AB兩座標中最大的y值 小於 CD兩座標中最小y值
④CD兩座標中最大的y值 小於 AB兩座標中最小y值

只要滿足了以上四種的其中一種,就可以認爲AB與CD不相交。

跨立計算

首先,這裏需要用到向量叉乘的算法:其中ABCD是三維空間上的向量,與xOy平面平行。

其次,如下圖。AB與CD相交必然有A、B在線段CD兩邊,C、D在線段AB兩邊。
根據上面的公式和右手螺旋法則:

左邊是“左手法則”, 右邊是“右手法則”, 用手錶示爲下圖:

 

如果相交,AB X AC的z座標值z1與AB X AD的z座標值z2必然異號;同樣的,DC X DA的z座標值z3與DC X DB的z座標值z4也必然異號

兩個向量ab的叉積寫作a × b(有時也被寫成a ∧ b,避免和字母x混淆)。叉積可以被定義爲:

\mathbf{a}\times\mathbf{b} = \mathbf\hat{n} \left| \mathbf{a} \right| \left| \mathbf{b} \right| \sin \theta

在這裏θ表示ab之間的角度(0° ≤ θ ≤ 180°),它位於這兩個矢量所定義的平面上。而n是一個與ab垂直單位矢量

特別的,如果B在CD上時,求得的z座標值是0。所以只要同時滿足z1 X z2 ≤ 0,z3 X z4 ≤ 0,就能保證必然相交。

參考代碼(Go)

type Point struct {
	X float64
	Y float64
}

type Segment struct {
	A Point
	B Point
}


// IsSegmentsIntersect 2個線段是否相交
func IsSegmentsIntersect(line1 Segment, line2 Segment, containsEnds bool) bool {
   //判斷矩形相交
   if math.Min(line1.A.X, line1.B.X) > math.Max(line2.A.X, line2.B.X) ||
      math.Max(line1.A.X, line1.B.X) < math.Min(line2.A.X, line2.B.X) ||
      math.Min(line1.A.Y, line1.B.Y) > math.Max(line2.A.Y, line2.B.Y) ||
      math.Max(line1.A.Y, line1.B.Y) < math.Min(line2.A.Y, line2.B.Y) {
      return false
   }
   //判斷點的位置:如果任意線段的2個端點在另一條線的兩側,則兩線相交
   a1 := IsPointOnLine(line1.A, line2)
   b1 := IsPointOnLine(line1.B, line2)
   a2 := IsPointOnLine(line2.A, line1)
   b2 := IsPointOnLine(line2.B, line1)

   // a*b < 0表示2個端點在另一條線的兩端
   x1 := a1 * b1
   x2 := a2 * b2
   if containsEnds && (x1 == 0 || x2 == 0) {
      return true
   }
   isCross := a1*b1 < 0 && a2*b2 < 0
   return isCross
}
// IsPointOnLine 判斷點是否在線段上, 返回值 > 0 在右側, = 0 在線上, < 0 在左側
func IsPointOnLine(p Point, s Segment) float64 {
	return (s.B.Y-p.Y)*(s.A.X-p.X) - (s.A.Y-p.Y)*(s.B.X-p.X)
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章