Как установить контрольные точки кривой Безье для изгиба линии? - PullRequest
1 голос
/ 12 марта 2020

Я хочу нарисовать слегка изогнутую линию, используя кривую Безье.

Это функция кривой Безье:

function bezierCurve(controlPoint1, controlPoint2, line) { }

Я не знаю, как установить контрольные точки, чтобы получить слегка изогнутую линию для любой линии.

Я попытался найти перпендикулярную точку этой линии:

function Line(a, b) {

  let mid = [(a[0] + b[0]) / 2,
             (a[1] + b[1]) / 2];

  let dX = b[0] - a[0],
      dY = b[1] - a[1];

  this.perpPoint = (y) => {
    let x1 = mid[0],
        y1 = mid[1],
        x;

    if (dX === 0 || dY === 0) {
      x = y;
      y = y1;

      return [x, y];
    }

    let m = dX / dY;

    let perpendicularM = - 1 / m;

    // y - y1 = m(x - x1)
    // x = (y - y1) / m + x1

    x = (y - y1) / m + x1;

    return [x, y];
  };

И использовать ее как-нибудь, чтобы найти точки чуть выше средняя точка линии в качестве контрольных точек, но не работает.

1 Ответ

2 голосов
/ 12 марта 2020

Функция

function bezierCurve(controlPoint1, controlPoint2, line) { }

Предположим, что кривая представляет собой куби c Безье (две контрольные точки). Функция, которую вы задаете, выглядит только как ее найти точку посередине и сместить некоторую фиксированную сумму. вам потребуется больше контроля над тем, где точка находится относительно линии.

Перпендикулярное смещение от линии.

Следующая функция даст вам немного больше контроля, позволяя вам указать, где вдоль линии Вы хотите указать точку и то, как далеко эта точка должна быть от линии.

Аргумент along и dist - это дроби линии, такие как along = 0.5 на полпути и dist = 0.2 на 1/5 длина линии прочь. dist > 0 и точка смещена вправо от линии dist < 0, а смещение находится влево (ось Y направлена ​​вниз)

function pointFromLine(along, dist, p1, p2, res = {}) {
    const dx = p2.x - p1.x;
    const dy = p2.y - p1.y;
    res.x = p1.x + dx * along - dy * dist;
    res.y = p1.y + dy * along + dx * dist;
    return res;
}   

Чтобы найти среднюю точку на 1/5 длины линии от центра

const p1 = {x: 10, y: 10};
const p2 = {x: 510, y: 310};
const offset = pointFromLine(0.5, 0.2, p1, p2);

Чтобы найти две контрольные точки для куби c Безье.

const p1 = {x: 10, y: 10};
const p2 = {x: 510, y: 310};
const cp1 = pointFromLine(0.333, 0.2, p1, p2);     // 1/3rd dist from start
const cp2 = pointFromLine(1 - 0.333, 0.2, p1, p2); // equal dist (1/3rd) from end of line

Пример использования

В следующем фрагменте показана используемая функция создать симметричную кривую для куби c Безье. Переместите ползунок для управления dist вдоль линии и смещением dist от линии

    function pointFromLine(along, dist, p1, p2, res = {}) {
        const dx = p2.x - p1.x;
        const dy = p2.y - p1.y;
        res.x = p1.x + dx * along - dy * dist;
        res.y = p1.y + dy * along + dx * dist;
        return res;
    }   

    const p1 = {x: 40, y: 40};
    const p2 = {x: 160, y: 160};
    const cp1 ={x: 0, y: 0};
    const cp2 ={x: 0, y: 0};        
    const ctx = canvas.getContext("2d");
    update();
    function drawPoint(p) {
        ctx.beginPath();
        ctx.arc(p.x, p.y, 4, 0, Math.PI * 2);
        ctx.fill();
    }
    function drawLine(p1, p2) {
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.lineTo(p1.x, p1.y);
        ctx.lineTo(p2.x, p2.y);
        ctx.stroke();
    }
    function drawCubicBezier(p1, p2, cp1, cp2) {
        ctx.lineWidth = 3;
        ctx.beginPath();
        ctx.lineTo(p1.x, p1.y);
        ctx.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, p2.x, p2.y);
        ctx.stroke();
        drawLine(p1,cp1);
        drawLine(p2,cp2);
        drawPoint(p1);
        drawPoint(p2);
        drawPoint(cp1);
        drawPoint(cp2);
    }
    
    alongIn.addEventListener("input", update);
    distIn.addEventListener("input", update);
    
    function update() {
        const a = alongIn.value * 1;
        const d = distIn.value * 1;
        info.textContent = `Along: ${a.toFixed(2)} Offset: ${d.toFixed(2)}`
        
        ctx.clearRect(0,0,200,200);
        drawCubicBezier(
            p1, 
            p2, 
            pointFromLine(a , d, p1, p2, cp1),
            pointFromLine(1 - a, d, p1, p2, cp2)
        );
    }
        


   
   

   
   
   canvas {
       border: 1px solid black;
   }
   Dist along: <input id="alongIn" type="range" min="-1" max="2" step="0.01" value="0.3"/>
   Dist out: <input id="distIn" type="range" min="-1" max="2" step="0.01" value="0.3"/>
   <div id="info"> </div>
   <canvas id="canvas" width="200" height="200" ></canvas>
...