Cubic Bezier Curve Implementation in C

What is a bezier curve?
So a Bezier curve is a mathematically defined curve used in two-dimensional graphic applications like abode illustrator,inkscape etc.The curve is defined by four points: the initial position and the terminating position i.e P0 and P3 respectively (which are called “anchors”) and two separate middle points i.e P1 and P2(which are called “handles”) in our example.Bezier curves are frequently used in computer graphics, animation, modeling etc.

How do we Represent Bezier Curves Mathematically ?
Bezier curves can be generated under the control of other points. Approximate tangents by using control points are used to generate curve. The Bezier curve can be represented mathematically as –
  P(u) = \sum_{i=0}^{n} P_{i}{B_{i}^{n}}(u)
Where p_{i} is the set of points and {B_{i}^{n}}(u) represents the Bernstein polynomials i.e. Blending Function which are given by –
 {B_{i}^{n}}(u) = \binom{n}{i} (1 - u)^{n-i}u^{i}
Where n is the polynomial order, i is the index, and u/t is the variable which have from 0 to 1.

Let us define our cubic bezier curve mathematically.
So a bezier curve id defined by a set of control points P_{0} to P_{n} where n is called its order(n = 1 for linear , n = 2 for quadratic , etc.). The first and last control points are always the end points of the curve; however, the intermediate control points (if any) generally do not lie on the curve.
For cubic bezier curve order(n) of polynomial is 3 , index(i) vary from i = 0 to i = n i.e. 3 and u will vary from 0\leq u \leq1.

Cubic Bezier Curve function is defined as :

 P(u) = P_{0}{B_{0}^{3}}(u) + P_{1}{B_{1}^{3}}(u) + P_{2}{B_{2}^{3}}(u) + P_{3}{B_{3}^{3}}(u)

Cubic Bezier Curve blending function are defined as :

 {B_{0}^{3}}(u) = \binom{3}{0} (1 - u)^{3-0}u^{0} \equiv  1(1 - u)^{3}u^{0}
 {B_{1}^{3}}(u) = \binom{3}{1} (1 - u)^{3-1}u^{1} \equiv  3(1 - u)^{2}u^{1}
 {B_{2}^{3}}(u) = \binom{3}{2} (1 - u)^{3-2}u^{2} \equiv  3(1 - u)^{1}u^{2}
 {B_{3}^{3}}(u) = \binom{3}{3} (1 - u)^{3-3}u^{3} \equiv  1(1 - u)^{0}u^{3}

So
 P(u) = (1 - u)^{3}P_{0} + 3u^{1}(1 - u)^{2}P_{1} + 3(1 - u)^{1}u^{2}P_{2} + u^{3}P_{3}
and
 P(u) = \{x(u) , y(u) \}

Now,

 x(u) = (1 - u)^{3}x_{0} + 3u^{1}(1 - u)^{2}x_{1} + 3(1 - u)^{1}u^{2}x_{2} + u^{3}x_{3}
 y(u) = (1 - u)^{3}y_{0} + 3u^{1}(1 - u)^{2}y_{1} + 3(1 - u)^{1}u^{2}y_{2} + u^{3}y_{3}

So we will calculate curve x and y pixel by incrementing value of u by 0.0001.

Cubic-Bezier-Curve-Diagram

Construction of a cubic Bézier curve

Properties of bezier curves

1. They always pass through the first and last control points.

2. They are contained in the convex hull of their defining control points.

3. The degree of the polynomial defining the curve segment is one less that the number of defining polygon point. Therefore, for 4 control points, the degree of the polynomial is 3, i.e. cubic polynomial.

4. A Bezier curve generally follows the shape of the defining polygon

5. The direction of the tangent vector at the end points is same as that of the vector determined by first and last segments.

6. Bezier curves exhibit global control means moving a control point alters the shape of the whole curve

NOTE:The following implementation uses SDL library to draw pixels on screen . If you are on debian system like ubuntu just run following command to install SDL library.

sudo apt-get install libsdl2-dev

To build use

gcc fileName.c -lSDL2 -lm
filter_none

edit
close

play_arrow

link
brightness_4
code

// C program to implement 
// Cubic Bezier Curve 
  
