In this section we’ll discuss how to draw links. Links can be used independently when the coordinates are predefined, or within a hierarchical layoutswhere the points are computed based on the layout.

A link is a path that creatse a smooth Bézier curve from a source point to a target point. When having a large amount of points that need to be shown as being connected, links can be used to easily show their relation.

There are three different types of link generators that D3 provides:

• d3.linkVertical() - Typically used when the root is on the left/right edge with the children going right/left.

• d3.linkHorizontal() - Typically used when the root is on the top/bottom edge with the children going down/up.

• d3.linkRadial() - Typically used when the root is centered with the children spreading outwards from the root.

Figure 1 shows a quick example of each:

```<script>
//Same data used for both diagrams
var nodeData = [
{id: "D3",       x: 100, y: 25},
{id: "Scales",   x: 25, y: 175},
{id: "Shapes",   x: 175, y: 175}];

{source: [100,25], target: [175,175]},   // D3 -> Shapes
{source: [100,25], target: [25,175]}]; // D3 -> Scales

//Begin making the horizontal link diagram
.source(function(d) {
return [d.source[1], d.source[0]];
})
.target(function(d) {
return [d.target[1], d.target[0]];
});

.selectAll("circle")
.data(nodeData)
.join("circle")
.attr("cx", d => d.y)
.attr("cy", d => d.x)
.classed("circle", true);

.selectAll("path")
.join("path")

.selectAll("text")
.data(nodeData)
.join("text")
.attr("font-size", "12px")
.attr("text-anchor", "middle")
.attr("x", d => d.y)
.attr("y", d => d.x + 20)
.text(d => d.id);

//Begin making the vertical link diagram

.selectAll("circle")
.data(nodeData)
.join("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.classed("circle", true);

.selectAll("path")
.join("path")

.selectAll("text")
.data(nodeData)
.join("text")
.attr("font-size", "12px")
.attr("text-anchor", "middle")
.attr("x", d => d.id === "D3" ? d.x + 15 : d.x) //If the node is the D3 node, move it over some so it fits right, otherwise d.x
.attr("y", d => d.y + 20)
.text(n => n.id);

.angle(d => xAngleScale(d[0]))

var xAngleScale = d3.scaleLinear().domain([25,175]).range([Math.PI, Math.PI *2 ]);

.selectAll("circle")
.data(nodeData)
.join("circle")
.classed("circle", true)
.attr("transform", "translate(100,100)");

.selectAll("path")
.join("path")
.attr("transform", "translate(100,100)");

.selectAll("text")
.data(nodeData)
.join("text")
.attr("font-size", "12px")
.attr("text-anchor", "left")
.text(n => n.id)
.attr("transform", "translate(100,100)");
</script>
<svg id="quickDemoV" width="200" height="200"></svg>
<svg id="quickDemoH" width="200" height="200"></svg>
<svg id="quickDemoR" width="200" height="200"></svg>```

A link generator needs an object with a source and a target; within each should be an array with two numbers representing the `x` and `y` values of where the link should start and end.

An example of a single link object and a default horizontal link generator:

```var linkGen = d3.linkHorizontal();
var singleLinkData = { source: [25,25], target: [75,75] } ;
```

We can also take multiples of these objects and put them into an array:

```var multiLinkData = [
{source: [50,50], target: [175,25]},
{source: [50,50], target: [175,50]},
{source: [50,50], target: [175,75]},
];
```

From here we simply select our svg, add data, `join` `path`s, and set the `d` attribute of the `path`s to the link generator:

```d3.select("#multiLink")
.selectAll("path")
.join("path")
.attr("fill", "none")
.attr("stroke", "black");
```

In Figure 2, we create a single link from one object and then multiple links from an array of objects.

