在多个点处拆分三次贝塞尔曲线

时间:2015-11-26 00:34:35

标签: math curve bezier

我正在编写一种将三次贝塞尔曲线分成多条曲线(最多4条)的算法。对于我想从头开始分割的每个点,我都有t值。我还有一个用于分割曲线的算法:

SubdivPoints subdivideBezier(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t)
{
    Vector2 p11 = (p1 - p0) * t + p0;
    Vector2 p21 = (p2 - p1) * t + p1;
    Vector2 p31 = (p3 - p2) * t + p2;

    Vector2 p12 = (p21 - p11) * t + p11;
    Vector2 p22 = (p31 - p21) * t + p21;

    Vector2 p13 = (p22 - p12) * t + p12;

    return SubdivPoints(p11, p12, p22, p31, p13);
}

我的问题是,是否有一种简单的方法来扩展它以进行多次拆分?我想在每次分裂之后我想重新计算t值;我想知道的一件事是简单的算法是否适用于此。例如。让我们说我的值为0.2和0.6。我在t = 0.2处分割曲线,给出了两条曲线。第二条曲线包括t值0.2

2 个答案:

答案 0 :(得分:4)

由于你的subdivideBezier()函数确实遵循De Casteljau算法,我假设它可以在参数t下细分一个三次贝塞尔曲线。所以,是的,要继续细分一个不同的参数(比如说t2),你需要做的就是弄清楚哪个细分曲线t2落在上面,计算新的t2 *与该曲线并细分。在您想要在t = 0.2和0.6处细分的示例中,0.6的新参数应为(0.6-0.2)/(1-0.2)= 0.5。因此,您可以在t = 0.5时简单地细分第二条曲线。

答案 1 :(得分:1)

很难说,即使您当前的方法有效,我们也看不到SubdivPoints背后的原因。我可以想到两种方法:

  1. <强>代数

    如果您将问题看作多项式的参数t缩放,那么它就会变得更清晰。例如,您需要部分t = <0.25,0.5>的控制点。这意味着我们需要形成表示具有参数u=<0.0,1.0>的相同曲线的新控制点。怎么做?

    1. 将BEZIER写为多项式
    2. 每个轴可以以相同的方式单独完成,让我们只关注x - 轴。我们有4个BEZIER控制点,x坐标为(x0,x1,x2,x3)。如果我们应用bernstein多项来形成三次多项式,我们得到:

      x(t)=      (                           (    x0))
          +    t*(                  (3.0*x1)-(3.0*x0))
          +  t*t*(         (3.0*x2)-(6.0*x1)+(3.0*x0))
          +t*t*t*((    x3)-(3.0*x2)+(3.0*x1)-(    x0))
      
      1. 通过替换重新缩放参数
      2. 使用线性插值:

        t0=0.25 -> u0=0.0
        t1=0.50 -> u1=1.0
        t=t0+(t1-t0)*(u-u0)/(u1-u0)
        t=0.25+0.5*u
        

        现在使用u而不是t

        重写多项式
        x(t)=             (                           (    x0))
            +(0.25+u/2)  *(                  (3.0*x1)-(3.0*x0))
            +(0.25+u/2)^2*(         (3.0*x2)-(6.0*x1)+(3.0*x0))
            +(0.25+u/2)^3*((    x3)-(3.0*x2)+(3.0*x1)-(    x0))
        
        1. 更改多项式形式以再次匹配BEZIER方程
        2. 现在您需要为u^0,u^1,u^2,u^3分隔多项式项,并将每项更改为匹配BEZIER样式(来自#1 )。从中提取新的控制点。

          例如,术语u ^ 3是这样的。获得u ^ 3的唯一可能性来自

          (1/4+u/2)^3= (1/8)*u^3 + (3/16)*u^2 + (3/32)*u + (1/64)
          

          如此分开的u^3字词将是:

          u*u*u*((    x3)-(3.0*x2)+(3.0*x1)-(    x0))/8
          

          其他的将会更复杂,因为您需要将所有线组合在一起...在简化之后,您需要分离新的坐标。正如你所看到的那样是轻微的疯狂但是有像代数导出的代数解算器......

          抱歉,我没有时间/情绪/胃来充分说明所有条款,但你会发现这将是一个多项式的疯狂......

        3. 曲线拟合

          这是基于你找到控制点的坐标并检查它与你想要的曲线的距离。所以测试&#34;&#34;所有可能&#34;指出并记住目标范围内原始曲线与新曲线之间的闭合匹配。这是无法解决的,因为可能存在无数个控制点,因此我们需要通过利用某些东西来减少那些可管理的大小。例如,我们现在控制点不会远离原始控制点,因此我们可以使用它来限制每个点的面积。您可以将approximation search用于此项或任何其他最小化技术。

          如果使用插值立方,你也可以获得更好的起点。见BEZIER vs Interpolation cubics。所以:

          1. 从BEZIER曲线计算4个新的插值立方控制点

            所以,如果我们有与之前相同的范围

            X0 = BEZIER(t0-(t1-t0))
            X1 = BEZIER(t0)
            X2 = BEZIER(t1)
            X3 = BEZIER(t1+(t1-t0))
            

            这些是插值立方控制点而不是BEZIER。它们应该均匀分散。 X1,X2是曲线端点,X0,X3位于它们之前和之后,以尽可能地保留参数的局部形状和线性

          2. (X0,X1,X2,X3)转换回BEZIER控制点(x0,x1,x2,x3)

            您可以使用上面链接中的我的公式:

            // input: X0,Y0,..X3,Y3  ... interpolation control points
            // output: x0,y0,..x3,y3 ... Bezier control points
                double x0,y0,x1,y1,x2,y2,x3,y3,m=1.0/9.0;
                x0=X1;             y0=Y1;
                x1=X1+((X1-X0)*m); y1=Y1+((Y1-Y0)*m);
                x2=X2+((X2-X3)*m); y2=Y2+((Y2-Y3)*m);
                x3=X2;             y3=Y2;
            

            如您所见,每个轴的计算方式相同......

          3. 对BEZIER控制点应用近似搜索

            新的(x0,x1,x2,x3)还不精确,因为通过盲目地改变控制点,我们可能会略微错过曲线扭曲形状的局部极端。所以我们需要搜索真实的。幸运的是,新的控制点(x0',x1',x2',x3')将非常接近这些点,所以现在我们必须在其对应部分附近以一些合理的半径搜索每个点(比如边界框size/8或者其他......你会看到结果,可以调整......

        4. <强> [注释]

          如果您需要精确的精确结果,则只能使用#1 方法。方法#2 总会出现一些错误。如果形状不需要精确,有时插值立方体转换为BEZIER而没有最终拟合就足够了(如果在切割区域中/附近形状不太复杂)。

          正如我之前所写的那样,在不知道你的SubdivPoints中使用了哪个原则是不可能回答重复使用它的结果是什么......

          此外,您没有指定解决方案的约束,如结果的准确性,速度/运行时限制......如果范围是固定的(常量),或者在运行期间可能会有所不同(这将使您需要的#1方法极为复杂化处理范围作为变量直到结束)