2008-10-13 13 views
17

Während ich an der SVG-Implementierung für Internet Explorer arbeitete, um auf einem eigenen VML-Format zu basieren, kam ich zu einem Problem der Übersetzung eines elliptischen SVG-Bogens in einen elliptischen VML-Bogen. zwei Winkel für zwei Punkte auf Ellipsen und Längen von Radien, In SVG wird ein Lichtbogen gegeben durch:Wie man den Mittelpunkt einer Ellipse durch zwei Punkte und Radiusgrößen berechnet

In VML wird ein Lichtbogen gegeben durch zwei Paare von Koordinaten für zwei Punkte auf Ellipsen und Größen von Ellipsen Grenzbox

So ist die Frage: Wie man Winkel von zwei Punkten auf Ellipse zu zwei Paaren ihrer Koordinaten ausdrückt. Eine Zwischenfrage könnte lauten: Wie finde ich den Mittelpunkt einer Ellipse anhand der Koordinaten eines Punktpaars auf ihrer Kurve?

Update: Lassen Sie uns eine Vorbedingung haben, dass eine Ellipse normalerweise platziert wird (ihre Radien sind parallel zur linearen Koordinatensystemachse), daher wird keine Rotation angewendet.

aktualisieren: Ellipsenelement, sondern auf „a“ elliptischen Bogen Befehl in SVG: diese Frage nicht zu SVG zusammenhängt Pfadelement (SVG Paths: The elliptical arc curve commands)

Antwort

21

So ist die Lösung hier:

Die parametrisierte Formel einer Ellipse:

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

Sagen wir bekannte Koordinaten von zwei Punkten auf es:

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

Jetzt haben wir ein System von Gleichungen mit 4 Variablen: Ellipsenmitte (x0/y0) und zwei Winkel t1, t2

Lassen Sie uns subtrahieren Gleichungen in der Reihenfolge der Mitte, um loszuwerden, Koordinaten:

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

Diese neu beschrieben werden kann (mit Produkt-to-Summe Identitäten Formeln) als:

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

Lassen Sie uns einige der Gleichungen ersetzen:

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

Dann erhalten wir einfache Gleichungen System:

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

Dividing erste Gleichung durch zweite produziert:

 
a1 = arctan(r1/r2) 

Hinzufügen dieses Ergebnis zu der ersten Gleichung ergibt:

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

Oder einfach (Zusammensetzungen TRIG und inverse trigonometrische Funktionen):

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

oder noch einfacher:

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

Jetzt kann das anfängliche Vier-Gleichungen-System mit einfachen und allen Winkeln sowie Eclipse-Mittelkoordinaten gefunden werden.

+2

Um explizit zu sein: t1 = a1 + a2, t2 = a1-a2, x0 = x1 - a * cos (t1), y0 = y1 - b * sin (t1) – AndrewS

+1

Könnten Sie bitte ein Beispiel schreiben, das von einem SVG-Bogen in einen VML-Bogen konvertiert? z.B. "A80 80 0 1 0 200 200" erzeugt einen großen Bogen im Uhrzeigersinn mit einem Radius von 80 bis 200.200. Wie könnte dies in VML repliziert werden? Vielen Dank! – DeadPassive

+0

Ist y0 = y1 - b * sin (t1) korrekt, oder sollte das sin (t2) sein? – DeadPassive

1

Eine Ellipse definiert werden kann, nicht nur zwei Punkte. Selbst ein Kreis (eine spezielle verrohrte Ellipse) ist durch drei Punkte definiert.

Sogar mit drei Punkten würden Sie unendlich viele Ellipsen haben, die durch diese drei Punkte gehen (denken Sie: Rotation).

Beachten Sie, dass eine Begrenzungsbox ein Zentrum für die Ellipse vorschlägt und höchstwahrscheinlich davon ausgeht, dass ihre Haupt- und Nebenachsen parallel zu den Achsen x, y (oder y, x) verlaufen.

+0

Tatsächlich habe ich vergessen, eine Notiz dazu hinzuzufügen. Will jetzt machen. –

1

Die Zwischenfrage ist ziemlich einfach ... Sie nicht. Sie berechnen den Mittelpunkt einer Ellipse aus der Begrenzungsbox (dh der Mittelpunkt der Box ist der Mittelpunkt der Ellipse, solange die Ellipse in der Box zentriert ist).

Für Ihre erste Frage würde ich die polare Form der Ellipse-Gleichung betrachten, die auf Wikipedia verfügbar ist. Sie müssten auch die Exzentrizität der Ellipse berechnen.

Oder Sie könnten Brute zwingen die Werte aus der Bounding Box ... erarbeiten, wenn ein Punkt auf der Ellipse liegt und den Winkel übereinstimmt, und durchlaufen Sie jeden Punkt in der Bounding Box.

+0

Ich spreche über elliptische Bögen in Svg: Pfad, keine Ellipse. Was die Mathematik anbelangt, habe ich drei Artikel über die Lösung von Gleichungen in polaren und linearen Koordinatensystemen geschrieben, immer noch kein Glück. –

+0

Es sollte immer noch der gleiche Prozess sein. Ein elliptischer Bogen ist nur ein Abschnitt einer Ellipse mit 2 'Begrenzungspunkten' der Ellipse. Das Begrenzungsrechteck sollte ausreichen, um Ihnen alle Informationen zum Konstruieren der vollständigen Ellipse zu geben. Sie müssen dann nur die Winkel in die Gleichung einspeisen. – workmad3

+0

Auch hier habe ich die Frage nicht richtig formuliert. Es wird kein begrenzendes Rechteck angegeben, sondern die Größe des Radius. –

4

Die von Ihnen gepostete elliptische Bogenverbindung enthält eine link to elliptical arc implementation notes.

Dort finden Sie die Gleichungen für conversion from endpoint to centre parameterisation.

Hier ist meine JavaScript-Implementierung dieser Gleichungen, aus an interactive demo of elliptical arc paths, mit Sylvester.js, um die Matrix-und Vektorberechnungen durchzuführen.

// 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);