【图形图像的C++ 实现 01/20】 2D 和 3D 贝塞尔曲线

news2024/9/24 11:21:22

目录

  • 一、说明
  • 二、贝塞尔曲线特征
  • 三、模拟
  • 四、全部代码如下

一、说明

   以下文章介绍了用 C++ 计算和绘制的贝塞尔曲线(2D 和 3D)。
   贝塞尔曲线具有出色的数学能力来计算路径(从起点到目的地点的曲线)。曲线的形状由“控制点”决定。所讨论的曲线最重要的特征是平滑度。
   在许多应用和领域中,平滑度是不可或缺的。我们可以考虑机器人或其他机器的运动,其中运动必须是可预测的,以确保人员和硬件的安全(低磨损系数)。当机器人关节的轨迹被计算为平滑路径时,我们可以假设机器人将按照规划的路径平滑地移动,不会出现急动或意外移动。请注意,在我们考虑的机器人技术中,除了路径之外,还有速度、加速度、冲击力和电机扭矩。所有这些参数主要影响最终路径。
   除了机器人技术之外,贝塞尔曲线还用于动画、游戏和设计。

   为了绘图的目的,我将使用我之前的文章中讨论过的 C++ 的 matplotlib 库。
   头文件(用于绘图库)必须与您的 cpp 位于同一文件夹中。您的程序可以按如下方式编译,

​//compile
g++ my_prog.cpp -o my_prog -I/usr/include/python3.8 -lpython3.8// 
//run
./my_prog
//folder tree
├── my_prog
├── my_prog.cpp
├── matplotlibcpp.h

二、贝塞尔曲线特征

   可以计算点集的贝塞尔曲线: { P0, P1, P2 …Pn},其中n定义我们建模的曲线(多项式)的阶数。在每种情况下,第一个点和最后一个点定义曲线的起点和终点的位置。其他点 - 控制点通常不属于计算的曲线,而是影响贝塞尔曲线的形状。

   2D中的每个点P都有两个{x,y}笛卡尔坐标,但在3D中,点P按预期由三个{x, y, z}定义。

   贝塞尔曲线的显式定义可以指定如下(我们将在模拟中使用这个公式)。

在这里插入图片描述
这里
在这里插入图片描述

   是二项式系数。

   在我们的例子中,二项式系数的计算如下(如果您查看维基百科,您会发现递归实现,但这是最简单的版本或更直观)。

   C++ 中的实现可以如下所示,

double computeBinominal(int n, int k)
{
    double value = 1.0;
    for (int i = 1; i <= k; i++)
    {
        value = value * ((n + 1 - i) / i);
    }
    if (n == k){
        value = 1;
    }
    return value;
}


平面空间中的四个点P 0 、P 1 、P 2 和P 3 定义三次贝塞尔曲线。该曲线可以建模为三阶多项式。
在这里插入图片描述

当提供六个点P 0、P 1、P 2、P 3、P4和P5时,贝塞尔曲线被计算为五阶多项式。

在这里插入图片描述

三、模拟

   现在我们将显示上面定义的曲线的 2D 和 3D 模拟(针对 4 点和 6 点)。下面的代码为您提供了计算和绘制您想要的任何数字点P 的贝塞尔曲线的绝佳机会。

x{2.5, 1.5, 6.0, 10.0}; 
y{0.5, 5.0, 5.0, 0.5};

x{2.5, 1.5, 6.0, 10.0}; 
//与 2D y{0.5, 5.0, 5.0, 0.5}相同;
//与 2D z{1.0, 2.0, 3.0, 4.0}相同;

X{2.5, 1.5, 6, 10, 7, 3}; 
Y{0.5, 5.0, 5.0, 0.5, 1.0, 2.0};

X{2.5, 1.5, 6.0, 10.0, 7.0, 3.0}; // 对于 2D 
Y{0.5, 5.0, 5.0, 0.5, 1.0 , 2.0}; // 对于 2D 
Z{1.0, 2.0, 3.0, 4.0, 5.0, 0.1};

在这里插入图片描述
对于相同阶的多项式(三阶),我们可以计算 3D 贝塞尔曲线。

x{2.5, 1.5, 6.0, 10.0}; //same as 2D
y{0.5, 5.0, 5.0, 0.5}; //same as 2D
z{1.0, 2.0, 3.0, 4.0};

