2008-10-13 12 views
17

Podczas pracy nad implementacją SVG, aby Internet Explorer opierał się na własnym formacie VML, pojawił się problem translacji łuku eliptycznego SVG do łuku eliptycznego VML.Jak obliczyć środek elipsy przez dwa punkty i rozmiary promienia

W VML łuk jest ze wzoru: dwóch stron dwóch punktach elipsy i długości promieni, W SVG łuk jest dana przez: dwie pary współrzędnych dla dwóch punktów na elipsy i wielkości elipsy polu brzegowym

Pytanie brzmi: Jak wyrazić kąty dwóch punktów na elipsie na dwie pary współrzędnych. Pośrednim pytaniem może być: Jak znaleźć środek elipsy za pomocą współrzędnych pary punktów na jej krzywej.

Aktualizacja: Niech ma warunek mówiący, że elipsa jest zwykle umieszczona (jej promieni równoległych liniowych osi współrzędnych systemu), a tym samym nie stosuje się obrót.

Aktualizacja: To pytanie nie jest związane z SVG: elipsa elementem, raczej do "a" eliptyczny komenda arc w SVG: element ścieżki (SVG Paths: The elliptical arc curve commands)

Odpowiedz

21

Więc rozwiązanie jest tutaj:

sparametryzowanego wzór elipsy:

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

put znanych współrzędnych Chodźmy dwóch punktów do niego:

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

Teraz mamy system równań z 4 zmiennymi: środek elipsy (x0/y0) i dwa kąty t1, t2

Powiedzmy równania Odejmowanie w celu pozbycia od centrum Współrzędne:

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

To może być przepisany (z tożsamościami formuł produktów-to-suma) jako:

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

Niech zastąpić niektóre z równań:

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

Następnie otrzymujemy prosty układ równań:

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

Dzielenie pierwszego równania przez drugi sporządza:

 
a1 = arctan(r1/r2) 

Dodanie tego wyniku do pierwszego równania otrzymujemy:

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

też proste (przy użyciu kompozycji trygonometrycznych i odwrotnych funkcji trygonometrycznych):

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

lub jeszcze bardziej prosta:

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

Teraz początkowy układ czterech równań można łatwo rozwiązać, a wszystkie kąty i współrzędne punktu środkowego zaćmienia można znaleźć.

+2

Aby być wyraźnym: t1 = a1 + a2, t2 = a1-a2, x0 = x1 - a * cos (t1), y0 = y1 - b * sin (t1) – AndrewS

+1

Czy mógłbyś podać przykład konwersji z łuku SVG na łuk VML? na przykład "A80 80 0 1 0 200 200" wytwarza duży łuk w kierunku zgodnym z ruchem wskazówek zegara o promieniu 80 do 200.200. W jaki sposób można to zreplikować w VML? Dzięki! – DeadPassive

+0

Również, czy y0 = y1 - b * sin (t1) jest poprawne, czy też powinien to być grzech (t2)? – DeadPassive

1

Elipsa nie może być określona przez zaledwie dwa punkty. Nawet okrąg (specjalna elipsa) jest zdefiniowany przez trzy punkty.

Nawet z trzema punktami, będziesz mieć nieskończoną elipsę przechodzącą przez te trzy punkty (pomyśl: obrót).

Należy zauważyć, że prostokąt ograniczający sugeruje środek elipsy i najprawdopodobniej zakłada, że ​​jego osie główna i pomocnicza są równoległe do osi x, y (lub y, x).

+0

Rzeczywiście, zapomniałem dodać notatkę na ten temat. Zrobię teraz. –

1

Pośrednie pytanie jest dość łatwe ... nie. Wykreślasz środek elipsy z obwiedni (środek pudełka jest środkiem elipsy, o ile elipsa jest wyśrodkowana w pudełku).

Na pierwsze pytanie przyjrzę się formie polarnej równania elipsy, która jest dostępna pod adresem Wikipedia. Trzeba by również opracować ekscentryczność elipsy.

Albo możesz brutalnie wymusić wartości z obwiedni ... Wypracuj, jeśli punkt leży na elipsie i dopasowuje kąt i iteruj przez każdy punkt obwiedni.

+0

Mówię o łukach eliptycznych w svg: path, a nie elipsie. Co do matematyki, zrobiłem trzy papiery rozwiązywania równań, zarówno w układzie współrzędnych biegunowych, jak i liniowych, wciąż bez powodzenia. –

+0

Powinien to być wciąż ten sam proces. Łuk eliptyczny to tylko część elipsy z 2 "punktami granicznymi" elipsy. Prostokąt ograniczający powinien być wystarczający, aby podać wszystkie informacje potrzebne do skonstruowania całej elipsy. Następnie wystarczy podać kąty w równaniu. – workmad3

+0

Ponownie, nie sformułowałem poprawnie pytania. Nie jest podany prostokąt, ale rozmiary promienia. –

4

Zamieszczony link do łuku eliptycznego zawiera link to elliptical arc implementation notes.

Tutaj znajdziesz równania dla conversion from endpoint to centre parameterisation.

Oto moja implementacja JavaScript tych równań, zaczerpnięta z an interactive demo of elliptical arc paths, przy użyciu Sylvester.js do wykonywania obliczeń macierzy i wektorów.

// 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); 
Powiązane problemy