d3 / d3-shape

Graphical primitives for visualization, such as lines and areas.
https://d3js.org/d3-shape
ISC License
2.48k stars 308 forks source link

Link (diagonal)? #27

Closed mbostock closed 7 years ago

mbostock commented 8 years ago

It’s quite likely we actually want to call this a link (shape) and put it in the d3-hierarchy module, since this shape is typically used to represent links in hierarchical visualizations.

mbostock commented 8 years ago

I dunno… Seems pretty generic and this seems like a reasonable place for it.

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;
}
mbostock commented 8 years ago

Also if you want it to render to both Canvas and SVG, then it becomes even more tedious to not have this available in d3-shape.

ZephryL commented 8 years ago

Hi there Mike. I use the svg.diagonal to associate arbitrary shapes (i.e. not necessarily links in a formal hierarchy) like this: var diagonal = d3.svg.diagonal().projection(function (d) { return [d.y, d.x]; }); I'm at a loss as to how I would implement this behavior in version 4. How would you suggest I go about this? Thanks!

ZephryL commented 8 years ago

Sorry Mike, I was being a dork. Sorted with your simple link(d) function, which I initially ignored. Apologies

brettbibby commented 8 years ago

I'm not understanding this exchange very well between you guys.

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

New???

Sorry to disturb!

mbostock commented 8 years ago

Here are some examples…

brettbibby commented 8 years ago

Perfect, thanks!

ZephryL commented 8 years ago

Thanks you Mike. Appreciate your time.

mbostock commented 8 years ago

It’s trivial to adjust that line function definition for V4:

var line = d3.line()
    .x(function(d) { return d.x; })
    .y(function(d) { return d.y; })
    .curve(d3.curveLinear);

The rest of what you described is unchanged.

mbostock commented 7 years ago

The simplest option here is to have two variants of a link shape: one with vertical tangents and one with horizontal tangents:

function linkHorizontal(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;
}

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

Then, in conjunction with d3/d3-hierarchy#63, it becomes much easier to change the orientation of a hierarchical layout and its links without the complexity of diagonal.projection from 3.x.

There’s a related question whether we want the midpoint of the link to be configurable: should it be specified as a parameter t in [0, 1], or as a fixed offset from the source or target? Of course, it’s probably sufficient to just keep it simple and not configurable. But if we want to be forwards-compatible then linkVertical should return a link generator function (like d3.line) rather than being a static, non-configurable shape generator.

mbostock commented 7 years ago

We will also need a radial link.

gauravtyagi77 commented 6 years ago

Hi @mbostock,

In my code written by someone else earlier for rendering d3-sankey with d3 v3 js by using diagonal is given below.

var path = d3.svg.diagonal() .source(function(d) { return {"x":d.source.y + d.source.dy / 2, "y":d.source.x + sankey.nodeWidth()/2}; })
.target(function(d) { return {"x":d.target.y + d.target.dy / 2, "y":d.target.x + sankey.nodeWidth()/2}; }) .projection(function(d) { return [d.y, d.x]; });

Can you help me how should I replace my calculation with your.

ialarmedalien commented 6 years ago

Would it be possible to expose a generic link generator function that could be supplied with custom directions for creating the link? Use case is generating more complex L-shaped and dogleg links, both with straight lines and with arcs. I have some old d3 v3-compatible code that currently generates the links using string concatenation, but it would be really nice to use pathTo, arcTo, and so on instead, without having to compile my own custom module that copies most of the contents of link().

Malex commented 5 years ago

I read all the exchange, and while I am an happy user of linkVertical/Horizontal and such, I'm having trouble implementing a different kinda of connection, as per example http://bl.ocks.org/jdarling/2503502 Is there an API I'm missing to get something similar?

specialCoder commented 5 years ago

I dunno… Seems pretty generic and this seems like a reasonable place for it.

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;
Horizontal:
```javascript
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}`;
}

Vertical:

function link(d) {
    return `M${d.source.x},${d.source.y}C${(d.source.x + d.target.x) / 2},${d.target.y} ${(d.source.x + d.target.x) / 2},${d.source.y} ${d.target.x},${d.target.y}`;
  }