```<script>
//Link generator used for both examples

//The single object containing a link
var singleLinkData = { source: [25,25], target: [175,75] };

//Since the single link is not an array of links, we do not add the data of it, we only pass it into the generator
.append("path")

{source: [100,25], target: [25,75]},
{source: [100,25], target: [100,75]},
{source: [100,25], target: [175,75]},
];

//Since this is an array of links, we add its data then join to add our paths
.selectAll("path")
.join("path")

</script>

`link.source()` and `link.target()`

In most situations, the source and target are not going to be separate and easily accessible from our data like they are in the previous examples. For these times the `.source()` and `.target()` can be manually changed to fit whatever data is being used.

Let’s use the following data:

```var nodeData = [
{id: "D3",      position: [100, 25],  parentPosition: [100, 25]},
{id: "Scales",  position: [25, 175],  parentPosition: [100, 25]},
{id: "Shapes",  position: [175, 175], parentPosition: [100, 25]}];
```

Now instead of having an array of links, we have an array of nodes with positions and a parent position. We can take this array and create links between each node’s position and its parent position by setting the `source` and `target` of our link generator:

• link.source([source]) - Where the link originates from, by default needs an array where `source[0]` is `x` and `source[1]` is `y`, however where source looks for `x` and `y` can be overridden

• link.target([target]) - Where the link goes to, same requirements as `link.source`

```var link = d3.linkHorizontal()
.source(d => d.parentPosition)
.target(d => d.position);
```

For this example, the link generator will create a link from the parent position to the node position.

In Figure 3, we create links by setting `source` and `target` to `position` and `parentPosition`, respectively.

```<script>
var nodeData = [
{id: "D3",       position: [100, 25],   parentPosition: [100, 25] },
{id: "Scales",   position: [25, 175],   parentPosition: [100, 25] },
{id: "Shapes",   position: [175, 175],  parentPosition: [100, 25] }];

//Adding in circles where the node positions are
d3.select("#demoSrcTar")
.selectAll(".circle")
.data(nodeData)
.join("circle")
.attr("cx", d => d.position[0])
.attr("cy", d => d.position[1])
.classed("circle", true);

//Link generator with .source and .target set
.source(d => d.parentPosition)
.target(d => d.position);

d3.select("#demoSrcTar")
.selectAll("path")
.data(nodeData)
.join("path")

d3.select("#demoSrcTar")
.selectAll("text")
.data(nodeData)
.join("text")
.attr("font-size", "12px")
.attr("text-anchor", "end")
.attr("x", d => d.position[0] + 20)
.attr("y", d => d.position[1] + 20)
.text(n => n.id);
</script>

<svg id="demoSrcTar" width="200" height="200"></svg>```

`link.x()` and `link.y()`

Other times, we may want to have our `x` and `y` positions put through a scale so we do not have to manually compute their positions. This can be very helpful in cases where our data is dynamic and we do not always know what exact positions to use.

For this example we will use a larger data set:

```var nodeData = [
{id: "D3",       position: [2, 0], parentPosition: [2, 0]},
{id: "Shapes",   position: [1, 1], parentPosition: [2, 0]},
{id: "Scales",   position: [3, 1], parentPosition: [2, 0]},
{id: "Links",    position: [0, 2], parentPosition: [1, 1]},
{id: "Areas",    position: [1, 2], parentPosition: [1, 1]},
{id: "Arcs",     position: [2, 2], parentPosition: [1, 1]},
{id: "Ordinal",  position: [3, 2], parentPosition: [3, 1]},
{id: "Quantize", position: [4, 2], parentPosition: [3, 1]}];
```

In our new data array the positions are no longer absolute and should be put through a `d3.linearScale()` to get the real values that will be displayed in the svg.

Since we changed how the real `x` and `y` positions are computed we will need to set up our link generator to accommodate this; we also need to set up our scales:

```var xScale = d3.scaleLinear().domain([0, 4]).range([25, 175]);
var yScale = d3.scaleLinear().domain([0,2]).range([25, 175]);

.source(d => d.position)
.target(d => d.parentPosition)
.x(d => xScale(d[0]))
.y(d => yScale(d[1]));
```

In Figure 4, we apply a scale to `x` and `y`.

```<script>
//Our larger node data
var nodeData = [
{id: "D3",       position: [2, 0], parentPosition: [2, 0]},
{id: "Shapes",   position: [1, 1], parentPosition: [2, 0]},
{id: "Scales",   position: [3, 1], parentPosition: [2, 0]},
{id: "Links",    position: [0, 2], parentPosition: [1, 1]},
{id: "Areas",    position: [1, 2], parentPosition: [1, 1]},
{id: "Arcs",     position: [2, 2], parentPosition: [1, 1]},
{id: "Ordinal",  position: [3, 2], parentPosition: [3, 1]},
{id: "Quantize", position: [4, 2], parentPosition: [3, 1]}];

//x and y scales
var xScale = d3.scaleLinear().domain([0, 4]).range([25, 175]);
var yScale = d3.scaleLinear().domain([0,2]).range([25, 175]);