/* install SDL library for running thing code*/
/* install by using this commamnd line : sudo apt-get install libsdl2-dev */
/* run this code using command : gcc fileName.c -lSDL2 -lm*/
  
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<SDL2/SDL.h>
  
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
int mousePosX , mousePosY ;
int xnew , ynew ;
  
/*Function to draw all other 7 pixels present at symmetric position*/
void drawCircle(int xc, int yc, int x, int y)
{
    SDL_RenderDrawPoint(renderer,xc+x,yc+y) ;
    SDL_RenderDrawPoint(renderer,xc-x,yc+y);
    SDL_RenderDrawPoint(renderer,xc+x,yc-y);
    SDL_RenderDrawPoint(renderer,xc-x,yc-y);
    SDL_RenderDrawPoint(renderer,xc+y,yc+x);
    SDL_RenderDrawPoint(renderer,xc-y,yc+x);
    SDL_RenderDrawPoint(renderer,xc+y,yc-x);
    SDL_RenderDrawPoint(renderer,xc-y,yc-x);
}
  
/*Function for circle-generation using Bresenham's algorithm */
void circleBres(int xc, int yc, int r)
{
    int x = 0, y = r;
    int d = 3 - 2 * r;
    while (y >= x)
    {
        /*for each pixel we will draw all eight pixels */
        drawCircle(xc, yc, x, y);
        x++;
  
        /*check for decision parameter and correspondingly update d, x, y*/
        if (d > 0)
        {
            y--;
            d = d + 4 * (x - y) + 10;
        }
        else
            d = d + 4 * x + 6;
        drawCircle(xc, yc, x, y);
    }
}
  
