2011-11-22 122 views
8

我工作在HTML5画布游戏。Bezier曲线总是相同的长度

我想是画两个点之间的S形三次Bezier曲线,但我正在寻找一种方式来计算控制点的坐标,以便曲线本身始终是相同的长度无论怎么接近那些没有点直到达到曲线成为直线的点。

+0

是它关键是线长的一模一样,或者只是在起点和终点越来越接近它得到零? – idanzalz

+0

贝塞尔的程度是多少?它是立方体吗? (4点 - 开始,结束,中间2点) – idanzalz

+0

一个快速而肮脏的解决方案是将其表述为一个优化问题:将曲线拆分为一系列直线段,并尽量减少这些段之间的总长度差线段和所需的恒定长度,重量免费的控制点。您可以使用诸如渐变下降http://en.wikipedia.org/wiki/Gradient_descent这样的算法。但分析解决方案会更好。 – Rulle

回答

2

这是可解的数值。我假设你有一个有4个控制点的立方贝塞尔曲线。 在每一步你有第一个(P0)和最后一个(P3)点,并且你想计算P1和P2,使得总长度是恒定的。

添加该约束消除一个自由度,所以我们有1个左(开始与4,确定的终点(-2)和恒定长度是另一个-1)。所以你需要做出决定。

Bezier曲线是在0和1之间限定的多项式,需要在元件的总和的平方根整合(2D?)。对于一个三次贝塞尔曲线来说,这意味着一个6阶多项式的sqrt,这个wolfram不知道如何求解。但是,如果您已知所有其他控制点(或者已知某个其他约束的依赖关系),则可以为该约束存储预先计算值的保存表。

+0

这真的很简单吗?如果我正确地理解了这个问题,我们有一个贝塞尔曲线(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

+0

对,我算错了。我将编辑 – idanzalz

+1

乍看之下,在我看来,整合该四次方的平方根将会很难。 – Harold

2

真的有必要,该曲线是贝塞尔曲线? 拟合总长度恒定的两个圆弧要容易得多。你将永远得到一个S形。

拟合两个圆弧的:

Fitting two circles

d是在端点之间的欧几里德距离。让C是我们想要的恒定长度。我得到了下面的表达式为b(图像中绘制):

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

,如果它是正确的,所以如果有人得到不同的东西,发表评论我没有检查。

编辑:您应该考虑的负面解决方案太,我得到求解方程时,检查哪一个是正确的。

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

我认为这个问题要求别的东西,即当终点非常接近时,他希望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); 
    } 
} 
+0

也许查询表可以从实验确定的数据构建? – Harold

+0

@Harold真,或简单的分段线性逼近。尽管如此,我仍然在寻找[数学上正确的答案](http://math.stackexchange.com/questions/85003/equation-for-control-point-distance-for-fixed-length-cubic-bezier-path -with-SPE)。 – Phrogz

+1

[崇高目标](http://ressalva.files.wordpress.com/2011/06/yak_crop2.png)。与此同时,近似解决方案可能对HTML5帆布剪切克隆的作者有价值。 ;) – Harold