I need to manually draw some links between points on a circle and points clustered in the centre of said circle. I have the x,y pairs for the source and target points, but I don't want a simple straight line between them; I want a curved line (similar to the links that are found in tree diagrams). I could use linkHorizontal
or linkVertical
but their tangents are constant; I want to use linkRadial
and have the tangent be the radial line at that particular arc point (the degree of which I also have).
I don't understand the linkRadial
API though; it wants an angle and a radius, not an x or y point. How do I convert my two x,y pairs (and the radial line angle) into the angle and radius this is expecting?
Since you have a data array with the x
and y
positions ("I have the x,y pairs for the source and target points"), you'll have to convert them to angle
and radius
. Let's see how to do it.
Firstly, let's see an example with fixed coordinates. For instance, suppose you have this data array, with the x
and y
positions:
var data = [{
source: {y: 150,x: 75
},
target: {y: 300,x: 0
}
}, {
source: {y: 150,x: 75
},
target: {y: 0,x: 0
}
}, {
source: {y: 150,x: 75
},
target: {y: 150,x: 150
}
}, ];
Using this link generator...
var link = d3.linkHorizontal()
.x(function(d) {
return d.y;
})
.y(function(d) {
return d.x;
});
... you'll have a chart like this:
var data = [{
source: {
y: 150,
x: 75
},
target: {
y: 300,
x: 0
}
}, {
source: {
y: 150,
x: 75
},
target: {
y: 0,
x: 0
}
}, {
source: {
y: 150,
x: 75
},
target: {
y: 150,
x: 150
}
}, ];
var svg = d3.select("svg");
var link = d3.linkHorizontal()
.x(function(d) {
return d.y;
})
.y(function(d) {
return d.x;
});
svg.selectAll(null)
.data(data)
.enter()
.append("path")
.attr("fill", "none")
.attr("stroke", "blue")
.attr("d", link);
<script src="https://d3js.org/d3.v4.js"></script>
<svg></svg>
How can we convert this to a data set that can be used with d3.linkRadial()
?
One option is iterating with each object, using basic trigonometry to populate the angle
and radius
properties:
var radialData = data.map(function(d) {
return {
source: {
x: 0,
y: 0
},
target: {
x: Math.atan2(d.target.y - d.source.y, d.target.x - d.source.x) - Math.PI,
y: Math.sqrt((d.target.x - d.source.x) * (d.target.x - d.source.x) + (d.target.y - d.source.y) * (d.target.y - d.source.y))
}
};
});
Then, using this link generator:
var linkRadial = d3.linkRadial()
.angle(function(d) {
console.log(d)
return d.x;
})
.radius(function(d) {
return d.y;
});
We'll have this:
var data = [{
source: {
y: 150,
x: 75
},
target: {
y: 300,
x: 0
}
}, {
source: {
y: 150,
x: 75
},
target: {
y: 0,
x: 0
}
}, {
source: {
y: 150,
x: 75
},
target: {
y: 150,
x: 150
}
}, ];
var svg = d3.select("svg");
var radialData = data.map(function(d) {
return {
source: {
x: 0,
y: 0
},
target: {
x: Math.atan2(d.target.y - d.source.y, d.target.x - d.source.x) - Math.PI,
y: Math.sqrt((d.target.x - d.source.x) * (d.target.x - d.source.x) + (d.target.y - d.source.y) * (d.target.y - d.source.y))
}
};
});
var g = svg.append("g")
.attr("transform", "translate(150,75)")
var linkRadial = d3.linkRadial()
.angle(function(d) {
return d.x;
})
.radius(function(d) {
return d.y;
});
g.selectAll(null)
.data(radialData)
.enter()
.append("path")
.attr("fill", "none")
.attr("stroke", "red")
.attr("d", linkRadial);
<script src="https://d3js.org/d3.v4.js"></script>
<svg></svg>
Now both generators together, for comparison:
var data = [{
source: {
y: 150,
x: 75
},
target: {
y: 300,
x: 0
}
}, {
source: {
y: 150,
x: 75
},
target: {
y: 0,
x: 0
}
}, {
source: {
y: 150,
x: 75
},
target: {
y: 150,
x: 150
}
}, ];
var svg = d3.select("svg");
var link = d3.linkHorizontal()
.x(function(d) {
return d.y;
})
.y(function(d) {
return d.x;
});
svg.selectAll(null)
.data(data)
.enter()
.append("path")
.attr("fill", "none")
.attr("stroke", "blue")
.attr("d", link);
var radialData = data.map(function(d) {
return {
source: {
x: 0,
y: 0
},
target: {
x: Math.atan2(d.target.y - d.source.y, d.target.x - d.source.x) - Math.PI,
y: Math.sqrt((d.target.x - d.source.x) * (d.target.x - d.source.x) + (d.target.y - d.source.y) * (d.target.y - d.source.y))
}
};
});
var g = svg.append("g")
.attr("transform", "translate(150,75)")
var linkRadial = d3.linkRadial()
.angle(function(d) {
return d.x;
})
.radius(function(d) {
return d.y;
});
g.selectAll(null)
.data(radialData)
.enter()
.append("path")
.attr("fill", "none")
.attr("stroke", "red")
.attr("d", linkRadial);
<script src="https://d3js.org/d3.v4.js"></script>
<svg></svg>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With