首页 > 解决方案 > 如何设置贝塞尔曲线的控制点来弯曲一条线?

问题描述

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

这是贝塞尔曲线函数:

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

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

标签: javascriptpixi.js

解决方案


功能

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>


推荐阅读