| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- /**
- * 感谢 https://github.com/chaangliu/ForceDirectedLayout/blob/master/javascript/force-directed.js
- * A force directed graph layout implementation by liuchang on 2018/05/10.
- */
- const CANVAS_WIDTH = 1000
- const CANVAS_HEIGHT = 1000
- let k
- let mNodeList = []
- let mEdgeList = []
- let mDxMap = {}
- let mDyMap = {}
- let mNodeMap = {}
- export function ForceDirected(data = {}) {
- // generate nodes and edges
- // for (let i = 0; i < 20; i++) {
- // mNodeList.push(new Node(i))
- // }
- k = 0
- mNodeList = []
- mEdgeList = []
- mDxMap = {}
- mDyMap = {}
- mNodeMap = {}
- let nodeList = data.nodeList
- for (let i = 0; i < nodeList.length; i++) {
- let node = nodeList[i]
- mNodeList.push(node)
- }
- // for (let i = 0; i < 20; i++) {
- // let edgeCount = Math.random() * 8 + 1
- // for (let j = 0; j < edgeCount; j++) {
- // let targetId = Math.floor(Math.random() * 20)
- // let edge = new Edge(i, targetId)
- // mEdgeList.push(edge)
- // }
- // }
- // line 转 edge
- let lineList = data.lineList
- for (let i = 0; i < lineList.length; i++) {
- let line = lineList[i]
- let edge = new Edge(line.from, line.to)
- mEdgeList.push(edge)
- }
- if (mNodeList && mEdgeList) {
- k = Math.sqrt(CANVAS_WIDTH * CANVAS_HEIGHT / mNodeList.length)
- }
- for (let i = 0; i < mNodeList.length; i++) {
- let node = mNodeList[i]
- if (node) {
- mNodeMap[node.id] = node
- }
- }
- // 随机生成坐标. Generate coordinates randomly.
- let initialX, initialY, initialSize = 40.0
- for (let i in mNodeList) {
- initialX = CANVAS_WIDTH * 0.5
- initialY = CANVAS_HEIGHT * 0.5
- mNodeList[i].x = initialX + initialSize * (Math.random() - 0.5)
- mNodeList[i].y = initialY + initialSize * (Math.random() - 0.5)
- }
- // 迭代200次. Iterate 200 times.
- for (let i = 0; i < 200; i++) {
- calculateRepulsive()
- calculateTraction()
- updateCoordinates()
- }
- // console.log(JSON.stringify(new Result(mNodeList, mEdgeList)))
- // 坐标添加px
- for (let i = 0; i < mNodeList.length; i++) {
- let node = mNodeList[i]
- node.left = node.x + 'px'
- node.top = node.y + 'px'
- node.x = undefined
- node.y = undefined
- }
- data.nodeList = mNodeList
- // console.log(data)
- return data
- }
- function Node(id = null) {
- this.id = id
- this.x = 22
- this.y = null
- }
- function Edge(source = null, target = null) {
- this.source = source
- this.target = target
- }
- /**
- * 计算两个Node的斥力产生的单位位移。
- * Calculate the displacement generated by the repulsive force between two nodes.*
- */
- function calculateRepulsive() {
- let ejectFactor = 6
- let distX, distY, dist
- for (let i = 0; i < mNodeList.length; i++) {
- mDxMap[mNodeList[i].id] = 0.0
- mDyMap[mNodeList[i].id] = 0.0
- for (let j = 0; j < mNodeList.length; j++) {
- if (i !== j) {
- distX = mNodeList[i].x - mNodeList[j].x
- distY = mNodeList[i].y - mNodeList[j].y
- dist = Math.sqrt(distX * distX + distY * distY)
- }
- if (dist < 30) {
- ejectFactor = 5
- }
- if (dist > 0 && dist < 250) {
- let id = mNodeList[i].id
- mDxMap[id] = mDxMap[id] + distX / dist * k * k / dist * ejectFactor
- mDyMap[id] = mDyMap[id] + distY / dist * k * k / dist * ejectFactor
- }
- }
- }
- }
- /**
- * 计算Edge的引力对两端Node产生的引力。
- * Calculate the traction force generated by the edge acted on the two nodes of its two ends.
- */
- function calculateTraction() {
- let condenseFactor = 3
- let startNode, endNode
- for (let e = 0; e < mEdgeList.length; e++) {
- let eStartID = mEdgeList[e].source
- let eEndID = mEdgeList[e].target
- startNode = mNodeMap[eStartID]
- endNode = mNodeMap[eEndID]
- if (!startNode) {
- console.log('Cannot find start node id: ' + eStartID + ', please check it out.')
- return
- }
- if (!endNode) {
- console.log('Cannot find end node id: ' + eEndID + ', please check it out.')
- return
- }
- let distX, distY, dist
- distX = startNode.x - endNode.x
- distY = startNode.y - endNode.y
- dist = Math.sqrt(distX * distX + distY * distY)
- mDxMap[eStartID] = mDxMap[eStartID] - distX * dist / k * condenseFactor
- mDyMap[eStartID] = mDyMap[eStartID] - distY * dist / k * condenseFactor
- mDxMap[eEndID] = mDxMap[eEndID] + distX * dist / k * condenseFactor
- mDyMap[eEndID] = mDyMap[eEndID] + distY * dist / k * condenseFactor
- }
- }
- /**
- * 更新坐标。
- * update the coordinates.
- */
- function updateCoordinates() {
- let maxt = 4, maxty = 3 // Additional coefficients.
- for (let v = 0; v < mNodeList.length; v++) {
- let node = mNodeList[v]
- let dx = Math.floor(mDxMap[node.id])
- let dy = Math.floor(mDyMap[node.id])
- if (dx < -maxt) dx = -maxt
- if (dx > maxt) dx = maxt
- if (dy < -maxty) dy = -maxty
- if (dy > maxty) dy = maxty
- node.x = node.x + dx >= CANVAS_WIDTH || node.x + dx <= 0 ? node.x - dx : node.x + dx
- node.y = node.y + dy >= CANVAS_HEIGHT || node.y + dy <= 0 ? node.y - dy : node.y + dy
- }
- }
- function Result(nodes = null, links = null) {
- this.nodes = nodes
- this.links = links
- }
|