Reputation: 53
I have adapted Christopher Manning's force-directed graph on a sphere. I would like to have the graph settle down and then rotate the sphere without changing the relationships among the points in the graph. Instead, when I drag, it seems to drag the graph, and not rotate the sphere. Dragging the graph activates the force start. If I turn off force.start()
nothing changes.
var svg = d3.select("#cluster").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.drag()
.origin(function() { var r = projection.rotate(); return {x: 2 * r[0], y: -2 * r[1]}; })
.on("drag", function() { force.start(); var r = [d3.event.x / 2, -d3.event.y / 2, projection.rotate()[2]]; t0 = Date.now(); origin = r; projection.rotate(r); }))
From http://bl.ocks.org/mbostock/3795040, I found that I could rotate the graticule, but then all of my links and nodes disappear.
var path = d3.geo.path()
.projection(projection);
var λ = d3.scale.linear()
.domain([0, width])
.range([-180, 180]);
var φ = d3.scale.linear()
.domain([0, height])
.range([90, -90]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.on("mousemove", function() {
var p = d3.mouse(this);
projection.rotate([λ(p[0]), φ(p[1])]);
svg.selectAll("path").attr("d", path);
});
Links and nodes get added like this:
var link = svg.selectAll("path.link")
.data(graph.links)
.enter().append("path").attr("class", "link")
.attr ("stroke-width", function(d){return d.value/3});
var node = svg.selectAll("path.node")
.data(graph.nodes)
.enter()
.append("g")
.attr("class", "gnode")
.attr("text-anchor", "middle")
.append("path").attr("class", "node")
.style("fill", function(d) { return d.color; })
.style("stroke", function(d) { return d3.rgb(fill(d.group)).darker(); })
What I would like to accomplish: when the graph settles into position, I would like to use the drag gesture to rotate the sphere with the nodes and links on it.
How do I make this happen?
Upvotes: 3
Views: 1059
Reputation: 21578
This could be done by adjusting the handler registered for the drag
event on the svg
which is defined in your first snippet. This requires two edits:
force.start()
because you don't want to restart the force on drag.tick()
. If the force had not been deactivated by step 1., this would be called repeatedly because the function is registered as the handler for the force layout's tick
event. Now, that you have deactivated the force, you will have to call it explicitely. The reformatted code will look like:
.on("drag", function() {
//force.start(); // 1. Don't restart the force.
var r = [d3.event.x / 2, -d3.event.y / 2, projection.rotate()[2]];
t0 = Date.now();
origin = r;
projection.rotate(r);
tick(); // 2. Trigger the rendering after adjusting the projection.
}))
Have a look at this working example.
Upvotes: 2
Reputation: 340
This may rotate along one axis
var rotateScale = d3.scale.linear().domain([0, width]).range([-180,180]);//width of SVG
d3.select("svg").on("mousedown",startRotating).on("mouseup",stopRotating);
function startRotating() {
d3.select("svg").on("mousemove",function() {
var p = d3.mouse(this);
projection.rotate([rotateScale(p[0]),0]);
});
}
function stopRotating() {
d3.select("svg").on("mousemove",null);
}
Upvotes: 0