/* Function that take input as Control Point x_coordinates and 
Control Point y_coordinates and draw bezier curve */
void bezierCurve(int x[] , int y[])
{
    double xu = 0.0 , yu = 0.0 , u = 0.0 ;
    int i = 0 ;
    for(u = 0.0 ; u <= 1.0 ; u += 0.0001)
    {
        xu = pow(1-u,3)*x[0]+3*u*pow(1-u,2)*x[1]+3*pow(u,2)*(1-u)*x[2]
             +pow(u,3)*x[3];
        yu = pow(1-u,3)*y[0]+3*u*pow(1-u,2)*y[1]+3*pow(u,2)*(1-u)*y[2]
            +pow(u,3)*y[3];
        SDL_RenderDrawPoint(renderer , (int)xu , (int)yu) ;
    }
}
int main(int argc, char* argv[])
{
    /*initialize sdl*/
    if (SDL_Init(SDL_INIT_EVERYTHING) == 0)
    {
        /*
            This function is used to create a window and default renderer.
            int SDL_CreateWindowAndRenderer(int width 
                                          ,int height
                                          ,Uint32 window_flags 
                                          ,SDL_Window** window 
                                          ,SDL_Renderer** renderer)
            return 0 on success and -1 on error
        */
        if(SDL_CreateWindowAndRenderer(640, 480, 0, &window, &renderer) == 0)
        {
            SDL_bool done = SDL_FALSE;
  
            int i = 0 ;
            int x[4] , y[4] , flagDrawn = 0 ;
  
            while (!done)
            {
                SDL_Event event;
  
                /*set background color to black*/
                SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
                SDL_RenderClear(renderer);
  
                /*set draw color to white*/
                SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
  
                /* We are drawing cubic bezier curve 
                which has four control points */
                if(i==4)
                {
                    bezierCurve(x , y) ;
                    flagDrawn = 1 ;
                }
  
                /*grey color circle to encircle control Point P0*/
                SDL_SetRenderDrawColor(renderer, 128, 128, 128, SDL_ALPHA_OPAQUE);
                circleBres(x[0] , y[0] , 8) ;
  
                /*Red Line between control Point P0 & P1*/
                SDL_SetRenderDrawColor(renderer, 255, 0, 0, SDL_ALPHA_OPAQUE);
                SDL_RenderDrawLine(renderer , x[0] , y[0] , x[1] , y[1]) ;
  
                /*grey color circle to encircle control Point P1*/
                SDL_SetRenderDrawColor(renderer, 128, 128, 128, SDL_ALPHA_OPAQUE);
                circleBres(x[1] , y[1] , 8) ;
  
                /*Red Line between control Point P1 & P2*/
                SDL_SetRenderDrawColor(renderer, 255, 0, 0, SDL_ALPHA_OPAQUE);
                SDL_RenderDrawLine(renderer , x[1] , y[1] , x[2] , y[2]) ;
  
                /*grey color circle to encircle control Point P2*/
                SDL_SetRenderDrawColor(renderer, 128, 128, 128, SDL_ALPHA_OPAQUE);
                circleBres(x[2] , y[2] , 8) ;
  
                /*Red Line between control Point P2 & P3*/
                SDL_SetRenderDrawColor(renderer, 255, 0, 0, SDL_ALPHA_OPAQUE);
                SDL_RenderDrawLine(renderer , x[2] , y[2] , x[3] , y[3]) ;
  
                /*grey color circle to encircle control Point P3*/
                SDL_SetRenderDrawColor(renderer, 128, 128, 128, SDL_ALPHA_OPAQUE);
                circleBres(x[3] , y[3] , 8) ;
  
                /*We are Polling SDL events*/
                if (SDL_PollEvent(&event))
                {
                    /* if window cross button clicked then quit from window */
                    if (event.type == SDL_QUIT)
                    {
                        done = SDL_TRUE;
                    }
                    /*Mouse Button is Down */
                    if(event.type == SDL_MOUSEBUTTONDOWN)
                    {
                        /*If left mouse button down then store 
                          that point as control point*/
                        if(event.button.button == SDL_BUTTON_LEFT)
                        {
                            /*store only four points 
                            because of cubic bezier curve*/
                            if(i < 4)
                            {
                                printf("Control Point(P%d):(%d,%d)\n" 
                                ,i,mousePosX,mousePosY) ;
  
                                /*Storing Mouse x and y positions 
                                in our x and y coordinate array */
                                x[i] = mousePosX ;
                                y[i] = mousePosY ;
                                i++ ;
                            }
                        }
                    }
                    /*Mouse is in motion*/
                    if(event.type == SDL_MOUSEMOTION)
                    {
                        /*get x and y postions from motion of mouse*/
                        xnew = event.motion.x ;
                        ynew = event.motion.y ;
  
                        int j ;
  
                        /* change coordinates of control point 
                         after bezier curve has been drawn */
                        if(flagDrawn == 1)
                        {
                            for(j = 0 ; j < i ; j++)
                            {
                                /*Check mouse position if in b/w circle then 
                          change position of that control point to mouse new 
                                position which are coming from mouse motion*/
                                if((float)sqrt(abs(xnew-x[j]) * abs(xnew-x[j]) 
                                     + abs(ynew-y[j]) * abs(ynew-y[j])) < 8.0)
                                {
                                    /*change coordinate of jth control point*/
                                    x[j] = xnew ;
                                    y[j] = ynew ;
                                    printf("Changed Control Point(P%d):(%d,%d)\n"
                                           ,j,xnew,ynew) ;
                                }
                            }
                        }
                        /*updating mouse positions to positions 
                        coming from motion*/
                        mousePosX = xnew ;
                        mousePosY = ynew ;
                    }
                }
                /*show the window*/
                SDL_RenderPresent(renderer);
            }
        }
        /*Destroy the renderer and window*/
        if (renderer)
        {
            SDL_DestroyRenderer(renderer);
        }
        if (window)
        {
            SDL_DestroyWindow(window);
        }
    }
    /*clean up SDL*/
    SDL_Quit();
    return 0;
}

chevron_right


Output

Move mouse when mouse position is b/w circle then only curve shape will be changed

References:
https://en.wikipedia.org/wiki/B%C3%A9zier_curve
https://www.tutorialspoint.com/computer_graphics/computer_graphics_curves.htm
http://www.math.ucla.edu/~baker/149.1.02w/handouts/bb_bezier.pdf

This article is contributed by Palkansh Khandelwal



My Personal Notes arrow_drop_up


If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.




Article Tags :
Practice Tags :


2


Please write to us at contribute@geeksforgeeks.org to report any issue with the above content.