【问题标题】:d3 force directed layout - link distance priorityd3 强制定向布局 - 链接距离优先
【发布时间】:2016-11-10 05:59:24
【问题描述】:

在 d3 中使用强制导向布局,如何在保持良好图形布局的同时优先考虑链接距离?

如果我指定动态链接距离,但保留默认费用,我的图表距离会被费用函数变形,不再是准确的距离:

但是,如果我移除费用,图表将如下所示:

任何建议表示赞赏!

【问题讨论】:

标签: javascript d3.js graph distance d3-force-directed


【解决方案1】:

如果我理解正确,我相信有一个潜在的解决方案。

要获得准确的链接距离,您需要将电荷和碰撞力设置为零,但正如您的图片所暗示的那样,节点的间距不会考虑其他节点,而只是它们共享链接的那些节点和。当 d3.force 初始化叶序排列中没有 x,y 值的节点时,链接和节点将以意想不到的方式聚集。但是,如果在整个模拟过程中施加排斥力,间距会得到改善,但距离会失真。

可能的解决方案是最初使用排斥力,因为您需要根据链接将节点分成可识别的集群。然后,在它们分开后,将排斥力减小到零,以便施加的唯一力与所需的链接距离有关。

这需要您随着图表的演变而修改刻度函数中的力。这还要求所有链接距离彼此兼容(节点三角形不能有两个角相隔 100 像素,而其余角与其他两个角相距 10 像素)。

在简单的情况下,这样的事情可能会在 tick 函数中起作用:

var alpha = this.alpha();   // starts at 1 by default, simulation ends at zero

var chargeStrength; // a multiplier for charge strength

if ( alpha > 0.2 ) {
    chargeStrength = (alpha - 0.2 / 0.8); // decrease for the first portion of the simulation
}
else {
    chargeStrength = 0; // leave at zero and give the link distance force time to work without competing forces
}

对于更复杂的可视化,您可以通过减少 alphaDecay 来留出更多的冷却时间,或者为更简单的可视化增加它。

我在这里做了一个简单的例子,在可视化的末尾记录了距离(我在下面的 sn-p 中增加了 alphaDecay 以加快速度,但以牺牲精度为代价,但它仍然很不错)和参考所需的距离。

var graph = {
  nodes: d3.range(15).map(Object),
  links: [
    {source:  0, target:  1, distance: 20 },
    {source:  0, target:  2, distance: 40},
    {source:  0, target:  3, distance: 80},
    {source:  1, target:  4, distance: 20},
    {source:  1, target:  5, distance: 40},
    {source:  1, target:  6, distance: 80},
    {source:  2, target:  7, distance: 12},
    {source:  2, target:  8, distance: 8},
    {source:  2, target:  9, distance: 6},
    {source:  3, target:  10, distance: 10},
    {source:  3, target:  11, distance: 10},
    {source:  3, target:  12, distance: 2},
	{source:  3, target:  13, distance: 2},
	{source:  3, target:  14, distance: 2}
  ]
};

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

var color = d3.scaleOrdinal(d3.schemeCategory20);

var simulation = d3.forceSimulation()
    .force("charge", d3.forceManyBody().strength(-30 ))
	.force("link", d3.forceLink().distance(function(d) { return d.distance } ).strength(2) )
    .force("center", d3.forceCenter(width / 2, height / 2))
	.force("collide",d3.forceCollide().strength(0).radius(0))
	.alphaDecay(0.03)
    .velocityDecay(0.4);
	
	
	
  var link = svg.append("g")
      .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("line")
      .attr("stroke-width", 1);

  var node = svg.append("g")
     .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .enter().append("circle")
     .attr("r", 3)
	  
 simulation
      .nodes(graph.nodes)
      .on("tick", ticked);

  simulation.force("link")
      .links(graph.links);

  
  
	  
  function ticked() {
	
	var alpha = this.alpha();
	var chargeStrength;

    if ( alpha > 0.2 ) {
		chargeStrength = (alpha - 0.2 / 0.8);
	}
	else {
		chargeStrength = 0;
	}

	this.force("charge", d3.forceManyBody().strength( -30 * chargeStrength ))
	
	
    link
        .attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });

    node
        .attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
		
	// validate:
	if (alpha < 0.001) {
		link.each(function(d,i) {
		
			var a = d.source.x - d.target.x;
			var b = d.source.y - d.target.y;
		    var c = Math.pow(a*a + b*b, 0.5);
			
			console.log("specified length: " + graph.links[i].distance + ", realized distance: " + c );
		})
	}
  }
.links line {
  stroke: #999;
  stroke-opacity: 0.6;
}

.nodes circle {
  stroke: #fff;
  stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<svg width="500" height="300"></svg>

根据图表的复杂性,您可能需要调整冷却时间、排斥力强度以及在 alpha 冷却时如何更改它、velocityDecay(可能在刻度函数中对其进行修改)和/或距离力自己。

【讨论】:

  • 这是一个不错的解决方案!我从来没有想过要修改运行中的力量。我一定会牢记这一点,因为它可能对其他应用程序也有帮助。赏金赚的很好!
  • 我无法弄清楚的一件事是,您在模拟的初始设置中覆盖了"link" 力。由于只使用名称的最后一个力,这似乎有些多余。你能解释一下吗?
  • 这对我来说是一个非常糟糕的疏忽,不知道我为什么这样做。感谢您指出。
猜你喜欢
  • 2012-10-11
  • 2013-05-10
  • 1970-01-01
  • 2015-05-23
  • 1970-01-01
  • 2018-09-20
  • 1970-01-01
  • 2014-07-09
  • 1970-01-01
相关资源
最近更新 更多