How to Implement a BéZier Curve in C++

How do I implement a Bézier curve in C++?

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.

How to convert quadratic bezier curve code into cubic bezier curve?

For cubic Bézier curve, as you see in the link you shared, the green lines are obtained from the same procedure as the quadratic one. the differences are: you have two green lines, and then you need to calculate a blue line based on them. So the for loop changes as:

for( float i = 0 ; i < 1 ; i += 0.01 )
{
// The Green Lines
xa = getPt( x1 , x2 , i );
ya = getPt( y1 , y2 , i );
xb = getPt( x2 , x3 , i );
yb = getPt( y2 , y3 , i );
xc = getPt( x3 , x4 , i );
yc = getPt( y3 , y4 , i );

// The Blue Line
xm = getPt( xa , xb , i );
ym = getPt( ya , yb , i );
xn = getPt( xb , xc , i );
yn = getPt( yb , yc , i );

// The Black Dot
x = getPt( xm , xn , i );
y = getPt( ym , yn , i );

drawPixel( x , y , COLOR_RED );
}

What is a generic data structure for bezier curves or b-splines?

The most generic data structure for a Bezier curve is simply one that contains an array of control points. The degree of the Bezier curve is the number of control points - 1. So, linear, quadratic and cubic Bezier curves can all use the same data structure with difference in number of control points.

For B-spline curve, the generic data structure will contain

  • Degree (D)
  • Number of control points (N)
  • Array of control points
  • Knot sequence.

The knot sequence is simply a "double[ ]" of length = N+D+1. The knot values need to be in non-decreasing order.

n-th order Bezier Curves?

The sum in your formula...

Sample Image

...runs from 0 to n, ie for an n-th order bezier you need n+1 points.

You have 4 points, so you're drawing a 3rd-order bezier.

The error in your code is here:

for(int j = 0; (unsigned int)j < nbPoint; j++)

it should be:

for(int j = 0; (unsigned int)j <= nbPoint; j++)

otherwise you're only iterating from 0 to n-1.

3rd-order bezier

EDIT:

Out of interest, the shape you were getting is the same as if the missing (5th) point was at (0,0), since that's the only point that would contribute nothing to your sum...

4th-order bezier with 5th point at origin

Bézier curve calculation

Bezier curves are parametric curves. That means that you will have 2 equations: 1 for the x coordinates over the curve, and the other for the y coordinates over the curve.

In this case, you're working with a quadratic bezier. The equation for quadratic beziers is:

x(t) = (1.0 - t^2) * p0.x + 2.0 * (1 - t) * t * p1.x + t^2 * p2.x;

y(t) = (1.0 - t^2) * p0.y + 2.0 * (1 - t) * t * p1.y + t^2 * p2.y;

In this case, p0 is the start point, p1 is the control point, and p2 is the end point.

t varies from 0 to 1 over the curve. So to draw it using a standard line drawing function, you'd do the following:

float numDivisions = 100.0; // <- You need to decide what this value should be. See below
float t;
float deltaT = t / numDivisions;
MoveTo (p0.x, p0.y);
for (t = 0; t <= 1.0; t += deltaT)
{
x = (1.0 - t*t) * p0.x + 2.0 * (1 - t) * t * p1.x + t * t * p2.x;
y = (1.0 - t*t) * p0.y + 2.0 * (1 - t) * t * p1.y + t * t * p2.y;
LineTo (x, y);
}

The numDivisions will control the resolution of the curves. If you only have 3 divisions, you'll get the start point, 1 point in the middle (parametrically speaking), and the end point. If you use 10,000 points, you'll probably draw much more than you need to. A rule of thumb is that the straight lines connecting the points will always be longer than the actual curve, so you never need to use a number of divisions greater than that. (You can use a Euclidean distance between the points.)



Related Topics



Leave a reply



Submit