匀速贝塞尔曲线运动的实现(转)

2023-11-04

二次贝塞尔曲线通常以如下方式构建,给定二维平面上的固定点P0,P1,P2,用B(t)表示该条曲线

用一个动画来演示,可以更加清楚的表明这条曲线的构建过程






如果t变量本身线形变化的话,这条贝塞尔曲线本身的生成过程是并不是匀速的,通常都是两头快中间慢。




如果t变量本身线形变化的话,这条贝塞尔曲线本身的生成过程是并不是匀速的,通常都是两头快中间慢。

如何想要得到匀速的贝塞尔曲线运动呢?比如我们在某款游戏中设计了一条贝塞尔曲线的路径,如何实现玩家匀速在这条路径上运动呢?
思考这个算法颇费了一番脑筋,其间还得到数学牛人Charlesgao的帮助,非常感谢他(比较糗的是,我问问题的时候就把其中的一个公式搞错了,见笑了-_-!)。

首先需要求得B(t)相对于t的速度公式s(t)


为了简化公式,我们定义如下变量:


计算出的s(t)可以表达为:


其中A,B,C是根据P0,P1,P2计算出的常数:


根据这个公式,求得贝塞尔曲线的长度公式L(t):


设t`就是能够使L实现匀速运动的自变量,那么显然L(t`)=L(1.0)*t,即:


由于L(t)函数非常复杂,直接求逆函数的表达式几乎不可能,还好我们可以知道它的导数为s(t),在实际使用中,可以使用牛顿切线法求出近似解。其迭代算法可以表达为:


