I want to allow users to view their network using either a d3 forceSimulation or a CoLa layout, which means when a user fires an event, I need to change out which of those layout algorithms is updating the x
and y
attributes of my nodes and edges.
In particular, this requires that I be able to stop the simulations and prevent them from updating those attributes on the data I give them while the other is "active" -- as well as removing the drag handlers associated with them.
My render function currently has:
if (use_cola) {
// MUST TURN OFF D3 AND ITS DRAG HANDLERS!
force = cola_force.nodes(graph.nodes)
.links(links)
.groups(groups[group_nodes_by])
.jaccardLinkLengths(repulsion_strength, 0.7)
.avoidOverlaps(true)
.start(50, 0, 50);
node.call(cola_force.drag);
group.call(cola_force.drag);
cola_force.on('tick', ticked);
} else { // d3
// MUST TURN OFF COLA AND ITS DRAG HANDLERS!
force = d3_force.nodes(graph.nodes)
.force("link", d3.forceLink(links))
.force("charge", d3.forceManyBody().strength(-repulsion_strength))
.force("center", d3.forceCenter(w / 2, h / 2));
node.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)); // where those are the conventional functions
d3_force.on('tick', ticked);
}
One solution might be to mangle these objects, e.g. delete d3_force['something_important']
Something simpler might work, like d3_force.nodes([])
or somesuch.
I'm not sure how I would do something similar to the drag handlers because I'm less familiar with how those work.
A partial solution suggested for the d3 drag handler (in d3v3) here:
var dragCallback = d3.select('rect#no-drag').property('__onmousedown.drag')['_'];
d3.select('rect#no-drag').on('mousedown.drag', null);
and then restoring it later:
d3.selectAll('rect#no-drag').on('mousedown.drag', dragCallback);
You need to do two things:
Stop the simulation if it is still running to prevent it from messing with your nodes coordinates. This can easily be done by calling d3_force.stop()
. There is no need to first check if it is running, though, because calling it on an already halted simulation will not hurt either.
You can later re-activate the simulation by just calling d3_force.restart()
probably pumping some energy back in to heat it up: d3_force.alpha(1).restart()
.
The docs tell us how to get rid of the drag behavior:
The listeners use the name
.drag
, so you can subsequently unbind the drag behavior as follows:selection.on(".drag", null);
In your case this would be node.on(".drag", null)
. If the user switches the layout back you can again bind the drag behavior to the node
selection. For this it might be worth considering to create the drag behavior beforehand and just pass around the reference when rebinding later on.