Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm trying to draw a bezier curve using 3 points. If I use the quadratic form:

enter image description here

I get this result:

enter image description here

And I believe it's correct.

Now since I need to draw it with a variable number of points, I'm trying to apply the explicit definition:

enter image description here

This is the code that implements it:

typedef struct Point2D
{
    GLfloat x;
    GLfloat y;
    Point2D operator+(GLfloat c) const 
    {
        return {x+c, y+c};
    }
    Point2D operator+ (const Point2D& other) const 
    {
        return {x+other.x, y+other.y};
    }
    void operator+= (GLfloat c)
    {
        y+=c;
        x+=c;
    }
    void operator+= (const Point2D& other)
    {
        x+= other.x;
        y+= other.y;
    }
    Point2D operator* (GLfloat factor) const 
    {
        return {x*factor, y*factor};
    }
    void operator*= (GLfloat factor)
    {
        x*=factor;
        y*= factor;
    }
}Point2D;

Point2D bezier(const vector<Point2D>& points, GLfloat u) // u is t
{
    Point2D point;
    unsigned int n= points.size();
    vector<int> factorials(n);
    // For efficiency, I put all factorials in an array instead
    // of recalculating them each time.
    GLfloat c1= pow(1.0-u,n-2); 
    // This is (1-t)^(n-1)
    GLfloat c2= 1.0;
    // This is t^i , I'll progressively multiply it for u.
    factorials[0]=1;
    for(unsigned int i=1; i<n;i++)
    {
        factorials[i]= factorials[i-1]*i;
    }
    point= points[0] * (GLfloat)(n-1) * c1; // This is the first point
    for(unsigned int i=1; i<n; i++)
    {
        c2*= u;
        GLfloat bCoeff= factorials[n-1]/(factorials[i]*factorials[n-2]);
            // bCoeff is the binomial coefficient: n! / ( i! * (n-1)! )
        point+= points[i] * bCoeff * c1 * c2;
    }
    return point;
}



void display()
{
    glClearColor(0.75,0.75,0.75,1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    glColor3f(1.0,0.0,0.0);
    glBegin(GL_POINTS);
    vector<Point2D> points{Point2D{200,200}, Point2D{400,400}, Point2D{600,200} };
    for(GLfloat u=0.0; u<=1.0; u+=1.0e-4)
    {
        Point2D point;
        point= bezier(points,u);
        glVertex2f(point.x,point.y); // For now I use the immediate mode
                                         // I'll put it in a vbo.
    }
    glEnd();

    glutSwapBuffers();
}

Unfortunately this is the result that I get:

enter image description here

Which I believe is incorrect. I guess I'm not applying correctly the explicit definition, but I don't know where's the mistake, I would like to know what I'm doing wrong.

share|improve this question
add comment

2 Answers

up vote 7 down vote accepted

It is indeed wrong for at least two reasons:

  • you never update c1.
  • you use n! / ( i! * (n-1)! instead of n! / ( i! * (n-i)!.

This means you'll need to reorganise your code. You might try something like this:

unsigned int n = points.size();

vector<float> v;
for (unsigned i = 0; i < n; i++)
    v.push_back(1.f);

/* (un1) is t^i, (un2) is (1-t)^i */
float un1 = 1.f, un2 = 1.f, inv_fact = 1.f;

/* At the end of the loop, we are still missing the
 * "n!" multiplication, but we can do it at the very
 * end instead. */
for (unsigned i = 0; i < n; i++)
{
    v[i] *= un1 * inv_fact;
    un1 *= u;
    v[n - 1 - i] *= un2 * inv_fact;
    un2 *= (1.f - u);
    if (i != 0 && i != n - 1) inv_fact /= i;
}

Point2D point{0,0};
for (unsigned i = 0; i < n; i++)
    point += v[i] * points[i];

return (1.f / inv_fact) * point;
share|improve this answer
add comment

You could also use my recursive approach to n-order bezier curves. This method can also be adapted for use with integers if needed.

class bezier{
    std::vector<Point2D> points;
public:
    bezier();
    void PushPoint2D( Point2D point );
    Point2D GetPoint( double time );
};

bezier::bezier(){}
void bezier::PushPoint2D( Point2D point ){
    points.push_back(point);
}
Point2D bezier::GetPoint( double time ){
    Point2D p;
    if( points.size() == 1 ) return points[0];
    if( points.size() == 0 ) return p;

    bezier b;
    for( int i = 0; i < points.size()-1; ++i ){
        p.x = ( points[i+1].x - points[i].x ) * time + points[i].x;
        p.y = ( points[i+1].y - points[i].y ) * time + points[i].y;
        if( points.size() <= 2 ) return p;
        b.PushPoint2D(p);
    }
    return b.GetPoint( time );
}
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.