"Pinning" of nodes in a D3 force plot
I've built a regular "grid" that places the nodes at evenly spaced points along the grid. Then, by randomizing the linkDistance, I can "fire up" the grid so that it is less regular.
I would like to "snap" all the endpoints so that they do not move, leaving only the interior points that are affected by the force layout.
My understanding was that since this is a regular square grid, any points with a weight less than 4 will be an "edge" point and therefore should be anchored.
I suppose the weight is only calculated after the nodes and links have been added to the force layout, so I go forEach
through the array nodes
by adding it to the force layout and conditionally setting the fixed
weight based property.
Then I re-use the property nodes
and start
for the simulation.
Not good. In the example, I attach ALL the move points.
force = d3.layout.force()
.size( [w, h ] )
.nodes( nodes )
.links( links )
.linkDistance( function( d ){ return Math.random() * GRID_SPACING; } )
.linkStrength( 1 )
.charge( 0 )
.gravity( 0 )
.friction( .5 )
.on( "tick", function() {
d3links.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; });
d3nodes.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
} );
// "Pin" all the edge nodes.
nodes.forEach( function( node ){
if ( node.weight < 4 ){
node.fixed = true;
}
} );
force.nodes( nodes ).start();
source to share
Your discernment is good! But timing is everything ... The "start" event is fired after the weights have been initialized, so this should work ...
force = d3.layout.force()
.size([w, h])
.nodes(nodes)
.links(links)
.linkDistance(function (d) { return Math.random() * GRID_SPACING; })
.linkStrength(1)
.charge(0)
.gravity(0)
.friction(.5)
.on("tick", function () {
d3links.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; });
d3nodes.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; });
})
.on("start", function () {
// "Pin" all the edge nodes.
nodes.forEach(function (node) {
if (node.weight < 4) {
node.fixed = true;
}
});
})
force.nodes(nodes).start();
(If you want to enable drag mode, you will also need to re-commit after dragend.)
source to share