Accepted answer

You had a pretty good implementation that was derailed by one major typo:

function transform(d) {
    return "translate(" + x(d.y) + "," + x(d.x) + ")";

Should have been

function transform(d) {
    return "translate(" + x(d.y) + "," + y(d.x) + ")";

To have your paths not flip you should have probably not reversed the y-axis:

y = d3.scale.linear().domain([0, h]).range([h, 0])

changed to

y = d3.scale.linear().domain([0, h]).range([0, h])

Fixes are here: But for future reference, you should probably have your x-axis operate on x-values, and y-axis operate on y-values, or you're just going to really confuse yourself.

Final remarks to polish your implementation:

  1. There is a little bit of jumpiness in the bezier drawing going from the default rendering (or right after opening/closing a node) to the zoom implementation. You just need to make those the same bezier function, and it will feel a lot better when you play with it.
  2. You need to update the zoom vector after the graph redraws itself, based on the relative movement of existing nodes. Otherwise, there will be a sudden jump when you try to zoom again after opening/closing a node.

Related Query