JSPlumb.js 绘制关系拓扑图(力导向布局算法简单实现)

JSPlumb.js 绘制关系拓扑图(力导向布局算法简单实现)

d3 echarts 都可以画关系拓扑图(有环),但是总觉得不是自己想要的,改起来也很麻烦,所以可以用JSPlumb 自己绘制自己的关系拓扑图,拓扑图 = 绘图 + 自动布局算法。jsplumb 可以实现绘自己的结点,而自动布局则可以采用 力导向布局算法。
这里参考的是 https://www.jianshu.com/p/d3c64a39535a

随机生成结点 与结点的位置

 /** * 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 = { };
var json ={ } ;

function ForceDirected() {
//generate nodes and edges
for (let i = 0; i < 20; i++) {
mNodeList.push(new Node(i));
}

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);
}
}
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 * .5;
initialY = CANVAS_HEIGHT * .5;
mNodeList[i].x = initialX + initialSize * (Math.random() - .5);
mNodeList[i].y = initialY + initialSize * (Math.random() - .5);
}

//迭代200次. Iterate 200 times.
for (let i = 0; i < 200; i++) {
calculateRepulsive();
calculateTraction();
updateCoordinates();
}

json = JSON.stringify(new Result(mNodeList, mEdgeList));
//console.log();

}

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 =%

点赞