我写了一个测试程序用于验证该算法,运算结果如下,可以看到,这条曲线已经是以匀速方式生成的了 ^_^:

 

 

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2.   
  3. #include <math.h>  
  4.   
  5. #include <windows.h>  
  6.   
  7.   
  8.   
  9. //三个控制点  
  10.   
  11. POINT P0={50,50},P1={500,600},P2={800,200};  
  12.   
  13.   
  14.   
  15. int ax = P0.x-2*P1.x+P2.x;  
  16.   
  17. int ay = P0.y-2*P1.y+P2.y;  
  18.   
  19. int bx = 2*P1.x-2*P0.x;  
  20.   
  21. int by = 2*P1.y-2*P0.y;  
  22.   
  23.   
  24.   
  25. double A = 4*(ax*ax+ay*ay);  
  26.   
  27. double B = 4*(ax*bx+ay*by);  
  28.   
  29. double C = bx*bx+by*by;  
  30.   
  31.   
  32.   
  33. //曲线总长度  
  34.   
  35. double total_length = 0.0;  
  36.   
  37.   
  38.   
  39. //曲线分割的份数  
  40.   
  41. const int STEP = 70;  
  42.   
  43.   
  44.   
  45. //用于保存绘制点数据的数组  
  46.   
  47. POINT pixels[STEP];  
  48.   
  49.   
  50.   
  51. //-------------------------------------------------------------------------------------  
  52.   
  53. //速度函数  
  54.   
  55. /* 
  56.  
  57. s(t_) = Sqrt[A*t*t+B*t+C] 
  58.  
  59. */  
  60.   
  61. double s(double t)  
  62.   
  63. {  
  64.   
  65.     return sqrt(A*t*t+B*t+C);  
  66.   
  67. }  
  68.   
  69.   
  70.   
  71. //-------------------------------------------------------------------------------------  
  72.   
  73. //长度函数  
  74.   
  75. /* 
  76.  
  77.  
  78.  
  79. L(t) = Integrate[s[t], t] 
  80.  
  81.  
  82.  
  83. L(t_) = ((2*Sqrt[A]*(2*A*t*Sqrt[C + t*(B + A*t)] + B*(-Sqrt[C] + Sqrt[C + t*(B + A*t)])) +  
  84.  
  85.             (B^2 - 4*A*C) (Log[B + 2*Sqrt[A]*Sqrt[C]] - Log[B + 2*A*t + 2 Sqrt[A]*Sqrt[C + t*(B + A*t)]])) 
  86.  
  87.                 /(8* A^(3/2))); 
  88.  
  89. */  
  90.   
  91. double L(double t)  
  92.   
  93. {  
  94.   
  95.     double temp1 = sqrt(C+t*(B+A*t));  
  96.   
  97.     double temp2 = (2*A*t*temp1+B*(temp1-sqrt(C)));  
  98.   
  99.     double temp3 = log(B+2*sqrt(A)*sqrt(C));  
  100.   
  101.     double temp4 = log(B+2*A*t+2*sqrt(A)*temp1);  
  102.   
  103.     double temp5 = 2*sqrt(A)*temp2;  
  104.   
  105.     double temp6 = (B*B-4*A*C)*(temp3-temp4);  
  106.   
  107.       
  108.   
  109.     return (temp5+temp6)/(8*pow(A,1.5));  
  110.   
  111. }  
  112.   
  113.   
  114.   
  115. //-------------------------------------------------------------------------------------  
  116.   
  117. //长度函数反函数,使用牛顿切线法求解  
  118.   
  119. /* 
  120.  
  121.     X(n+1) = Xn - F(Xn)/F'(Xn) 
  122.  
  123. */  
  124.   
  125. double InvertL(double t, double l)  
  126.   
  127. {  
  128.   
  129.     double t1=t, t2;  
  130.   
  131.       
  132.   
  133.     do  
  134.   
  135.     {  
  136.   
  137.         t2 = t1 - (L(t1)-l)/s(t1);  
  138.   
  139.         if(abs(t1-t2)<0.000001) break;  
  140.   
  141.         t1=t2;  
  142.   
  143.     }while(true);  
  144.   
  145.     return t2;  
  146.   
  147. }  
  148.   
  149.   
  150.   
  151. //-------------------------------------------------------------------------------------  
  152.   
  153. LRESULT CALLBACK _WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
  154.   
  155. {  
  156.   
  157.     switch (message)   
  158.   
  159.     {  
  160.   
  161.     case WM_TIMER:  
  162.   
  163.         {  
  164.   
  165.             static nIndex = 0;  
  166.   
  167.             if(nIndex>=0 && nIndex<=STEP)  
  168.   
  169.             {  
  170.   
  171.                 double t = (double)nIndex/STEP;  
  172.   
  173.                 //如果按照线形增长,此时对应的曲线长度  
  174.   
  175.                 double l = t*total_length;  
  176.   
  177.                 //根据L函数的反函数,求得l对应的t值  
  178.   
  179.                 t = InvertL(t, l);  
  180.   
  181.   
  182.   
  183.                 //根据贝塞尔曲线函数,求得取得此时的x,y坐标  
  184.   
  185.                 double x = (1-t)*(1-t)*P0.x +2*(1-t)*t*P1.x + t*t*P2.x;  
  186.   
  187.                 double y = (1-t)*(1-t)*P0.y +2*(1-t)*t*P1.y + t*t*P2.y;  
  188.   
  189.   
  190.   
  191.                 //取整  
  192.   
  193.                 pixels[nIndex].x = (int)(x+0.5);  
  194.   
  195.                 pixels[nIndex].y = (int)(y+0.5);  
  196.   
  197.   
  198.   
  199.                 nIndex++;  
  200.   
  201.                 InvalidateRect(hWnd, 0, 0);  
  202.   
  203.             }  
  204.   
  205.             else  
  206.   
  207.             {  
  208.   
  209.                 KillTimer(hWnd, 101);  
  210.   
  211.             }  
  212.   
  213.         }  
  214.   
  215.         break;  
  216.   
  217.     case WM_PAINT:  
  218.   
  219.         {  
  220.   
  221.             PAINTSTRUCT ps;  
  222.   
  223.             HDC hdc = BeginPaint(hWnd, &ps);  
  224.   
  225.             ::MoveToEx(hdc, P0.x, P0.y, 0);  
  226.   
  227.             LineTo(hdc, P1.x, P1.y);  
  228.   
  229.             LineTo(hdc, P2.x, P2.y);  
  230.   
  231.   
  232.   
  233.             for(int i=0; i<STEP; i++)  
  234.   
  235.             {  
  236.   
  237.                 const POINT &pt = pixels[i];  
  238.   
  239.                 if(pt.x==0 && pt.y==0) break;  
  240.   
  241.   
  242.   
  243.                 ::MoveToEx(hdc, pt.x-2, pt.y, 0);  
  244.   
  245.                 ::LineTo(hdc, pt.x+2, pt.y);  
  246.   
  247.                 ::MoveToEx(hdc, pt.x, pt.y-2, 0);  
  248.   
  249.                 ::LineTo(hdc, pt.x, pt.y+2);  
  250.   
  251.             }  
  252.   
  253.             EndPaint(hWnd, &ps);  
  254.   
  255.         }  
  256.   
  257.         break;  
  258.   
  259.     case WM_DESTROY:  
  260.   
  261.         PostQuitMessage(0);  
  262.   
  263.         break;  
  264.   
  265.     default:  
  266.   
  267.         return DefWindowProc(hWnd, message, wParam, lParam);  
  268.   
  269.     }  
  270.   
  271.     return 0;  
  272.   
  273. }  
  274.   
  275.   
  276.   
  277. //-------------------------------------------------------------------------------------  
  278.   
  279. int APIENTRY WinMain(HINSTANCE hInstance,  
  280.   
  281.                      HINSTANCE hPrevInstance,  
  282.   
  283.                      LPTSTR    lpCmdLine,  
  284.   
  285.                      int       nCmdShow)  
  286.   
  287. {  
  288.   
  289.     //注册窗口类  
  290.   
  291.     WNDCLASSEX wcex;  
  292.   
  293.     ZeroMemory(&wcex, sizeof(WNDCLASSEX));  
  294.   
  295.   
  296.   
  297.     wcex.cbSize = sizeof(WNDCLASSEX);   
  298.   
  299.     wcex.style          = CS_HREDRAW | CS_VREDRAW;  
  300.   
  301.     wcex.lpfnWndProc    = (WNDPROC)_WndProc;  
  302.   
  303.     wcex.hInstance      = hInstance;  
  304.   
  305.     wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);  
  306.   
  307.     wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);  
  308.   
  309.     wcex.lpszClassName  = "BezierClass";  
  310.   
  311.     RegisterClassEx(&wcex);  
  312.   
  313.   
  314.   
  315.     //创建窗口  
  316.   
  317.     HWND hWnd = CreateWindow("BezierClass""BezierDemo", WS_OVERLAPPEDWINDOW,  
  318.   
  319.       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);  
  320.   
  321.     ShowWindow(hWnd, nCmdShow);  
  322.   
  323.     UpdateWindow(hWnd);  
  324.   
  325.   
  326.   
  327.     //计算总长度  
  328.   
  329.     total_length = L(1);  
  330.   
  331.   
  332.   
  333.     //清空绘制点数据  
  334.   
  335.     ZeroMemory(&pixels, sizeof(pixels));  
  336.   
  337.   
  338.   
  339.     //设定定时刷新计时器  
  340.   
  341.     SetTimer(hWnd, 101, 10, 0);  
  342.   
  343.   
  344.   
  345.     //消息循环  
  346.   
  347.     MSG msg;  
  348.   
  349.     while(GetMessage(&msg, NULL, 0, 0))   
  350.   
  351.     {  
  352.   
  353.         TranslateMessage(&msg);  
  354.   
  355.         DispatchMessage(&msg);  
  356.   
  357.     }  
  358.   
  359.   
  360.   
  361.     return (int) msg.wParam;  
  362.   
  363. }  
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