在这里插入图片描述
这是一条 2D 贝塞尔曲线,它是针对五阶多项式(六点)计算的。

X{2.5, 1.5, 6, 10, 7, 3};
Y{0.5, 5.0, 5.0, 0.5, 1.0 , 2.0};

在这里插入图片描述
和以前一样,我们可以绘制 3D 贝塞尔曲线。

X{2.5, 1.5, 6.0, 10.0, 7.0, 3.0}; //as for 2D
Y{0.5, 5.0, 5.0, 0.5, 1.0 , 2.0}; //as for 2D
Z{1.0, 2.0, 3.0, 4.0, 5.0, 0.1};

在这里插入图片描述

四、全部代码如下

/// g++ bezier_curve.cpp -o t -I/usr/include/python3.8 -lpython3.8

#include <iostream>
#include <vector>
#include <tuple>
#include <math.h>

#include "matplotlibcpp.h"

namespace plt = matplotlibcpp;

//-----------------------------------------------------------

std::tuple<std::vector<double>, std::vector<double>> computeBesierCurve2D(std::vector<double> xX, std::vector<double> yY)
{

    std::vector<double> bCurveX;
    std::vector<double> bCurveY;
    double bCurveXt;
    double bCurveYt;

    for (double t = 0.01; t <= 1; t += 0.01)
    {

        bCurveXt = std::pow((1 - t), 3) * xX[0] + 3 * std::pow((1 - t), 2) * t * xX[1] + 3 * std::pow((1 - t), 1) * std::pow(t, 2) * xX[2] + std::pow(t, 3) * xX[3];
        bCurveYt = std::pow((1 - t), 3) * yY[0] + 3 * std::pow((1 - t), 2) * t * yY[1] + 3 * std::pow((1 - t), 1) * std::pow(t, 2) * yY[2] + std::pow(t, 3) * yY[3];

        bCurveX.push_back(bCurveXt);
        bCurveY.push_back(bCurveYt);
    }

    return std::make_tuple(bCurveX, bCurveY);
}

//-----------------------------------------------------------

void plot2D(std::tuple<std::vector<double>, std::vector<double>> data)
{

    std::vector<double> xX = std::get<0>(data);
    std::vector<double> yY = std::get<1>(data);


    plt::plot(xX, yY);
    plt::show();
}

//-----------------------------------------------------------

std::tuple<std::vector<double>, std::vector<double>, std::vector<double>> computeBesierCurve3D(std::vector<double> xX, std::vector<double> yY, std::vector<double> zZ)
{

    std::vector<double> bCurveX;
    std::vector<double> bCurveY;
    std::vector<double> bCurveZ;
    double bCurveXt;
    double bCurveYt;
    double bCurveZt;

    for (double t = 0.01; t <= 1; t += 0.01)
    {

        bCurveXt = std::pow((1 - t), 3) * xX[0] + 3 * std::pow((1 - t), 2) * t * xX[1] + 3 * std::pow((1 - t), 1) * std::pow(t, 2) * xX[2] + std::pow(t, 3) * xX[3];
        bCurveYt = std::pow((1 - t), 3) * yY[0] + 3 * std::pow((1 - t), 2) * t * yY[1] + 3 * std::pow((1 - t), 1) * std::pow(t, 2) * yY[2] + std::pow(t, 3) * yY[3];
        bCurveZt = std::pow((1 - t), 3) * yY[0] + 3 * std::pow((1 - t), 2) * t * yY[1] + 3 * std::pow((1 - t), 1) * std::pow(t, 2) * yY[2] + std::pow(t, 3) * yY[3];

        bCurveX.push_back(bCurveXt);
        bCurveY.push_back(bCurveYt);
        bCurveZ.push_back(bCurveZt);
    }

    return std::make_tuple(bCurveX, bCurveY, bCurveZ);
}

//-----------------------------------------------------------

void plot3Dexample()
{

    std::vector<double> xX;
    std::vector<double> yY;
    std::vector<double> zZ;
    double theta;
    double r;
    double z_inc = 4.0 / 99.0;
    double theta_inc = (8.0 * M_PI) / 99.0;

    for (double i = 0; i < 100; i += 1)
    {
        theta = -4.0 * M_PI + theta_inc * i;
        zZ.push_back(-2.0 + z_inc * i);
        r = zZ[i] * zZ[i] + 1;
        xX.push_back(r * std::sin(theta));
        yY.push_back(r * std::cos(theta));
    }

    plt::plot3(xX, yY, zZ);
    plt::show();
}

