2011-04-12 128 views
43

我想计算二次曲线上的点。将其与HTML5的canvas元素一起使用。二次Bézier曲线:计算点

当我在JavaScript中使用quadraticCurveTo()函数时,我有一个源点,一个目标点和一个控制点。

我该如何计算创建的二次曲线上的一个点,比如说t=0.5只有“知道这三点?

回答

93

使用二次贝塞尔公式,发现,例如,维基百科页面Bézier Curves上:

quadratic Bezier formula

在伪代码,这是

t = 0.5; // given example value 
x = (1 - t) * (1 - t) * p[0].x + 2 * (1 - t) * t * p[1].x + t * t * p[2].x; 
y = (1 - t) * (1 - t) * p[0].y + 2 * (1 - t) * t * p[1].y + t * t * p[2].y; 

p[0]是起点, p[1]是控制点,而p[2]是终点。 t是参数,它从0变为1

+37

是的,太棒了。除此之外,我什么都不懂...... – 2011-04-12 12:06:30

+3

在这种情况下乘以(加)点意味着你乘(加)每个组件。也就是说,'3 P = [3 * P.x,3 * p.y]'和'P1 + P2 = [P1.x + P2.x,P1.y + P2.y]'。最后,为了平分一些东西,你可以把它与自身相乘:x²='x * x'。最后一部分“t∈[1,0]”表示* t *应该在0和1之间。 – 2011-04-12 12:38:07

+7

因此,这意味着: Point.x =(1-t)^ 2 * P0 .x + 2 *(1-t)* t * P1.x + t^2 * P2.x; (1-t)^ 2 * P0.y + 2 *(1-t)* t * P1.y + t^2 * P2.y; 经过测试,它的工作原理! =) 谢谢! – 2011-04-12 13:13:22

22

如果有人需要立方形式:

 //B(t) = (1-t)**3 p0 + 3(1 - t)**2 t P1 + 3(1-t)t**2 P2 + t**3 P3 

     x = (1-t)*(1-t)*(1-t)*p0x + 3*(1-t)*(1-t)*t*p1x + 3*(1-t)*t*t*p2x + t*t*t*p3x; 
     y = (1-t)*(1-t)*(1-t)*p0y + 3*(1-t)*(1-t)*t*p1y + 3*(1-t)*t*t*p2y + t*t*t*p3y; 


如果有人需要的第n个形式,这里的算法。你喂它N点,它将返回一个N + (N-1) + (N-2) ...点数组,这解决了(N * (N*1))/2。最后一点是T.

9 
    7 8 
4 5 6 
0 1 2 3 
给定值你会喂算法0 1 2 3作为控制点的曲线上的位置,并且这些位置将是阵列的其余部分。最后一点(9)是你想要的值。

这也是你如何细分贝塞尔曲线,你给它的价值t你想然后你声称细分曲线作为金字塔的两侧。然后,您可以对金字塔侧面和金字塔另一侧的各个点进行索引,这些点是从底座构建的。因此,例如在五次:

E 
    C D 
    9 A B 
5 6 7 8 
0 1 2 3 4 

(原谅六角,我想它很)

你会的指数为0,两个完全细分曲线5,9,C,E和E,d, B,8,4.请特别注意,第一条曲线以控制点(0)开始,以曲线上的点(E)结束,第二条曲线以曲线(E)开始并以控制点结束(4)鉴于此,您可以完美地细分贝塞尔曲线,这就是您所期望的。连接两条曲线的新控制点在曲线上。

