cpp实现贝塞尔曲线,生成缓动和回弹动画

⌚Time: 2023-07-10 18:54:24

👨‍💻Author: Jack Ge

贝塞尔曲线于1962年由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。

一般参数公式

贝兹曲线可如下推断。给定点P0、P1、…、Pn,其贝兹曲线即:

几何学的方向上理解贝塞尔曲线:

一阶贝塞尔曲线

二阶贝塞尔曲线

三阶贝塞尔曲线

贝塞尔曲线点计算方法

使用c++graphics图形库实现代码


#include<iostream>

#include<graphics.h>

#include<conio.h>

#include<vector>

#include<windows.h>

using namespace std;

struct PointXY{

    int x;

    int y;

};



void drawBezier(vector<PointXY> v){



    initgraph(1000, 700);

    for (int i = 0; i < v.size()-1; i++)

    {

        line(v.at(i).x,v.at(i).y,v.at(i+1).x,v.at(i+1).y);

    }

    for (float t = 0; t <= 1; t += 0.001){

        vector<PointXY> vt = v;

        for(;vt.size()>1;){

            for(int i=0;i<vt.size()-1;i++){

                vt.at(i).x = (1 - t) * vt.at(i).x + t * vt.at(i+1).x;

                vt.at(i).y = (1 - t) * vt.at(i).y + t * vt.at(i+1).y;

            }

            vt.pop_back();

        }

        putpixel(vt.at(0).x, vt.at(0).y, GREEN);

        Sleep(1);



    }

    _getch();

    closegraph();

}

int main(void)

{

    //绘制(100,200)(200,400)(400,100)三个点的贝塞尔曲线

    vector<PointXY> v;

    PointXY xy;

    xy.x = 100;

    xy.y = 200;

    v.push_back(xy);

    xy.x = 200;

    xy.y = 400;

    v.push_back(xy);

    xy.x = 400;

    xy.y = 100;

    v.push_back(xy);

    drawBezier(v);

    return 0;

}






演示

缓动动画

使用三次贝塞尔曲线cubic-bezier,生成速度曲线的函数,规定是cubic-bezier(x1,y1,x2,y2)

三次贝塞尔曲线需要四个点来定义,P0 、P1 、P2、P3。P0、P1、P2、P3 四个点在平面或在三维空间中定义了三次方贝塞尔曲线。曲线起始于 P0 走向 P1,并从 P2 的方向来到 P3。一般不会经过 P1 或 P2;这两个点只是在那里提供方向资讯。

通过公式 t 在 [0, 1] 上运动,得到一条开始于 [0, 0],终止于 [1, 1] 的曲线,我们将动画当前已运行的时间除以目标时长,得到缩放到 [0, 1] 区间的值 x,那么 x 作为横坐标在曲线上会对应一个唯一点,得到该点的 y。因为 P0,P3 固定,假设我们需要平滑的从 0 运动到 500,那么这 500 也被缩放到了 [0, 1] 区间。将 y 重新缩放到回我们的目标区间,就得到了中间帧元素应该处于的位置。简单点就是,x 轴代表时间进度, y 轴代表动画完成的进度。

这里有一点需要注意的是,P1 和 P2 点允许超出由 P0 和 P3 点所划分出的矩形区域。导致曲线也可能超出矩形区域,也就是函数值不一定落在 [0, 1] 区间内,这时候就形成一个弹性动画,即元素经过预期位置后还会继续运动,但在最终时刻还是会返回预期位置。

代码


#include "stdafx.h"

#include<iostream>

#include<graphics.h>

#include<conio.h>

#include<vector>

#include <windows.h>

using namespace std;

//计算三阶贝塞尔曲线 返回1000个轨迹点

vector<pair<float,float> > cubicBezier(float x1, float y1, float x2, float y2){

    

    

    vector<pair<float,float> > v;

    

    float x0 = 0.0f;

    float y0 = 0.0f;

    float x3 = 1.0f;

    float y3 = 1.0f;



    for (float t = 0; t <= 1; t += 0.001){

        float x0_1 = (1-t) * x0 + t * x1;

        float y0_1 = (1-t) * y0 + t * y1;



        float x1_1 = (1-t) * x1 + t * x2;

        float y1_1 = (1-t) * y1 + t * y2;



        float x2_1 = (1-t) * x2 + t * x3;

        float y2_1 = (1-t) * y2 + t * y3;







        float x0_2 = (1-t) * x0_1 + t * x1_1;

        float y0_2 = (1-t) * y0_1 + t * y1_1;



        float x1_2 = (1-t) * x1_1 + t * x2_1;

        float y1_2 = (1-t) * y1_1 + t * y2_1;







        float x0_3 = (1-t) * x0_2 + t * x1_2;

        float y0_3 = (1-t) * y0_2 + t * y1_2;



        pair<float,float> p;

        p.first = x0_3;

        p.second = y0_3;

        v.push_back(p);

    }

    return v;

}

//绘制动画

void drawAnimation(vector<pair<float,float> > v, long durationMS){

    long t = 0 ;//逝去时间

    int tt = 5;//动画帧间隔

    

    initgraph(600, 400);

    setfillcolor(RGB(255,0,0));//设置颜色

    while (t < durationMS){

        BeginBatchDraw();//双缓冲绘制

        cleardevice();//清空画面

        //根据当前时间,获取动画进度

        float progress = (float)t/durationMS;

        //画圆

        for(int i=0;i<v.size();i++){

            //

            if(v.at(i).first - progress >= -0.001 && v.at(i).first - progress <= 0.001){//浮点数判断相等

                    solidcircle(100+v.at(i).second*300,200,40);//x起始点100,运动范围0-300,y点200,半径40的圆

            }

        }

            

        EndBatchDraw();//FlushBatchDraw();

        

        Sleep(tt);

        t += tt;

    }



    _getch();

    closegraph();



    

}

int main(){

    vector<pair<float,float> > v = cubicBezier(0.6f,0.0f,0.3f,1.0f);

    drawAnimation(v,1000);//绘制缓动动画

    v = cubicBezier(0.6f,-0.5f,0.3f,1.5f);

    drawAnimation(v,1000);//绘制回弹动画

    return 0;

}

效果

回弹动画

动画演示网站

https://cubic-bezier.com/