d3.select("#demoXY")
.selectAll(".circle")
.data(nodeData)
.join("circle")
.attr("cx", d => xScale(d.position[0]))
.attr("cy", d => yScale(d.position[1]))
.classed("circle", true);

// Our link generator with the new .x() and .y() definitions
.source(d => d.position)
.target(d => d.parentPosition)
.x(d => xScale(d[0]))
.y(d => yScale(d[1]));

d3.select("#demoXY")
.selectAll("path")
.data(nodeData)
.join("path")

d3.select("#demoXY")
.selectAll("text")
.data(nodeData)
.join("text")
.attr("font-size", "10px")
.attr("text-anchor", "middle")
.attr("x", function(d) {
if(d.position[1] == 2 || d.position[1] == 0)
return xScale(d.position[0]);
var xOffset = xScale(d.position[0]);
if(xOffset > 100)
return xScale(d.position[0]) +25;
return xScale(d.position[0]) -25;
})
.attr("y", function(d) {
if(d.position[1] == 2)
return yScale(d.position[1]) + 15;
if(d.position[1] == 1)
return yScale(d.position[1])
return yScale(d.position[1]) - 10
})
.text(d => d.id);
</script>

<svg id="demoXY" width="200" height="200"></svg>```

Using `d3.linkHorizontal()` and `d3.linkVertical()`

We have been using `d3.linkVertical()` for most of this section. `d3.linkVertical()` should be used when your graph is rooted at the top or bottom. Another link generator, `d3.linkHorizontal()` can be used when your graph is rooted to the left or right.

It is easy to make a `d3.linkHorizontal()` graph out of an existing `d3.linkVertical()` graph. All we have to do is flip the `x` and `y` positions of a `d3.linkVertical()` graph and we will get a `d3.linkHorizontal()` graph.

You could do this by making a new array and flipping the `x` and `y` values, however an easier way is to flip the `x` and `y` in the `source` and `target` of `d3.linkHorizontal()`:

```var link = d3.linkHorizontal()
.source( d => [d.position[1], d.position[0]] )
.target( d => [d.parentPosition[1], d.parentPosition[0]] );
```

Since we flipped the `x` and `y` positions in our link generator we will need to make sure to flip them when we are creating the `circle` and `text` nodes.

• d3.linkVertical() - The default link generator, assumes root at top or bottom.

• d3.linkHorizontal() - Assumes left to right, converted from `d3.linkVertical()` by flipping `x` and `y` positions.

In Figure 5, we use `d3.linkHorizontal` to display our chart left-to-right instead.

```<script>
//Same data used for both diagrams
var nodeData = [
{id: "D3",       position: [100, 25],   parentPosition: [100,25] },
{id: "Scales",   position: [25, 175],   parentPosition: [100, 25] },
{id: "Shapes",   position: [175, 175],  parentPosition: [100, 25] }];

//Begin making the horizontal link diagram
.source( d => [d.position[1], d.position[0]] )
.target( d => [d.parentPosition[1], d.parentPosition[0]] );

.selectAll("circle")
.data(nodeData)
.join("circle")
.attr("cx", d => d.position[1]) // Flipped X
.attr("cy", d => d.position[0]) // Flipped Y
.classed("circle", true);

.selectAll("path")
.data(nodeData)
.join("path")

.selectAll("text")
.data(nodeData)
.join("text")
.attr("font-size", "12px")
.attr("text-anchor", "middle")
.attr("x", d => d.position[1]) // Flipped X
.attr("y", d => d.position[0]+ 20) // Flipped Y
.text(d => d.id);

//Begin making the vertical link diagram

.selectAll("circle")
.data(nodeData)
.join("circle")
.attr("cx", d => d.position[0])
.attr("cy", d => d.position[1])
.classed("circle", true);

.selectAll("path")
.join("path")

.selectAll("text")
.data(nodeData)
.join("text")
.attr("font-size", "12px")
.attr("text-anchor", "middle")
.attr("x", d => d.id === "D3" ? d.position[0] + 15 : d.position[0])
.attr("y", d => d.position[1] + 20)
.text(n => n.id);
</script>
<svg id="demoH" width="200" height="200"></svg>
<svg id="demoV" width="200" height="200"></svg>```

D3 also provides a circular link generator, `d3.linkRadial()`. Just as with `d3.linkHorizontal()`, `d3.linkRadial()` can be easily converted from an existing `d3.linkVertical()` graph.

To convert a `d3.linkVertical()` into a `d3.linkRadial()` all we need to do is change our `x` position to become an `angle` and our `y` position will become the `radius`. We will also need to change our scales to reflect angles and radii. Note that for this example the data set is expanded again.

```var xScale = d3.scaleLinear().domain([0, 8]).range([0, Math.PI * 2]);
var yScale = d3.scaleLinear().domain([0,2]).range([0, 80]);

