我可以从完整的bezier制作一个半bezier吗?

时间:2014-09-08 10:56:17

标签: javascript canvas bezier

采用JavaScript绘制的典型立方贝塞尔曲线(这个例子我用googled ......) http://jsfiddle.net/atsanche/K38kM/

具体来说,这两行:

context.moveTo(188, 130);
context.bezierCurveTo(170, 10, 350, 10, 388, 170);

我们有一个立方贝塞尔曲线,从188, 130开始,到388, 170结束,并有控制点a:170, 10和b:350, 10

我的问题是,是否可以通过数学方式调整终点和控制点,使另一条曲线只是原始曲线的一部分?

理想的结果是能够从头开始获取贝塞尔曲线的一个百分比切片,其中0.5将仅绘制贝塞尔曲线的一半,0.75将绘制贝塞尔曲线的大部分(等等)

我已经开始使用De Castelau的一些实现,它允许我跟踪[0 ... 1]之间的bezier的轮廓,但这并没有提供一种数学重新计算的方法贝塞尔曲线的终点和控制点,以制作一个子贝塞尔... ...

提前致谢

3 个答案:

答案 0 :(得分:5)

De Casteljau确实是要去的算法。对于由4个控制点P0,P1,P2和P3定义的三次贝塞尔曲线,子贝塞尔曲线(0,u)的控制点为P0,Q0,R0和S0以及子贝塞尔曲线的控制点。 (u,1)是S0,R1,Q2和P3,其中

Q0 =(1-u)* P0 + u * P1
   Q1 =(1-u)* P1 + u * P2
   Q2 =(1-u)* P2 + u * P3
   R0 =(1-u)* Q0 + u * Q1
   R1 =(1-u)* Q1 + u * Q2
   S0 =(1-u)* R0 + u * R1

请注意,如果您想从原始贝塞尔曲线“提取”一个片段(u1,u2),则必须两次申请De Casteljau。第一次将输入Bezier曲线C(t)分解为参数u1处的C1(t)和C2(t),第二次将曲线C2(t)分割为调整后的参数u2 * =(u2) -u1)/(1-U1)。

答案 1 :(得分:1)

这是怎么做的。你可以用这个功能得到左半边或右半边。感谢此处的标记:https://stackoverflow.com/a/23452618/1828637

我对它进行了修改,因此它可以适用于单元格,因此我们可以在css过渡中将其用于cubic-bezier

function splitCubicBezier(options) {
  var z = options.z,
      cz = z-1,
      z2 = z*z,
      cz2 = cz*cz,
      z3 = z2*z,
      cz3 = cz2*cz,
      x = options.x,
      y = options.y;

  var left = [
    x[0],
    y[0],
    z*x[1] - cz*x[0], 
    z*y[1] - cz*y[0], 
    z2*x[2] - 2*z*cz*x[1] + cz2*x[0],
    z2*y[2] - 2*z*cz*y[1] + cz2*y[0],
    z3*x[3] - 3*z2*cz*x[2] + 3*z*cz2*x[1] - cz3*x[0],
    z3*y[3] - 3*z2*cz*y[2] + 3*z*cz2*y[1] - cz3*y[0]];

  var right = [
    z3*x[3] - 3*z2*cz*x[2] + 3*z*cz2*x[1] - cz3*x[0],
    z3*y[3] - 3*z2*cz*y[2] + 3*z*cz2*y[1] - cz3*y[0],
                    z2*x[3] - 2*z*cz*x[2] + cz2*x[1],
                    z2*y[3] - 2*z*cz*y[2] + cz2*y[1],
                                    z*x[3] - cz*x[2], 
                                    z*y[3] - cz*y[2], 
                                                x[3],
                                                y[3]];

  if (options.fitUnitSquare) {
    return {
      left: left.map(function(el, i) {
        if (i % 2 == 0) {
          //return el * (1 / left[6])
          var Xmin = left[0];
          var Xmax = left[6]; //should be 1
          var Sx = 1 / (Xmax - Xmin);
          return (el - Xmin) * Sx;
        } else {
          //return el * (1 / left[7])
          var Ymin = left[1];
          var Ymax = left[7]; //should be 1
          var Sy = 1 / (Ymax - Ymin);
          return (el - Ymin) * Sy;
        }
      }),
      right: right.map(function(el, i) {
        if (i % 2 == 0) {
          //xval
          var Xmin = right[0]; //should be 0
          var Xmax = right[6];
          var Sx = 1 / (Xmax - Xmin);
          return (el - Xmin) * Sx;
        } else {
          //yval
          var Ymin = right[1]; //should be 0
          var Ymax = right[7];
          var Sy = 1 / (Ymax - Ymin);
          return (el - Ymin) * Sy;
        }
      })
    }
  } else {
   return { left: left, right: right};
  }
}

这是该功能,现在将它与您的参数一起使用。

var myBezier = {
  xs: [188, 170, 350, 388],
  ys: [130, 10, 10, 170]
};

var splitRes = splitCubicBezier({
  z: .5, //percent
  x: myBezier.xs,
  y: myBezier.ys,
  fitUnitSquare: false
});

这会给你

({
    left: [188, 130, 179, 70, 219.5, 40, 267, 45],
    right: [267, 45, 314.5, 50, 369, 90, 388, 170]
})

小提琴证明了它的一半,我把它叠加在原来的上面:

http://jsfiddle.net/K38kM/8/

答案 2 :(得分:0)

是的!看看这里的bezier部分

http://en.m.wikipedia.org/wiki/De_Casteljau“s_algorithm

总而言之并非如此困难。