53

I was trying to execute the code of collapsible-tree as mentioned here. But it seems the diagonal method is not applicable in v4 (I may be wrong).

For:

var diagonal = d3.svg.diagonal()

I get this error:

TypeError: Cannot read property 'diagonal' of undefined

What is the equivalent in v4? Looking at d3.js API reference didn't give me any clue.

Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
Mopparthy Ravindranath
  • 3,014
  • 6
  • 41
  • 78

4 Answers4

58

D3 version 4.9.0 introduced link shapes, which have the same functionality of the old d3.svg.diagonal in D3 v3.

According to the API:

The link shape generates a smooth cubic Bézier curve from a source point to a target point. The tangents of the curve at the start and end are either vertical, horizontal or radial.

There are three methods:

So, for a collapsible tree like that one you linked, you define the path d attribute as:

.attr("d", d3.linkHorizontal()
    .x(function(d) { return d.y; })
    .y(function(d) { return d.x; }));

Demo:

Suppose you have an object with source and target, each one with x and y properties:

var data = {
  source: {
    x: 20,
    y: 10
  },
  target: {
    x: 280,
    y: 100
  }
};

First, you create the link generator:

var link = d3.linkHorizontal()
  .x(function(d) {
    return d.x;
  })
  .y(function(d) {
    return d.y;
  });

And then you can draw the path just by passing that data to the link generator:

.attr("d", link(data))

Here is the demo:

var svg = d3.select("svg");

var data = {
  source: {
    x: 20,
    y: 10
  },
  target: {
    x: 280,
    y: 100
  }
};

var link = d3.linkHorizontal()
  .x(function(d) {
    return d.x;
  })
  .y(function(d) {
    return d.y;
  });

svg.append("path")
  .attr("d", link(data))
  .style("fill", "none")
  .style("stroke", "darkslateblue")
  .style("stroke-width", "4px");
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
22

See GitHub issue here.

While the issue is still open, it doesn't seem that Mr. Bostock is in a rush to re-implement it in version 4. Why? Because it's trivial to implement yourself:

function link(d) {
  return "M" + d.source.y + "," + d.source.x
      + "C" + (d.source.y + d.target.y) / 2 + "," + d.source.x
      + " " + (d.source.y + d.target.y) / 2 + "," + d.target.x
      + " " + d.target.y + "," + d.target.x;
}
Mark
  • 106,305
  • 20
  • 172
  • 230
  • 3
    Like in your example above, I have seen many instances where 'y' is used for 'x' and vice versa. What is the reason? – Mopparthy Ravindranath Nov 28 '16 at 17:48
  • 3
    @MupparthyRavindranath, horizontol vs vertical drawing for tree layouts. Vertical drawing reverses the x and ys like like [this example](http://bl.ocks.org/mbostock/ff91c1558bc570b08539547ccc90050b) – Mark Nov 28 '16 at 17:56
17

I had a really hard time with this and then after a couple of hours, I realized how easy it really is (just like everyone else that mentions it). Replace:

var diagonal = d3.svg.diagonal()
  .projection(function(d) { return [d.y, d.x]; });

...with this:

var diagonal = function link(d) {
  return "M" + d.source.y + "," + d.source.x
      + "C" + (d.source.y + d.target.y) / 2 + "," + d.source.x
      + " " + (d.source.y + d.target.y) / 2 + "," + d.target.x
      + " " + d.target.y + "," + d.target.x;
};

That should be the only change. Hope this helps anyone else. This should work with a visualization like Patrick Brockman's Collapsible/Searchable Tree.

Pete Adam Bialecki
  • 183
  • 1
  • 3
  • 9
  • 2
    How is this different from the [accepted answer](https://stackoverflow.com/a/40846487/4235784)? – altocumulus Apr 26 '19 at 16:31
  • 1
    Apologies. I just want to add additional clarification. – Pete Adam Bialecki Apr 26 '19 at 19:11
  • 2
    I find this answer clearer because it mentions explicitly which part is to be replaced by which other part. That is exactly what one is looking for. In the other answers, it is not clarified what exactly is to be replaced. – exchange Aug 20 '20 at 19:32
0

For those with vertical trees, the function below will yield curved diagonals such as shown in this example.

This function was modified from Mark's answer by (1) switching the x and y calls, and (2) changing the calculated coordinates in the two middle lines. Without the coordinate change, the curve is inverted such as in this post.

var diagonal = function link(d) {
  return "M" + d.source.x + "," + d.source.y
      + "C" + d.source.x + "," + (d.source.y + d.target.y) / 2
      + " " + d.target.x + "," + (d.source.y + d.target.y) / 2
      + " " + d.target.x + "," + d.target.y;
};

As a side note, you can replace "C" with " " if you want boxy, instead of curved, edges.

halcyon
  • 101
  • 3