//-----------------------------------------------------------

void plot3D(std::tuple<std::vector<double>, std::vector<double>, std::vector<double>> data)
{

    std::vector<double> xX = std::get<0>(data);
    std::vector<double> yY = std::get<1>(data);
    std::vector<double> zZ = std::get<2>(data);

    plt::plot3(xX, yY, zZ);
    plt::xlabel("x");
    plt::ylabel("y");
    plt::set_zlabel("z");
    plt::show();
}

//-----------------------------------------------------------

double computeBinominal(int n, int k)
{

    double value = 1.0;

    for (int i = 1; i <= k; i++)
    {

        value = value * ((n + 1 - i) / i);
    }

    if (n == k){
        value = 1;
    }
    
    return value;
}

//-----------------------------------------------------------

std::tuple<std::vector<double>, std::vector<double>> computeNVertexBasierCurve2D(std::vector<double> xX, std::vector<double> yY)
{

    std::vector<double> bCurveX;
    std::vector<double> bCurveY;

    int n = xX.size() - 1;
    std::cout << "n :" << n << "\n";

    for (double t = 0.0; t <= 1.0; t += 0.01)
    {

        double bCurveXt{0};
        double bCurveYt{0};

        for (int i = 0; i <= n; ++i)
        {

            bCurveXt += computeBinominal(n, i) * std::pow((1 - t), (n - i)) * std::pow(t, i) * xX[i];
            bCurveYt += computeBinominal(n, i) * std::pow((1 - t), (n - i)) * std::pow(t, i) * yY[i];
            //std::cout << " t= "<< t<< " i=" << i << " bCurveXt=" << bCurveXt << " = " << computeBinominal(n, i)  << " * " << std::pow((1 - t), (n - i))  << " * " << std::pow(t, i) << " * " << xX[i] << std::endl;
        }

        bCurveX.push_back(bCurveXt);
        bCurveY.push_back(bCurveYt);
    }

    return std::make_tuple(bCurveX, bCurveY);
}

std::tuple<std::vector<double>, std::vector<double>, std::vector<double>> computeNVertexBasierCurve3D(std::vector<double> xX, std::vector<double> yY, std::vector<double> zZ)
{

    std::vector<double> bCurveX;
    std::vector<double> bCurveY;
    std::vector<double> bCurveZ;

    int n = xX.size() - 1;
    std::cout << "n :" << n << "\n";

    for (double t = 0.0; t <= 1.0; t += 0.01)
    {

        double bCurveXt{0};
        double bCurveYt{0};
        double bCurveZt{0};

        for (int i = 0; i <= n; ++i)
        {

            bCurveXt += computeBinominal(n, i) * std::pow((1 - t), (n - i)) * std::pow(t, i) * xX[i];
            bCurveYt += computeBinominal(n, i) * std::pow((1 - t), (n - i)) * std::pow(t, i) * yY[i];
            bCurveZt += computeBinominal(n, i) * std::pow((1 - t), (n - i)) * std::pow(t, i) * zZ[i];
            //std::cout << " t= "<< t<< " i=" << i << " bCurveXt=" << bCurveXt << " = " << computeBinominal(n, i)  << " * " << std::pow((1 - t), (n - i))  << " * " << std::pow(t, i) << " * " << xX[i] << std::endl;
        }

        bCurveX.push_back(bCurveXt);
        bCurveY.push_back(bCurveYt);
        bCurveZ.push_back(bCurveZt);
    }

    return std::make_tuple(bCurveX, bCurveY, bCurveZ);
}


//-----------------------------------------------------------

