【问题标题】:How do I implement a Bézier curve in C++?如何在 C++ 中实现贝塞尔曲线?
【发布时间】:2009-04-24 09:12:40
【问题描述】:

我想实现一个Bézier curve。我以前在 C# 中做过这个,但我对 C++ 库完全不熟悉。我应该如何创建二次曲线?

void printQuadCurve(float delta, Vector2f p0, Vector2f p1, Vector2f p2);

显然我们需要使用线性插值,但这是否存在于标准数学库中?如果没有,我在哪里可以找到它?

我正在使用 Linux。

【问题讨论】:

    标签: c++ math interpolation spline


    【解决方案1】:

    最近我遇到了同样的问题,并想自己实现它。 这张来自维基百科的图片帮助了我:

    以下代码是用 C++ 编写的,展示了如何计算二次贝塞尔曲线。

    int getPt( int n1 , int n2 , float perc )
    {
        int diff = n2 - n1;
    
        return n1 + ( diff * perc );
    }    
    
    for( float i = 0 ; i < 1 ; i += 0.01 )
    {
        // The Green Line
        xa = getPt( x1 , x2 , i );
        ya = getPt( y1 , y2 , i );
        xb = getPt( x2 , x3 , i );
        yb = getPt( y2 , y3 , i );
    
        // The Black Dot
        x = getPt( xa , xb , i );
        y = getPt( ya , yb , i );
    
        drawPixel( x , y , COLOR_RED );
    }
    

    其中 (x1|y1)、(x2|y2) 和 (x3|y3) 分别为图像中的 P0、P1 和 P2。只是为了展示基本思想......

    对于那些要求三次贝塞尔曲线的人,它只是模拟(也来自维基百科):

    This 答案提供了代码。

    【讨论】:

    • 很好的回复,但这在技术上是 DeCasteljau 的代表。这更容易理解,但通常不太理想。
    • 谢谢!你完全正确。我当时需要的是对贝塞尔曲线的生动理解(人们感到类似的愿望可能是这个答案被多次投票的原因)。大多数其他算法是(通常是必要的)对 DeCasteljau 的优化,因此它似乎是一个非常好的基础,可以在此基础上轻松构建知识。
    【解决方案2】:

    这里是具有任意数量点的曲线的一般实现。

    vec2 getBezierPoint( vec2* points, int numPoints, float t ) {
        vec2* tmp = new vec2[numPoints];
        memcpy(tmp, points, numPoints * sizeof(vec2));
        int i = numPoints - 1;
        while (i > 0) {
            for (int k = 0; k < i; k++)
                tmp[k] = tmp[k] + t * ( tmp[k+1] - tmp[k] );
            i--;
        }
        vec2 answer = tmp[0];
        delete[] tmp;
        return answer;
    }
    

    请注意,它使用堆内存作为临时数组,效率并不高。如果您只需要处理固定数量的点,您可以硬编码 numPoints 值并改用堆栈内存。

    当然,上面假设你有一个 vec2 结构和它的运算符,如下所示:

    struct vec2 {
        float x, y;
        vec2(float x, float y) : x(x), y(y) {}
    };
    
    vec2 operator + (vec2 a, vec2 b) {
        return vec2(a.x + b.x, a.y + b.y);
    }
    
    vec2 operator - (vec2 a, vec2 b) {
        return vec2(a.x - b.x, a.y - b.y);
    }
    
    vec2 operator * (float s, vec2 a) {
        return vec2(s * a.x, s * a.y);
    }
    

    【讨论】:

    • @ifroce2d 那是哪种曲线?
    • ??根据问题和函数名称“getBezierPoint”,它是贝塞尔曲线
    • @ifroce2d Vector3的基本思想应该是一样的吧?
    【解决方案3】:

    您可以选择 de Casteljau 的方法,即递归地分割控制路径,直到使用线性插值到达该点,如上所述,或 Bezier 的方法,即混合控制点。

    贝塞尔法是

     p = (1-t)^3 *P0 + 3*t*(1-t)^2*P1 + 3*t^2*(1-t)*P2 + t^3*P3 
    

    对于立方和

     p = (1-t)^2 *P0 + 2*(1-t)*t*P1 + t*t*P2
    

    对于二次方。

    t 通常在 0-1 上,但这不是必需的 - 实际上曲线延伸到无穷大。 P0、P1 等是控制点。曲线经过两个端点,但通常不经过其他点。

    【讨论】:

      【解决方案4】:

      您之前是否使用过 C# 库?

      在 C++ 中,没有可用的贝塞尔曲线标准库函数(目前)。你当然可以自己动手(CodeProject sample)或寻找数学库。

      This blogpost 很好地解释了这个想法,但在 Actionscript 中。翻译应该不是什么大问题。

      【讨论】:

      • 仅供参考。博客文章的链接显示一个空白页面。
      • @RSahu 感谢您的举报;我在 Wayback Machine 中找到了一份存档副本并编辑了答案。
      【解决方案5】:

      为了在给定的行程百分比(t) 上沿三次曲线获得一个单独的点(x, y),以及给定的控制点(x1, y1)(x2, y2)(x3, y3)(x4, y4),我扩展了De Casteljau 的算法并重新排列方程以最小化指数:

      //Generalized code, not C++
      variables passed to function:   t,  x1, y1, x2, y2, x3, y3, x4, y4
      variables declared in function: t2, t3, x,  y
      t2 = t * t
      t3 = t * t * t
      x = t3*x4 + (3*t2 - 3*t3)*x3 + (3*t3 - 6*t2 + 3*t)*x2 + (3*t2 - t3 - 3*t + 1)*x1
      y = t3*y4 + (3*t2 - 3*t3)*y3 + (3*t3 - 6*t2 + 3*t)*y2 + (3*t2 - t3 - 3*t + 1)*y1
      

      (t) 是一个介于 0 和 1 之间的十进制值 (0 &lt;= t &lt;= 1),表示沿曲线行驶的百分比。

      x 和 y 的公式相同,您可以编写一个函数来获取一组通用的 4 个控制点或将系数组合在一起:

      t2 = t * t
      t3 = t * t * t
      
      A = (3*t2 - 3*t3)
      B = (3*t3 - 6*t2 + 3*t)
      C = (3*t2 - t3 - 3*t + 1)
      
      x = t3*x4 + A*x3 + B*x2 + C*x1
      y = t3*y4 + A*y3 + B*y2 + C*y1
      

      对于二次函数,类似的方法产生:

      t2 = t * t
      
      A = (2*t - 2*t2)
      B = (t2 - 2*t + 1)
      
      x = t2*x3 + A*x2 + B*x1
      y = t2*y3 + A*y2 + B*y1
      

      【讨论】:

        【解决方案6】:
        • 如果您只想显示贝塞尔曲线,可以在 Windows 上使用 PolyBezier 之类的东西。

        • 如果您想自己实现例程,可以在 Intarnetz 各处找到linear interpolation code

        • 我相信Boost libraries 对此有支持。线性插值,而不是贝塞尔曲线。但是,请不要引用我的话。

        【讨论】:

        • 太棒了!我认为代码项目示例会做得很好:)
        • 虽然理论上这可以回答这个问题,it would be preferable 在这里包含答案的基本部分,并提供链接以供参考。
        【解决方案7】:

        我基于这个例子https://stackoverflow.com/a/11435243/15484522做了一个实现,但是对于任意数量的路径点

        void bezier(int [] arr, int size, int amount) {
          int a[] = new int[size * 2];    
          for (int i = 0; i < amount; i++) {
            for (int j = 0; j < size * 2; j++) a[j] = arr[j];
            for (int j = (size - 1) * 2 - 1; j > 0; j -= 2) 
              for (int k = 0; k <= j; k++) 
                a[k] = a[k] + ((a[k+2] - a[k]) * i) / amount;
            circle(a[0], a[1], 3);  // draw a circle, in Processing
          }
        }
        

        其中 arr 是点数组 {x1, y1, x2, y2, x3, y3... xn, yn},size 是点数(两倍小于数组大小),数量是输出点的数量。

        为了优化计算,您可以使用 2^n 数量和位移:

        void bezier(int [] arr, int size, int dense) {
          int a[] = new int[size * 2];    
          for (int i = 0; i < (1 << dense); i++) {
            for (int j = 0; j < size * 2; j++) a[j] = arr[j];
            for (int j = (size - 1) * 2 - 1; j > 0; j -= 2) 
              for (int k = 0; k <= j; k++) 
                a[k] = a[k] + (((a[k+2] - a[k]) * i) >> dense);
            circle(a[0], a[1], 3);  // draw a circle, in Processing
          }
        }
        

        【讨论】:

          【解决方案8】:

          这个implementation on github 展示了如何计算一个简单的三次贝塞尔曲线,其中 't' 的值从 0->1 的正常值和正切值。 它是维基百科上formulas 的直接换位。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-03-10
            • 2016-10-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-01-21
            相关资源
            最近更新 更多