2011-11-22 79 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