二次Bézier曲线:计算点

Chr*_*gel 54 math curve point quadratic

我想计算二次曲线上的一个点.将它与HTML5的canvas元素一起使用.

当我quadraticCurveTo()在JavaScript中使用该函数时,我有一个源点,一个目标点和一个控制点.

我怎么能计算出创建的二次曲线上的一个点,让我们说t=0.5"只"知道这三点?

xan*_*xan 114

使用二次Bézier公式,例如,在BézierCurves的维基百科页面上找到:

二次贝塞尔公式

在伪代码中,就是这样

t = 0.5; // given example value
x = (1 - t) * (1 - t) * p[0].x + 2 * (1 - t) * t * p[1].x + t * t * p[2].x;
y = (1 - t) * (1 - t) * p[0].y + 2 * (1 - t) * t * p[1].y + t * t * p[2].y;
Run Code Online (Sandbox Code Playgroud)

p[0]是起点,p[1]是控制点,p[2]是终点.t是参数,从0到1.

  • 所以,这意味着:Point.x =(1-t)^ 2*P0.x + 2*(1-t)*t*P1.x + t ^ 2*P2.x; Point.y =(1-t)^ 2*P0.y + 2*(1-t)*t*P1.y + t ^ 2*P2.y; 经过测试,确实有效!=)谢谢! (8认同)
  • @xan:IMO你应该用一些代码(或伪代码)而不是数学符号回答,因为这是一个编程问题. (4认同)
  • @openfrog,t给出了关于起点和终点的点的一小部分.它是假设起点和终点总计1的位置的百分比,因此t通常是一个分数.p0是你的出发点.p1是你的控制/锚点.p2是你的终点. (4认同)
  • 在这种情况下乘以(加)点意味着您乘以(添加)每个组件.即,"3 P = [3*Px,3*py]"和"P1 + P2 = [P1.x + P2.x,P1.y + P2.y]".最后,为了解决问题,你可以将它与自身相乘:x²=`x*x`.最后一部分"t∈[1,0]"表示*t*应该介于0和1之间. (3认同)

Tat*_*ize 32

如果有人需要立方体形式:

        //B(t) = (1-t)**3 p0 + 3(1 - t)**2 t P1 + 3(1-t)t**2 P2 + t**3 P3

        x = (1-t)*(1-t)*(1-t)*p0x + 3*(1-t)*(1-t)*t*p1x + 3*(1-t)*t*t*p2x + t*t*t*p3x;
        y = (1-t)*(1-t)*(1-t)*p0y + 3*(1-t)*(1-t)*t*p1y + 3*(1-t)*t*t*p2y + t*t*t*p3y;
Run Code Online (Sandbox Code Playgroud)

如果有人需要第n种形式,这是算法.你喂N点,它将返回一个__CODE__点数组,这解决了__CODE__.最后一点是给定T值的曲线上的位置.

        //B(t) = (1-t)**3 p0 + 3(1 - t)**2 t P1 + 3(1-t)t**2 P2 + t**3 P3

        x = (1-t)*(1-t)*(1-t)*p0x + 3*(1-t)*(1-t)*t*p1x + 3*(1-t)*t*t*p2x + t*t*t*p3x;
        y = (1-t)*(1-t)*(1-t)*p0y + 3*(1-t)*(1-t)*t*p1y + 3*(1-t)*t*t*p2y + t*t*t*p3y;
Run Code Online (Sandbox Code Playgroud)

您将算法0 1 2 3作为控制点,这些位置将是阵列的其余部分.最后一点(9)是您想要的值.

这也是你细分bezier曲线的方法,你给它__CODE__你想要的值,然后你将细分曲线声明为金字塔的边.然后,您可以索引金字塔侧面的各个点以及从基座构建的金字塔的另一侧.所以例如在五联中:

        //B(t) = (1-t)**3 p0 + 3(1 - t)**2 t P1 + 3(1-t)t**2 P2 + t**3 P3

        x = (1-t)*(1-t)*(1-t)*p0x + 3*(1-t)*(1-t)*t*p1x + 3*(1-t)*t*t*p2x + t*t*t*p3x;
        y = (1-t)*(1-t)*(1-t)*p0y + 3*(1-t)*(1-t)*t*p1y + 3*(1-t)*t*t*p2y + t*t*t*p3y;
Run Code Online (Sandbox Code Playgroud)

(原谅十六进制,我希望它很漂亮)

您可以将两条完美细分的曲线编入索引,分别为0,5,9,C,E和E,D,B,8,4.需要特别注意的是,第一条曲线以控制点(0)开始,以点结束在曲线(E)上,第二条曲线从曲线(E)开始,到控制点(4)结束.鉴于此,您可以完美地细分贝塞尔曲线,这是您所期望的.连接两条曲线的新控制点位于曲线上.

        //B(t) = (1-t)**3 p0 + 3(1 - t)**2 t P1 + 3(1-t)t**2 P2 + t**3 P3

        x = (1-t)*(1-t)*(1-t)*p0x + 3*(1-t)*(1-t)*t*p1x + 3*(1-t)*t*t*p2x + t*t*t*p3x;
        y = (1-t)*(1-t)*(1-t)*p0y + 3*(1-t)*(1-t)*t*p1y + 3*(1-t)*t*t*p2y + t*t*t*p3y;
Run Code Online (Sandbox Code Playgroud)

你会发现它只是通过每组点数量的公式.对于N个解,你得到(N-1)个中值为(t),然后你得到那些中点,得到(N-2)个点,然后是(N-3)个等,直到你只有一个点.这一点在曲线上.因此,对于t,在0到1之间的值解决问题,将为您提供整个曲线.知道了这一点,我的实现只是在一个数组中向前传播值,节省了不止一次重新计算的内容.我已经将它用于100分,并且它仍然闪电般快速.

(如果你想知道,不是没有它真的值得.SVG是正确的停止立方体).


tal*_*abi 7

我创建了这个演示:

// x = a * (1-t)³ + b * 3 * (1-t)²t + c * 3 * (1-t)t² + d * t³
//------------------------------------------------------------
// x = a - 3at + 3at² - at³ 
//       + 3bt - 6bt² + 3bt³
//             + 3ct² - 3ct³
//                    + dt³
//--------------------------------
// x = - at³  + 3bt³ - 3ct³ + dt³
//     + 3at² - 6bt² + 3ct²
//     - 3at + 3bt
//     + a
//--------------------------------
// 0 = t³ (-a+3b-3c+d) +  => A
//     t² (3a-6b+3c)   +  => B
//     t  (-3a+3b)     +  => c
//     a - x              => D
//--------------------------------

var A = d - 3*c + 3*b - a,
    B = 3*c - 6*b + 3*a,
    C = 3*b - 3*a,
    D = a-x;

// So we need to solve At³ + Bt² + Ct + D = 0 
Run Code Online (Sandbox Code Playgroud)

完整的例子

可以帮助别人.