.source(d => d.position)
.target(d => d.parentPosition)
.angle( d => xScale(d[0]))
```

Our `circle` and `text` nodes will no longer be at the right point without changing their coordinates as well, so we will use `d3.pointRadial`s to place them into the right spot.

```circleSelection
.attr("cx", d => d3.pointRadial(xScale(d.position[0]), yScale(d.position[1]) )[0] )
.attr("cy", d => d3.pointRadial(xScale(d.position[0]), yScale(d.position[1]) )[1] )
```

In Figure 6, we use `d3.linkRadial` to display our chart radially instead.

```<script>
//Our larger node data
var nodeData = [
{id: "D3",       position: [2, 0], parentPosition: [2, 0]},
{id: "Shapes",   position: [1, 1], parentPosition: [2, 0]},
{id: "Scales",   position: [3, 1], parentPosition: [2, 0]},
{id: "Layouts",  position: [6, 1], parentPosition: [2, 0]},
{id: "Links",    position: [0, 2], parentPosition: [1, 1]},
{id: "Areas",    position: [1, 2], parentPosition: [1, 1]},
{id: "Arcs",     position: [2, 2], parentPosition: [1, 1]},
{id: "Ordinal",  position: [3, 2], parentPosition: [3, 1]},
{id: "Quantize", position: [4, 2], parentPosition: [3, 1]},
{id: "Tree", 	 position: [5, 2], parentPosition: [6, 1]},
{id: "Cluster",	 position: [6, 2], parentPosition: [6, 1]},
{id: "Partition",position: [7, 2], parentPosition: [6, 1]}];

// Updated scales
var xScale = d3.scaleLinear().domain([0, 8]).range([0, Math.PI * 2]);
var yScale = d3.scaleLinear().domain([0,2]).range([0, 133]);

.selectAll("circle")
.data(nodeData)
.join("circle")
.attr("cx", d => d3.pointRadial(xScale(d.position[0]), yScale(d.position[1]) )[0] )
.attr("cy", d => d3.pointRadial(xScale(d.position[0]), yScale(d.position[1]) )[1] )
.classed("circle", true)
.attr("transform", "translate(175,175)");

.source(d => d.position)
.target(d => d.parentPosition)
.angle( d => xScale(d[0]))

.selectAll("path")
.data(nodeData)
.join("path")
.attr("transform", "translate(175,175)");

// Adding text nodes, the x and y have special functions to get them in a place where they do not overlap the links
.selectAll("text")
.data(nodeData)
.join("text")
.attr("font-size", "11px")
.attr("text-anchor", "middle")
.attr("x", function(d) {
if(d.position[1] == 2)
if(d.position[1] == 1){
xPos = xPos > 0 ? xPos - 25 : xPos + 15;
return xPos;
}
return 0;})
.attr("y", function(d){
return d.position[1] == 2 ? d3.pointRadial(xScale(d.position[0]), yScale(d.position[1]) + 20)[1] + 4 :
d.position[1] == 1 ? d3.pointRadial(xScale(d.position[0]) + .15, yScale(d.position[1]) + 10)[1] :
20
})
.text(d => d.id)
.attr("transform", "translate(175,175)");
</script>

Canvasses

Our examples in the section all use an `SVG` as the graphic medium. If we want to work with a Canvas instead, we just pass in the `context` of a canvas into the `.context()` accessor of any of our link generators.

In Figure 7 we use the same links in Figure 4, but apply them to a canvas instead. Note that we have to use additional CanvasPathMethods for our graphics to display.

```<script>
var nodeData = [
{id: "D3",       position: [100, 25],   parentPosition: [100,25] },
{id: "Scales",   position: [25, 175],   parentPosition: [100, 25] },
{id: "Shapes",   position: [175, 175],  parentPosition: [100, 25] }];

var selection = d3.select("#demo7");
var context = selection.node().getContext("2d");

.source( d => {console.log(d); return d.position;} )
.target( d => d.parentPosition )
.context(context);

context.beginPath();
context.strokeStyle = "black";
context.fillStyle = "white";
for(node of nodeData) {