📜  C中三次Bezier曲线的实现

📅  最后修改于: 2021-05-04 14:22:04             🧑  作者: Mango

什么是贝塞尔曲线?
因此,贝塞尔曲线是数学定义的曲线,用于居家Illustrator,Inkscape等二维图形应用程序中。该曲线由四个点定义:初始位置终止位置,分别为P0P3 (称为“锚定点”) )和两个单独的中间点,在我们的示例中为P1P2 (称为“句柄”)。贝塞尔曲线经常用于计算机图形,动画,建模等。
我们如何用数学方式表示贝塞尔曲线?
贝塞尔曲线可以在其他点的控制下生成。使用控制点的近似切线可生成曲线。贝塞尔曲线可以用数学方式表示为–
P(u) = \sum_{i=0}^{n} P_{i}{B_{i}^{n}}(u)
在哪里p_{i}         是点集, {B_{i}^{n}}(u)         表示伯恩斯坦多项式,即混合函数,由-
{B_{i}^{n}}(u) = \binom{n}{i} (1 - u)^{n-i}u^{i}
其中n是多项式阶, i是索引, u / t是变量,范围是01
让我们用数学方法定义三次贝塞尔曲线。
因此,由一组控制点定义的贝塞尔曲线ID P_{0}         P_{n}         其中n称为其阶数(线性的n = 1,二次的n = 2,依此类推)。第一个和最后一个控制点始终是曲线的端点。但是,中间控制点(如果有)通常不在曲线上。
对于三次贝塞尔曲线,多项式的阶数(n)为3 ,则索引(i)从i = 0到i = n变化,即3和u从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}
所以
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}

P(u) = \{x(u) , y(u) \}
现在,
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}
因此,我们将通过将u的值增加0.0001来计算x和y像素曲线。

三次贝塞尔曲线图

三次贝塞尔曲线的构造

Properties of bezier curves

它们总是经过第一个和最后一个控制点。
2.它们包含在其定义控制点的凸包中。
3.定义曲线段的多项式的度数比定义多边形点的数目小一。因此,对于4个控制点,多项式的次数为3,即三次多项式。
4.贝塞尔曲线通常遵循定义的多边形的形状
5.端点处的切向量的方向与由第一个和最后一个段确定的向量的方向相同。
6.贝塞尔曲线具有全局控制功能,移动控制点会改变整个曲线的形状
注意:以下实现使用SDL库在屏幕上绘制像素。如果您使用的是像ubuntu这样的Debian系统,则只需运行以下命令即可安装SDL库。

例:我们得到了四个控制点B 0 [1,0],B 1 [2,2],B 2 [6,3],B 3 [8,2],因此确定位于控制点上的五个点。曲线还会在图形上绘制曲线。

答案:给定曲线具有四个控制点,因此它是三次贝塞尔曲线,因此,三次贝塞尔曲线的参数方程为

\hspace{1.2cm}\mathbf{P(t)=B_0(1-t)^3+3*B_1*(1-t)^2*t+3*B_2*(1-t)*t^2+B_3*t^3} \\\\\hspace{5.8cm}\textbf{Where t is 0$\leq$t$\leq$1}\\\\ \\\\\hspace{2.8cm}\textbf{Where }\mathbf{[B_x\,\,B_y]}\textbf{ is representing scalar x \& y coordinates}

现在,将控制点代入上述方程式,这样我们就可以得到,

\hspace{0cm}\mathbf{P(t)=[1\,\,0]*(1-t)^3+3*[2\,\,2]*(1-t)^2*t+3*[6\,\,2]*(1-t)*t^2+[8\,\,2]*t^3}

假设t的五个不同值为{0,0.2,0.5,0.7,1}。

因此,对于t = 0,坐标为

\hspace{0cm}\mathbf{P(0)=[1\,\,0]*(1-0)^3+3*[2\,\,2]*(1-0)^2*0+3*[6\,\,2]*(1-0)*0^2+[8\,\,2]*0^3}\\ \hspace{0cm}\mathbf{P(0)=[1\,\,0]}

因此,对于t = 0.2,坐标为

\hspace{0cm}\mathbf{P(0.2)=[1\,\,0]*(1-0.2)^3+3*[2\,\,2]*(1-0.2)^2*0.2+3*[6\,\,2]*(1-0.2)*0.2^2+[8\,\,2]*0.2^3}\\ \\ \hspace{1.16cm}\mathbf{=[1\,\,0]*0.576+[2\,\,2]*0.384+[6\,\,2]*0.032+[8\,\,2]*0.2^3}\\ \hspace{1.16cm}\mathbf{=[0.576\,\,0]+[0.768\,\,0.768]+[0.192\,\,0.064]+[0.064\,\,0.016]}\\ \\ \hspace{0cm}\mathbf{P(0.2)=[1.48424\,\,\,0.848]}

因此,对于t = 0.5,坐标为

\\\hspace{0cm}\mathbf{P(0.5)=[1\,\,0]*(1-0.5)^3+3*[2\,\,2]*(1-0.5)^2*0.5+3*[6\,\,2]*(1-0.5)*0.5^2+[8\,\,2]*0.5^3}\\ \\\hspace{0cm}\mathbf{P(0.5)=[4.125\,\,1.75]}

因此,对于t = 0.7,坐标为

\hspace{0cm}\mathbf{P(0.7)=[1\,\,0]*(1-0.7)^3+3*[2\,\,2]*(1-0.7)^2*0.7+3*[6\,\,2]*(1-0.7)*0.7^2+[8\,\,2]*0.7^3}\\ \\\hspace{0cm}\mathbf{P(0.7)=[5.417\,\,\,2.108]}\\

因此,对于t = 1.0,坐标为

\\\hspace{0cm}\mathbf{P(1.0)=[1\,\,0]*(1-1)^3+3*[2\,\,2]*(1-1)^2*1+3*[6\,\,2]*(1-1)*1^2+[8\,\,2]*1^3}\\ \hspace{0cm}\mathbf{P(1.0)=[8\,\,\,2]}

图。1

贝塞尔曲线的缺点

a)贝塞尔曲线的程度取决于与相应曲线关联的控制点的数量,随着控制点数量的增加,曲线方程的多项式次数也随之增加,这使得曲线方程非常复杂且难以处理。

Degree of curve = no. of control points - 1

b)使用贝塞尔曲线的一个主要缺点是,它们会对该曲线进行全局控制。这意味着,如果曲线的相对位置发生变化,则整个曲线形状也会发生变化。这使其使用起来不太方便。

在更改任何一个控制点的相对位置时,整个曲线形状都会发生变化:

c)贝塞尔曲线的另一个问题是,其混合函数对于任何参数都永远不会为零,而与曲线的程度无关。

sudo apt-get install libsdl2-dev

建立使用

gcc fileName.c -lSDL2 -lm
C
// 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
#include
#include
#include
 
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 positions 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;
}


Output

当鼠标位置为黑白圆圈时移动鼠标,则仅曲线形状将被更改