流程圖——正交連線的演算法的一種簡單實現

caijiaoasi發表於2018-08-15

起源

要用svg做一個流程圖類似visio的連線,如下圖的

流程圖——正交連線的演算法的一種簡單實現

其實有很多庫已經實現了流程圖,比如 jointjs,gojs,jsplumb 等等。可惜都不是免費的。

分析

如果要做的簡單呢,就用貝塞爾曲線就好了,只需要提供起點終點兩個點的座標,就能用簡單的演算法生成一個svg 的path了。這樣演算法網上應該找得到。

但是如果要用正交連線來實現,這個複雜度就上升了幾個檔次。我們來看看正交連線的多個情況:

流程圖——正交連線的演算法的一種簡單實現

假設起點往下,目標節點在起始節點的右下方,那麼可能存在的情況一共有四種。考慮到目標節點相對起始節點的方位有四種可能,那麼所有連線的可能有4*4=16 種,再考慮到起點出發方向也有四種,那麼所有的可能性有16 *4 = 64種。也就是說,如果你要用if else 來寫,需要寫64個分支。

怎麼辦

當然不必寫64個if else ,寫程式不就是要抽象出規律嘛! 將這個連線分成這麼幾個部分:

流程圖——正交連線的演算法的一種簡單實現
那麼這個連線規律在哪裡?有的情況它轉了一個彎,有的情況它又轉了兩個彎,這個規律太難找了。下圖舉了兩個例子,同樣是終點相對於起點在左上角,第一種情況是轉了兩個彎,第二種情況是轉了一個彎。

流程圖——正交連線的演算法的一種簡單實現

但是,我用了十八牛二虎之力找到了它的規律。

真相

這個連線的最終走向與這幾個條件有關:

  1. 起點的出發方向
  2. 終點的進入方向
  3. 終點相對於起點的方向
  4. 起點開始的最小延伸線
  5. 終點進入前的最小延伸線 (看下圖)

流程圖——正交連線的演算法的一種簡單實現
6. 正交線的轉折時機(我實際在做的時候,是用了中點轉折)

實現思路

  1. 因為使用svg 的path 來畫正交線。核心點是要找到轉折點的座標(其實用什麼都一樣)。
  2. 先獲取起點到終點的向量(下文稱為直接向量),以及兩個正交向量(下文稱為 直接向量的水平向量和豎直向量)
    流程圖——正交連線的演算法的一種簡單實現
    3.開始計算正交線了,先要獲取到正交線的起始方向。可以看到:起點方向和直接方向的豎直向量 方向相反,正交線沒有必要往下走,因此正交線的起始一定是水平的。事實上我們並不知道豎直和水平的具體情況,在實際操作中是這樣的:先從直接向量的水平向量和豎直向量中找到與起點方向平行的向量,再判斷是否同向,如果是同向,則正交線的起始方向與起點方向同向,如果不是則取另一個垂直方向。
判斷平行: x0 * y1-x1 * y0 === 0;
判斷是否同公式,向量夾角為0度,則向量點積為1:x0 * y0 + x1 * y1 === 1
複製程式碼
  1. 然後要獲取正交線的最終方向。也是一樣的思路,如果終點的進入方向與直接方向相反則取垂直方向,如果相同則取同向
  2. 得到了正交線起始方向和最終方向,現在需要獲取正交線中間轉彎的情況。判斷這正交線起始方向和最終方向是否同向,如果同向,則需要轉兩個彎,如果不是同向,則只需轉一個彎。

如果 正交線起始方向 == 正交線最終方向
那麼 轉彎數 = 2
否則 轉彎數 = 1
複製程式碼
  1. 如果轉一個彎,那麼獲取轉彎的座標點

轉彎的座標點 = 正交線的第一個點 + 正交線起始方向向量
複製程式碼

如果轉兩個彎,先獲取第一個轉彎點,既正交線始末方向的中點

第一個轉彎點= 正交線的第一個點 + 正交線起始方向向量 * 0.5
複製程式碼

再獲取第二個轉彎點 ,

非起始方向向量 = 從直接向量的水平向量和豎直向量 中取出與起始方向向量垂直的那個向量
第二個轉彎點 = 第一個轉彎點 + 非起始方向向量
複製程式碼
  1. 最終生成所有點的座標集合

所有點的座標集合 = [起點,正交線的起點,...正交線的轉彎點,正交線的終點,終點]
複製程式碼

總結

這個演算法的計算量不是很大,效能ok,適合邊拖曳邊重繪。但不是尋路演算法,意味著沒有避讓節點的功能。

下一步要用曼哈頓演算法來做一個。

這個demo拿去玩

desdesdesgo.github.io/flow-connec…

生成點的演算法 github.com/Desdesdesgo…

相關文章