/** 
* Performs deCasteljau's algorithm for a bezier curve defined by the given control points. 
* 
* A cubic for example requires four points. So it should get at least an array of 8 values 
* 
* @param controlpoints (x,y) coord list of the Bezier curve. 
* @param returnArray Array to store the solved points. (can be null) 
* @param t Amount through the curve we are looking at. 
* @return returnArray 
*/ 
public static float[] deCasteljau(float[] controlpoints, float[] returnArray, float t) { 
    int m = controlpoints.length; 
    int sizeRequired = (m/2) * ((m/2) + 1); 
    if (returnArray == null) returnArray = new float[sizeRequired]; 
    if (sizeRequired > returnArray.length) returnArray = Arrays.copyOf(controlpoints, sizeRequired); //insure capacity 
    else System.arraycopy(controlpoints,0,returnArray,0,controlpoints.length); 
    int index = m; //start after the control points. 
    int skip = m-2; //skip if first compare is the last control point. 
    for (int i = 0, s = returnArray.length - 2; i < s; i+=2) { 
     if (i == skip) { 
      m = m - 2; 
      skip += m; 
      continue; 
     } 
     returnArray[index++] = (t * (returnArray[i + 2] - returnArray[i])) + returnArray[i]; 
     returnArray[index++] = (t * (returnArray[i + 3] - returnArray[i + 1])) + returnArray[i + 1]; 
    } 
    return returnArray; 
} 

你会注意到它只是通过每组点的数量公式。对于N个解,你可以得到(N-1)个中点的值(t),然后从中间得到(N-2)个点,然后是(N-3)个点等,直到你只有一个点。这一点在曲线上。因此,解决t之间的值为0,1之间的事情,会给你整个曲线。知道这一点,我在那里的实现只是向数组中传播值,从而不止一次地重新计算任何东西。我已经使用了100秒的点数,并且仍然很快。

(如果你想知道,不,它不是真的值得,SVG是正确的停在立方体)。

1

只是说明:如果您使用的是这里提供的常用公式,那么不要指望t = 0.5将曲线长度的一半返回。在大多数情况下不会。

关于此的更多信息here根据“§23-以固定距离间隔跟踪曲线”here

+0

无论如何,曲线的长度真的很难测量。在t = 0.5时,如果你假设随机控制点位于中心,那么平均来说,但是,请注意它与大多数不同速度的曲线有相同的问题。寻找中间点通常需要测量曲线的一部分并用二进制搜索找到中心位。它不是完全超级通常需要的。但是,值得了解的是,如果你发现所有的点数都是t = .1的增量,那么它们的长度就不相等。 - 尽管这与曲线的性质无关,但与问题无关。 – Tatarize 2016-09-12 23:04:25

+1

@Tata​​rize:正如所提供的链接中所解释的,大部分都是如此。一个非常典型的场景是一个摄像机或者一个沿恒定速度路径的网格运动......一个可能最终会使用从曲线计算出来的多段线并使用二进制搜索... – 2016-09-13 04:27:35

6

我创造了这个演示:

// x = a * (1-t)³ + b * 3 * (1-t)²t + c * 3 * (1-t)t² + d * t³ 
//------------------------------------------------------------ 
// x = a - 3at + 3at² - at³ 
//  + 3bt - 6bt² + 3bt³ 
//    + 3ct² - 3ct³ 
//     + dt³ 
//-------------------------------- 
// x = - at³ + 3bt³ - 3ct³ + dt³ 
//  + 3at² - 6bt² + 3ct² 
//  - 3at + 3bt 
//  + a 
//-------------------------------- 
// 0 = t³ (-a+3b-3c+d) + => A 
//  t² (3a-6b+3c) + => B 
//  t (-3a+3b)  + => c 
//  a - x    => D 
//-------------------------------- 

var A = d - 3*c + 3*b - a, 
    B = 3*c - 6*b + 3*a, 
    C = 3*b - 3*a, 
    D = a-x; 

// So we need to solve At³ + Bt² + Ct + D = 0 

Full example here

可以帮助别人。

+0

您的JSFiddle示例实际上并不显示y 。但我仍然尝试过。它工作转换为swift:https://gist.github.com/eonist/f5bb11533ee52ce24bad3ee47044239a THX! – eonist 2017-03-18 16:34:55

+0

@GitSyncApp它因为'cubic'函数。它返回3个答案,我只用第一个答案。见http://www.1728.org/cubic.htm – talkhabi 2017-03-24 11:34:16

+0

我知道。但那是我需要的。在一个三次贝塞尔图上找到x。我的观点是,你的小提琴在x轴上缩放。可能是一个浏览器的东西¯\ _(ツ)_ /¯真是棒极了。奖励! – eonist 2017-03-24 16:43:54