简体   繁体   English

D3js 力导向图链接交叉点避免

[英]D3js Force-directed graph link intersections avoid

There is an example of force-directed graph i've tried to draw with the help of the d3.js.有一个我试图在 d3.js 的帮助下绘制的力导向图示例。

I have 3 big questions at all.我有3个大问题。 And this is the code (you can run code snippet below, it might works):这是代码(您可以在下面运行代码片段,它可能会起作用):

 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>

The image looks like this:图像如下所示:

在此处输入图片说明

The first question is: What about to avoid this intersections?第一个问题是:如何避免这种交叉? 在此处输入图片说明 I know that I can't dodge all of edge intersections but I want to minimize them.我知道我不能躲避所有的边缘交叉点,但我想尽量减少它们。 This example is a tree-graph with no cycles.这个例子是一个没有循环的树图。 I know that there is a way to build it without edge crossings.我知道有一种方法可以在没有边缘交叉的情况下构建它。 But I don't know how to do it with this algorithm.但我不知道如何用这个算法来做。
在此处输入图片说明 But still annoying intersection.但是还是很烦人的路口。

The second question is: What about NOT to simulate forces in-time (I need no animation) but just to draw final result?第二个问题是:如何不及时模拟力(我不需要动画)而只是绘制最终结果? When I use forceSimulation.on("end", cb) it is great, but delay between start and stop is big.. but this is graph is just a small example.当我使用forceSimulation.on("end", cb)它很棒,但是开始和停止之间的延迟很大..但这只是一个小例子。 I can't wait so long on a bigger once.我不能等这么久的一次更大的。

And the third question is.. how to apply force-derected settings?第三个问题是..如何应用强制引导设置? Force energy, stiffness, repulsion, damping etc.?力能、刚度、排斥力、阻尼等? Can't find them on d3@5在 d3@5 上找不到它们

The final result my project lead wants is:我的项目负责人想要的最终结果是:

  • no node overlap;无节点重叠;
  • minimize edge-edge intersections;最小化边与边相交;
  • minimize edge-node intersections.最小化边缘节点交叉点。

I'm ready for dialog.我准备好对话了。

I solved this issue by playing with the forceCollide and forceLink distance parameters :我通过使用 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));

The idea is to make unrelated nodes to repel each other, while keeping the link distance short.这个想法是让不相关的节点相互排斥,同时保持较短的链接距离。

It works very well in my case, but my node graph is much simpler than yours.在我的情况下它工作得很好,但我的节点图比你的简单得多。

You apply the force settings in the initialization portion.您在初始化部分应用强制设置。 Here is an example -这是一个例子——

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));

This can be used to solve one of your problems...if you set the "charge" strength to some large negative number like -150, the nodes will be strongly repelled such that they don't overlap, nor do any of their links.这可用于解决您的一个问题……如果您将“电荷”强度设置为一些大的负数,例如 -150,节点将被强烈排斥,因此它们不会重叠,它们的任何链接也不会重叠. If you aren't looking to drag the graph at all then this should be all you need to avoid overlaps.如果您根本不想拖动图形,那么这应该是您避免重叠所需的全部内容。

A side effect of a highly negative charge is that the graph settles quite quickly, and as you don't want to simulate the forces in real time after the initial display, you can call simulation.stop() to freeze or stop the simulation.高度负电荷的副作用是图形很快稳定下来,并且由于您不想在初始显示后实时模拟力,您可以调用simulation.stop()来冻结或停止模拟。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM