引言:
看了标题的人可能会有这样的反应:两点不是能确定无限条曲线吗,这不是扯淡吗。。其实现在的需求是这样的:在一个游戏里,我们需要根据起点和落点生成一条看上去合理的曲线运动轨迹。在我的想象中,它至少应该是往近处抛弧度小,往远处抛弧度大的这样一个表现。于是我设计了如下曲线生成方案,给游戏客户端那边去用。
算法如下:
1.通过起点终点,算出两点距离。用这个距离根据配置换算出一个圆心角a
2.用起点,终点,圆心角a确定一个圆o,取o与通过中点((起点+终点)/2)的竖线的上方的交点,作为参考点
3.用起点,终点,参考点生成曲线(矩阵,拉格朗日插值,高斯消元都行)
4.算法示意图
算法解析:
首先两点是无法确定曲线的,这时候需要引入第三个点,这个第三个点的表现直接影响到曲线的轨迹表现,所以我在设计的时候将输入的起点终点放到一个圆上,然后第三个点在两点构成的弧上取,这样我可以通过控制圆的大小来间接控制曲线的弧度。
然后圆的大小怎么控制呢,两点距离固定了,我们可以控制两点和圆心连线构成的圆心角大小来控制。所以我设计的转换方式是:
max_distance = 100.0
angle = [30, 160]
def getAngle(distance):
return (1 - distance / max_distance) * (angle[1] - angle[0]) + angle[0]
给定配置一个最远距离和圆心角范围,距离越远,圆心角越小,这样出来的参考点就离起点终点连线的线段越远。最终表现出来曲线更陡峭,而且这个程度也能通过最远距离和圆心角范围手动调参。
接下来,为什么我要取过中点的直线和圆的交点作为参考点呢。这是因为为了防止出现曲线往一定倾斜角度歪(事后想想是多余的,因为后面我用拉格朗日插值求抛物线方程,既然能求出来,那肯定不会出现歪的,毕竟这是函数的定义,一个x对应一个y)
接下来,我们有三个点了,我们就能选择拟合的函数模型了,我最初其实是选择了抛物线的,考虑问题都以抛物线来考虑。但是在写blog的时候才发现一些逻辑上的BUG,这个放后面总结。最简洁的姿势肯定是用矩阵求y=a*x^2+b*x+c
里的abc,其次是用拉格朗日插值求(写起来相对复杂,运算量也相对大一点点,但是不需要矩阵运算支持,只需要基础的四则运算,移植性强),再者就是直接推出a,b,c关于三个点的式子。综合考虑下来,我选择的是拉格朗日插值法。
具体代码:
为了快速验证我的想法,我用py一边查API一边把我的想法实现了出来,代码如下:
# coding=utf-8#
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D