贝塞尔曲线始终保持相同长度

9
我正在制作一个HTML5画布游戏。
我想绘制两个点之间的S形三次贝塞尔曲线,但我正在寻找一种计算控制点坐标的方法,以使曲线本身的长度始终相同,无论这些点有多近,直到达到曲线变成直线的点。

线的长度是否完全相同至关重要,还是只要开始和结束点越来越接近就可以了? - idanzalz
贝塞尔曲线的程度是多少(有多少个点)?它是三次的吗?(4个点 - 开始,结束和中间的2个点) - idanzalz
一个即兴的解决方案是将其制定为一个优化问题:将曲线分成一系列直线段,并最小化这些直线段的总长度与所需常数长度之间的差异,相对于自由控制点。您可以使用梯度下降等算法进行优化; 但是,解析解会更好。 - Rulle
1
为确保我理解正确,您说“直到曲线变成一条直线”,这是在端点最接近还是尽可能远的情况下发生的? - idanzalz
曲线的目标是什么?平滑、视觉上令人愉悦还是其他?如果你知道了这一点,你可以将其表述为一个受限制的优化问题,以在曲线长度恒定的约束下最大化目标。如果存在时间连续性,比如在动画中,迭代求解器会很有效,因为它可以使用前一帧的解作为初始化。 - Rulle
我在一个游戏项目中也遇到了完全相同的问题 :) - Sebastian Hoffmann
3个回答

2
这是可以通过数值方法解决的问题。我假设您有一个具有4个控制点的三次贝塞尔曲线。 在每一步,您都有第一个(P0)和最后一个(P3)点,并且希望计算出P1和P2,使得总长度保持不变。
添加此约束条件会消除一个自由度,因此我们还剩下1个(起始为4个,确定了端点(-2),常数长度又减去1)。因此,您需要做出决策。
贝塞尔曲线是一个在0到1之间定义的多项式,您需要对元素之和的平方根进行积分(2D?)。对于三次贝塞尔曲线,这意味着要对一个6次多项式的平方根进行求解,但Wolfram不知道如何解决。但是,如果您已经知道了所有其他控制点(或者已知它们与某些其他约束的相关性),则可以预先计算出该约束条件的安全表格。

1
这真的这么简单吗?如果我正确理解问题,我们有一个贝塞尔曲线(x(t),y(t)),其中x和y是三次多项式。假设它们的导数,dx / dt(t)= P(t)和dy / dt(t)= Q(t),它们是二次多项式。然后,我们想要积分弧长ds = sqrt(dx ^ 2 + dy ^ 2)dt = sqrt(P(t)^ 2 + Q(t)^ 2)dt。我不知道积分四次多项式的平方根表达式有多难,但像Mathematica这样的数学软件可能可以做到。 - Rulle
2
乍一看,我觉得将那个四次方程的平方根进行整合会很困难。 - Harold

2
这个曲线一定要是贝塞尔曲线吗?拟合两个总长度相等的圆弧会更容易,而且你总能得到一个S形状。 拟合两个圆弧: Fitting two circlesD为两端点之间的欧几里得距离,C为我们想要的常数长度。我得到了下面这个表达式来表示b(如图所示):
b = sqrt(D*sin(C/4)/4 - (D^2)/16)

我还没有检查它是否正确,如果有人得到不同的结果,请留言评论。

编辑:当解方程时,您应该考虑负解,并检查哪个是正确的。

b = -sqrt(D*sin(C/4)/4 - (D^2)/16)

我认为问题要求的是另外一件事,即当端点非常接近时,他希望S最宽。据我理解,他想模拟端点之间的固定长度绳索,因此这个解决方案行不通。 - idanzalz
1
你可以通过绘制半个椭圆来改进这种方法。要绘制一个椭圆,你可以先画一个圆,然后使用非均匀缩放将其变成椭圆。但是,我认为椭圆周长也是非解析的,所以我们回到了原点。 - idanzalz

0

这是一个使用SVG的工作示例,非常接近完美:
http://phrogz.net/svg/constant-length-bezier.xhtml

enter image description here

通过实验我确定当端点重合时,处理柄应该与处理点距离为
desiredLength × cos(30°)
而且(当然),当端点之间最远距离时,处理柄应该重合。所有理想点的绘制看起来像一个椭圆形:

Graph showing actual points compared to ellipse

蓝线是实际的理想方程,而上面的红线是近似理想的椭圆形。使用椭圆形的方程(如我上面的示例所示)允许线在中间部分过长约9%。

以下是相关的JavaScript代码:

// M is the MoveTo command in SVG (the first point on the path)
// C is the CurveTo command in SVG:
//   C.x is the end point of the path
//   C.x1 is the first control point
//   C.x2 is the second control point
function makeFixedLengthSCurve(path,length){
  var dx   = C.x - M.x, dy = C.y - M.y;
  var len  = Math.sqrt(dx*dx+dy*dy);
  var angle = Math.atan2(dy,dx);
  if (len >= length){
    C.x  = M.x + 100 * Math.cos(angle);
    C.y  = M.y + 100 * Math.sin(angle);
    C.x1 = M.x; C.y1 = M.y;
    C.x2 = C.x; C.y2 = C.y;
  }else{
    // Ellipse of major axis length and minor axis length*cos(30°)
    var a = length, b = length*Math.cos(30*Math.PI/180);
    var handleDistance = Math.sqrt( b*b * ( 1 - len*len / (a*a) ) ); 
    C.x1 = M.x + handleDistance * Math.sin(angle);
    C.y1 = M.y - handleDistance * Math.cos(angle);
    C.x2 = C.x - handleDistance * Math.sin(angle);
    C.y2 = C.y + handleDistance * Math.cos(angle);
  }
}

1
也许可以从实验确定的数据中构建一个查找表? - Harold
@Harold 确实,或者一个简单的分段线性逼近。不过,我仍在寻找数学上正确的答案 - Phrogz
1
一个崇高的目标。与此同时,对于HTML5画布切绳索克隆的作者来说,一个近似的解决方案可能非常有价值。 ;) - Harold
1
如果没有数学上正确的答案,我认为我的上面的答案可以通过预测中间的9%超调并根据距离从中间缩放值来改进。 - Phrogz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接