Function 在相对于弧长的恒定速度下计算函数

Function 在相对于弧长的恒定速度下计算函数,function,math,polynomial-math,Function,Math,Polynomial Math,我正在实现一个实时图形引擎(C++/OpenGL),它可以沿着多项式函数描述的指定路线随时间移动车辆。函数本身是在应用程序之外以编程方式生成的,并且是高阶的(我相信>25),所以我不能在这里发布它(我认为它并不重要)。在运行期间,函数不会改变,因此很容易计算一次一阶导数和二阶导数,以便以后快速使用 我的问题是,我必须以恒定的速度(比如10个单位/秒)沿着曲线移动,因此我的函数参数不直接等于时间,因为两点x1和x2之间的弧长取决于函数值而不同。例如,差值f(a+1)-f(a)可能比f(b+1)-f

我正在实现一个实时图形引擎(C++/OpenGL),它可以沿着多项式函数描述的指定路线随时间移动车辆。函数本身是在应用程序之外以编程方式生成的,并且是高阶的(我相信>25),所以我不能在这里发布它(我认为它并不重要)。在运行期间,函数不会改变,因此很容易计算一次一阶导数和二阶导数,以便以后快速使用

我的问题是,我必须以恒定的速度(比如10个单位/秒)沿着曲线移动,因此我的函数参数不直接等于时间,因为两点x1和x2之间的弧长取决于函数值而不同。例如,差值f(a+1)-f(a)可能比f(b+1)-f(b)大或小,这取决于函数在点a和点b上的外观

我不需要100%的精确解,因为运动只是视觉上的,不会被进一步处理,所以任何近似都可以。另外,请记住,整个过程必须在运行时每帧(60fps)计算一次,因此,根据计算时间的不同,用复杂的数学解决庞大的方程可能是不可能的


我有点不知道从哪里开始,所以即使是任何想法都将受到高度赞赏

给定恒定的速度和帧间时间,可以计算帧间所需的弧长。因此,以下函数应完成此工作:

#include <cmath>
typedef double (*Function)(double);

double moveOnArc(Function f, const double xStart, const double desiredArcLength, const double dx = 1e-2)
{
  double arcLength = 0.;
  double fPrev = f(xStart);
  double x = xStart;
  double dx2 = dx*dx;
  while (arcLength < desiredArcLength)
  {
    x += dx;
    double fx = f(x);
    double dfx = fx - fPrev;
    arcLength += sqrt(dx2 + dfx*dfx);
    fPrev = fx;
  }
  return x;
}
#包括
typedef double(*函数)(双精度);
double moveOnArc(函数f,常数double xStart,常数double desiredArcLength,常数double dx=1e-2)
{
双弧长=0。;
双fPrev=f(xStart);
双x=xStart;
双dx2=dx*dx;
而(弧长<所需弧长)
{
x+=dx;
双fx=f(x);
双dfx=fx-fPrev;
弧长+=sqrt(dx2+dfx*dfx);
fPrev=外汇;
}
返回x;
}

由于您说准确度不是首要标准,因此选择适当的
dx
上述功能可能会立即起作用。当然,可以通过自动调整dx(例如基于二阶导数)或通过二元搜索细化端点来改进。因为标准不是要有精确的解,而是视觉上吸引人的近似值,所以有多种可能的解决方案可以尝试


我实施的第一个方法(由Alnitak在评论中建议,随后由coproc回答),通过微小迭代近似实际弧长积分。这个版本在大多数情况下运行得非常好,但在非常陡峭的角度下不可靠,在平坦的角度下使用了太多的迭代。正如coproc在回答中已经指出的,一个可能的解决方案是将dx基于二阶导数

所有这些调整都可以进行,但是,我需要一个运行时友好的算法。有了这个,很难预测迭代的次数,这就是为什么我对它不满意的原因


第二种方法(同样受Alnitak启发)利用一阶导数,沿计算的坡度“推动”车辆(等于当前x值的导数)。计算下一个x值的函数非常紧凑和快速。视觉上没有明显的误差,结果总是一致的。(这就是我选择它的原因)


然而,这种方法可能只适用于高帧时间(我的帧时间大于60fps)的情况,因为物体总是沿着一条长度取决于所述帧时间的直线推动。

你知道任何演算吗?需要移动车辆的角度是多项式的第一个微分。另请参见“谢谢!”!我会尝试那种方法。不过,我可能不得不稍微调整一下,因为linked math.stackexchange页面中的人似乎对sqrt的精度和小数字有问题(我在这里也处理小数字)你给我的另一个想法是,我可以试着根据一阶导数来设定物体的旋转,然后沿着一个有一定长度的直线向量推动它。(然而这可能会让我更加不精确)FWIW,我可能会使用一阶导数来计算运动向量的
dx
dy
分量,然后将得到的
x+dx
插入原始多项式,以确保结果点最终位于曲线上。(基于该曲线上坐标的精度比速度的严格精度更重要而工作)
float current_x = ...; //stores current x
float f(x) {...}
float f_derv(x) {...}

void calc_next_x(float units_per_second, float time_delta) {
  float arc_length = units_per_second * time_delta;
  float derv_squared = f_derv(current_x) * f_derv(current_x);
  current_x += arc_length / sqrt(derv_squared + 1);
}