如何在C ++中实现Bézier曲线?

时间:2009-04-24 09:12:41

标签: c++ math interpolation spline

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

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

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

更新1:

抱歉,我忘了提及我正在使用Linux。

8 个答案:

答案 0 :(得分:102)

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

http://upload.wikimedia.org/wikipedia/commons/3/35/Bezier_quadratic_anim.gif

以下代码是用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。只是为了展示基本想法...

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

http://upload.wikimedia.org/wikipedia/commons/a/a3/Bezier_cubic_anim.gif

This回答提供了代码。

答案 1 :(得分:11)

以下是包含任意点数的曲线的一般实现。

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);
}

答案 2 :(得分:7)

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

在C ++中,Bezier曲线的标准库函数尚未可用(尚未)。您当然可以自己动手(CodeProject sample)或寻找数学库。

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

答案 3 :(得分:7)

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

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 :(得分:1)

  • 如果您只想显示贝塞尔曲线,可以使用类似PolyBezier的Windows。

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

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

答案 5 :(得分:0)

我基于这个例子 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
  }
}

答案 6 :(得分:0)

以给定的行程百分比 (x, y) 沿三次曲线获得单个点 (t),并具有给定的控制点 (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 <= t <= 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

答案 7 :(得分:0)

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