簡體   English   中英

D3js 力導向圖鏈接交叉點避免

[英]D3js Force-directed graph link intersections avoid

有一個我試圖在 d3.js 的幫助下繪制的力導向圖示例。

我有3個大問題。 這是代碼(您可以在下面運行代碼片段,它可能會起作用):

 function getRandomInt(max, min = 0) { return Math.floor(Math.random() * (max - min + 1)) + min; } function fdSortShit(g, nodeDimensions) { const gNodes = []; const gLinks = []; g.children().forEach(child => { gNodes.push({ id: child, w: nodeDimensions[child].w, h: nodeDimensions[child].h, radius: Math.sqrt( nodeDimensions[child].w * nodeDimensions[child].w + nodeDimensions[child].h * nodeDimensions[child].h ) / 2 }); }); g.edges().forEach(edge => { gLinks.push({ source: edge.v, target: edge.w }); }); const data = { nodes: gNodes, links: gLinks }; const nodes = data.nodes; const links = data.links; const linkNodeRad = 5; const linkNodes = []; links.forEach((link, idx) => { if (link.source != link.target) { linkNodes.push({ id: `link-node-${idx}`, source: nodes.filter(e => { return e.id == link.source; })[0], target: nodes.filter(e => { return e.id == link.target; })[0], radius: linkNodeRad }); } }); const width = 800; const height = 600; var svg = d3 .select("body") .append("svg") .attr("width", width) .attr("height", height) .attr("viewBox", "-400, -300, 800, 600"); function forceSimulation(nodes, links) { return d3 .forceSimulation(nodes) .force("link", d3.forceLink(links).id(d => d.id)) .force("charge", d3.forceManyBody()) .force("center", d3.forceCenter()) .force( "collision", d3.forceCollide().radius(function(d) { return d.radius; }) ); } var link = svg .selectAll(".link") .attr("stroke", "#fff") .data(links) .enter() .append("line") .attr("class", "link"); var node = svg .append("g") .selectAll("g") .data(nodes) .enter() .append("g"); var circles = node .append("circle") .attr("class", "node") .attr("r", node => { return node.radius; }); var text = node .append("text") .text(d => { return d.id; }) .attr("class", "node-caption") .attr("x", 0) .attr("y", 0); var linkNode = svg .selectAll(".link-node") .data(linkNodes) .enter() .append("circle") .attr("class", "link-node") .attr("r", linkNodeRad); function ticked() { link .attr("x1", d => d.source.x) .attr("y1", d => d.source.y) .attr("x2", d => d.target.x) .attr("y2", d => d.target.y); node.attr("transform", function(d) { return "translate(" + dx + "," + dy + ")"; }); linkNode .attr("cx", function(d) { return (dx = (d.source.x + d.target.x) * 0.5); }) .attr("cy", function(d) { return (dy = (d.source.y + d.target.y) * 0.5); }); } forceSimulation(nodes.concat(linkNodes), links) .on("tick", ticked) .on("end", () => { console.warn("END"); }); } const coords = {}; const size = { min: 10, max: 30 }; const dotStr = "graph g { a--a;a--b;a--b;a--c;a--d;a--e;b--b1;c--c1;c--c2;d--d1;d--d2;d--d3;d--d4;e--e1;v--w;v--x;v--y;w--z;w--w1;x--x1;x--x2;y--y1;y--y2;y--y3;y--y4;z--z1;v--a; }"; const g = graphlibDot.read(dotStr); g.children().forEach(child => { const x = getRandomInt(1024 - 10, 10); const y = getRandomInt(768 - 10, 10); coords[child] = { x: x, y: y, w: getRandomInt(size.max, size.min), h: getRandomInt(size.max, size.min) }; }); fdSortShit(g, coords);
 svg { background-color: lightgray; } circle.node { fill: lightcoral; } circle.link-node { fill: rgba(0, 0, 255, 0.2); /* fill: transparent; */ } line.link { stroke: lightseagreen; } text.node-caption { font: normal 10px courier new; }
 <script src="https://cdn.jsdelivr.net/npm/graphlib-dot@0.6.2/dist/graphlib-dot.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

圖像如下所示:

在此處輸入圖片說明

第一個問題是:如何避免這種交叉? 在此處輸入圖片說明 我知道我不能躲避所有的邊緣交叉點,但我想盡量減少它們。 這個例子是一個沒有循環的樹圖。 我知道有一種方法可以在沒有邊緣交叉的情況下構建它。 但我不知道如何用這個算法來做。
在此處輸入圖片說明 但是還是很煩人的路口。

第二個問題是:如何不及時模擬力(我不需要動畫)而只是繪制最終結果? 當我使用forceSimulation.on("end", cb)它很棒,但是開始和停止之間的延遲很大..但這只是一個小例子。 我不能等這么久的一次更大的。

第三個問題是..如何應用強制引導設置? 力能、剛度、排斥力、阻尼等? 在 d3@5 上找不到它們

我的項目負責人想要的最終結果是:

  • 無節點重疊;
  • 最小化邊與邊相交;
  • 最小化邊緣節點交叉點。

我准備好對話了。

我通過使用 forceCollide 和 forceLink 距離參數解決了這個問題:

var simulation = d3.forceSimulation()                   
      .force('link', d3.forceLink().id(d => d.id).distance(100).strength(1))
      .force('charge', d3.forceManyBody())               // ^ change this value
      .force('collide', d3.forceCollide(110)) // change this value
      .force('center', d3.forceCenter(width / 2, height / 2));

這個想法是讓不相關的節點相互排斥,同時保持較短的鏈接距離。

在我的情況下它工作得很好,但我的節點圖比你的簡單得多。

您在初始化部分應用強制設置。 這是一個例子——

var simulation = d3.forceSimulation()                              //Modify link distance/strength here
    .force("link", d3.forceLink().id(function (d) { return d.id; }).distance(80).strength(1))
    .force("charge", d3.forceManyBody().strength(-15)) //Charge strength is here
    .force("center", d3.forceCenter(width / 2, height / 2));

這可用於解決您的一個問題……如果您將“電荷”強度設置為一些大的負數,例如 -150,節點將被強烈排斥,因此它們不會重疊,它們的任何鏈接也不會重疊. 如果您根本不想拖動圖形,那么這應該是您避免重疊所需的全部內容。

高度負電荷的副作用是圖形很快穩定下來,並且由於您不想在初始顯示后實時模擬力,您可以調用simulation.stop()來凍結或停止模擬。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM