iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0

前面已經把 Dijkstra 寫出來了,接下來終於要開始做視覺化。

我原本的構想是:

  • 使用者可以設定 Node 數量
  • Node ID 使用流水號產生
  • Weight 隨機產生

Node 和 Weight 感覺都不難,真正卡住我的是:

這些 Node 到底要擺在哪裡?

我想到的方式有點像棋盤:先把畫面切成一格一格,再把 Node 放到格子上。這樣每個 Node 都會有自己的位置,之後要畫 Edge 也比較容易。

https://ithelp.ithome.com.tw/upload/images/20261007/20184088L5lSBtyFxZ.png

圖片來源:奇想深淵 Imagination Abyss

但真的開始想怎麼實作後,又冒出幾個問題:

  1. Node 放進棋盤後,要怎麼知道它附近有哪些 Neighbor?
  2. 找到 Neighbor 後,哪些 Node 之間真的要建立 Edge?
  3. 棋盤需要真的畫在畫面上嗎?
  4. 最後又要怎麼把 Node 和 Edge 畫到 Vue 裡?

先整理整個 Graph 產生流程

實作後,我把流程整理成:

Node 數量
    ↓
排進棋盤,取得 row、col
    ↓
找出前面的 Neighbor
    ↓
決定要連哪些 Edge
    ↓
Random Weight
    ↓
產生 Adjacency List
    ↓
交給 Dijkstra

所以第一步不是急著畫 Graph,而是先知道:Node 在哪裡,以及它附近有哪些 Node 可以連線。


怎麼知道相鄰的 Node?哪些 Node 要有 Edge?

我的做法是先把 Node 排進一個「看不見的棋盤」。

程式會根據 Node 數量以及 SVG 的長寬比例,先算出 rows 和 cols。

接著每個 index 都可以換算成自己所在的 row 和 col:

const row = Math.floor(index / cols)
const col = index % cols

知道位置後,就可以找到附近的 Neighbor。

我讓每個 Node 往前面的 Neighbor 找,也就是左邊、上面,部分位置再加入左上和右上。

接著從這些 Neighbor 中隨機挑一個一定要連線,其他的再隨機決定要不要增加 Edge。

這樣每個 Node 至少都會連到前面的 Node,一路往前最後都能連回第一個 Node,避免出現孤立的 Node;額外隨機增加的 Edge,則可以讓 Dijkstra 有不同路線可以比較。


棋盤真的要畫出來嗎?

棋盤比較像是一張看不見的座標紙,只是用來幫我決定每個 Node 應該放在哪裡,所以不用把整個棋盤畫出來,我要的只是點跟線。

前面算出 rows、cols 後,再算每一格在 SVG 上相隔多少距離:

const stepX =
  (VIEW_WIDTH - PADDING * 2) / (cols - 1)

const stepY =
  (VIEW_HEIGHT - PADDING * 2) / (rows - 1)

接著根據 Node 的 row、col 換算真正的 SVG 座標:

x = PADDING + col * stepX
y = PADDING + row * stepY

所以可以把它理解成:

row / col 決定 Node 在第幾格,x / y 決定 Node 最後畫在哪裡。


最後再交給 Vue 畫出 Graph

有了 x、y 之後,Vue 就可以直接使用 SVG 畫出 Node:

<svg viewBox="0 0 820 520">
  <line ... />

  <circle
    v-for="node in graph.nodes"
    :cx="node.x"
    :cy="node.y"
  />

  <text ...>
    {{ node.id }}
  </text>
</svg>

其中:

positions → <circle> → Node
data → toEdges() → <line> → Edge

Edge 只要取得兩端 Node 的 x / y,就能用 <line> 連起來;Weight 則使用 <text> 顯示在線段附近。

這樣整理後我才發現,原本以為只是「隨機產生一張 Graph」,實際上中間還多了一個重要步驟:

先用看不見的棋盤決定 Node 的位置與相鄰關係,再把這些資料轉成真正可以交給 Dijkstra 的 Graph。


最後實際畫出來的樣子

按照前面的方式產生 Node、Edge 和 Weight 後,最後就能在 Vue 裡畫出這張 Graph:

https://ithelp.ithome.com.tw/upload/images/20261007/20184088LBlthWBP5h.png

所以這個版本其實已經可以做到:根據 Node 數量決定位置、隨機產生 Edge 和 Weight,再將產生的 Graph 交給 Dijkstra。

如果想看這一版更完整的實作細節,我另外整理在:

Dijkstra Grid Layout 完整實作紀錄

不過雖然這個版本已經成功做出來,我最後還是決定換另一種方式來畫 Graph。

至於為什麼要換,就留到下一篇繼續。


上一篇
【Day 23】用 TypeScript 寫 Dijkstra:我有最短距離了,現在出發前往終點!
系列文
看得到的演算法:用 Vue 3 打造演算法互動視覺化平台 共 24 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言