0

I am creating a link between points with bezier curve for Sankey chart.

Here is the code I have tried:

let 
  margin = 10,
  x0 = 20,
  y0 = 20,
  x1 = 150,
  y1 = 100,
  width = 30,
  path = d3.path(),
  path2 = d3.path(),
  controlPointX = (x0 + x1) / 2;

/* with filled path */
path.moveTo(x0, y0);
path.bezierCurveTo(controlPointX, y0, controlPointX, y1, x1, y1);
path.lineTo(x1, y1 + width);
path.bezierCurveTo(controlPointX, y1 + width, controlPointX, y0 + width, x0, y0 + width);
path.closePath();

let svg = d3.select('svg');
let g = svg.append('g').attr('transform', `translate(${margin}, ${margin})`);
g.append('path')
  .attr('d', path)
  .attr('fill', 'red');

// curve 2 offset
y0 += 120;
y1 += 120;

// with stroke
path2.moveTo(x0, y0);
path2.bezierCurveTo(controlPointX, y0, controlPointX, y1, x1, y1);
g.append('path')
  .attr('d', path2)
  .attr('stroke-width', width)
  .attr('stroke', 'blue')
  .attr('fill', 'none');

enter image description here

In the first approach, the perpendicular width between two curve is not same. But in the second example, I have used the same single curve and stroke-with for height and that works perfectly. I want to achieve the same with filled path.

Fiddle Link: https://jsfiddle.net/3cxt40Lz/

Mehdi
  • 7,204
  • 1
  • 32
  • 44
Abdul Alim
  • 346
  • 3
  • 15
  • 1
    Does this answer your question? [How to offset a cubic bezier curve?](https://stackoverflow.com/questions/4148831/how-to-offset-a-cubic-bezier-curve) – r3mainer Apr 03 '20 at 09:49

1 Answers1

3

Here is an approximate solution.

I opened the generated SVG in a vector editing software, in order to check the Bezier curve's control points.

enter image description here

Playing around a little, it turned out that shifting the lower control points horizontally to the left hand side adapted the curve as desired:

enter image description here

So back to the code, created a new control point shifted to the left by width / 2:

controlPointX2 = (x0 + x1 - width) / 2;

/* with filled path */
path.moveTo(x0, y0);
path.bezierCurveTo(controlPointX, y0, controlPointX, y1, x1, y1);
path.lineTo(x1, y1 + width);
path.bezierCurveTo(controlPointX2, y1 + width, controlPointX2, y0 + width, x0, y0 + width);

The result is not exactly as the blue curve, but very close, as illustrated below where the red curve is shown above the blue one (with some transparency).

result

Updated jsFiddle:

https://jsfiddle.net/zbe90rjq/1/

Mehdi
  • 7,204
  • 1
  • 32
  • 44