Giter Site home page Giter Site logo

Comments (10)

Fil avatar Fil commented on April 28, 2024 1

We finally decided against this change. Use .stop() then .restart() when the data is ready.

from d3-force.

mbostock avatar mbostock commented on April 28, 2024

It starts upon creation. That is what is meant by “automatically”. If you prefer you can explicitly stop the force layout upon creation and then start it again later. (However stopping the force layout does not prevent the forces from being reinitialized when the nodes change, since starting and stopping only affects the internal timer, and the simulation can be run manually by calling simulation.tick.)

You can change the nodes after the force layout starts. Yes, this does cause the constituent forces for be reinitialized, but this cost is typically negligible so it doesn’t seem urgently necessary to optimize this. I don’t want to debounce this work until the next tick because it makes debugging slightly harder, but I suppose it would be an option.

You could not implement the type of simulation.start API you propose without the simulation knowing about all possible forces and their parameters. The design is intended to be generic and extensible in that the simulation can be run with arbitrary forces that have arbitrary behavior and arbitrary parameters; the simulation does not know what links are, for instance. Furthermore forces can be bound and unbound dynamically, so it doesn’t make sense to pass all the parameters through start. I suppose you could use weakly (string) named parameters defined in the simulation as is done with the forces, but that seems worse than having the forces define their own parameters and by worry about potential name conflicts.

There is a slight problem with the example on that the simulation can run for a tick with an empty graph (no nodes). This is mostly harmless especially since the ticked listener is not yet registered, but it does mean that the simulation can cool slightly while the data is loading. I can see if there is an easy way to avoid this, though I don’t think it will require any changes to the API. (Deferring creation of the simulation until the data loads, for example, would do it.)

from d3-force.

urbanhop avatar urbanhop commented on April 28, 2024

Think its fine as it is. After some hours working with it and now reading your reply, I got the feeling for the API, which favors convenience over minor inefficiencies.

Thank you for your time.

from d3-force.

mbostock avatar mbostock commented on April 28, 2024

I am reopening this issue as a reminder to myself to fix the issue you raised with the example. Thank you for your feedback!

from d3-force.

urbanhop avatar urbanhop commented on April 28, 2024

I ended up with this sequence:

var canvas = document.querySelector("canvas"),
    context = canvas.getContext("2d"),
    width = canvas.width,
    height = canvas.height;

d3.json("miserables.json", function (error, graph) {
    if (error) throw error;

    var simulation = d3.forceSimulation()
        .nodes(graph.nodes)
        .force("link",
            d3.forceLink()
            .id(d => d.id)
            .links(graph.links))
        .force("charge", d3.forceManyBody())
        .force("center", d3.forceCenter(width / 2, height / 2))
        .on("tick", ticked);

    function ticked() { ...

which is simple, but limited to cases where reheating and restarting is not required. (any maybe has even more issues I am not aware of)

from d3-force.

mbostock avatar mbostock commented on April 28, 2024

Some options:

  1. Defer automatic starting until the first tick listener is scheduled. This way, a simulation won’t start until someone is listening. This would be backwards-compatible with the current behavior, avoids needing simulation.stop when computing a static layout, and reduces the likelihood of the simulation starting before it‘s ready. (I say likelihood because you can still register your tick listener before configuring the simulation… This would fix the example, however.)

  2. Disable automatic starting, period. This would not be backwards-compatible with d3@4, but it’s how d3@3 did it.

  3. Defer automatic starting until nodes is non-empty. This feels a little too magical because a simulation could have forces that do things even if the simulation has no nodes.

  4. Just fix the example to call simulation.stop upon creation, and then simulation.start when the data is loaded. Or don’t define the simulation until the data is loaded.

from d3-force.

IPWright83 avatar IPWright83 commented on April 28, 2024

@mbostock on option 1, wouldn't that cause a problem if you opt for a static layout with no progressive rendering? I'd typically run the force and subscribe to end to render the positioned output, rather than on each tick.

from d3-force.

rohit0710 avatar rohit0710 commented on April 28, 2024

@mbostock
I happen to build a force layout. My requirement is on clicking of a node several other nodes are to be added to the graph and are to be linked to the initial clicked node. After I update my node_array and link_array, the tick function runs its implementation to plot the nodes. But I see that all links appear on the graph but not all nodes, the nodes are situated at the left corner and multiple clicks of the initial node the nodes come back and sit in their position.

function update(graph_nodes, graph_links,selectedNode)
{
var gl = [], gn = [];

    if(selectedNode &&  selectedNode.id !== concept)
    { 
       level2_relations = level2.get(selectedNode.id)
        // console.log(level2_relations);
        level2_relations.forEach(function(value){
            if (value.CUI1 !== concept && value.CUI2 !== concept)
            {
            gn.push({'id':value.CUI2})
            gl.push({'source':value.CUI1, 'target':value.CUI2})
            }
        });
        console.log("New Nodes: ",gn.length,gl.length);
    }
   let updated_nodes = graph_nodes.concat(gn);
   let updated_links = graph_links.concat(gl);
   console.log("Updation",updated_nodes.length, updated_links.length)
    return [updated_nodes, updated_links];
}

function restart(graph_nodes, graph_links,selectedNode) {

    updated = update(graph_nodes, graph_links,selectedNode)
    let updated_nodes = updated[0];
    let updated_links = updated[1];

    console.log("Restarting", updated_nodes.length, updated_links.length);
    
    // Apply the general update pattern to the links.
    link = g.selectAll(".link").data(updated_links);
    link.exit().remove();
    link = link.enter().append("line").attr("class", "link")
                .attr("stroke","black").attr('stroke-width', 1).merge(link);

    // Apply the general update pattern to the nodes.
    node =g.selectAll(".node").data(updated_nodes);
    node.exit().remove();
    node.enter().append("circle").attr("class", "node")
    .style("fill","#81D4FA").attr("r", 8).merge(node);
  
    // Update and restart the simulation.
    simulation.nodes(updated_nodes);
    simulation.force("link").links(updated_links);
    simulation.restart();
  }
  


function selectNode(selectedNode, graph_nodes, graph_links) {
    
    // level2_specific_relation = level2.get(selectedNode.id);
    // level2_specific_relation.forEach(function(value_level2){
    //     graph_nodes.push({'id':value_level2.CUI2});
    //     graph_links.push({'source':value_level2.CUI1, 'target':value_level2.CUI2});
    // });

    flag = true;
    restart(graph_nodes, graph_links, selectedNode);   
    // const neighbors = getNeighbours(selectedNode, graph_links)
    // node.style('fill', nod => getNodeColor(nod, neighbors)); 
    // textElements.attr('fill', nod => getTextColor(nod, neighbors))
    // link.attr('stroke', lin => getLinkColor(selectedNode, lin));
 
}

The execution goes like on clicking on a node selectNode function is called.

from d3-force.

Fil avatar Fil commented on April 28, 2024

I've implemented solution 1 in #165. We start when any listener is added (ie "tick" or "end") so it should cover @IPWright83 's use case too (though I'd be reassured if it could be tested independently).

demo notebook @ https://observablehq.com/d/0e3586acc3d9cd55

from d3-force.

IPWright83 avatar IPWright83 commented on April 28, 2024

@Fil really disappointingly my new job doesn't give me the chance to use D3 anymore, which is such a shame as it's an amazing library.

I will see if I can find some time to test though

from d3-force.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.