匀速贝塞尔曲线运动的实现(转) 的相关文章

  • TensorFlow在MNIST中的应用-Softmax回归分类

    参考 TensorFlow技术解析与实战 http wiki jikexueyuan com project tensorflow zh tutorials mnist beginners html http www jianshu com
  • 7-2 一元多项式的乘法与加法运算 JAVA_MAP方案

    7 2 一元多项式的乘法与加法运算 30 分 题目要求 设计函数分别求两个一元多项式的乘积与和 输入格式 输入分2行 每行分别先给出多项式非零项的个数 再以指数递降方式输入一个多项式非零项系数和指数 绝对值均为不超过1000的整数 数字间以
  • PCB layout 注意事项

    过孔方面的注意事项 放置过孔时 放置过孔时 2个过孔之间的能走过一根信号线 注意美观横平竖直 过孔与焊盘的间距 PCB layout 时过孔与焊盘的间距 最好在6mil 1 524mm 以上 因为过孔一般不开阻焊 离焊盘太近会导致过孔露铜
  • BigDecimal中divide方法详解

    1 首先说一下用法 BigDecimal中的divide主要就是用来做除法的运算 其中有这么一个方法 public BigDecimal divide BigDecimal divisor int scale int roundingMod
  • 「Python 基础」异步 I/O 编程

    I O 密集型应用程序大大提升系统多任务处理能力 异步 I O 模型 一个消息循环 主线程在消息循环中不断重复 读取消息 处理消息 获取线程池 loop get event loop while True 接收事件消息 event loop
  • R语言实现个人信用风险评估(数据科学导引)

    1 案例背景 在很多国家 政府机构会密切监控贷款业务银行需要明确解释申请者的贷款申请被拒绝或者批准的原因 这种可解释性对于贷款申请者也是很重要的 在贷款申请被银行拒绝时 申请者需要知道为什么自己的信用级别不符合银行的要求 通过构建自动化的信
  • es脚本 实现字段之间进行比对

    script script source ctx source extra test lang painless query term user kimchy java BoolQueryBuilder boolQueryBuilder Q
  • 详解比较古怪的字符串拆分函数:strtok函数

    对于字符串中的标点符号 字符呀 如何能够当作分隔符来对原字符串进行分割呢 分割出来的为一段一段的字符 因此 在这里面就用到了 本文 比较古怪的字符串拆分函数 strtok函数 假设 对于这个字符串 woaini wangyijun com
  • 3.4 三级指针

    char p NULL 注 1 可以通过三级指针间接的改变二级指针的指向 2 p表示 三级指针指向的二级指针中保存的内存地址 3 三级指针做函数参数时 主调函数需要传2级指针的地址 4 n级指针可以间接修改n 1级指针的指向 下面的例子通过
  • @Component注解的作用

    Spring自带的 Component注解及扩展 Component 定义Spring管理Bean 也就是将标注 Component注解的类交由spring管理 AspectJ风格的切面可以通过 Compenent注解标识其为Spring管
  • VMware10上新建虚拟机步骤图解 + 安装Centos 7(64位) 系统

    原文 https blog csdn net hometing218 article details 79486172
  • RTKlib单点定位-部分思考

    塔奇克敲代码 博主的博客 RTKLIB源码解析 单点定位 将单点定位部分整理成函数小卡片 为我理解RTKlib提供了很大的帮助 他在单点定位部分列出了一些疑惑 在此我记录下我对部分疑惑的理解 文章目录 1 pntpos函数 2 satpos
  • vcs+verdi,以及Makefile注意点

    Makefile 命令行之前是以Tab开头的不然会报错 gvim里面强制输入tab 使用Ctr v i 直接使用tab键可能输入不成功 注释用 下面是makefile内容 L8 可选debug debug pp debug pp 使能ucl
  • 树莓派通信协议——MQTT的安装及使用

    MQTT是一种基于TCP IP协议栈构建的异步通信协议 是一种轻量级的发布 订阅信息传输协议 基于topic订阅关系的发布和推送 在实践中可空间上 将消息发送者和接受者分离 可以再不可靠的网络环境中进行扩展 适用于设备硬件存储空间有限或网络
  • 【满分】【华为OD机试真题2023 JAVA&JS】木板

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 木板 时间限制 1s 空间限制 256MB 限定语言 不限 题目描述 小明有n块木板 第i 1 i n 块木板的长度为ai 小明买了一块长度为m的木料 这块木料可以切割成任意块
  • R手册(NLP)--wordcloud2

    文章目录 wordlcoud2函数 letterCloud函数 shiny支持 wordcloud2 R interface to wordcloud for data visualization Wordcloud2主要包括两个函数 wo
  • ‘XXX’ is already defined @typescript-eslint/no-redeclare 警告 问题解决

    上文React Typescript项目环境中搭建并使用redux环境 结束是 其实不算完全写完吧 还会留下一个警告 这个报错 好像是说 这两个值已经定义过了 可能很多人 会觉得小问题 但你会发现 无论你名字怎么改都会一直 带着你新的名字继

