Converting a list of points to an SVG cubic piecewise Bezier curve

扶醉桌前 提交于 2021-01-27 21:52:14

问题


I have a list of points and want to connect them as smoothly as possible. I have a function that I evaluate to get these points. I could simply use more sampling points but that would only increase the SVG file size. I think using a piecewise cubic Bezier curve would be better suited. How do I do this?

I did some research and came across the svgpathtools package, which looked promising. However, I did not find any functionality like this.


回答1:


If you ever used Illustrator you certainly remember that the first control points of a bezier is connected with the last control point of the previous one. This line is tangent to the curve. In my code I am using the size of this line to draw the curve.

Also: the first & the last curve are quadratic Bezier (only one control point). All the other curves are cubic Bezier (2 control points).

// size of the tangent
var t = 1 / 5;

// points array
var p = [{
  x: 100,
  y: 100
}, {
  x: 250,
  y: 150
}, {
  x: 300,
  y: 300
}, {
  x: 450,
  y: 250
}, {
  x: 510,
  y: 140
}, {
  x: 590,
  y: 250
}, {
  x: 670,
  y: 140
}];

function controlPoints(p) {
  // given the points array p calculate the control points for the cubic Bezier curves

  var pc = [];
  for (var i = 1; i < p.length - 1; i++) {
    var dx = p[i - 1].x - p[i + 1].x; // difference x
    var dy = p[i - 1].y - p[i + 1].y; // difference y
    // the first control point
    var x1 = p[i].x - dx * t;
    var y1 = p[i].y - dy * t;
    var o1 = {
      x: x1,
      y: y1
    };

    // the second control point
    var x2 = p[i].x + dx * t;
    var y2 = p[i].y + dy * t;
    var o2 = {
      x: x2,
      y: y2
    };

    // building the control points array
    pc[i] = [];
    pc[i].push(o1);
    pc[i].push(o2);
  }
  return pc;
}

function drawCurve(p) {

  var pc = controlPoints(p); // the control points array
  
  let d =`M${p[0].x},${p[0].y}
Q${pc[1][1].x},${pc[1][1].y}, ${p[1].x},${p[1].y}
`
  if (p.length > 2) {
    // central curves are cubic Bezier
    for (var i = 1; i < p.length - 2; i++) {
    d += `C${pc[i][0].x}, ${pc[i][0].y}, ${pc[i + 1][1].x}, ${pc[i + 1][1].y}, ${p[i + 1].x},${p[i + 1].y}`;
    }
  
    // the first & the last curve are quadratic Bezier
    var n = p.length - 1;
    d+= `Q${pc[n - 1][0].x}, ${pc[n - 1][0].y}, ${p[n].x}, ${p[n].y}`;

}
  return d
}


thePath.setAttribute("d", drawCurve(p))
svg{border:solid; fill:none;stroke:black;}
<svg id = 'svg' viewBox="0 0 800 400">
  <path id="thePath" />
</svg>

This is a pen where I'm doing the same in canvas https://codepen.io/enxaneta/pen/PqLNLv . Here you can see the tangents.



来源:https://stackoverflow.com/questions/62855310/converting-a-list-of-points-to-an-svg-cubic-piecewise-bezier-curve

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!