Comments (10)
We finally decided against this change. Use .stop() then .restart() when the data is ready.
from d3-force.
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.
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.
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.
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.
Some options:
-
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.)
-
Disable automatic starting, period. This would not be backwards-compatible with d3@4, but it’s how d3@3 did it.
-
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.
-
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.
@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.
@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.
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.
@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)
- Saving, restoring and continuing a d3-force page HOT 6
- Iterative-Relaxation in d3.forceLink
- README has links to non-force-directed beeswarms
- Forcing angle in crystalline structure HOT 1
- simulation compute x/y/vx/vy as NAN sometimes HOT 1
- Collide force doesn't handle identical initial positions well HOT 7
- use in nuxt js
- Method to get all forces currently set (or at least clear them)
- Adding labels on top of nodes HOT 3
- Cannot assign to read only property 'vx' of object '#<Object>' HOT 1
- forceSimulation: number too small: cx="-3.2056569513393925e-168", Safari 15.4 HOT 2
- consider making .tick() reversable / accept negative iterations HOT 6
- All nodes starts to move when click and holding one node HOT 3
- Importing to NestJs causes module import error
- Cant pass extra data in nodes in simulation.nodes function HOT 1
- `forceCollide` ignores fixed positions HOT 5
- d3 v7 chained methods of forceLink "is not a fucntion" HOT 2
- `README` links to deprecated notebook HOT 1
- if you change a variable in an expression and use it twice gives undetermined result ( a = i++ + i;)
- Opportunity for performance gains via d3-force-reuse?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from d3-force.