随机推荐

  • Jenkins+sonarqube+sonar-scanner扫描java文件乱码怎么解决?

    在jenkins上扫描java项目 提示乱码 有没有知道怎么解决啊 WARN Invalid character encountered in file root jenkins workspace sonar scanner rrdl t
  • Element中table组件根据属性合并行数据

    在实际开发中 要求使用elementUI的table组件对表格数据上下行相邻相同的数据进行合并 在elem官网上查看到是有对应的组件和合并方法
  • apache doris和StarRocks的区别

    Apache Doris是一个分布式的列式存储系统 它的设计目标是提供大规模数据处理的可靠性和高性能 Doris采用了集群方式 通过将数据分布在多个机器上进行处理来提高性能 并提供了SQL查询接口方便用户使用 StarRocks是一个分布式
  • 【Windows10下MySQL(8.0)免费版安装与卸载图文教程】

    WIN10安装MYSQL8 0图文教程 Windows10下MySQL 8 0版本 免费版安装与卸载 一 下载 二 解压 三 创建配置文件以及存放数据的文件夹 四 配置系统环境变量 五 以管理员打开命令窗口 Win键 X键 六 安装 七 连
  • idea access数据库连接_几款好用的数据库,选择适合自己的那盘菜

    先来说一下什么是数据库 通俗讲 它就像物品仓库一样 可以存储电子数据 但是不同的是 它是以一定数据结构进行存储的 用户也可以对其中的数据通过一定的语言方式进行新增 查询 更新 删除等操作 大家接触的比较多的是Excel数据表格 实际它只能称
  • Elasticsearch-head插件安装教程

    目录标题 前言 一 安装node 二 安装Elasticsearch head插件 1 上传压缩包到 opt es路径下去 2 解压安装包 3 192 168 43 10机器修改Gruntfile js 4 192 168 43 10机器修
  • spdk探秘-----块设备开发指导

    这里的块设备是一种存储设备 它支持在固定大小的块中读写数据 这些块通常是512或4096字节 这些设备可能是软件中的逻辑结构 或者对应于像NVMe ssd这样的物理设备 通用库的公共头文件是bdev h 它是与任何类型的块设备交互所需的全部
  • 面经四(多线程的状态)

    援引于线程有哪些状态 每个状态是什么意思 又是如何切换的 线程状态 生命周期 1 新建状态 new 创建线程对象 但是没有交给CPU 也就是没有调用start 方法 只有一个Thread对象 还没有一个真正的线程 每个线程只存在一次新建状态
  • FATFS 0.13 f_mount(&fs,““,1)挂载失败的原因

    这两天学着用了一下FATS文件系统 虽然工作中没用到 但是对个人的经验积累还是有用的 看了一下 代码并不多 但是精简啊 指针跳来跳去的一不小心就晕了 所以也遇到了不少问题啊 这里就讲一下我遇到的第一步就懵逼的问题 那就是 FRESULT f
  • vue其他之“命名规则”

    骆驼式命名法在许多新的函数库和Microsoft Windows这样的环境中使用得相当多 另一方面 下划线法是c出现后开始流行起来的 在许多旧的程序和UNIX这样的环境中 它的使用非常普遍 驼峰式命名法 由一个或多个单词连结在一起 而构成的
  • 攻防世界(adworld) WEB Exercise area(练习区)10题解题思路

    攻防世界 https adworld xctf org cn view source 点右键看源代码被禁止了 要么F12 要么去其他工具中请求就可以了 robots 机器人协议 是规定了爬虫哪些页面可以爬 哪些不可以 robots txt
  • LeetCode每日一题(2444. Count Subarrays With Fixed Bounds)

    You are given an integer array nums and two integers minK and maxK A fixed bound subarray of nums is a subarray that sat
  • [OpenGL] CentOS7 安装 mesa

    CentOS 7安装 mesa How to install mesa on centos 7 1 下载原文件 下载连接 mesa 11 1 3 tar gz freeglut 3 0 0 tar gz libdrm 2 4 66 tar
  • PCB中焊盘和字体整体变透明原因

    之前画PCB时候发现怎么PCB元器件颜色看着很透明 想想看自己也没干什么 怎么变透明了 透明图如下所示 从上图来看是不是焊盘和字体都透明一样 后来发现原来是之前自己看3D时候把颜色改了 切换回2D时候设置为2D透明模式了 如图所示 把这个2
  • 2023年大厂裁员严重,软件测试行业真的饱和了吗?

    这短时间以来后台有很多小伙伴说找工作难 并且说软件测试行业饱和了 竟然登上了热榜 那么我今天带大家看看真实的市场行情 往下看 这个是公司联合某厂的HR招聘真实情况 很明显 软件测试 投简历竟然高达9999 沟通才1千多 说明什么 软件测试
  • 常见的端口服务及漏洞(详细)

    端口 对应的服务 存在的相关漏洞 21 69 ftp tftp 文件传输协议 爆破 嗅探溢出 后门 匿名访问 22 ssh 爆破 openssh漏洞 23 telnet 远程连接 爆破嗅探 25 smtp 邮件服务 弱口令 未授权访问 邮件
  • zookeeper+dubbo+springmvc,搭建

    Dubbo是一个分布式服务框架 在这基础上可以做成分布式计算机网络解决很多高并发或者数据处理量大的问题 二zookeeper又是dubbo的一个重要组件 在此 我借助java语言和springmvc框架介绍如何初步构建一个dubbo服务框架
  • 2023有哪些更好用的网页制作工具

    过去 专业人员使用HTMLL CSS Javascript等代码手动编写和构建网站 现在有越来越多的智能网页制作工具来帮助任何人实现零代码基础 随意建立和设计网站 在本文中 我们将向您介绍2023年流行的网页制作工具 我相信一旦选择了正确的
  • 网络安全技术期末复习——理论部分

    复习 考核 网络安全基本理论知识点 实际设备 网络安全实验 出勤 作业 讨论 课堂练习 11次课的作业完成情况 偏实践和操作 60 综合系统复习 理论考核 时间占三分之一 150分钟 理论就是50 操作100分钟 120分钟 理论40 操作
  • 匀速贝塞尔曲线运动的实现(转)

    二次贝塞尔曲线通常以如下方式构建 给定二维平面上的固定点P0 P1 P2 用B t 表示该条曲线 用一个动画来演示 可以更加清楚的表明这条曲线的构建过程如果t变量本身线形变化的话 这条贝塞尔曲线本身的生成过程是并不是匀速的 通常都是两头快中