Reputation: 7004
I'm creating a Sankey diagram in D3 using paths as chunky connectors to indicate flow. However, I'm finding the paths that are thicker than they are long start behaving really oddly. You can see an example here, where I'm splitting the path into sections:
The blue and orange overlap, because the blue (and also the grey behind it) don't curve in the same way as the thinner paths, they've got a kind of "kink" in them.
All the line curves work well, except these large ones. I made a simple example with just SVG:
<SVG height=800 width=800>
<g transform="translate(40,400)">
<Path class="link1" d="M29,-129C104.5,-129 104.5,-202.125 180,-202.125" />
<Path class="link2" d="M29,-129C104.5,-129 104.5,-202.125 180,-202.125" />
<Path class="link3" d="M29,-129C104.5,-129 104.5,-202.125 180,-202.125" />
<Path class="normal" d="M29,-129 L104.5,-129" />
<Path class="normal" d="M104.5,-202 L180,-202.125" />
</g>
You can see it here:
https://jsfiddle.net/hanvyj/t91pbp4w/
I couldn't find anything on google, I was hoping someone would have come across the issue and know a fix, or has more SVG experience and know a good alternative to 'SVG:Path' that wouldn't do this?
Upvotes: 3
Views: 693
Reputation: 7004
While Persijn's solution was good, it wasn't quite perfect - it matched the curve of the 'overaying' thinner line to the underlying kinked line. Ideally, I wanted to remove the kink altogether.
As maioman suggested, for this kind of shape it is probably best I define the full path and use fill, rather than stroke width. This gives me a lot more control. The stroke-width keeps the thickness constant throughout the lines length, that's not what I actually wanted and as people pointed out, it's impossible when the width is larger than it's length.
I wanted the height in the x axis to be constant along it's length. I created a function that produces the shape I actually want, in case anyone comes across this:
var linkPath = function(sourceTop,sourceBottom,
targetTop,targetBottom) {
//middle
var MiddleDelta =(targetTop.x - sourceTop.x)/2;
console.log(MiddleDelta);
//start in the top left
var d = "M";
//top left corner
d += sourceTop.x + ", " + sourceTop.y + " ";
d += "C" + (sourceTop.x+MiddleDelta) + "," + sourceTop.y + " ";
d += (targetTop.x - MiddleDelta) + "," + targetTop.y + " ";
//top right corner
d += "" + targetTop.x + "," + targetTop.y + " ";
//bottom right corner
d += "L" + targetBottom.x + "," + targetBottom.y + " ";
d += "C" + (targetBottom.x-MiddleDelta) + "," + targetBottom.y + " ";
d += (sourceBottom.x + MiddleDelta) + "," + sourceBottom.y + " ";
//bottom left corner
d += "" + sourceBottom.x + "," + sourceBottom.y + " ";
return d;
};
It produces this:
as oposed to this:
Upvotes: 3
Reputation: 14990
Problem example:
<svg width="800px" height="600px" viewBox="0 0 100 100">
<path stroke-width="15" stroke="rgba(0,0,0,0.5)" fill="none" d="m0,40 c 20,0 20,-10 40,-10" />
<path stroke-width="3" stroke="rgba(0,0,0,0.5)" fill="none" D="m40,24 c 5,0 5,-5 10,-5" />
<path stroke-width="3" stroke="rgba(0,0,0,0.5)" fill="none" D="m40,27 c 5,0 5,-2 10,-2" />
<path stroke-width="3" stroke="rgba(0,0,0,0.5)" fill="none" D="m40,30 10,0" />
<path stroke-width="3" stroke="rgba(0,0,0,0.5)" fill="none" D="m40,33 c 5,0 5,2 10,2" />
<path stroke-width="3" stroke="rgba(0,0,0,0.5)" fill="none" D="m40,36 c 5,0 5,5 10,5" />
<path stroke-width="3" stroke="rgba(255, 200, 50, 0.7)" fill="none" d="m0,46 c 20,0 20,-10 40,-10" />
</svg>
Solution:
<svg width="800px" height="600px" viewBox="0 0 100 100">
<path stroke-width="15" stroke="rgba(0,0,0,0.5)" fill="none" d="m0,40 c 20,0 20,-10 40,-10" />
<path stroke-width="3" stroke="rgba(0,0,0,0.5)" fill="none" D="m40,24 c 5,0 5,-5 10,-5" />
<path stroke-width="3" stroke="rgba(0,0,0,0.5)" fill="none" D="m40,27 c 5,0 5,-2 10,-2" />
<path stroke-width="3" stroke="rgba(0,0,0,0.5)" fill="none" D="m40,30 10,0" />
<path stroke-width="3" stroke="rgba(0,0,0,0.5)" fill="none" D="m40,33 c 5,0 5,2 10,2" />
<path stroke-width="3" stroke="rgba(0,0,0,0.5)" fill="none" D="m40,36 c 5,0 5,5 10,5" />
<path stroke-width="3" stroke="rgba(255, 200, 50, 0.7)" fill="none" d="m0,46 c 22,0 22,-10 40,-10" />
</svg>
Lets take a look at the d attribute on those orange paths.
Problem path: m0,46 c 20,0 20,-10 40,-10
Solution path: m0,46 c 22,0 22,-10 40,-10
I simply adjusted the C curve command by 2 units.
I want pictures of the differences... ohhhh wel okay:
Upvotes: 3
Reputation: 101820
Your line is so thick that when it follows the bezier curve, it passes outside the starting point of the other end of the line.
Almost any non-straight bezier curve will have a limit on how thick it can get before it starts self-intersecting. You are just exceeding that limit here. If the start and end points were further apart, you would be able to get a bit thicker.
Upvotes: 2