描述

  使用贝塞尔曲线生成路径 已知:若干个二维路径点(x, y),生成一段由一系列(x, y)点组成的点集  

公式

  网上有很多贝塞尔曲线的概念和知识,这里不做赘述 贝塞尔曲线上的路径点计算公式:   QQ截图20210311000515   公式的理解:   假设我有6个点,按照点的顺序依次连接,这样我有5条线段 我有一个系数t,在每条线段上我都能通过线性插值找到一个点,这个点的位置是 t 倍的直线总距离。(比如线段长度为10,t为0.3时,生成的点就在距离起点3长度的位置上,自行二维拓展理解) 5个线段上有由t生成的5个插值点,这5个点依次连接,又生成了4条新的线段 依次执行1,2,3步骤,直到我们只剩下两个点形成的一条线段,在这条线段上的插值点就是贝塞尔曲线点   在这里插入图片描述  

代码

  由输入点直接得到输出点的函数,请尽情拷贝使用  
int fac(int x) // 求阶乘
{
    int f=1;  
	for(int i = 1; i <= x; i++)
    {
        f*=i;
    }
	return f;
}
// 由t和输入点,得到输出的贝塞尔曲线点
std::vector<cv::Point2d> Bezier(double dt, std::vector<cv::Point2d> input)
{
    std::vector<cv::Point2d> output;

    double t = 0;
    while(t<=1)
    {
        cv::Point2d p;
        double x_sum = 0.0;
        double y_sum = 0.0;
        int i = 0;
        int n = input.size()-1;
        while (i<=n)
        {
            double k = fac(n)/(fac(i)*fac(n-i))*pow(t,i)*pow(1-t,n-i);
            x_sum += k*input[i].x;
            y_sum += k*input[i].y;
            i++;
        }
        p.x = x_sum;
        p.y = y_sum;
        output.push_back(p);
        t += dt;
    }
    return output;
}
  可以动态看到每个点生成过程的代码  
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc.hpp>

#define random(a,b) (rand()%(b-a)+a)

cv::Point2d lerp(double t, double x1, double y1, double x2, double y2)
{
    cv::Point p;
    p.x = (1-t) * x1 + t * x2;
    p.y = (1-t) * y1 + t * y2;
    return p;
}

std::vector<cv::Point2d> getLine(double t, std::vector<cv::Point2d> input)
{
    std::vector<cv::Point2d> result;

    for (int i = 0 ; i < input.size()-1 ; i++)
    {   
        cv::Point2d p;
        p = lerp(t, input[i].x, input[i].y, input[i+1].x, input[i+1].y);
        result.push_back(p);
    }

    return result;
}

int main(int argc, char** argv)
{
    int points_size = 5; // 输入的点有多少个
    double dt = 0.005; // t的变化量,能决定曲线生成的有多密

    int matrix_size = 1600; // 空白图大小
    
    std::vector<cv::Point2d> input; // 输入的点
    std::vector<cv::Point2d> output; // 贝塞尔曲线生成的点

    srand((int)time(0));  // 提供随机种子,要加这句话不然还会生成不变的数
    for (int i = 0 ; i < points_size; i++) // 随机生成输入点
    {   
        int x= random(-matrix_size/2,matrix_size/2);
        int y = random(-matrix_size/2,matrix_size/2);
        cv::Point2d p(x, y);
        input.push_back(p);
    }

    double t = 0 ; // 贝塞尔曲线算法的t参数

    std::vector<cv::Point2d> pt_tmp; // 用于储存每步递归的点
    pt_tmp = input;

    // 以t==1为最终目标
    while (t<1)
    {
        cv::Mat image = cv::Mat(matrix_size, matrix_size, CV_8UC3, cv::Scalar(255,255,255));
        while (pt_tmp.size() >1)
        {
            // 降维至不同个数时,设置直线为不同的颜色
            cv::Scalar color;
            if (pt_tmp.size() == 2)
            {
                // 这句话比较重要, 意思是最后只有两个点的时候,插值出的点就是我们需要的贝塞尔曲线
                cv::Point2d p = lerp(t, pt_tmp[0].x, pt_tmp[0].y, pt_tmp[1].x, pt_tmp[1].y);
                output.push_back(p);

                color = cv::Scalar(0,255,0);
            }
            else if (pt_tmp.size() == 3)
            {
                color = cv::Scalar(0,220,0);
            }
            else if (pt_tmp.size() == 4)
            {
                color = cv::Scalar(0,190,0);
            }
            // ...颜色可以继续补充

            // 画点和直线
            for (int i = 0 ; i < pt_tmp.size(); i++)
            {
                cv::circle(image, cv::Point(matrix_size/2+pt_tmp[i].x, matrix_size/2-pt_tmp[i].y), 1, color, 1);
            }
            for (int i = 0 ; i < pt_tmp.size()-1; i++)
            {
                cv::line(image, cv::Point(matrix_size/2+pt_tmp[i].x, matrix_size/2-pt_tmp[i].y), cv::Point(matrix_size/2+pt_tmp[i+1].x, matrix_size/2-pt_tmp[i+1].y), color, 1);
            }   

            // 计算点由n个变为n-1个
            pt_tmp = getLine(t, pt_tmp);

        }

        // 每一次都画出贝塞尔曲线点
        for (int i = 0 ; i < output.size(); i++)
        {
            cv::circle(image, cv::Point(matrix_size/2+output[i].x, matrix_size/2-output[i].y), 5, cv::Scalar(0,0,255), -1);
        }

        // 计算点变为输入的全部点
        pt_tmp = input;

        cv::imshow("Bezier curve", image);
        cv::waitKey(10);

        std::cout<<"t: "<<t<<std::endl;
        
        t = t + 0.005;
    }

    return 1;
}