這一章把直線連線改為折線連線,沿用原來連線點的關係資訊。關於折線的計算,使用的是開源的 AStar 演算法進行路徑規劃,啟發方式為 曼哈頓距離,且不允許對角線移動。
請大家動動小手,給我一個免費的 Star 吧~
大家如果發現了 Bug,歡迎來提 Issue 喲~
github原始碼
gitee原始碼
示例地址
靈感來源主要來自於下面優秀的文章:
關聯線探究,如何連線流程圖的兩個節點
主要參考了:如何挑選連線點及其真正的出入口、演算法的選型。具體程式碼沒有仔細瞭解,畢竟佈局和元素的想法不一樣,沒必要參考程式碼。
路徑規劃之 A* 演算法
主要了解一下演算法的介紹。
歐式距離、曼哈頓距離、切比雪夫距離、Octile距離
主要了解一下 AStar 演算法的各種啟發方式的差異。
路徑規劃視覺化動畫
形象的感受路徑搜尋的差異。
至於演算法本身,在目前階段下不是必須深入分析,這裡應用為主。
最優路徑
參考這張圖,基於當前案例,可以把折線想象為路徑,目標就是查詢最優路徑,例如:
又或者:
上面明顯不是我們直覺最優的路徑選擇,如:
- 太貼近節點了
- 轉彎太多
更希望是這樣:
開啟除錯模式,來說說連線點的出入口:
人為地,距離”連線點“偏離一些,定義所謂的”出入口“(途中綠色的點),作為折線真的起點和終點。
把連線先移除,看看其他點:
一共定義了 3 種點:
- 連線點(紅色)
- 出入口(綠色)
- 途徑點(藍色)
關於途徑點,是人為挑選的,主要(中心點除外)來自於圖中不同顏色區域(線框),這裡定義了 ?種區域:
- 連線點最小區域
為什麼不叫節點區域呢?因為此前設計的連線點是動態的,它可以節點內部的其他位置,只是目前定義的都是上下左右邊緣而已。所以,它可能比節點區域更小。
- 連線不可透過區域
- 連線不可透過擴充套件區域
兩個區域共同所在的最小區域
- 連線透過區域
- 連線透過擴充套件區域
同理,兩個區域共同所在的最小區域
演算法建模(關鍵)
上面說了那麼多點和區域,最終目的就是為了建模,可供演算法使用。
這個模型,就是一個陣列矩陣 matrix,可以理解成一個格子地圖,如:
0 代表可透過,1 代表不可透過(稱之為“牆”吧),對應的陣列矩陣,就是
[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]
計算結果是一個途徑格子座標陣列:
座標就是陣列1、2層下標,可以視作 x、y 軸。
[
[5, 3],
[6, 3],
[7, 3],
[8, 3],
[8, 4],
[8, 5]
]
主要問題來了,畢竟在這裡的畫板,不同於演算法示例那樣“走格子”,800x800 的畫布大小,不可能建一個 800x800 陣列矩陣,效能可吃不消,別說更大的畫布了。
所以,如何建模才是這個案例畫折線的關鍵!
這裡,拿一個大一點的例子說明:
既然,拿“畫素”當作格子不現實,可以拿“點”作為格子不就好了嗎?
陣列矩陣變成:
[
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]
]
這裡缺少了“牆”,哪些是牆?其實就是上面說的不可透過區域:
“牆”不同於連線點,需要補充一些點:
陣列矩陣變成(增加了 2 列、2 行):
[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]
然後給陣列矩陣設定“牆”:
這裡把 2 定義為牆,所以 0、1 均能透過,方便後面區分和理解。
[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 0],
[0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 0],
[0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 0],
[0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 0],
[0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 0],
[0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]
連線點、連線線的出入口不應該是“牆”,調整一下:
設定為 1,方便區分
起點:[2, 3]
終點:[8, 5]
現在交給演算法,計算結果得出:
就是:
畫成線:
主要思路就是如此,雖然不是完美的,請看:
原因主要是演算法並不知道拐彎的“代價”,暫且如此吧。
思路的介紹到此為止,下一章再說說程式碼大概是如何實現的。
More Stars please!勾勾手指~
原始碼
gitee原始碼
示例地址