如何设置贝塞尔曲线的控制点来弯曲一条线?

How to set control points of a bezier curve to bend a line?

我想用贝塞尔曲线画一条稍微弯曲的线。

这是贝塞尔曲线函数:

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];
  };

并用它以某种方式找到线中点正上方的点作为控制点,但没有成功。

函数

function bezierCurve(controlPoint1, controlPoint2, line) { }

建议曲线是三次贝塞尔曲线(两个控制点)你给出的函数看起来只是在中间找到一个点并偏移一些固定量。您将需要更多地控制点与线的关系。

与直线的垂直偏移。

下面的函数会给你更多的控制权,让你指定你想要的点沿线的位置以及该点应该离线多远。

参数 alongdist 是一条线的一部分,例如 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);

为立方贝塞尔曲线找到两个控制点。

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



 

使用示例

以下代码片段显示了用于为立方贝塞尔曲线创建对称曲线的函数。移动滑块控制沿线的距离和距离线的距离偏移

    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>