【问题标题】:D3-Force updating parameters after initializing graphD3-初始化图后强制更新参数
【发布时间】:2016-08-17 11:49:15
【问题描述】:

在带有 force 模块的 D3 v4 中,初始化图形后如何更新模拟参数?

更准确地说,当用户单击其中一个节点时,我正在尝试更改力有向图的 .forceLink.forceManyBody

 var node = svg
    .append("g")
    .attr("class", "gnodes")
    .selectAll(".node")
    .data(graph.nodes)
    .enter()
    .append("g")
    .attr("class", "node")
    .on('dblclick', connectedNodes); //calls for change in simulation parameters

到目前为止,我已经能够通过在 connectedNodes 函数下复制模拟来更新它:

function connectedNodes() {

//new parameters
linkDistance = 5;
fCharge = -10;

//replicates the initial simulation code
simulation = d3.forceSimulation()
    .force("link", d3.forceLink()
        .id(function(d) {return d.id;})
        .distance(linkDistance)
        )
    .force("collide", d3.forceCollide()
        .radius(function(d){return d.r + 10})
        .strength(1)
        )
    .force("charge", d3.forceManyBody()
        .strength(fCharge)
        )
    .force("center", d3.forceCenter(width / 2, height / 2));

simulation.nodes(graph.nodes).on("tick", ticked);

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

虽然这行得通,但它是非常多余的。有没有办法用新参数刷新模拟?我尝试了以下方法,但它不起作用

function connectedNodes() { 

//new parameters
linkDistance = 5;
fCharge = -10;

//restart simulation with new parameters
simulation.restart();
}

【问题讨论】:

标签: d3.js force-layout


【解决方案1】:

您需要参考您想要更新的力量。这可以使用以下两种方式之一来完成:

  1. 正如Cool Blue 在他们的comment 中指出的那样,您可以通过调用simulation.force() 来轻松获取对部队的引用,只需传入最初注册的部队的名称即可。如果我们假设我们在传递匿名的原地力时创建了我们的模拟,如下所示:

    var simulation = d3.forceSimulation()
      .force("link", d3.forceLink()            // "link" is the name to register the force
        .id(function(d) { return d.id; })
        .distance(linkDistance)
      );
    

    以后在需要的时候可以通过它的名字来获取原力:

    var forceLink = simulation.force("link");  // Get the force by its name
    

    就我个人而言,我喜欢这种方法,并且如果可能的话,我更喜欢第二种方法,因为我不喜欢周围有很多引用/变量。

  2. 在创建力时保留对力的引用。

    var forceLink = d3.forceLink()      // Keep a reference to the force
      .id(function(d) { return d.id; })
      .distance(linkDistance);
    
    var simulation = d3.forceSimulation()
      .force("link", forceLink )        // Pass the reference when creating the simulation
    

无论您选择哪种方式,您都可以通过执行类似的操作来更新您的力量

linkDistance += 10;
forceLink.distance(linkDistance);

一旦计算出下一个分时,这将考虑新值。如果模拟已经停止,或者您只是想再次加热,您可以致电

simulation.alpha(1).restart();

我设置了一个example,当您将鼠标悬停在 SVG 上时,它会显示这些实时更新。这将更新linkDistance 并重新启动强制布局。

【讨论】:

  • 我在更新 forcecollide 函数时遇到问题。该图首先被初始化,然后在用户单击时更改。在此更改期间,某些节点的大小会根据您的建议进行修改,模拟力会被召回。所有的力都起作用,除了 forceCollide 没有更新以考虑新的半径。我正在编辑问题以包含一些代码。
  • @PepeG-a 这本身就是一个问题。请不要在您的问题中编辑此内容,因为它只会让读者感到困惑。而是发布一个新的并在其中放回对这个的引用。一旦你发布它,我会调查它。
  • 您可以使用simulation.force("link") 轻松获得参考,这就是它们被命名的原因。
  • @CoolBlue 非常感谢您的评论!你是绝对正确的!我不确定,这怎么可能逃脱了我。我想,有时你只是太专注于自己的解决方案而没有注意到另一个更明显的解决方案。然而,我的核心陈述是你需要参考,我很高兴,我能坚持这一点;-)
  • @altocumulus,也许我变老了,但在学习新东西时我总是会这样;)v4 有很多变化,尤其是力模拟;有很多事情要做。我建议的另一件事是使用forceLink.initialize(simulation.nodes()) 来更新具有函数访问器的力。这也将重新计算内部值数组。
猜你喜欢
  • 1970-01-01
  • 2014-03-21
  • 2013-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-21
  • 2012-07-12
  • 2020-08-06
相关资源
最近更新 更多