2008-10-13 110 views
17

在針對Internet Explorer的SVG實現基於自己的VML格式工作時,我遇到了將SVG橢圓弧轉換爲VML橢圓弧的問題。兩個角度對橢圓的兩個點和半徑的長度, 在SVG圓弧由下式給出:2對座標對橢圓的兩個點和橢圓邊界框如何通過兩點和半徑大小來計算橢圓的中心

的尺寸

在VML的圓弧由下式給出

所以,問題是:如何表達橢圓上兩點的角度到兩對座標。 一箇中間問題可能是:如何通過曲線上的一對點的座標找到橢圓的中心。

更新:讓我們前提說的橢圓被正常放置(其半徑是平行於線性座標系軸),從而沒有旋轉施加。

更新:這個問題是不相關的SVG:橢圓元素,而「一」橢圓弧在SVG命令:路徑元件(SVG Paths: The elliptical arc curve commands

回答

21

因此,解決辦法是在這裏:

橢圓的參數化公式:兩點

 
x = x0 + a * cos(t) 
y = y0 + b * sin(t) 

,讓我們把已知座標它:

 
x1 = x0 + a * cos(t1) 
x2 = x0 + a * cos(t2) 
y1 = y0 + b * sin(t1) 
y2 = y0 + b * sin(t2) 

現在我們有一個系統具有4個變量的方程:橢圓的中心(x0/y0)和兩個角度t1,t2

個讓我們爲了減方程式擺脫中心的座標:

 
x1 - x2 = a * (cos(t1) - cos(t2)) 
y1 - y2 = b * (sin(t1) - sin(t2)) 

這可以被改寫(以產品對和身份的公式)爲:

 
(x1 - x2)/(2 * a) = sin((t1 + t2)/2) * sin((t1 - t2)/2) 
(y2 - y1)/(2 * b) = cos((t1 + t2)/2) * sin((t1 - t2)/2) 

讓我們來替換一些公式:

 
r1: (x1 - x2)/(2 * a) 
r2: (y2 - y1)/(2 * b) 
a1: (t1 + t2)/2 
a2: (t1 - t2)/2 

然後我們得到簡單的公式系統:

 
r1 = sin(a1) * sin(a2) 
r2 = cos(a1) * sin(a2) 

通過第二分隔第一等式產生:

 
a1 = arctan(r1/r2) 

添加此結果提供給第一方程給出:

 
a2 = arcsin(r2/cos(arctan(r1/r2))) 

或者,簡單(使用三角函數的組合物和逆三角函數):

 
a2 = arcsin(r2/(1/sqrt(1 + (r1/r2)^2))) 

或甚至更簡單:

 
a2 = arcsin(sqrt(r1^2 + r2^2)) 

現在初始的四方程系統可以用簡單的方法和所有的角度來解決,以及可以找到eclipse的中心座標。

1

的橢圓不能由僅兩個點來限定。甚至一個圓圈(一個特殊的套管橢圓)由三點定義。

即使有三個點,你也會有無限的橢圓通過這三點(想想:旋轉)。

請注意,邊界框表示橢圓的中心,並且很可能假定其主軸和副軸與x,y(或y,x)軸平行。

+0

確實,我忘了在那裏添加一個註釋。現在會做。 – 2008-10-13 14:05:51

1

中間問題相當簡單......你不需要。您可以從邊界框中計算橢圓的中心(即,只要橢圓在框中居中,則框的中心就是橢圓的中心)。

對於你的第一個問題,我會看看橢圓公式的極座標形式,可在Wikipedia上找到。你也需要計算橢圓的偏心率。

或者你可以強制邊界框的值......如果一個點位於橢圓上並匹配角度,並遍歷邊界框中的每個點,

+0

我在說svg:路徑中的橢圓弧,而不是橢圓。 至於數學,我已經完成了三篇關於在極座標系和直線座標系中求解方程的論文,但仍然沒有運氣。 – 2008-10-13 14:10:29

+0

它應該仍然是相同的過程。橢圓弧僅僅是橢圓的2個「邊界點」的一部分。邊界矩形應該足以給你所有的信息來構造完整的橢圓。然後您只需要將角度輸入到等式中即可。 – workmad3 2008-10-13 14:16:08

4

您發佈的橢圓曲線圓弧鏈接包括一個link to elliptical arc implementation notes

在那裏,您可以找到conversion from endpoint to centre parameterisation的等式。

這裏是我的JavaScript實現這些方程,從an interactive demo of elliptical arc paths拍攝的,使用Sylvester.js執行矩陣和向量計算。

// Calculate the centre of the ellipse 
// Based on http://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter 
var x1 = 150; // Starting x-point of the arc 
var y1 = 150; // Starting y-point of the arc 
var x2 = 400; // End x-point of the arc 
var y2 = 300; // End y-point of the arc 
var fA = 1; // Large arc flag 
var fS = 1; // Sweep flag 
var rx = 100; // Horizontal radius of ellipse 
var ry = 50; // Vertical radius of ellipse 
var phi = 0; // Angle between co-ord system and ellipse x-axes 

var Cx, Cy; 

// Step 1: Compute (x1′, y1′) 
var M = $M([ 
       [ Math.cos(phi), Math.sin(phi)], 
       [-Math.sin(phi), Math.cos(phi)] 
      ]); 
var V = $V([ (x1-x2)/2, (y1-y2)/2 ]); 
var P = M.multiply(V); 

var x1p = P.e(1); // x1 prime 
var y1p = P.e(2); // y1 prime 


// Ensure radii are large enough 
// Based on http://www.w3.org/TR/SVG/implnote.html#ArcOutOfRangeParameters 
// Step (a): Ensure radii are non-zero 
// Step (b): Ensure radii are positive 
rx = Math.abs(rx); 
ry = Math.abs(ry); 
// Step (c): Ensure radii are large enough 
var lambda = ((x1p * x1p)/(rx * rx)) + ((y1p * y1p)/(ry * ry)); 
if(lambda > 1) 
{ 
    rx = Math.sqrt(lambda) * rx; 
    ry = Math.sqrt(lambda) * ry; 
} 


// Step 2: Compute (cx′, cy′) 
var sign = (fA == fS)? -1 : 1; 
// Bit of a hack, as presumably rounding errors were making his negative inside the square root! 
if((((rx*rx*ry*ry) - (rx*rx*y1p*y1p) - (ry*ry*x1p*x1p))/((rx*rx*y1p*y1p) + (ry*ry*x1p*x1p))) < 1e-7) 
    var co = 0; 
else 
    var co = sign * Math.sqrt(((rx*rx*ry*ry) - (rx*rx*y1p*y1p) - (ry*ry*x1p*x1p))/((rx*rx*y1p*y1p) + (ry*ry*x1p*x1p))); 
var V = $V([rx*y1p/ry, -ry*x1p/rx]); 
var Cp = V.multiply(co); 

// Step 3: Compute (cx, cy) from (cx′, cy′) 
var M = $M([ 
       [ Math.cos(phi), -Math.sin(phi)], 
       [ Math.sin(phi), Math.cos(phi)] 
      ]); 
var V = $V([ (x1+x2)/2, (y1+y2)/2 ]); 
var C = M.multiply(Cp).add(V); 

Cx = C.e(1); 
Cy = C.e(2);