int main()
{

    std::vector<double> xX{2.5, 1.5, 6, 10};
    std::vector<double> yY{0.5, 5, 5, 0.5};
    std::vector<double> zZ{1.0, 2.0, 3.0, 4.0};

    std::tuple<std::vector<double>, std::vector<double>> bCurve2D = computeBesierCurve2D(xX, yY);
    plot2D(bCurve2D);
    
    std::tuple<std::vector<double>, std::vector<double>, std::vector<double>> bCurve3D = computeBesierCurve3D(xX, yY, zZ);
    plot3D(bCurve3D);
    
    std::vector<double> xXn{2.5, 1.5, 6, 10, 7, 3};
    std::vector<double> yYn{0.5, 5, 5, 0.5, 1.0 , 2.0};
    std::vector<double> zZn{1, 2, 3, 4, 5, 0.1};

    std::tuple<std::vector<double>, std::vector<double>> bCurve2DxN = computeNVertexBasierCurve2D(xXn, yYn);
    plot2D(bCurve2DxN);
    
    std::tuple<std::vector<double>, std::vector<double>, std::vector<double>> bCurve3DxN = computeNVertexBasierCurve3D(xXn, yYn, zZn);
    plot3D(bCurve3DxN);

}

​​

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1440265.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

个体诊所门诊电子处方开单管理系统软件,配方模板病历模板设置一键导入操作教程

个体诊所门诊电子处方开单管理系统软件&#xff0c;配方模板病历模板设置一键导入操作教程 一、前言 以下操作教程以 佳易王诊所电子处方软件V17.2为例说明&#xff0c;最新版V17.3下载可以点击最下方官网卡片了解。 1、在现实生活中&#xff0c;医师开单可谓是争分夺秒&…

苏宁易购移动端首页(rem布局)

