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

发布于 2024-07-18 02:51:32 字数 293 浏览 7 评论 0原文

我想实现贝塞尔曲线。 我应该如何创建二次曲线?

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

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

我正在使用Linux。

I'd like to implement a Bézier curve. How should I go about creating a quadratic curve?

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

Clearly we'd need to use linear interpolation, but does this exist in the standard math library? If not, where can I find it?

I'm using Linux.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

嗫嚅 2024-07-25 02:51:32

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

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

以下代码演示了如何计算二次贝塞尔曲线。

int interpolate( int from , int to , float percent )
{
    int difference = to - from;
    return from + ( difference * percent );
}    

for( float i = 0 ; i < 1 ; i += 0.01 )
{
    // The Green Line
    xa = interpolate( x1 , x2 , i );
    ya = interpolate( y1 , y2 , i );
    xb = interpolate( x2 , x3 , i );
    yb = interpolate( y2 , y3 , i );

    // The Black Dot
    x = interpolate( xa , xb , i );
    y = interpolate( 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"

这个答案提供了代码。

Recently I ran across the same question and wanted to implemented it on my own.
This image from Wikipedia helped me:

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

The following code shows how to compute a quadratic bezier.

int interpolate( int from , int to , float percent )
{
    int difference = to - from;
    return from + ( difference * percent );
}    

for( float i = 0 ; i < 1 ; i += 0.01 )
{
    // The Green Line
    xa = interpolate( x1 , x2 , i );
    ya = interpolate( y1 , y2 , i );
    xb = interpolate( x2 , x3 , i );
    yb = interpolate( y2 , y3 , i );

    // The Black Dot
    x = interpolate( xa , xb , i );
    y = interpolate( ya , yb , i );
    
    drawPixel( x , y , COLOR_RED );
}

With (x1|y1), (x2|y2) and (x3|y3) being P0, P1 and P2 in the image. Just for showing the basic idea...

For the ones who ask for the cubic bezier, it just works analogue (also from Wikipedia):

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

This answer provides Code for it.

守不住的情 2024-07-25 02:51:32

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

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

Here is a general implementation for a curve with any number of points.

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

Note that it uses heap memory for a temporary array which is not all that efficient. If you only need to deal with a fixed number of points you could hard-code the numPoints value and use stack memory instead.

Of course, the above assumes you have a vec2 structure and operators for it like this:

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);
}
痴情换悲伤 2024-07-25 02:51:32

您可以选择 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等为控制点。 曲线穿过两个端点,但通常不穿过其他点。

You have a choice between de Casteljau's method, which is to recursively split the control path until you arrive at the point using a linear interpolation, as explained above, or Bezier's method which is to blend the control points.

Bezier's method is

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

for cubics and

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

for quadratics.

t is usually on 0-1 but that's not an essential - in fact the curves extend to infinity. P0, P1, etc are the control points. The curve goes through the two end points but not usually through the other points.

仅冇旳回忆 2024-07-25 02:51:32

您之前使用过 C# 库吗?

在 C++ 中,尚无可用的贝塞尔曲线标准库函数。 您当然可以自己动手(CodeProject 示例)或寻找数学图书馆。

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

Did you use a C# library earlier?

In C++, no standard library function for Bezier curves is available (yet). You can of course roll your own (CodeProject sample) or look for a math library.

This blogpost explains the idea nicely but in Actionscript. Translation should not be much of a problem.

千紇 2024-07-25 02:51:32

使用给定控制点 (x1, y1),以给定行程百分比 (t) 沿三次曲线获取单个点 (x, y)(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

To get an individual point (x, y) along a cubic curve at a given percent of travel (t), with given control points (x1, y1), (x2, y2), (x3, y3), and (x4, y4) I expanded De Casteljau’s algorithm and rearranged the equation to minimize exponents:

//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) is a decimal value between 0 and 1 (0 <= t <= 1) that represents percent of travel along the curve.

The formula is the same for x and y, and you can write a function that takes a generic set of 4 control points or group the coefficients together:

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

For quadratic functions, a similar approach yields:

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
脱离于你 2024-07-25 02:51:32
  • 如果您只想显示贝塞尔曲线,您可以使用类似 PolyBezier for Windows。

  • 如果您想自己实现该例程,可以找到 线性插值代码遍布Intarnetz。

  • 我相信 Boost 库对此有支持。 线性插值,而不是特指贝塞尔曲线。 不过,请不要引用我的观点。

  • If you just want to display a Bezier curve, you can use something like PolyBezier for Windows.

  • If you want to implement the routine yourself, you can find linear interpolation code all over the Intarnetz.

  • I believe the Boost libraries have support for this. Linear interpolation, not Beziers specifically. Don't quote me on this, however.

层林尽染 2024-07-25 02:51:32

我基于此示例 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
  }
}

I made an implementation based on this example https://stackoverflow.com/a/11435243/15484522 but for any amount of path points

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

Where arr is array of points {x1, y1, x2, y2, x3, y3... xn, yn}, size is amount of points (twice smaller than array size), and amount is number of output points.

For optimal calculations you can use 2^n amount and bit shift:

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
  }
}
深白境迁sunset 2024-07-25 02:51:32

从版本 1.82 开始,Boost 支持 bezier_polynomial< /a> (实际上来自 1.78 版本,但在 1.82 中修复了一个 bug) 。

std::vector<std::array<double, 3>> control_points(4);
control_points[0] = {0,0,0};
control_points[1] = {1,0,0};
control_points[2] = {0,1,0};
control_points[3] = {0,0,1};
auto bp = bezier_polynomial(std::move(control_points));
// Interpolate at t = 0.1:
std::array<double, 3> point = bp(0.1);

Starting from version 1.82 Boost supports bezier_polynomial (actually from version 1.78, but there were a bug fixed in 1.82).

std::vector<std::array<double, 3>> control_points(4);
control_points[0] = {0,0,0};
control_points[1] = {1,0,0};
control_points[2] = {0,1,0};
control_points[3] = {0,0,1};
auto bp = bezier_polynomial(std::move(control_points));
// Interpolate at t = 0.1:
std::array<double, 3> point = bp(0.1);
没企图 2024-07-25 02:51:32

github 上的实现展示了如何计算简单的三次贝塞尔曲线,“t”值的法线值和正切值范围为 0->1。
它是维基百科公式的直接转置。

This implementation on github shows how to calculate a simple cubic bezier, with normal and tangent values for values of 't' from 0->1.
It is a direct transposition of the formulas at wikipedia.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文