贝塞尔曲线的长度始终相同

时间:2011-11-22 21:09:39

标签: html5 canvas bezier

我正在开发HTML5画布游戏。

我想在两点之间绘制一个S形的三次贝塞尔曲线,但我正在寻找一种计算控制点坐标的方法,这样曲线本身总是相同的长度,无论这些点有多接近是,直到它到达曲线变成直线的点。

3 个答案:

答案 0 :(得分:2)

这可以用数字方法解决。我假设你有一个带有4个控制点的立方贝塞尔曲线。 在每个步骤中,您有第一个(P0)和最后一个(P3)点,并且您想要计算P1和P2,使得总长度不变。

添加此约束会移除一个自由度,因此我们有1个左边(从4开始,确定终点(-2),常量长度是另一个-1)。所以你需要做出决定。

贝塞尔曲线是一个定义在0和1之间的多项式,你需要积分在元素之和(2d?)的平方根上。对于一个三次贝塞尔曲线,这意味着一个6度多项式的sqrt,其中wolfram不知道如何解决。但是,如果已知所有其他控制点(或者已知某些其他约束的依赖关系),则可以为该约束设置一个预先计算值的保存表。

答案 1 :(得分: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)

答案 2 :(得分: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);
  }
}