技术选型 方案∶采取单独制作移动页面方案技术:布局采取rem适配布局( less rem &#xff0b;媒体查询)设计图:设计图采用750px设计尺寸 设置视口标签以及引入初始化样式 <meta name"viewport" content"widthdevice-width, initial-scale1.0, user-scalable…

Vulnhub靶机:hacksudo-FOG

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;hacksudo-FOG&#xff08;10.0.2.48&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://www.vulnhub.com/entry/…

TCP和UDP相关问题(重点)(5)——5.TCP三次握手和四次挥手(非常重要)

5.1三次握手的过程 一次握手&#xff1a;客户端发送带有SYN(x)标志的数据包到服务端&#xff0c;然后客户端进入SYN_SEND状态&#xff0c;等待服务器端的确认。 二次握手&#xff1a;服务端发送带有SYN(y)ACK(x1)标志的数据包到客户端&#xff0c;然后服务端进入SYN_RECV状态。…

SCI 1区论文:Segment anything in medical images(MedSAM)[文献阅读]

基本信息 标题&#xff1a;Segment anything in medical images中文标题&#xff1a;分割一切医学图像发表年份: 2024年1月期刊/会议: Nature Communications分区&#xff1a; SCI 1区IF&#xff1a;16.6作者: Jun Ma; Bo Wang(一作&#xff1b;通讯)单位&#xff1a;加拿大多…

负载均衡(3)

文章目录 一、HAProxy介绍企业版社区版版本对比HAProxy功能支持功能不具备的功能 二、编译安装HAProxy解决lua环境Centos 基础环境 编译安装HAProxy验证HAProxy版本HAProxy启动脚本配置文件启动haproxy验证haproxy状态查看haproxy的状态页面 三、HAProxy基础配置详解global配置…

SSH口令问题

SSH&#xff08;Secure Shell&#xff09;是目前较可靠、专为远程登录会话和其他网络服务提供 安全性的协议&#xff0c;主要用于给远程登录会话数据进行加密&#xff0c;保证数据传输的安全。 SSH口令长度太短或者复杂度不够&#xff0c;如仅包含数字或仅包含字母等时&#xf…

正版软件 - Proxyman:让网络调试变得更智能、更高效

在软件开发的世界里&#xff0c;网络调试一直是开发者和测试工程师的痛点。传统的调试工具往往操作复杂&#xff0c;界面不够直观&#xff0c;而且性能上也难以满足现代应用的需求。今天&#xff0c;我要向大家介绍一款名为Proxyman的网络调试工具&#xff0c;它以其简洁的界面…

项目02《游戏-12-开发》Unity3D

基于 项目02《游戏-11-开发》Unity3D &#xff0c; 人物&#xff1a;实现场景怪物自动巡航 &#xff0c; 首先在场景中创建小球命名为路径点WayPoint0&#xff0c; 取消小球的碰撞器Collider&#xff0c; 再复制两个改名为WayPoint1 和 WayPoint2 &#xff0c; 在…

懒人精灵 之 Lua 捕获 json解析异常 ,造成的脚本停止.

Time: 2024年2月8日20:21:17 by:MemoryErHero 1 异常代码 Expected value but found T_END at character 12 异常代码 Expected value but found T_OBJ_END at character 223 处理方案 - 正确 json 示范 while true do--Expected value but found T_END at character 1--Ex…

【数据分享】1929-2023年全球站点的逐日平均风速数据(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、能见度等指标&#xff0c;说到气象数据&#xff0c;最详细的气象数据是具体到气象监测站点的数据&#xff01; 有关气象指标的监测站点数据&#xff0c;之前我们分享过1929-2023年全球气象站…

Java毕业设计-基于ssm的仓库管理系统-第76期

获取源码资料&#xff0c;请移步从戎源码网&#xff1a;从戎源码网_专业的计算机毕业设计网站 项目介绍 基于ssm的游泳馆管理系统&#xff1a;前端jsp、jquery、bootstrap&#xff0c;后端 springmvc、spring、mybatis&#xff0c;集成游泳课程报名、游泳卡在线售卖、购物车、…

c++设计模式之装饰器模式

作用 为现有类增加功能 案例说明 class Car { public:virtual void show()0; };class Bmw:public Car { public:void show(){cout<<"宝马汽车>>"<<endl;} };class Audi:public Car { public:void show(){cout<<"奥迪汽车>>&q…

【数据分享】1929-2023年全球站点的逐日平均能见度(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff0c;说到常用的降水数据&#xff0c;最详细的降水数据是具体到气象监测站点的降水数据&#xff01; 有关气象指标的监测站点数据&#xff0c;之前我们分享过1929-2023年全…

05 06 Verilog基础语法与应用讲解

05. 1. 位操作 计数器实验升级&#xff0c;设计8个LED灯以每个0.5s的速率循环闪烁&#xff08;跑马灯&#xff09; 1.1 方法1&#xff1a;使用移位操作符<<来控制led灯的循环亮灭 设计代码 Verilog中&#xff0c;判断操作的时候不加位宽限定是可以的&#xff0c;比如i…

【Flink入门修炼】1-3 Flink WordCount 入门实现

本篇文章将带大家运行 Flink 最简单的程序 WordCount。先实践后理论&#xff0c;对其基本输入输出、编程代码有初步了解&#xff0c;后续篇章再对 Flink 的各种概念和架构进行介绍。 下面将从创建项目开始&#xff0c;介绍如何创建出一个 Flink 项目&#xff1b;然后从 DataStr…

pwn学习笔记(1)前置基础

pwn学习笔记&#xff08;1&#xff09; &#xff08;1&#xff09;pwn简介&#xff1a; ​ 以下来自于百度百科&#xff1a;”Pwn”是一个黑客语法的俚语词&#xff0c;是指攻破设备或者系统发音类似“砰”&#xff0c;对黑客而言&#xff0c;这就是成功实施黑客攻击的声音—…

【开源】SpringBoot框架开发医院门诊预约挂号系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 功能性需求2.1.1 数据中心模块2.1.2 科室医生档案模块2.1.3 预约挂号模块2.1.4 医院时政模块 2.2 可行性分析2.2.1 可靠性2.2.2 易用性2.2.3 维护性 三、数据库设计3.1 用户表3.2 科室档案表3.3 医生档案表3.4 医生放号…

【前端素材】bootstrap4实现绿色植物Lukani平台

一、需求分析 绿色植物商城是一个专门销售绿色植物的零售商店或在线平台。它提供各种类型和品种的室内植物、室外植物和盆栽等。绿色植物商城的作用可以从以下几个方面来分析&#xff1a; 1. 提供多样化的选择&#xff1a;绿色植物商城通常会提供各种各样的绿色植物选项&…

.NET Core 实现 JWT 认证

写在前面 JWT&#xff08;JSON Web Token&#xff09;是一种开放标准, 由三部分组成&#xff0c;分别是Header、Payload和Signature&#xff0c;它以 JSON 对象的方式在各方之间安全地传输信息。通俗的说&#xff0c;就是通过数字签名算法生产一个字符串&#xff0c;然后在网络…