【问题标题】:Split a cubic bezier curve at multiple points在多个点拆分三次贝塞尔曲线
【发布时间】:2016-02-28 23:15:29
【问题描述】:

我正在编写一个算法,它将三次贝塞尔曲线分割成多条曲线(最多 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 值;我想知道的一件事是简单的算术是否可以在这里工作。例如。假设我的 t 值为 0.2 和 0.6。我在 t = 0.2 处分割曲线,得到两条曲线。第二条曲线覆盖原始值 0.2

【问题讨论】:

    标签: math curve bezier


    【解决方案1】:

    由于您的 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 处细分第二条曲线。

    【讨论】:

    • 这是我希望我能做到的,但这在数学上是否得到证明?我的意思是,我可以这样做,看看它是否有效,但显然我想知道它是否会一直如此。 (也感谢您纠正我的算术!哎呀)
    • 就个人而言,我没有尝试用数学方法证明它。但我知道它有效。
    • 现在我有时间在数学上证明这一点。这是一个简单但繁琐的代数操作。我们可以证明 C(t2) = S2(u) 其中 u=(t2-t1)/(1-t1) 和 S2 是在 t=t1 处拆分 C(t) 导出的第二条拆分曲线。
    【解决方案2】:

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

    1. 代数

      如果您将问题视为多项式的参数t 缩放,那么它会变得更加清晰。例如,您想要零件 t=<0.25,0.5> 的控制点。这意味着我们需要使用参数u=<0.0,1.0> 形成代表同一曲线的新控制点。怎么做?

      1. 将 BEZIER 写为多项式

      每个轴都可以以相同的方式单独完成让我们只关注x-axis。我们有 4 个 BEZIER 控制点,x 坐标为(x0,x1,x2,x3)。如果我们应用伯恩斯坦多项式形成三次多项式,我们得到:

      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. 通过替换重新调整参数

      为此使用线性插值:

      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 方程

      现在您需要将 u^0,u^1,u^2,u^3 的多项式项分开并重新调整每个项以匹配 BEZIER 样式(来自 #1)。从中提取新的控制点。

      例如 term 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
      

      其他的会有点复杂,因为你需要将所有的线组合在一起......简化后你需要分离新的坐标。正如您所看到的,这有点疯狂,但是那里有代数求解器,例如 Derive for Windows ...

      对不起,我没有时间/心情/胃来完整地举例说明所有术语,但你会发现这将是一个多项式的疯狂......

    2. 曲线拟合

      这是基于您正在查找控制点的坐标并检查它与所需曲线的距离。因此,测试“所有可能的”点并记住目标范围上的原始曲线与新曲线之间的紧密匹配。这将是无法解决的,因为可能存在无限数量的控制点,因此我们需要通过利用某些东西将它们减少到可管理的大小。例如,我们现在的控制点将不会远离原始控制点,因此我们可以使用它来限制每个点的区域。您可以使用 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 或其他任何东西......你会看到结果并且可以调整...

    [注释]

    如果您需要精确的精度结果,那么只有 #1 方法可用。方法 #2 总会有一些错误。如果形状不需要精确,有时在没有最终拟合的情况下将插值三次转换为 BEZIER 就足够了(如果切割区域中/附近的形状不太复杂)。

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

    您还没有指定解决方案的约束条件,例如结果的准确性、速度/运行时限制……如果范围是固定的(恒定的)或在运行时可以变化(这将使您需要的 #1 方法极度复杂化将范围作为变量处理到最后)

    【讨论】:

    • SubdivPoints 实际上只是这些点的结构,它没有做任何特别的事情。我稍后使用点和控制点进行绘图,这是一个预处理步骤。所以我有一个点和控制点的集合来定义一条路径,我使用这一步在某些地方划分它(准确地说是在线曲线的交点处)。然后它们在程序的其余部分被固定。我很欣赏深入的回应,但我希望我可以扩大我已经在使用的算法。这不可能吗?
    • @jasonericson 您的subdivideBezier 函数看起来就像带有反转参数tgeometric De Casteljau's algorithm 并且wiki 说可以使用它进行拆分,因此它应该可以工作。 Splitting a bezier curve 也以同样的方式使用它。因此,您可以尝试渲染/拆分/渲染一些测试曲线,以查看您所询问的算法是否真的有效,但我担心这并不精确,因为 t 参数与曲线开始的距离不是线性距离
    • 并且您的 t 边缘的线性组合仅在曲线的点密度恒定时才有效,而对于任意 BEZIER 则不是这种情况。您可以改为通过近似搜索找到第二条边 t(它只是 1 个变量)
    • @jasonericson 计算原始曲线点t=0.6 然后将其拆分t=<0.2,1.0> 并计算t=<0.75> 周围的点选择t,其中点与原始曲线之间的距离与拆分点之间的距离最小。 .. 然后使用 t 进行第二次拆分 ....
    • 啊哈,非常量点密度大致是我关心的(无法用语言表达)。幸运/不幸的是,我实际上不再需要在我的项目中执行此操作,否则我会尝试您的最后一个建议。我将继续将此答案标记为正确,因为它提供了很多有用的信息。感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 2017-12-12
    • 2011-03-10
    • 1970-01-01
    • 2012-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-24
    相关资源
    最近更新 更多