我想計算二次曲線上的點。將其與HTML5的canvas元素一起使用。二次Bézier曲線:計算點
當我在JavaScript中使用quadraticCurveTo()
函數時,我有一個源點,一個目標點和一個控制點。
我該如何計算創建的二次曲線上的一個點,比如說t=0.5
只有「知道這三點?
我想計算二次曲線上的點。將其與HTML5的canvas元素一起使用。二次Bézier曲線:計算點
當我在JavaScript中使用quadraticCurveTo()
函數時,我有一個源點,一個目標點和一個控制點。
我該如何計算創建的二次曲線上的一個點,比如說t=0.5
只有「知道這三點?
使用二次貝塞爾公式,發現,例如,維基百科頁面Bézier Curves上:
在僞代碼,這是
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
如果有人需要立方形式:
//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是正確的停在立方體)。
無論如何,曲線的長度真的很難測量。在t = 0.5時,如果你假設隨機控制點位於中心,那麼平均來說,但是,請注意它與大多數不同速度的曲線有相同的問題。尋找中間點通常需要測量曲線的一部分並用二進制搜索找到中心位。它不是完全超級通常需要的。但是,值得了解的是,如果你發現所有的點數都是t = .1的增量,那麼它們的長度就不相等。 - 儘管這與曲線的性質無關,但與問題無關。 – Tatarize 2016-09-12 23:04:25
@Tatarize:正如所提供的鏈接中所解釋的,大部分都是如此。一個非常典型的場景是一個攝像機或者一個沿恆定速度路徑的網格運動......一個可能最終會使用從曲線計算出來的多段線並使用二進制搜索... – 2016-09-13 04:27:35
我創造了這個演示:
// 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
可以幫助別人。
您的JSFiddle示例實際上並不顯示y 。但我仍然嘗試過。它工作轉換爲swift:https://gist.github.com/eonist/f5bb11533ee52ce24bad3ee47044239a THX! – eonist 2017-03-18 16:34:55
@GitSyncApp它因爲'cubic'函數。它返回3個答案,我只用第一個答案。見http://www.1728.org/cubic.htm – talkhabi 2017-03-24 11:34:16
我知道。但那是我需要的。在一個三次貝塞爾圖上找到x。我的觀點是,你的小提琴在x軸上縮放。可能是一個瀏覽器的東西¯\ _(ツ)_ /¯真是棒極了。獎勵! – eonist 2017-03-24 16:43:54
是的,太棒了。除此之外,我什麼都不懂...... – 2011-04-12 12:06:30
在這種情況下乘以(加)點意味着你乘(加)每個組件。也就是說,'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
因此,這意味着: 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