Как соединить несколько кривых Безье? - PullRequest
0 голосов
/ 11 марта 2020

Я хотел бы нарисовать диаграмму, используя bezierCurveTo(). Насколько я знаю, можно установить только 3 опорные точки с 1 bezierCurveTo(). Если я использую несколько из них, я получаю негладкую линию. Как я могу решить это?

<canvas id="myCanvas" width="600" height="150" style="border:1px solid #d3d3d3;"></canvas>

<script>
  var canvas = document.getElementById('myCanvas');
  var context = canvas.getContext('2d');

  context.beginPath();
  context.moveTo(0, 150);
  context.bezierCurveTo(100, 0, 200, 100, 300, 20);
  context.bezierCurveTo(400, 0, 500, 100, 600, 20);
  
  context.strokeStyle = 'blue';
  context.stroke();
</script> 

Ответы [ 2 ]

1 голос
/ 11 марта 2020

Это нетривиальная проблема. Это зависит от того, какой степени гладкости вы хотели бы достичь (просто соедините касательные или сделайте одинаковые радиусы в точке соединения). Простейший способ показан на рисунке ([A3-X] / [X-B2] = [A3-A4] / [B1-B2]; запуск вектора [A3-X] с A4 и [X-B2] с B1 до получить точки привязки A3x и B2x). enter image description here

Но вы также можете взглянуть на D3 Shape module (например, кривая Catmul Rom), он сгенерирует вам сплайн Безье из точек, которые он должен go через. Или посмотрите где-нибудь алгоритм.

0 голосов
/ 11 марта 2020

Мы можем поместить все кривые в массиве l oop поверх них, двигаясь к последней точке, прежде чем нарисовать следующую кривую Безье. Ниже приведен пример кода:

<canvas id="myCanvas" width="600" height="150"></canvas>
<script>
    var canvas = document.getElementById('myCanvas');
    var context = canvas.getContext('2d');

    function drawCurve(x, y, curves) {
        context.beginPath();
        context.moveTo(x, y);
        for (i = 0; i < curves.length; i++) {
            c = curves[i]
            context.bezierCurveTo(c[0], c[1], c[2], c[3], c[4], c[5]);
            context.moveTo(c[4], c[5]);
            context.stroke();
        }
    }

    context.strokeStyle = 'blue';
    drawCurve(0, 150, [
        [100, 0, 200, 100, 300, 50],
        [400, 0, 500, 100, 600, 20]
    ]);

    context.strokeStyle = 'red';
    drawCurve(0, 10, [
        [100, 0, 180, 90, 280, 50],
        [400, 0, 400, 80, 600, 120]
    ]);
    
    context.strokeStyle = 'green';
    drawCurve(0, 80, [
        [100, 0, 90, 45, 140, 25],
        [200, 0, 200, 40, 300, 50],
        [500, 60, 400, 80, 300, 120],
        [300, 120, 200, 160, 100, 80],
    ]);
</script>

Но «неровная линия» также зависит от ваших кривых, если они будут в совершенно противоположных направлениях, мы увидим острый край.

См. Пример ниже я рисую что-то вроде звезды.

<canvas id="myCanvas" width="150" height="150"></canvas>
<script>
  var canvas = document.getElementById('myCanvas');
  var context = canvas.getContext('2d');

  function drawCurve(x, y, curves) {
    context.moveTo(x, y);
    for (i = 0; i < curves.length; i++) {
      c = curves[i]
      context.bezierCurveTo(c[0], c[1], c[2], c[3], c[4], c[5]);
      context.moveTo(c[4], c[5]);
    }
    context.stroke();
  }

  data = []
  numPoints = 12
  size = 35
  angle = 45
  for (j = 0; j < numPoints; j++) {
    a = angle * Math.PI / 180
    points = []
    points.push(80 + Math.round(size / 2 * Math.sin(a)))
    points.push(80 + Math.round(size / 2 * Math.cos(a)))
    points.push(80 + Math.round(size * Math.sin(a)))
    points.push(80 + Math.round(size * Math.cos(a)))
    points.push(80 + Math.round(size * 2 * Math.sin(a)))
    points.push(80 + Math.round(size * 2 * Math.cos(a)))

    angle += 360 / numPoints
    data.push(points)
  }
  drawCurve(80, 80, data);
</script>
...