The most commonly used mathematical solutions, like this one, won't probably suit you because you want the curved path imaginarily continuing its way until the center of the target circle.
While you can create a more refined math for this (or use the suggested libraries in the comments), a very simple solution is just drawing your path without any change and then, using stroke-dasharray
, removing the last part, making the path ending exactly (almost, see the post scriptum below) at the circle's border.
So, suppose this path (here I'm using your function, which I named drawPath
):
var svg = d3.select("svg");
var sourceX = 50,
sourceY = 50;
var targetX = 300,
targetY = 150;
var radius = 50;
svg.append("circle")
.attr("cx", sourceX)
.attr("cy", sourceY)
.attr("r", 4);
svg.append("circle")
.attr("cx", targetX)
.attr("cy", targetY)
.attr("r", 4);
svg.append("circle")
.attr("cx", targetX)
.attr("cy", targetY)
.attr("r", radius)
.style("fill", "none")
.style("stroke", "black");
svg.append("path")
.style("fill", "none")
.style("stroke", "steelblue")
.style("stroke-width", "2px")
.attr("d", drawPath);
function drawPath() {
newX = sourceX
newY = sourceY
c1x = newX + ((targetX - newX) * 0.5)
c1y = newY - ((targetY - newY) * 0.5)
c2x = targetX - ((targetX - newX) * 0.05)
c2y = targetY - ((targetY - newY) * 0.5)
pathString = "M " + newX + "," + (newY) + " C " + c1x + "," + c1y + "," + c2x + ", " + c2y + " " + targetX + ", " + targetY;
return pathString
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="500" height="300"></svg>
We can change the stroke-dasharray
to remove the final part of the path with just this:
.attr("stroke-dasharray", function() {
return this.getTotalLength() - radius;
});
Here is the resulting code:
var svg = d3.select("svg");
var sourceX = 50,
sourceY = 50;
var targetX = 300,
targetY = 150;
var radius = 50;
svg.append("circle")
.attr("cx", sourceX)
.attr("cy", sourceY)
.attr("r", 4);
svg.append("circle")
.attr("cx", targetX)
.attr("cy", targetY)
.attr("r", 4);
svg.append("circle")
.attr("cx", targetX)
.attr("cy", targetY)
.attr("r", radius)
.style("fill", "none")
.style("stroke", "black");
svg.append("path")
.style("fill", "none")
.style("stroke", "steelblue")
.style("stroke-width", "2px")
.attr("d", drawPath)
.attr("stroke-dasharray", function() {
return this.getTotalLength() - radius;
});
svg.append("circle")
.attr("cx", function(d) {
var path = d3.select("path").node()
var point = path.getPointAtLength(path.getTotalLength() - radius);
return point.x
})
.attr("cy", function(d) {
var path = d3.select("path").node()
var point = path.getPointAtLength(path.getTotalLength() - radius);
return point.y
})
.attr("r", 4);
function drawPath() {
newX = sourceX
newY = sourceY
c1x = newX + ((targetX - newX) * 0.5)
c1y = newY - ((targetY - newY) * 0.5)
c2x = targetX - ((targetX - newX) * 0.05)
c2y = targetY - ((targetY - newY) * 0.5)
pathString = "M " + newX + "," + (newY) + " C " + c1x + "," + c1y + "," + c2x + ", " + c2y + " " + targetX + ", " + targetY;
return pathString
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="500" height="300"></svg>
You can see that the path will continue until the circle's center if we add another unmodified path, in red:
var svg = d3.select("svg");
var sourceX = 50,
sourceY = 50;
var targetX = 300,
targetY = 150;
var radius = 50;
svg.append("circle")
.attr("cx", sourceX)
.attr("cy", sourceY)
.attr("r", 4);
svg.append("circle")
.attr("cx", targetX)
.attr("cy", targetY)
.attr("r", 4);
svg.append("circle")
.attr("cx", targetX)
.attr("cy", targetY)
.attr("r", radius)
.style("fill", "none")
.style("stroke", "black");
svg.append("path")
.style("fill", "none")
.style("stroke", "tomato")
.style("stroke-width", "2px")
.style("stroke-dasharray", "2,2")
.attr("d", drawPath);
svg.append("path")
.style("fill", "none")
.style("stroke", "steelblue")
.style("stroke-width", "2px")
.attr("d", drawPath)
.attr("stroke-dasharray", function() {
return this.getTotalLength() - radius;
});
svg.append("circle")
.attr("cx", function(d) {
var path = d3.select("path").node()
var point = path.getPointAtLength(path.getTotalLength() - radius);
return point.x
})
.attr("cy", function(d) {
var path = d3.select("path").node()
var point = path.getPointAtLength(path.getTotalLength() - radius);
return point.y
})
.attr("r", 4);
function drawPath() {
newX = sourceX
newY = sourceY
c1x = newX + ((targetX - newX) * 0.5)
c1y = newY - ((targetY - newY) * 0.5)
c2x = targetX - ((targetX - newX) * 0.05)
c2y = targetY - ((targetY - newY) * 0.5)
pathString = "M " + newX + "," + (newY) + " C " + c1x + "," + c1y + "," + c2x + ", " + c2y + " " + targetX + ", " + targetY;
return pathString
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg width="500" height="300"></svg>
PS: It's worth mentioning that here I'm assuming (wrongly) that the length of the portion of that path inside the circle is equal to the circle's radius. It's not: the difference is visually negligible for the chart if the two points are not very close, but it's mathematically well defined. However, if the origin is close to the circle's border the difference will be noticeable. In that case, use the libraries indicated.