D3-Force updating parameters after initializing graph

落花浮王杯 提交于 2019-12-10 10:07:06

问题


In D3 v4 with the force module, how do you update the parameters of the simulation once you have initialized the graph?

More precisely, I am trying to change the .forceLink and .forceManyBody of the force directed graph when the user clicks one of its nodes.

 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

So far I have been able to update it by duplicating the simulation under the connectedNodes function:

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

Although this works it is very redundant. Is there a way in which the simulation can be refreshed with the new parameters? I have tried the following but it does not work

function connectedNodes() { 

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

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

回答1:


You need a reference to the forces you would like to update. This can be done using either of two ways:

  1. As pointed out by Cool Blue in their comment, you can easily get a reference to the force by calling simulation.force() passing in just the name of the force it was registered with in the first place. If we had, supposedly, created our simulation while passing in an anonymous, in-place force like so:

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

    Later on the force can be obtained by its name whenever needed:

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

    Personally, I like this approach and would prefer it over the second one, whenever possible, because I do not like having to many references / variables around.

  2. Keep a reference to the force when creating it.

    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
    

No matter, which way you chose, you may then update your force by doing something like

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

This will take the new value into account once the next tick is calculated. If the simulation has already come to a stop or you just want to heat it up again you may call

simulation.alpha(1).restart();

I have set up an example which shows these live updates when you hover over the SVG. This will update the linkDistance and restart the force layout.



来源:https://stackoverflow.com/questions/38995816/d3-force-updating-parameters-after-initializing-graph

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!