XHTML SVG CSS PHP

Dr. O. Hoffmann

SVG- und PHP-Labor

Künstler haben gewöhnlich die Meinung von uns,
die wir von ihren Werken haben.
Georg Christoph Lichtenberg

Skalierbare Vektorgraphik: Kurven und Pfade

Bei der Durchsicht der Grundformen haben wir ja schon gesehen, daß SVG auch echte Kurven in Form von Ellipsen oder Kreisen zur Verfügung stellt und auch Linienzüge und Polygone. Dieses Konzept läßt sich noch verallgemeinern. Mittels des path-Elementes lassen sich auch Kurven beschreiben, die Krümmungen aufweisen. Dazu werden in SVG elliptische Bögen oder Bézierkurven verwendet. Es sind Kurvenstücke erster, zweiter und dritter Ordnung möglich und miteinander mischbar. Das erste ist wieder ein Linienzug, das zweite ein Polynom zweiten Grades, realisiert als quadratische Bézierkurve und das dritte ein Polynom dritten Grades, realisiert als kubische Bézierkurve. Bézierkurven sollen zum Zeichnen besonders anschaulich sein, sind aber erstmal nichts anderes als eine besondere Darstellung von Polynomen.
Solche Kurven sind nicht nur zum direkten Hinmalen nützlich, sie können zum Beispiel auch als Trajektorien für Animationen genutzt werden - also als Weg, den ein animiertes Objekt nimmt.
Das Attribut d nimmt die Pfaddaten auf, wie in den Beispielen im einzelnen beschrieben. Das generelle Konzept der Pfadkodierung besteht aus einbuchstabigen Kommandos gefolgt von Koordinaten, entweder gefolgt von weiteren Kommandos oder weiteren Koordinaten. Verschiedene Pfadtypen können beliebig kombiniert werden und ein Pfad kann aus verschiedenen Unterpfaden bestehen, die nicht miteinander verbunden sind. Pfade können gefüllt sein oder nicht gefüllt, je nach den Angaben des Autors und gegebenenfalls der Form des Pfades.

Mit dem Attribut pathLength kann der Autor seine Schätzung für die Länge des Pfades angeben. Kommt das Darstellungsprogramm zu einem anderen Ergebnis, soll es diese Angabe zur Korrektur beziehungsweise zur Skalierung verwenden. Dies kann etwa passieren, wenn mittels stroke-dasharray die Linien gemustert wird. Diese Angaben sollten anhand der pathLength-Angaben skaliert werden. Somit kann der Autor nicht nur bestimmen, wie das Muster anfängt, sondern auch, wie es aufhört. Die Berechnung von Pfadlängen erfordert besonders bei gekrümmten Kurven oft eine numerische Integration und ist daher nicht für jeden trivial, wenn auch generell kein wirklich schwieriges Problem. Siehe dazu den Abschnitt 'Weglänge'.

Intuitives und Dekoratives

Generell gilt, daß große Buchstaben als Kommanods für absolute Koordinaten verwendet werden, kleine als Angaben für relative Koordinaten. Ein Unterpfad beginnt jeweils mit dem Kommando M beziehungsweise m. Falls das erste Pfadkommando m ist, werden das direkt folgende Koordinatenpaar allerdings trotzdem absolut ausgewertet oder eben relativ zum Nullpunkt, was auf das Gleiche hinausläuft. Auf M beziehungsweise m können mehrere Koordinatenpaare folgen, das entspricht dann einem Linienzug.
Z beziehungsweise z schließen einen Unterpfad wieder mit einer Linie zurück zu den Koordinaten des letzten Kommandos M beziehungsweise m.

Zum Einstieg erstmal ein paar einfache Pfade mit einigen bereits erwähnten Kommandos und einigen weiteren, einschließlich auch solcher, die auf dieser Seite nicht weiter erwähnt werden, wie horizontale und vertikale Linien: Einfache Kurven (Quelltext zu einfache Kurven)

Gucken wir uns noch kurz ein Beispiel mit offenen Linienzügen an, um die Füllregeln zu verstehen. Gefüllt wird, als wäre der Linienzug geschlossen, einzelne gerade Linien haben keine Füllung: offene Linienzüge (Quelltext zu offene Linienzüge)

Ich denke, die Konstruktion eines einfachen Linienzuges sollte so weit klar sein. Wir greifen erstmal auf unsere Polygonmalerei zurück und ersetzen dort die Linien durch quadratische Bézierkurven und gucken mal, was wir damit erreichen können: Dekorative Kurven
(Quelltext zu dekorativen Kurven)

Die Breite der Kurven stroke-width ist immer die gleiche. Braucht man etwas, was wie ein Strich mit veränderlicher Dicke aussieht, berechnet man das entweder als gefüllte Kurve statt einer einfachen Kurve ohne Füllung oder man versucht, die Kurve geschickt mit einer anderen zu maskieren:
Dekorative Pfade
(Quelltext zu dekorativen Pfade)
Dekorative Pfade 2
(Quelltext zu dekorativen Pfade 2)
Der blaue Pfad ist der maskierte, der hellrote der unmaskierte, der hellgelbe die Maske, letztlich täte man natürlich nur den blauen Pfad wirklich malen. Das Prinzip kann natürlich variert werden, indem statt der hier verwendeten einfachen Schnittmenge mit mehreren Pfaden in der Maske etwa der genaue linke und rechte Rand festgelegt wird. Es kann sich dabei als vorteilhaft erweisen, in der Erstellungsphase die Maske schwarz-weiß sichtbar zu malen, sofern man sie nicht ohnehin gleich mit PHP berechnet.

Ein anderer Trick besteht nun darin, den Pfad innerhalb des defs-Elementes zu definieren und widerholt mit use, aber dann mit unterschiedlichen Farben, Strichbreiten und Strichmustern zu nutzen:
Dekorative Pfade mit Strichmuster
(Quelltext zu dekorativen Pfade mit Strichmuster)
Dekorative Pfade mit Strichmuster 2
(Quelltext zu dekorativen Pfade mit Strichmuster 2)

Lineare und quadratische Bézierkurven

Eine lineare Bézierkurve ist einfach ein Streckenzug, also die Kommandos L beziehunsweise l. Die Parametrisierung ist trivial. Seien Anfangs- und Endpunkt P(0) und P(1), sei der Laufparameter u aus [0,1], so gilt für eine lineare Bézierkurve c(u)

(L1) c(u) = (1 - u)P(0) + uP(1)
mit der Ableitung
(L2) dc(u)/du = P(1) - P(0)
und der zweiten Ableitung
(L3) d2c(u)/du2 = 0

Quadratische Bézierkurven werden mit den Kommandos Q beziehungsweise q angegeben, die Koordinatenangaben bestehen jeweils aus einem Kontrollpunkt und einem Endpunkt. Bei mehreren Kurvensegmenten kann die Kurve ohne Kontrollpunkte automatisch mit den Kommandos T beziehungsweise t fortgesetzt werden. Um quadratische Bézierkurven intuitiv besser in den Griff zu bekommen, schauen wir uns erst einmal dieses Beispiel an - es zeigt sehr gut, daß die Verbindungslinie von einem Endpunkt zum Kontrollpunkt jeweils der Tangente an die Kurve am jeweiligen Endpunkt entspricht: Verhalten einer quadratischen Bézierkurve (Quelltext zum Verhalten einer quadratischen Bézierkurve). Nur zum besseren Verständnis sind Endpunkte, Kontrollpunkt und Verbindungslinien zusätzlich eingezeichnet, blau ist der Kontrollpunkt, rot und grün die Endpunkte.

Einzelne solcher Kurvenstücke können aneinander gehängt werden. Interessiert ist man dabei oft an stetig differenzierbaren Kurven, für den Laien: Kurven ohne Knicke. SVG kann diese nun selbst berechnen, wenn man Punkte auf der Kurve angibt und einen Kontrollpunkt. Sind die ersten beiden Punkte der Kurve eng benachbart, kann man sogar auf den Kontrollpunkt verzichten. Stetig diffenzierbare quadratische Bézierkurve (Quelltext zur stetig diffenzierbaren quadratischen Bézierkurve). Nur zum besseren Verständnis sind Endpunkte und Kontrollpunkt zusätzlich eingezeichnet, die Endpunkte des Kurvenstückes mit Kontrollpunkt sind gün markiert, der Kontrollpunkt mit einem magenta Kreis gekennzeichnet, Punkte mit automatisch berechneten Kontrollpunkten orange. Der geneigte Leser möge verifizieren, daß lediglich die Verschiebung eines Punktes, etwa des Kontrollpunktes den gesamten weiteren Kuvenverlauf modifiziert.

Nun haben wir gelernt, wie man mit Kurven dekorative Bilder erstellen kann und daß man auch irgendwie 'hübsche' Kurven ohne Knicke damit hinbekommen kann. Aber wir wollen natürlich mehr, wir wollen bestimmte Kurven ausrechnen und dann ordentlich darstellen. Dazu brauchen wir die funktionale Darstellung eines solchen Kurvensegmentes. Seien Anfangs- und Endpunkt P(0) und P(1) und der Kontrollpunkt P(k), P(i) sind Vektoren und sei der Laufparameter u aus [0,1], so gilt für eine quadratische Bézierkurve c(u) (man achte auf die Symmetrie bezüglich einer Vertauschung von P(0) und P(1) und von 1-u und u):

(1) c(u) = (1 - u)2P(0) + 2u(1-u)P(k) + u2P(1)

Und für die Ableitungen nach u gilt folglich:

(2) dc(u)/du = 2(u-1) P(0) + 2(1 -2u) P(k) + 2u P(1)
oder in der Parametrisierung einer linearen Bézierkurve
(2a) dc(u)/du = (1-u) (2P(k) - 2P(0)) + u (2P(1) - 2P(k))
mit der zweiten Ableitung
(2b) d2c(u)/du2 = 2P(0) + 2P(1) - 4P(k)

Mit P(0) = c(u=0) (Anfangspunkt) und P(1) = c(u=1) Endpunkt ist

(3a) dc(0)/du = -2 P(0) + 2P(k)
(3b) dc(1)/du = -2 P(k) + 2P(1)

(4) P(k) = P(1) - dc(1)/du/2 = P(0) + dc(0)/du/2

Sei nun f(v) mit den Komponenten fx, fy etc eine näherungsweise darzustellende Funktion mit f(v) = f(u + i) auf einem Intervall u aus [0,1] und i eine entsprechende ganze Zahl als Konstante, so soll c(u,i) näherungsweise f(v) zwischen v = i und v = i+1 darstellen. An die Näherung können nun Forderungen gestellt werden, etwa daß die jeweiligen Randpunkte der c-Kurve
mit der f-Kurve übereinstimmen sollen, als Formel:
(5a) f(i) = c(0,i) und
(5b) f(i+1) = c(1,i) = c(0, i+1) = f(i+1)

Da wir drei Parameter haben, können wir noch eine Bedingung stellen. Ist f(v) eine stetig differenzierbare Kurve, so möchten wir auch für die c(u,i) eine stetig differenzierbare Kurve haben. Wegen (4) können wir die Ableitung aber im Allgemeinen nur für ein i festlegen. Um die Ableitung für jedes i festlegen zu können, täte man einen weiteren Kontrollparameter brauchen, dieser wird uns aber erst in den noch zu besprechenden kubischen Bézierkurven zur Verfügung stehen. Jetzt wollen wir erst mal gucken, wie weit wir mit den quadratischen kommen.

Weil nur die Richtung vom Anfangspunkt zum Kontrollpunkt und die vom Kontrollpunkt zum Endpunkt mit den Richtungen der Ableitungen übereinstimmen müssen, ist es in einigen Fällen möglich, den Kontrollpunkt als den Schnittpunkt der Strahlen zu wählen, die sich ergeben, wenn der eine Strahl vom Anfangspunkt aus in Richtung der dortigen Ableitung läft und der andere Strahl vom Endpunkt entgegen der Ableitung dort. Da sich die beiden Strahlen oft nicht schneiden, ist das aber keine allgemeine Lösung des Problems:
Versuch, zwei Kurvensegmente mit einem quadratischen Kurvensegment zu verbinden (Quelltext zum Versuch).
Wenn die Kurven für eine Verbindung geeignet sind, wird die neue Kurve in grün dargestellt, sonst in rot. Die Kurven werden bei jedem Aufruf zufällig ausgewählt. Die Richtungen der zufällig vorgegebenen Kurvensegmente sind blau dargestellt. Mit Kreisen wird jeweils dargestellt, wo der Kontrollpunkt ist (rosa) oder wo die jeweiligen Pfade beginnen.

Zu diesem Zwecke wollen wir eine sinus-Kurve näherungsweise darstellen. Dargestellt werden soll auf einem Intervall v aus [0,n] fx(v) = v*900/n und fy(v) = 400* sin(2 πv/n)) offenbar dann mit n Bézierkurven. Dazu malen wir das erstmal punktweise für v=i: 0 bis n hin und konstruieren den Pfad, das Ergebnis ist natürlich eher enttäuschend, da wirkt sogar der ebenfalls in grau angegebene Linienzug überzeugender:
Schlechtes Beispiel für Bézierkurven (Quelltext zum schlechten Beispiel) - das Problem liegt darin, daß wir pro Kurve einen Parameter zu wenig haben, um die Bedingung der stetigen Differenzierbarkeit mit der Übereinstimmung der Ableitung an den Endpunkten zu vereinbaren - die Kurve ist zwar stetig differenzierbar, hat aber keine Ähnlichkeit mit einem Sinus.

Bevor wir die quadratischen Kurven ganz aufgeben, versuchen wir zumindest noch die geraden Linien zu übertreffen, indem wir fordern, daß der Punkt in der Mitte des Intervall exakt auf der Kurve liegt, also f(i+0.5) = c(0.5,i) :

(6) 0.25 P(0) + 0.5 P(k) + 0.25 P(1) = f(i + 0.5)

da P(0) und P(1) bereits festgelegt sind, ergibt sich somit

(7) P(k) = 2 f(i + 0.5) - 0.5 (f(i) + f(i+1))

Beispiel-Kurve mit quadratischer Bézierkurve genähert (Quelltext zur Beispiel-Kurve mit quadratischer Bézierkurve genähert) - das jedenfalls klappt bereits deutlich besser, schon mit wenigen Stützstellen erhält man eine gute Näherung - zum Vergleich als dünne gelbe Kurve eine Näherung mit vielen Stützstellen. Daß das so gut klappt, liegt natürlich auch daran, daß eine Parabel bereits eine gute Näherung für einen Bereich um ein Extremum herum ist. Bei anderen Kurven wird man mehr Stützstellen einplanen müssen, wieviele hängt von der Struktur der Kurve ab - in Bereichen, wo sich die Steigung nur wenig ändert, braucht man auch nur wenig Punkte, um die Kurve anzunähern. Der Betrag der zweiten Ableitung könnte also genutzt werden, um die Anzahl und die Abstände von Stützstellen automatisch zu bestimmen.

Kubische Bézierkurven

Kubische Bézierkurven werden mit den Kommandos C, beziehungsweise c realisiert, denen Angaben zu zwei Kontrollpunkten und einem Endpunkt folgen. Bei einer Fortsetzung mit den Kommanods S, beziehungsweise s entfällt ein Kontrollpunkt, der dann automatisch berechnet wird. Zunächst versuchen wir wieder, uns den Kurven intuitiv zu nähern. Kubische Bézierkurven haben für jeden Randpunkt einen Kontrollpunkt, mit dem wieder die Steigung bei den Randpunkten festgelegt wird. Beide Kontrollpunkte zusammen haben zudem Einfluß auf die Krümmung der Kurve: Beispiele für kubische Bézierkurven (Quelltext zu den Beispielen für kubische Bézierkurven). Nur zum besseren Verständnis sind Endpunkte, Kontrollpunkt und Verbindungslinien zusätzlich eingezeichnet, blau sind die Kontrollpunkte, rot und grün die Endpunkte. Für die stetig differenzierbare Fortsetzung sind die Endpunkte orange und die Kontrollpunkte cyan.

Entsprechend der quadratischen Kurve wollen wir uns nun die Darstellung einer kubischen Kurve angucken, die Notation ist entsprechend gewählt, wir numerieren nur die Punkte einfach durch: P(0), P(1), P(2), P(3). P(0) und P(3) sind die Endpunkte für u=0 beziehungsweise u=1, P(1) und P(2) sind die Kontrollpunkte:

(8) c(u) = (1 - u)3P(0) + 3u(1-u)2P(1) + 3u2(1-u)P(2) + u3P(3)

und die Ableitungen:

(9) dc(u)/du = -3(1 - u)2P(0) + 3(1-4u +3u2)P(1) + 3(2u-3u2)P(2) + 3u2P(3)
oder in der Parametrisierung einer quadratischen Bézierkurve
(9a) dc(u)/du = (1 - u)2 (3P(1) - 3P(0)) + 2u(1-u) (3P(2) - 3P(1)) + u2(3P(3) - 3P(2))
und der zweiten Ableitung
(9b) d2c(u)/du2 = (1-u) (6P(0) - 12P(1) + 6P(2)) + u (6P(3) -12P(2)) + 6P(1))

Speziell gilt an den Endpunkten:

(10a) dc(0)/du = -3P(0) + 3P(1)
(10b) dc(1)/du = -3P(2) + 3P(3)

oder entsprechend

(11a) P(1) = P(0) + dc(0)/du/3
(11b) P(2) = P(3) - dc(1)/du/3

Braucht man eine stetig differenzierbare Kurve, also eine ohne Knicke, kann man damit leicht die Anschlußbedingung finden.
Haben wir die benachbarten Punkte Pn(i) und Pn+1(i), so liegt ein stetig differenzierbarer Anschluß vor, wenn zum einen dcn(3)/du nicht gerade null ist (also Pn(3) = Pn(2), siehe auch den Abschnitt Kurvenverlauf, Richtung, Tangente) und zum anderen gilt: Pn(3) = Pn+1(0) und Pn+1(1) = (1+g) Pn(3) - g Pn(2) mit einer positiven Zahl g. g bestimmt die Krümmung der Kurve am jeweiligen Punkt.
Wird der Kontrollpunkt vor und nach einem Punkt mit gleichem g bestimmt, so bleibt die Krümmung gleich, auch die Ableitung der Kurve hat dann keine Knicke.
Entsprechend ist statt Pn(2) bei Ableitung 0 Pn(1) zu nehmen, beziehungsweise wenn dies auch identisch mit Pn(3) ist, dann Pn(0).
Ist auch der Anfangspunkt mit dem Endpunkt identisch, gibt es keine stetig differenzierbare Fortsetzung.
Ist g gleich 1, so stimmt die Kurve mit der überein, die aus dem Kommando S folgt.
Bei relativen Kommandos ist natürlich Pn(3) jeweils wieder abzuziehen.
Ist hingegen g eine negative Zahl, läuft der Pfad am Verbindungspunkt offenbar in sich zurück.

Beispiel Kurve mit einer Schar stetig differenzierbarer Fortsetzungen (Quelltext zur Kurve mit stetig differenzierbarer Fortsetzung). Ins Bild klickern, um Details am Verbindungspunkt zu sehen, der rot gekennzeichnet ist.
Gestalterische Anwendung des Dargelegten.

Haben wir die Punkte P0(i) bis Pn(i), so ist die Kurve dann offenbar geschlossen, wenn gilt:
(11c) P0(0) = Pn(3)
Und wenn an der Stelle die Ableitung gleich sein soll, ergibt sich aus (11a) und (11b):
(11d) Pn(2) = (1+g) P0(0) - g P0(1)
Entsprechende Ausnahmen, wenn die Ableitung 0 ist, wie oben beschrieben.
Beispiele für stetig differenzierbare geschlossene Kurven:
M 100 500 C 100,100 900,100 900,500 S 100,900 100,500 z
und
M 100 500 C 300,50 800,200 900,600 S -100,950 100,500 z
Oder allgemein mit den Punkten a, b, c, d und e:
(11e) M a C b c d ... S e ((1+g)a - g b) a Z

Als Beispiel dazu: Stetig differenzierbare geschlossene Kurve (Quelltext zur stetig differenzierbare geschlossene Kurve).
Es werden mindestens zwei Punkte und zwei Kontrollpunkte benötigt, um eine geschlossene Kurve zu erhalten. Die Anzahl zusätzlicher Punkte kann mit dem GET-Parameter anz (zwischen 0 und 100) angegeben werden.

Entsprechend obigem Beispiel mit dem Sinus mit einer quadratischen Bézierkurve hier dann der Sinus mit einer kubishen - das sieht dann noch besser aus als mit dem quadratischen und festgelegtem mittlerem Kurvenpunkt: Beispiel-Kurve mit kubischer Bézierkurve genähert (Quelltext zur genäherter kubischer Bézierkurve).

Werden kleine Buchstaben benutzt, wird also relativ zum vorherigen Endpunkt positioniert. So ist es etwa möglich, die Kurve einfach nur über die erste M-Anweisung zu verschieben: Beispiel Kurve mit kubischer Bézierkurve genähert, gelbe und schwarze Kurve mit relativen Angaben und mit M ? ? verschoben. (Quelltext zu den verschobenen Kurven) So lassen sich auch relativ einfach Kurven darstellen, die etwa ein Integral einer als Formel darstellbaren Funktion sind, etwa die Fehlerfunktion (bis auf Vorfaktoren das Integral über f(x)=exp(x2)). Da die Endpunkte auch gebraucht werden, wird man natürlich trotzdem numerisch Integrieren müssen.

Als kleiner Spaß am Rande kommt hier noch eine kleine Animation, der Tanz des Schlangenmannes.

Als kleine Anwendung eines gemischten Dokumentes mit XHTML und SVG ein Vorschlag für ein neues SVG-Element: Polar (deutsch), Polar (englisch).

Konversion zwischen Bézierkurven

Werden oben angeführte Parametrisierungen von linearen und quadratischen Kurven verwendet, kann auch eine Konversion in eine kubische Kurve vorgenommen werden. Das ist manchmal ganz praktisch, etwa weil es bei Animationen in SVG 1.1 nicht zulässig ist, Kommandos zwischen zwei Animationsschritten zu wechseln, auch sonst kann das zu Vereinfachungen in der Handhabung führen, wenn nur kubische Bézierkurven vorliegen. Berechnet werden kann die Konversion durch Vergleich der Ableitungen, die Forderung ist dann also, daß obige Parametrisierungen vorgenommen werden, Anfangs- und Endpunkt gleich bleiben und ebenfalls die Ableitung dort. Dann ergibt sich:

Konversion L nach Q oder C mit i, f Anfangs- und Endpunkt der Kurve:
(K1) M i L f = M i Q (i+f)/2, f = M i C (2i+f)/3, (i+2f)/3 f

Konversion Q nach C (p Kontrollpunkt):
(K2) M i Q p, f = M i C (2p+i)/3, (2p+f)/3, f

Kurvenverlauf, Richtung, Tangente, Normale

Manchmal ist es nützlich, den Kurvenverlauf, die Richtung oder die Tangente an eine Kurve zu kennen. Teilweise wird dies in SVG mit den Eigenschaften marker-start, marker-end, marker-mid oder marker zusammen mit dem Element marker bereitgestellt. Aus verschiedenen Gründen, etwa weil diese in SVG tiny nicht verfügbar sind oder weil ausgerichtete Markierungen an Positionen angebracht werden sollen, die SVG für marker nicht vorsieht, kann es sinnvoll sein, diese selbst zu berechnen. Zumeist ist die Richtung oder die Tangente an die Kurve direkt mit oben angegebenen Ableitungen dc(u)/du korreliert.

Die Ableitung gibt die Richtung an, in der der Pfad gemalt wird. Bei einer linearen Bézierkurve:
(RL1) M c(u) l g(P(1) - P(0))
mit einer Zahl g größer als 0, sofern die beiden Punkte nicht gleich sind, dann gibt es keine Richtung.

Bei einer quadratischen Bézierkurve:
(RQ1) M c(u) l g dc(u)/du
mit einer Zahl g größer als 0, sofern die Ableitung nicht 0 ist. Das kann auftreten, wenn der Kontrollpunkt mit dem Anfangs- oder Endpunkt übereinstimmt. Sind alle drei Punkte gleich, gibt es keine Richtung. Stimmt der Kontrollpunkt nur mit entweder Anfangs- oder Endpunkt überein, so ist die Richtung offenbar:
(RQ2) M c(u) l g(P(1) - P(0))

Bei einer kubischen Bézierkurve:
(RK1) M c(u) l g dc(u)/du
mit einer Zahl g größer als 0, sofern die Ableitung nicht 0 ist. Das kann auftreten, wenn der erste Kontrollpunkt mit dem Anfangspunkt oder der zweite mit dem Endpunkt übereinstimmt. Sind alle vier Punkte gleich, gibt es keine Richtung.
Stimmt der erste Kontrollpunkt mit dem Anfangspunkt und der zweite mit dem Endpunkt überein, so ist Kurve eine Gerade und die Richtung ist gegeben durch:
(RK2) M c(u) l g(P(3) - P(0))
Stimmt der erste Kontrollpunkt mit dem Anfangspunkt überein, so ist die Richtung am Anfangspunkt:
(RK3) M P(0) l g(P(2) - P(0)),
ansonsten gilt (RK1).
Stimmt der zweite Kontrollpunkt mit dem Endpunkt überein, so ist die Richtung am Endpunkt:
(RK4) M P(3) l g(P(3) - P(1)), ansonsten gilt (RK1).

Zufällige kubische Bézierkurve mit Tangenten (Quelltext zu den zufälligen kubischen Bézierkurven mit Tangenten ).
Beispiele für kubische Bézierkurven mit übereinstimmenden Kontroll- und Endpunkten (Quelltext zu den Beispielen für kubische Bézierkurven mit übereinstimmenden Kontroll- und Endpunkten).

Liegen Punkte und Kontrollpunkte alle und in nicht monotoner Reihenfolge auf einer Linie, so kann es Umkehrpunkte in der Kurve geben, ab der diese also in sich zurückläuft. An solchen Punkten gibt es keine Tangente oder eine Richtung des Kurvenverlaufs. Etwa liegt bei
M P(0) C P(1) P(1) P(0) an der Stelle u=0.5 oder bei 0.25 P(0) + 0.75 P(1)
ein solcher Punkt vor.
Entsprechend mit
M P(0) Q P(k) P(0) an der Stelle u=0.5 oder bei 0.5 P(0) + 0.5 P(k).

Die Normale oder der Normalenvektor N steht einfach senkrecht auf der Tangente T. Hat man die Tangente, erhält man in der Ebene die Normale einfach durch Koordinatentausch der Tangente: Die x-Koordinate der Normale ist die y-Koordinate der Tangente und die y-Koordinate der Normale ist die mit -1 multiplizierte x-Koordinate der Tangente. Oder als Transformationsmatrix M:

01
-10

N = M * T

Ableitung, Darstellung von Funktionen

Nun möchte oder kann man die Ableitung einer beliebigen Funktion f(v) vielleicht nicht immer selbst angeben, sondern ebenfalls näherungsweise berechnen lassen, numerische Ableitung wird das dann genannt. Dazu nehmen wir einen kleinen v-Schritt h an, welcher genau genommen je nach Problem optimiert gewählt werden muß. Nehmen wir zum Beispiel h = 0.001. Das ist zumindest klein gegenüber dem Intervall der Bézierkurve u aus [0, 1]. Es gilt dann näherungsweise bei ausreichend glatten Kurven der Differenzenquotient:

(12) df(v)/dv = (f(v+h)-f(v-h))/(2h)

Oder eine Näherung über mehrere Testpunkte

(13) df(v)/dv = (f(v-2h) - 8f(v-h) + 8f(v+h) - f(v+2h))/(12h)

Damit also einige Beispiele:
Funktion mit kubischer Bézierkurve genähert (Quelltext zur Funktion mit kubischer Bézierkurve genähert),
Gaußkurven (Quelltext zu Gaußkurven ),
Archimedische Spirale mit kubischer Bézierkurve genähert (Quelltext zur Archimedischen Spirale mit kubischer Bézierkurve genähert),
Hypozykloide mit kubischer Bézierkurve genähert (Quelltext zur Hypozykloide mit kubischer Bézierkurve genähert).
Dekorativ gefüllte zufällige Lissajous-Figur mit kubischer Bézierkurve genähert (Quelltext zur zufälligen Lissajous-Figur mit kubischer Bézierkurve genähert).
Das gleiche Skript zur Darstellung einer Lissajous-Figur mit kubischer Bézierkurve genähert, mittels der GET-Parameter anz, kx und ky kann angegeben werden, welche Figur dargestellt wird. anz ist die Anzahl der Kontrollpunkte (zwischen 20 und 250 zu wählen, sonst ist sie 60). Bei niedrigen Werten für kx und ky reichen auch wenige Kontrollpunkte, bei hohen Werten muß auch anz hoch sein, sonst kommt es zu sichtbaren Fehlern bei der Darstellung. Mit 2π multipliziert ergeben kx und ky die Kreisfrequenzen (jeweils zwischen 1 und 30 zu wählen, sonst zufällig zwischen 1 und 5). Bei nicht teilerfremden kx und ky sollte wirklich durch den ganzzahligen Teiler dividiert werden, sonst wird natürlich das Verhältnis von Dateigröße zu Genauigkeit ungünstiger als notwendig. Die Phasen werden immer zufällig bestimmt, damit keine Langeweile aufkommt.

Interpolation

In der Praxis kommt es oft vor, daß die darzustellende Kurve nicht explizit als Formel bekannt ist, sondern nur als Datensatz von einzelnen Punkten, die auf der darzustellenden Kurve liegen. Hier soll davon ausgegangen werden, daß es sich um eine stetig differenzierbare Kurve (also ohne Knicke und Sprünge) handelt. Ist das nicht der Fall, kann die Kurve einfach in stetig differenzierbare Segmente unterteilt werden, für die das Verfahren dann jeweils getrennt durchzuführen ist. Wie kann nun die Kurve näherungsweise rekonstruiert werden, beziehungsweise wie werden die Werte der Kurve zwischen den gegebenen Punkten interpoliert? Die Näherung mit einer linearen Bézierkurve ist trivial - die Punkte werden miteinander verbunden, die resultierende Kurve ist in der Regel nicht stetig differenzierbar. Da in SVG kubische Bézierkurven verfügbar sind, werden solche Kurvensegmente zwischen den gegebenen Punkten die beste Näherung sein.

Einfach ist die Angelegenheit, wenn der Datensatz auch die Ableitungen der Punkte nach dem Laufparameter enthält. Das ist oft für das Programm kein Problem, welches den Datensatz erzeugt und sollte dann auf jeden Fall genutzt werden. Mit obigen angegebenen Formeln 8-11 zu kubischen Bézierkurven kann dann die Interpolation einfach vorgenommen werden.

Enthält der Datensatz nur die Datenpunkte und nicht die Ableitungen, so ist die Kurve aus diesen Daten zu rekonstruieren.
Genaugenommen gibt es für einen Datensatz von einigen Punkten natürlich überabzählbar viele Möglichkeiten, diese durch eine glatte Kurve miteinander zu verbinden.
Im engeren Sinne wird immer davon ausgegangen, daß die vorliegenden Punkte so dicht benachbart sind, daß die korrekte Funktion auf diesem Abschnitt nirgends eine große Krümmung aufweist, also nicht sehr stark von einer geraden Verbindung zweier aufeinanderfolgender Punkte abweicht.
Im Falle einer kubischen Kurve werden die Punkte als so dicht benachbart angenommen, daß eine solches kubisches Segment ausreicht, um zwischen den Punkten näherungsweise zu interpolieren.

Die bestmögliche Lösung bezieht alle Punkte des Datensatzes ein und ist gemeinhin unter dem Stichwort Spline-Interpolation zu finden. Dies führt zu einem inhomogenen linearen Gleichungssystem, welches auf eine Matrixdiagonalisierung zurückgeführt werden kann. Das ist numerisch alles handhabbar, ist aber doch etwas aufwendiger. Für graphische Zwecke reicht es in der Regel, nur die benachbarten Punkte zu verwenden, um die Ableitung an einem Punkt abzuschätzen, dabei können Formeln wie 12 oder 13 verwendet werden.

Wieviele benachbarte Punkte zur Schätzung der Ableitung verwendet werden, bestimmt, wie lokal das jeweilige Kurvensegment ist. Tragen nur benachbarte Punkte bei, hat eine Änderung von Punkten weiter weg offenbar keinen Einfluß auf einen Punkt. Mehr Punkte verbessern andererseits die Schätzung.
Was sinnvoll ist, hängt von der jeweiligen Anwendung ab.

Bei graphischen Anwendungen ist es häfig eher erwünscht, mit den Punkten frei Formen festzusetzen. Es besteht gar nicht die Absicht, eine bestimmte Kurve möglichst gut zu nähern, es besteht nur der Bedarf nach einer glatten Interpolation. Daher ist es dafür eher sinnvoll, nur die direkt benachbarten Punkte zu verwenden, also Formel 12, damit die lokale Kurvenform unabhängig von weiter weg befindlichen Punkten bleibt.
13 ist eher ein guter Kompromiß zwischen lokaler Stabilität, Aufwand und Qualität. Darauf beziehen sich folgende Beispiele.

Einfach ist die Situation bei geschlossenen Kurven, wo also notierter Anfang und Ende übereinstimmen - in solchen Fällen wird bei der Interpolation an den Enden einfach so vorgegangen, daß der Datensatz periodisch fortgesetzt wird, wenn weitere Daten benötigt werden, also ist der nächste Datenpunkt für den letzten Punkt einfach der erste und der übernächste der zweite. Umgedreht ist für den ersten Datenpunkt der vorherige der letzte Punkt und der davorliegende der vorletzte.

Gucken wir uns ein Beispiel an:
verschiedene Interpolationen eines Datensatzes für einen Kreis (Quelltext zur Interpolation eines Kreises).
Rot ist der originale Kreis, Kontrollpunkte sind als kleine Kreise eingezeichnet. Die Interpolationsvarianten und die Kontrollpunkte sind jeweils mit title-Elementen gekennzeichnet.

Die Punkte müssen natürlich hinreichend dicht liegen, um gute Resultate zu gewährleisten. Ausprobiert werden kann das mit diesem Beispiel, wo entsprechende Parameter in der URI übergeben werden können:
verschiedene Interpolationen eines Datensatzes für einer Ellipse (Quelltext zur Interpolation einer Ellipse).
Bedeutung der Parameter:
anz - Anzahl der Datenpunkte (zwischen 2 und 100),
kontrast - Kontrast der Ellipse zwischen 0 und 1,
alpha - Drehwinkel der Ellipse in Grad,
delta - Startwinkel der Datenpunkte in Grad.

Immer C-Kommandos mit zwei Kontrollpunkten zu verwenden, ist natürlich nicht notwendig, da wir stetig differenzierbare Kurven haben, das gleiche Ergebnis ist mit S-Kommandos und kleineren Dateien zu erreichen:
Interpolationen eines Datensatzes für einer Ellipse, Kurzversion (Quelltext zur Interpolation einer Ellipse, Kurzversion).
Bedeutung der Parameter:
anz - Anzahl der Datenpunkte (zwischen 2 und 100),
kontrast - Kontrast der Ellipse zwischen 0 und 1,
alpha - Drehwinkel der Ellipse in Grad,
delta - Startwinkel der Datenpunkte in Grad.

Nun gibt es nicht nur geschlossene Kurven, sondern auch welche mit offenen Enden. Dann ist die Bestimmung der Ableitung an den Enden nicht mehr trivial, wenn diese aus den Datenpunkten numerisch geschätzt wird. Eine Möglichkeit besteht darin, für Anfang und Ende zusätzliche, nicht direkt dargestellte Datenpunkte anzugeben oder die Ableitungen am Rand direkt vorzugeben. Ist dies nicht möglich, sind spezielle asymmetrische Formeln notwendig, die allerdings auch weniger genau sind, weswegen die Ungenauigkeit der Schätzung der Kurve an den Enden immer größer ist als in der Mitte oder bei einer geschlossenen Kurve. Sofern möglich, sollten also mindestens jeweils zwei Datenpunkte außerhalb des wichtigen Bereiches an den Enden vorliegen, um im wichtigen Bereich überall die gleiche Genauigkeit zu erhalten.

Nützliche Formeln für die Ableitungen an den Rändern sind:

(14a) df(v)/dv = (-3f(v) + 4f(v+h) - f(v+2h))/(2h)
(14b) df(v)/dv = (3f(v) - 4f(v-h) + f(v-2h))/(2h)
(15a) df(v)/dv = (-3f(v-h) -10f(v) + 18f(v+h) - 6f(v+2h) + f(v+3h))/(12h)
(15b) df(v)/dv = (3f(v+h) +10f(v) - 18f(v-h) + 6f(v-2h) - f(v-3h))/(12h)

Interpolationen eines Datensatzes für einen Ellipsenbogen (Quelltext zur Interpolation eines Ellipsenbogens).
Bedeutung der Parameter:
anz - Anzahl der Datenpunkte (zwischen 5 und 100),
kontrast - Kontrast der Ellipse zwischen 0 und 1,
alpha - Drehwinkel der Ellipse in Grad,
delta - Startwinkel der Datenpunkte in Grad.

Die Skripte zur Interpolation können natürlich für beliebige Funktionen und auch als einfache Funktionenzeichner verwendet werden, siehe etwa folgendes Beispiel:
Interpolationen eines Datensatzes für eine Funktion (Quelltext zur Interpolation eines Datensatzes für eine Funktion).

Ein weiteres Beispiel mit einer teils zufällig gegebenen Kurve:
Interpolationen eines zufälligen Datensatzes (Quelltext zur Interpolation eines zufälligen Datensatzes)
Als Parameter kann das g von oben verwendet werden, um die Krümmung der Kurve zu beeinflussen, 1 entspricht der automatischen Fortsetzung in SVG.

Als Beispiele für die einfachere Variante, wo die Ableitungen nur aus den benachbarten Punkten bestimmt werden noch eine offene Kurve und eine geschlossene Kurve:

Interpolationen eines Datensatzes für eine Spirale (Quelltext zur Interpolation eines Datensatzes für eine Spirale)
Als Parameter kann das g von oben verwendet werden, um die Krümmung der Kurve zu beeinflussen, 1 entspricht der automatischen Fortsetzung in SVG.

Interpolationen eines Datensatzes für einen Kreis (Quelltext zur Interpolation eines Datensatzes für einen Kreis)
Als Parameter kann das g von oben verwendet werden, um die Krümmung der Kurve zu beeinflussen, 1 entspricht der automatischen Fortsetzung in SVG.

Interpolationen eines Datensatzes für ein Kreissegment (Quelltext zur Interpolation eines Datensatzes für ein Kreissegment)
Interpolation eines Kreises oder Kreissegmentes als Beispiel für eine Kurve. Um die Ableitungen/Kontrollpunkte zu berechnen, werden nur die benachbarten Punkte berücksichtigt.

Verfügbare Parameter:
n: Anzahl der Punkte
t: Typ der Kurve, 0 geschlossen, 1 offen
r,s: Krümmungsfaktoren, 1 entspricht automatischer Bestimmung wie in SVG. r erster Kontrollpunkt, s zweiter

In rot ist ein Vergleichskreis angegeben, blau der kubische Spline. Kleine Kreise geben jeweils die vorgegebenen Punkte an (grüngelb), beziehungsweise die Kontrollpunkte (cyan).

Kurvenscharen

Ich mag ja immer Scharen von gleichartigen Objekten, die mittels funktionaler Parameter variiert werden. Kurz hatte ich die Methode ja schon bei den Linienscharen angesprochen. Die Möglichkeiten mit Kurven sind natürlich ungleich vielfältiger. Natürlich können einerseits die Linien als Scharobjekte beibehalten werden, wobei die Parametrisierung nur mit einer Bézierkurve oder sonst etwas nahezu Beliebiges ist. Aber die Bézierkurven selbst können ja auch selbst zum Scharobjekt werden. Da die Bézierkurve ja immer innerhalb des Dreiecks beziehungsweise Vierecks liegt, welches durch Anfangs- End- und Kontrollpunkte aufgespannt wird, machen es einem diese besonders einfach, die Kurven innerhalb des sichtbaren Bereiches zu halten. Verwendet man auch Kontrollpunkte, die außerhalb des sichtbaren Bereiches liegen, muß man sich den Kurvenverlauf genauer ansehen, der dann aber noch spektakulärer aussehen kann. Hier einige Beispiele:
Linienschar mit quadratischer Scharfunktion
Linienschar mit kubischer Scharfunktion (und einigen anderen kubisch variierten Scharparametern)
Quadratische Bézierkurvenschar mit affiner Scharfunktion
Quadratische Bézierkurvenschar mit quadratischer Bézierkurve als Scharfunktion
Kubische Bézierkurvenschar mit affiner Scharfunktion
Kubische Bézierkurvenschar mit quadratischer Bézierkurve als Scharfunktion
Kubische Bézierkurvenschar mit kubischer Bézierkurve als Scharfunktion
Kubische Bézierkurvenschar mit kubischer Bézierkurve als Scharfunktion (und einigen anderen kubisch variierten Scharparametern)
Als Beispiel für einen Quelltext gebe ich mal den für die quadratische Bézierkurvenschar mit quadratischer Bézierkurve als Scharfunktion an:

Durch umgedrehte Interpretation läßt sich natürlich leicht eine Gitterstruktur erzeugen, welches eine Fläche repräsentiert:
Kubische Bézierkurvenschar mit kubischer Bézierkurve als Scharfunktion zur Erzeugung von Gitterstrukturen

Bei den hier verwendeten Zufallsparametern kreuzen sich mehrere Flächen, zudem sind diese natürlich lediglich in der Zeichenebene. Bei flüchtiger Betrachtung mag indes bereits der Eindruck eines räumlichen Objektes entstehen.

Nimmt man eine Projektion hinzu, welche an anderer Stelle erläutert wird, lassen sich damit auch Flächen in drei Dimensionen präsentieren, welche auf die Zeichenebene projiziert werden. Dabei ist natürlich analog zu den x- und y-Koordinaten auch die z-Koordinate anzugeben, ferner ist unterschiedlich farbigen Objekten von hinten nach vorne zu sortieren, um einen realistischeren Eindruck zu erzeugen, einfacher ist es bei einer einfarbigen Darstellung ohne Transparenz.

Weglänge

Nützlich für verschiedene Anwendungen ist die Kenntnis der Weglänge einer Kurve, etwa um stroke-dasharray gezielt einzusetzen, einschließlich des Zeichnens eines Pfades oder animateMotion entlang eines Pfades zu planen. Ist c(u) wie oben angegeben eine Parametrisierung eines Pfadfragmentes mit u von 0 bis 1, so ist die allgemeine Definition der Weglänge eines zweidimensionalen Pfades (siehe auch oben) mit u von 0 bis f (von 0 bis 1)
(W1) w(f):= 0f du ((dcx/du)2 + (dcy/du)2)1/2

Bei einer geraden Strecke ist das Ergebnis offenbar trivial, die gesamte Weglänge ist der Betrag des Differenzvektors oder als Formel
für den Anteil f:
(W2) w(f) = f * ((cx(0)-cx(1))2 + (cy(0)- cy(1))2)1/2

Bei einer quadratischen Bézierkurve gibt es eine nicht triviale analytische Lösung des Integrals, eine Formel, welche eine Wurzelfunktion und den Logarithmus enthält:

Weglänge einer quadratischen Kurve berechnen und testen (Quelltext zur Berechnung der quadratischen Weglänge).
Testen, wie genau Darstellungsprogramme Kurven, die Kurvenlänge und Verwandte berechnen.
Der GET-Parameter bereich bestimmt den Bereich, in dem die Kurve zufällig gewählt wird.
Ein korrektes Ergebnis besteht aus einer dunkelblauen Kurve, die teilweise von einer magenta Kurve verdeckt wird. Wo die magenta Kurve endet, ist ein gelber Kreis. Über diesem befinden sich sieben schwarze Kreise (nach einer kurzen Animation). Kreise und Pfade haben an der Stelle gleiche Abstände voneinander, die Kreise sind konzentrisch. Drübergelegt ist ein dunkelblau-gelber Maßstab, bei welchem das Muster in der Mitte des Maßstabes genau passen muß (Ausnahme starke Krümmung der Kurve an dieser Stelle) - gelb auf gelb, dunkelblau auf dunkelblau in der Mitte, außen einmal dunkelblau auf magenta und ein Abschluß in gelb.
Nach 5s wird der gelbe Kreis mit den konzentrischen Ringen im Bild vergrößert und zentriert.

Für elliptische Bögen ist bekannt, daß das Ergebnis ein elliptisches Integral ist, also in dem Sinne keine analytische Lösung gegeben ist. Soweit ich das nachvollziehen konnte, läuft es bei kubischen Bézierkurven ebenfalls auf kompliziertere elliptische Integrale hinaus. Weil sich alle Kurven wie oben gezeigt gut durch kubische Bézierkurven nähern lassen, beschränke ich mich darauf, dafür in einen Skript eine numerische Lösung anzugeben.
Nun gibt es zahlreiche Verfahren zur numerischen Integration (oder auch Quadratur genannt). Ich bin mir allerdings nicht sicher, welches für Bézierkurven optimal geeignet ist. Einfach und ohne besondere Tricks ist die Simpsonsche Regel. Diese arbeitet mit äquidistanten Stützstellen ungerader Anzahl von 0 bis 2N. Bei einer Integration von 0 bis f ist h:=f/(2N). Dann werden drei Summen gebildet:
(W3.1) s(1) = c(0)+c(f)
(W3.2) s(2) = ∑(n von 1 bis N) c((2n-1) h)
(W3.3) s(3) = ∑(n von 1 bis N-1) c(2 n h)
dann ist die numerisch bestimmte Weglänge:
(W3.4) w(f) = h/3(s(1) + 4s(2) + 2s(3))
Die Güte kann man etwa schätzen, indem N verdoppelt wird. Ist die Differenz der beiden Integrale kleiner als die gewünschte Unsicherheit, kann das Ergebnis mit größerem N verwendet werden.

Weglänge einer kubischen Kurve berechnen und testen (Quelltext zur Berechnung der kubischen Weglänge).
Testen, wie genau Darstellungsprogramme Kurven, die Kurvenlänge und Verwandte berechnen. Beschreibung und Parameter wie für die quadratische Kurve...

Manchmal ist es auch erwünscht, daß ein bestimmter Kurvenanteil zu malen ist oder etwas an einer bestimmten Stelle entlang einer Kurve zu positionieren ist. Für letzteres kann gut animateMotion mit keyPoints und genau einem Wert im Attribut value verwendet werden. Für andere Anwendungen ist es meist notwendig zu bestimmen, bei welchem Parameterwert der Kurvenanteil liegt, um die Koordinaten dieses Punktes zu berechnen. Dieser Parameterwert kann iterativ gefunden werden, etwa mit der bekannten Newtonregel, die sich hier besonders gut eignet, weil die Weglänge eine monotone Funktion des Parameterwertes ist.
Teilweglänge einer kubischen Kurve berechnen und testen (Quelltext zur Berechnung der Teilweglänge).
Testen, wie genau Darstellungsprogramme Kurven, die Kurvenlänge und Verwandte berechnen. Das Skript funktioniert wie das vorherige, zusätzlich kann aber der GET-Parameter anteil angegeben werden, um den Anteil der Teilweglänge vorzugeben.
Das entsprechende Skript für eine quadratische Kurve:
Teilweglänge einer quadratischen Kurve berechnen und testen (Quelltext zur Berechnung der Teilweglänge).

Auch der Vorgang des Malens einer Kurve kann von der präzisen Bestimmung der Pfadlänge profitieren. Dazu ein Beispiel:
Kurve malen (Quelltext zum Malen einer Kurve).
Eine Kurve wird vorwärts und rückwärts gemalt.

Kurvenabstand und Überlappung

Immer mal wieder ist es von Interesse zu bestimmen, wo und wie groß der kleinste Abstand zwischen zwei Kurven ist oder ob sich Kurven überlappen oder nur ineinanderliegen, wobei die Strichbreite ebenfalls relevant sein kann.

Sind K(s) und L(t) Parametrisierungen zweier Kurven, so ist offenbar F(s,t) = |K(s) - L(t)| eine Funktion, die den euklidischen Abstand zwischen den Punkten beider Kurven ergibt.
Da man bei einem Abstand auch das Quadrat untersuchen kann, statt explizit die Wurzel aus dem Quadrat zu untersuchen, eignet sich auch die Funktion G(s,t) = (K(s) - L(t))2.
Sind die Stellen gesucht, wo sich die Kurven schneiden, entspricht dies offenbar der Aufgabe einer (numerischen) Nullstellensuche auf F(s,t) oder G(s,t). Wird nur der kleinste Abstand gesucht, so wäre die Aufgabe, das absolute Minimum von F(s,t) oder G(s,t) zu finden. Bei Berücksichtigung der Strichbreiten wäre davon dann jeweils die halbe Strichbreite beider Kurven abzuziehen.

Dies durchzuführen, kann numerisch recht aufwendig sein. Siehe auch den nächsten Abschnitt. Sofern den Genauigkeit einer Messung reicht, genügt es hingegen auch, eine der Kurven als Skala auszuführen, indem man sie mittels use mehrfach mit verschiedenen Strichbreiten und -farben dargestellt wird. Zudem kann die eine Kurve mit der anderen maskiert dargestellt werden, um nur dem Überlapp sichtbar werden zu lassen:
Kurvenabstand mit Skala messen (Quelltext zum Kurvenabstand mit Skala messen).
Der schwarze Pfad soll innerhalb des hellblauen Pfades bleiben. Ist der Pfad außerhalb des hellblauen Pfades oder auch nur über dem Strich, wird dieser Überlapp rot angezeigt, was mit einer entsprechenden Maske realisiert wird, die außerhalb einschließlich Strichbreite repräsentiert.
Um zu sehen, wie groß der Überlapp ist oder der Abstand vom Außenpfad, ist eine bunte Skala angebracht. Diese hat alle fünf Einheiten einen Farbwechsel.
Der schwarze Pfad wird animiert. Klicken oder aktivieren des Hintergrundes stoppt die Animation, klicken oder aktivieren der Skala startet sie wieder.

Beim Maskieren (siehe entsprechendes Kapitel dazu) ist es also auch problemlos möglich, die Strichbreit zu verändern, auch wenn sie variabel ist, etwa indem sie animiert wird (prinzipiell könnte man damit aber auch die Abstandsmessung auf eine Zeitmessung zurückführen):
Überlappung von Kurven (Quelltext zur Überlappung von Kurven).
Der schwarze Pfad soll innerhalb des blauen Pfades bleiben. Ist der Pfad außerhalb des blauen Pfades oder auch nur über dem Strich, wird dieser Überlapp rot angezeigt, was mit einer entsprechenden Maske realisiert wird, die außerhalb einschließlich Strichbreite repräsentiert.
Der schwarze Pfad und die Breite des Striches des blauen Pfades werden animiert.

Ist entsprechend nur eine Warnung erforderlich, wenn der Abstand einen gewissen Abstand unterschreitet, kann dies bei gleicher Maskierung durch geeignete Farbwahl erfolgen:
Warnung vor Überlappung von Kurven (Quelltext zur Warnung vor Überlappung von Kurven).
Ein Pfad soll innerhalb eines anderen Pfades bleiben. Ist der Pfad außerhalb des Pfades oder auch nur über dem Strich, wird dieser Überlapp magenta angezeigt, was mit einer entsprechenden Maske realisiert wird, die außerhalb einschließlich Strichbreite repräsentiert.
Der innere Pfad und die Breite des Striches des äußeren Pfades werden animiert.

Kurvenabstand numerisch

Statt gleich sofort das Problem des kleinsten Abstandes zwischen zwei Kurven numerisch anzugehen, gucken wir uns erst einmal etwas einfachere Probleme an:

Der Euklidische Abstand zwischen zwei Punkten P(1)=(x,y)1 und P(2)=(x,y)2 ist wie bekannt die Wurzel aus der Summe der Quadrate der Differenzen der Koordinaten, in zwei Dimensionen also:
D(P(1),P(2)) = ( (x1 - x2)2 + (y1 - y2)2 )1/2

Mit einer Geraden oder einem affinen Kurvenstück gemäß der vorherigen Parametrisierung:
c(u) = (1 - u)P(0) + uP(1)
ergibt sich für jeden Punkt der Kurve c(u) der Abstand zum Punkt P offenbar zu:
D(P, c(u))

Um den Punkt des minimalen Abstandes zu der Kurve zu bestimmen, geht man zunächst von der Geraden statt einer endlichen Strecke aus und nutzt am elegantesten aus, daß die Verbindungslinie zwischen diesem Punkt und P senkrecht auf der Geraden stehen muß. Dies kann mit dem Skalarprodukt als Bedingung formuliert werden.

Sei P(2) = P(1) - P(0) (dies sei im Folgenden ungleich 0, sonst handelt es sich einfach um einen Punkt und die Lösung ist trivial), so ist
P(2) * (c - P) = 0 oder
P(2) * ((1 - u)P(0) + uP(1) - P) = 0 oder
P(2) * (P(0) + u P(2) - P) = 0 oder
u = P(2) * (P - P(0)) / (P(2)*P(2))
Für das affine Kurvenstück ergibt sich ferner die Beziehung
uk = Maximum(Minimum(1,u),0)

Der Abstand ist dann natürlich:
D = |P(0) + uk P(2) - P|

Hat man ein allgemeines Kurvenstück wie eine kubische Bézierkurve, so läßt sich das Problem allgemein nicht analytisch lösen. Ist die Kurve hinreichend einfach und gibt es für den Abstand nur ein absolutes Minimum, so kommt man natürlich mit den üblichen einfachen Verfahren zum Ziel, etwa durch eine Nullstellensuche bei der Ableitung. Ansonsten wird die Angelegenheit komplizierter, weil es neben dem absoluten Minimum noch weitere Extrema geben kann.

Daher geht es hier jetzt gleich mit dem komplizierteren Fall weiter, das Prinzip läßt sich dann auch auf den minimalen Abstand zwischen zwei Kurvenstücken verallgemeinern. Um auch beim Vorhandensein von Nebenextrema eine gute Chance zu haben, das absolute Minimum zu finden und weil es mal etwas anderes ist, soll hier ein genetischer oder evolutionärer Algorithmus verwendet werden, der für das eindimensionale Problem vielleicht nicht die optimale Wahl ist, aber beim späteren Problem des minimalen Abstandes zwischen zwei Kurvenstücken schon recht nützlich werden kann.

Die folgenden Beispiele beschränken sich auf einzelne Kurvensegmente. Hat man mehrere Segmente, so führt man das Verfahren entweder für jedes Segment einzeln zu oder hängt die Parametrisierung aller Segmente aneinander und verwendet entsprechend mehr Testpunkte. Mit mehreren Segmenten steigt natürlich auch die Anzahl lokaler Minima, die bei einem Segment stärker begrenzt ist.

Die Idee besteht darin, entlang des Pfades für eine erste Generation von Testpunkten die Abstände zu berechnen und dann durch gewichtete Kombination und zufällige Änderung einige neue Testpunkte zu bestimmen, von denen hoffentlich einige näher am Minimum liegen als anderen. Durch stärkere Gewichtung von guten Ergebnissen bei der Kombination oder durch Beseitigung von schlechten Ergebnissen kann so von Generation zu Generation der mittlere Abstand verkleinert werden, so daß man hoffen kann, recht schnell in der Nähe des Minimums anzukommen.

Natürlich lassen sich nahezu beliebige Kriterien für die Kombination formulieren. Einfach und klassisch ist es erstmal, zwei Punkte mit zufälligem Gewicht zu kombinieren. Kritisch kann das Verhältnis der Punkte der alten Generation zur Anzahl der ersetzten Punkte sein. Werden zu viele Punkte ersetzt, wird der Lösungsraum schnell sehr stark auf eine Möglichkeit eingegrenzt. Ist die Anzahl klein, so konvergiert die Methode sehr schlecht.
Minimaler Abstand Punkt Kurve (1) (Quelltext zum minimalen Abstand Punkt Kurve (1)).
Mit einem genetischen oder evolutionären Algorithmus wird bestimmt, welcher Punkt der blauen Kurve dem roten Punkt am nächsten kommt.
Eine Verteilungsfunktion des Abstandes kann eingeblendet werden, indem der kleine grüne Knopf gedrückt wird. Entsprechend gibt es eine Verteilungsfunktion für den Laufparameter mit dem kleinen gelben Knopf. Bei einem breiten Bildschirm ist die aber ohnehin zu sehen. Zurück geht es jeweils mit dem hellblauen Knopf. Pro Durchlauf gibt es jeweils eine Verteilung. Diese werden übereinandergelegt. Jeder Durchlauf bekommt eine andere Farbe und Teiltransparenz, der erste Durchlauf hellgrün und nicht transparent, der letzte rot und halbtransparent. Bei der Verteilungsfunktion für den Laufparameter sollte der Algorithmus gut funktionieren, wenn es einen deutlichen roten Häufungspunkt gibt. Bei der Verteilungsfunktion für den Abstand sollte es einen sehr schmalen und hohen Ausschlag links geben und rechts davon kein rot mehr. Die grünen Verteilungen sind hingegen recht breit zu erwarten.
Eine Vergrößerung des Bereiches rund um das Ergebnis kann erreicht werden, indem der rote Punkt angeklickert wird. Noch größer geht es, wenn die blaue Kurve angeklickert wird. Zurück geht es, wenn der schwarz gerandete Ergebnispunkt auf der Kurve angeklickert wird.
GET-Parameter:
anz - Anzahl der Testpunkte (> 10, <2001)
n - Anzahl der Generationen (> 3, <101)
weg - Anzahl der ersetzten Testpunkte (zwischen 1 und anz -10)
opt - Welche Kurve - 0 zufällig, sonst Kurven mit mehr als einem Maximum, die problematisch für den Algorithmus sind

Statt nun nur zwei Testpunkte zu kombinieren, kann man es natürlich auch mit mehr Punkten versuchen, zum Beispiel fünf: Minimaler Abstand Punkt Kurve (2) (Quelltext zum minimalen Abstand Punkt Kurve (2)).
Je mehr Testpunkte verwendet werden, desto wahrscheinlicher konzentriert sich allerdings die Suche auf den mittleren Bereich der Kurve und es wird etwas schwieriger, ein Minimum an Rand zu finden, sonst aber einfacher.

Neben einer freien Wahl der Anzahl der Testpunkte pro Kombination kann bei folgendem Beispiel mehr variiert werden: Minimaler Abstand Punkt Kurve (3) (Quelltext zum minimalen Abstand Punkt Kurve (3)).
Parameter wie zuvor, zudem:
m - Anzahl der Testpunkte pro neuem Punkt (zwischen 2 und anz)
uvar - Laufparameter bei neuem Punkt zufällig leicht variieren? 0 nein, sonst ja
gew - Gewicht eines Beitrages auswählen; 1: nach Abstand 1/(1+d); 2: Mischung Abstand und Zufall; sonst: Zufall

Statt Punkte der alten Generation zu ersetzen, kann man auch einfach neue Punkte hinzufügen. Bei dieser neuen Generation werden die Punkte zur Kombination nun aus der gesamten alten Generation gewählt. Einerseits verschlechtert das zwar etwas die Konvergenz, vermindert aber gleichzeitig das Risiko, den Lösungraum durch die Entsorgung von Punkten zu weit einzuschränken:
Minimaler Abstand Punkt Kurve (4) (Quelltext zum minimalen Abstand Punkt Kurve (4)).
Statt des Parameters weg gibt es hier den Parameter dazu, mit dem die Anzahl der neuen Testpunkte pro Generation angegeben wird.

Bei folgendem Beispiel werden dann noch ein paar Kombinationen der besten Punkte angehängt:
Minimaler Abstand Punkt Kurve (5) (Quelltext zum minimalen Abstand Punkt Kurve (5)).

Bei der Ermittlung des minimalen Abstandes zwischen zwei Kurven wird im Grunde ähnlich vorgegangen. Statt einem Laufparameter hat man man nun zwei, also einen Vektor. Der Abstand ist mit den Parametern u (mit Punkten P) und v (mit Punkten Q) also:
D = |P(0) + uk P(2) - Q(0) - vk Q(2)|

Das Problem entspricht also dem, in einem Gelände den höchsten oder tiefsten Punkt zu finden. Je mehr Parameter es gibt, desto schwieriger natürlich das Problem.

Minimaler Abstand Kurve zu Kurve (Quelltext zum minimalen Abstand Kurve zu Kurve).
Mit dem zusätzlichen Parameter an kann bestimmt werden, ob man noch ein paar Kombinationen der besten Punkte angehängt, mit 0 nicht, sonst schon. Ansonsten entsprechen die Parameter dem vorherigen Beispiel.
Die Bedienung ist etwas an das Problem angepaßt:
Eine Vergrößerung des Bereiches rund um das Ergebnis kann erreicht werden, indem der rote Punkt angeklickert wird. Noch größer um den Punkt der gleichfarbigen Kurve geht es, wenn die einer der blauen Punkte oben links angeklickert wird. Zurück geht es durch Anklickern einer Kurve oder eines schwarz gerandeten Ergebnispunktes.

Da man deutlich erkennen kann, daß selbst bei klaren Überschneidungen (Offenbar gibt es dann einen Abstand 0) das Ergebnis nicht besonders genau ist, daher bietet es sich an, bei den besten Punkten ein klassisches Verfahren anzuhängen. Weil die den hier diskutierten Kurventypen die Ableitungen gut bekannt sind, bietet sich das Verfahren nach Newton an - es wird also nach Nullstellen der Ableitung gesucht, wofür die zweite Ableitung des Abstandes benötigt wird oder, weil es einfacher ist und auch funktioniert, die zweite Ableitung des Quadrates des Abstandes.

Sofern es exakt einen Schnittpunkt von zwei stetig differenzierbaren Kurven ohne weiteres Nebenminimum des Abstandes gibt, kann der Schnittpunkt natürlich viel schneller nach Newton bestimmt werden:
Schnittpunkt Kurve zu Kurve (Quelltext zum Schnittpunkt Kurve zu Kurve).
Eine Vergrößerung des Bereiches rund um das Ergebnis kann erreicht werden, indem der rote Punkt angeklickert wird. Anklickern der Kurven führt zurück zur Übersicht.
GET-Parameter:
opt - Welche Kurven - 1 zufällig, sonst ein paar Problemfälle

Bei mehreren Schnittpunkten kann man bei dem Newton-Verfahren allenfalls versuchen, eine Kollektion von Startpunkten zu verwenden und zu gucken, ob man damit verschiedene Schnittpunkte findet - es kann dabei natürlich immer noch sein, daß nicht alle Schnittpunkte gefunden werden. Folgendes Beispiel versucht es mit 11 Startpunkten pro Kurve, also insgesamt 121 Startpunkten - häufig gibt es gute Ergebnisse, aber auch Fehlschläge (etwa weil die Krümmung der Kurven an einigen Stellen das Verfahren an den Rand führt und nicht zum Schnittpunkt der Kurven) - mindestens der kleinste gefundene Abstand wird dargestellt:
Schnittpunkte Kurve zu Kurve (Quelltext zu Schnittpunkte Kurve zu Kurve).
GET-Parameter:
opt - Welche Kurven - 1 zufällig, 2 bis 7 ein paar Problemfälle, 8 zufällig, sonst komplett zufällig mit keinem oder mehreren Schnittpunkten
msw - Maximale Schrittweite

Um mit dem Newton-Verfahren Extrema des Abstandes zu finden, kann eigentlich recht ähnlich wie für den Schnittpunkt vorgegangen werden - mit ähnlichen Einschränkungen, es sollte also exakt ein Extremum geben und die Krümmung sollte nicht so sein, daß das Suchverfahren am Rand nach dem Extremum sucht. Natürlich wird jetzt nicht direkt nach einem Schnittpunkt gesucht, sondern nach einem Schnittpunkt der Ableitungen der Kurven. Entsprechend ist nun die Hesse-Matrix (zweite Ableitung) der Abstandsfunktion zu bestimmen - sofern die invertierbar ist, ergibt sich aus der Inversen die Schrittweite des Verfahrens. Die Eigenwerte geben Auskunft darüber, ob es zu einem Minimum oder Maximum geht oder ob dies unbestimmt ist, etwa bei einem Sattelpunkt. Da nach einem Minimum gesucht wird, wird man die Richtung des berechneten nächsten Schrittes umdrehen, wenn die Eigenwerte anzeigen, daß man auf ein Maximum zusteuert. Bei einem Sattelpunkt kann man nur einen nicht zu kleinen Schritt in eine geeignete Richtung gehen und hoffen, daß sich die Situation bessert.

Minimaler Abstand Kurve zu Kurve nach Newton (Quelltext zu minimaler Abstand Kurve zu Kurve nach Newton).
GET-Parameter:
opt - Welche Kurven - 1 zufällig, 2 bis 7 ein paar Problemfälle, 8 zufällig, sonst komplett zufällig mit keinem oder mehreren Schnittpunkten
msw - Maximale Schrittweite
Der ermittelte Abstand ist hier als Strichbreite bei den dargestellten Kurven hell hinterlegt - wenn diese hellen Bereiche überlappen, wurde offenbar nicht das absolute Minimum gefunden. Zudem kann es per Text Hinweise geben, wenn etwas nicht geklappt hat, was dann meist daran liegt, daß die Kurvenformen nicht zu einem eindeutigen Ergebnis führen. Ein anderes Problem sind Randextrema, die wären getrennt zu untersuchen mit einer entsprechenden Punkt-Kurve-Abstandsuntersuchung.

Sofern die Situation eindeutig ist, ist dies Verfahren natürlich deutlich effektiver als ein evolutionärer oder genetischer Algorithmus. Oft ist dies aber nicht der Fall, dann empfiehlt es sich etwa, mit einem evolutionären Algorithmus nach guten Stellen zu suchen und dort dann das (lokale) Minimum zügig mit dem Newton-Verfahren genau zu finden. Alternativ kann man es natürlich auch wieder mit einem engen Raster von Startpunkten versuchen und dann gucken, welches das kleinste der so ermittelten lokalen Minima ist.

Minimaler Abstand Kurve zu Kurve nach Newton mit mehreren möglichen Nullstellen (Quelltext zu minimaler Abstand Kurve zu Kurve nach Newton mit mehreren möglichen Nullstellen).

Kurven teilen - Algorithmus nach De Casteljau

Manchmal kann es erforderlich sein, ein Kurvensegment in zwei Teile zu teilen, etwa aufgrund der Einschränkungen, die es bei den Angaben zur Pfadanimation gibt. Dort ist die Anzahl und Folge der Kommandos für jeden Animationsschritt gleich zu wählen, so daß man die Pfade der einzelnen Animationsschritte aneinander anpassen muß, etwa indem man die Anzahl der kubischen Pfadfragmente auf das kleinste gemeinsame Vielfache erweitert. Zudem möchte man eventuell bestimmen, welche Fragmente bei einer Animation ineinander übergehen sollen. Eine solche Aufteilung eines Fragmentes einer Bézierkurve in zwei Fragmente jedenfalls geht recht effektiv und einfach mit dem Algorithmus von De Casteljau. Dieser wird auch oft verwendet, um Bézierkurven mit einem Programm näherungsweise durch Geraden darzustellen, wobei dann so oft aufgeteilt wird, daß die Länge eines Fragmentes so klein wird, daß der Unterschied zu einer Geraden bei der Darstellung nicht mehr auffällt.

Das Verfahren betrachten wir für ein kubisches Fragment. An der Stelle 0 < s < 1 soll aufgeteilt werden. Haben wir die Kontrollpunkte P(0), P(1), P(2), P(3) wie oben eingeführt, so erweitern wir dies um einen weiteren Parameter, den Iterationsschritt i: P(i,0), P(i,1), P(i,2), P(i,3). Wobei P(0,j) = P(j) ist.

Nach dem Algorithmus ist ferner, soweit es zu den Indizes Werte gibt:
(C1.0) P(i+1,j) = (1 - s) P(i,j) + s P(i,j+1), oder ausgeschrieben:
(C1.1) P(1,0) = (1 - s) P(0) + s P(1)
(C1.2) P(1,1) = (1 - s) P(1) + s P(2)
(C1.3) P(1,2) = (1 - s) P(2) + s P(3)
(C1.4) P(2,0) = (1 - s) P(1,0) + s P(1,1)
(C1.5) P(2,1) = (1 - s) P(1,1) + s P(1,2)
(C1.6) P(3,0) = (1 - s) P(2,0) + s P(2,1)

Die beiden neuen kubischen Fragmente sind dann gegeben durch die Kontrollpunkte:
(C2) P(0), P(1,0), P(2,0), P(3,0) für den ersten Teil und
(C3) P(3,0), P(2,1), P(1,2), P(3) für den zweiten Teil.

Folgendes zeigt eine Beispielgraphik mit zufälligem Pfad und zufälliger Teilung:
Algorithmus nach De Casteljau - Beispiel (Quelltext zum Algorithmus nach De Casteljau - Beispiel).

Das Verfahren allein garantiert allerdings nicht, daß die Interpolation im Rahmen einer Animation besonders sinnvoll oder schön ist. Weil die aufgeteilten Fragmente nicht einfach mit dem Kommando S zusammengefügt werden können, werden in der Regel am Trennpunkt innerhalb der Animation Knicke entstehen:
Algorithmus nach De Casteljau - Animation (Quelltext zum Algorithmus nach De Casteljau - Animation).

Das sieht anders aus, wenn bei allen Kurven das s gleich gewählt wird, was recht einsichtig ist, weil jede einzelne Kurven ohnehin schon stetig differenzierbar am Trennpunkt ist, bei gleichem s sind dies dann auch die interpolierten Kurven, das gleiche s entspricht in der Ableitung in obiger Betrachtung kubischer Kurven einem gleichen g.
Algorithmus nach De Casteljau - Animation mit gleichem s (Quelltext zum Algorithmus nach De Casteljau - Animation mit gleichem s).
Zwar könnte auch eine stetig differenzierbare Fortsetzung am Trennpunkt mit dem Kommando S/s erzwungen werden, was aber den Kurvenverlauf verändert und meistens nicht erwünscht sein wird. Wenn die Aufgabe also darin besteht, mit einer Animation eine bestimmte Kurve in eine bestimmte andere zu verwandeln, wobei während der Animation keine zuvor nicht vorhandenen Knicke entstehen sollen, so sind derartige Feinheiten bei der Zerlegung zu beachten und die Kurvenfragmente jeweils passend zu wählen. Sozusagen ist es durchaus nicht trivial, aus der Abbildung einer Mücke eine eines Elefanten zu machen, ohne dabei während der Verwandlung eigenartige Artefakte zu erhalten.

Elliptische Bögen

Bei den Vollversionen von SVG können auch elliptische Bögen als Pfadkommandos angegeben werden, wie anfangs bereits kurz eingeführt. Die verwendete Notation ist recht nützlich für die Pfadkommandos, beim Erstellen von Dokumenten, besonders mit Skripten wird man jedoch häufig eine andere Parametrisierung verwenden, die Zentraldarstellung, diese sieht meist so aus:
(E1) e(u) = D(φ) (rx cos(u), ry sin(u)) + c
Dabei ist D(φ) eine Drehung um den Winkel φ, rx und ry sind die Hauptachsen der Ellipse und c der Mittelpunkt der Ellipse. Der Parameter u beginnt beim Startwinkel θ und läuft um den Winkel δ weiter.
Als Ableitung ergibt sich offenbar:
(E2) de(u)/du = D(φ) (- rx sin(u), ry cos(u))
Mittels einer Transformation kann aus (E1) das jeweilige Pfadkommando bestimmt werden. In folgendem Skript wird das realisiert, wobei die entsprechenden Größen als GET-Parameter wie im Element desc im Detail beschrieben angegeben werden können. Ferner wird unter Ausnutzung von (E2) und obiger Überlegungen zur Interpolation ein mit einer kubischen Kurve genäherter Bogen berechnet, welcher dann auch bei den tiny-Versionen verfügbar ist. Anzahl der Interpolationspunkte und Rundungsgenauigkeiten können auch angegeben werden:
SVG-Ellipsenbogen bestimmen (Quelltext SVG-Ellipsenbogen bestimmen).

Das kann nun auch wieder sehr einfach genutzt werden, um die allgemein beliebten Kreis- oder Tortendiagramme zu realisieren, welche sich ja ebenfalls einfacher in Polarkoordinaten umsetzen lassen als mit SVG-Kommandos:
Zufälliges Tortendiagramm (Quelltext zum zufälligen Tortendiagramm)

Nun kann es auch passieren, daß man bereits ein SVG-Kommando hat, trotzdem aber gerne die kubische Interpolationskurve haben möchte, etwa weil man eine entsprechende Kurve in einem tiny-Dokument verwenden möchte. Interpolieren oder Approximieren eines elliptischen Bogens mit einem kubischen Pfad:
SVG-Ellipsenbogen approximiert (Quelltext SVG-Ellipsenbogen approximiert).
Bedeutung der Parameter:
anz - Anzahl der Datenpunkte (zwischen 2 und 100)
rnd - nach wievielen Stellen gerundet werden soll (zwischen 0 und 4)
Weitere Parameter sind im desc-Element erläutert.

Die ermittelten kubischen Kurven eignen sich so jedenfalls nicht, um damit eine gleichwertige Animation wie mir echten elliptischen Bögen zu realisieren. Das ist kein Wunder, weil Animation immer auf einer affinen Interpolation zwischen den angegebenen Werten basiert, die Transformationen von einer Parametrisierung zur anderen hier aber nicht affin ist. Um eine halbwegs passable Animation anzunähern, müßte man die Bögen in ganz kleinen Schritten angeben. Bei geeignet ausgewählten Bögen verläuft die Animation mit kubischen Kurven jedenfalls deutlich ruhiger, man sollte nur Änderungen der Laufrichtung und allzu stark differierende Drehungen und Kurvenanteile vermeiden, sonst wird innerhalb der Interpolation recht schnell deutlich, daß es sich um kubische Kurven und nicht um Ellipsenbögen handelt. Zu beachten ist auch, daß diverse Programme ohnehin die echten elliptischen Bögen fehlerhaft animieren. Das Adobe plugin macht dies nur diskret, diverse Versionen von Opera bieten diverse mangelhafte Resultate an, Opera 9.5x scheint es dann aber doch endlich richtig auf die Reihe zu bekommen, ebenso wie Squiggle 1.7.
SVG-Ellipsenbogen animieren (Quelltext SVG-Ellipsenbogen animieren).
Für values-Animationen werden elliptische Bögen zufällig gewählt (grau) und alternativ auch mit kubischen Kurven genähert (grün). Die Animationen werden miteinander verglichen. Die einzelnen Werte der Animation sind als dünne Kurven dargestellt.
Wie zu erwarten, sind die genäherten Kurven nur bei den ausgewählten Werten elliptische Bögen, nicht dazwischen.
Mit dem GET-Parameter anz kann die Anzahl der Interpolationspunkte angegeben werden, Voreinstellung ist 6 (Bereich 2 bis 100).
rnd gibt an, auf wieviele Stellen gerundet wird, Voreinstellung ist 2 (Bereich 0 bis 4).
ani gibt an, wieviele Animationswerte gewählt werden, Voreinstellung ist 6 (Bereich 2 bis 20).

Für elliptische Bögen gibt es auch Darstellungen in Polarkoordinaten (r, θ=u), etwa
(E3) r(u) = p/(1 + e cos(u))
und die Ableitung:
(E4) dr(u)/du = - p e sin(u) / (1 + e cos(u))2
Alternativ kann auch einfach durch Verschieben aus der Zentraldarstellung eine Darstellung gewonnen werden:
(E5) r(u) = rx(1 - e cos(u))
mit der Ableitung:
(E6) dr(u)/du = rxe sin(u)
wobei dann für SVG ohnehin wieder in kartesische Koordinaten umgerechnet werden muß:
(E7) e(u) = r D(φ) (cos(u), sin(u)) + f
und die Ableitung de(u)/du = r D(φ) (-sin(u), cos(u)) + dr/du * D(φ) (cos(u), sin(u))
Dabei ist
(E8) rx = p/(1-e2)
(E9) ry = p/(1-e2)1/2
Oder
(E10) p = ry2/rx
(E11) e = (1 - (ry/rx)2 )1/2

Folgendes Beispiel vergleicht die erste Parametrisierung in Polarkoordinaten mit dem entsprechenden SVG-Kommando:
SVG-Ellipsenbogen in Polarkoordinaten ... (Quelltext SVG-Ellipsenbogen in Polarkoordinaten ...).
Bei Bedarf können neben anz und rnd wie bei den vorherigen Beispielen angegeben werden, ferner p, e (0 bis <1), phi, Winkel von theta bis theta + delta, fx und fy als Ellipsenfokus. Aufgrund der symmetrischeren Verteilung der Interpolationswinkel eignet sich die Zentraldarstellung jedenfalls besser zur Interpolation mit einer kubischen Kurve. Im Menübereich 'Bewegung' (weitere Beispiele) wird die andere oben erwähnte Darstellung zusammen mit einer geschickten Parametrisierung der Zeit für eine Darstellung von Planetenbewegungen und ähnlichem verwendet.

Spiralen

SVG bietet keine spezifischen Kommandos für Spiralen oder allgemein für Objekte, die sich in Polarkoordinaten besonders gut darstellen lassen, daher ist es notwendig, diese mit Bézierkurven zu nähern. Oben hatten wir ja schon eine numerische Näherung für eine Archimedische Spirale angesehen. Als Spiralen kann man Objekte bezeichnen mit folgender Parametrisierung in Polarkoordinaten. u sei der Laufparameter. Radius r(u) und Winkel φ(u) sind monoton. Der Nullpunkt kann natürlich beliebig verschoben sein, im Falle von Klothoiden oder Spinnlinien gibt es sogar zwei verschobene Zentren.

Gilt insbesondere r(u) = a φ(u) mit einer Konstante a, so handelt es sich um eine Archimedische Spirale. Typisch verwendet man φ=b u mit einer Konstante b. Entsprechend ist dann die Ableitung
dφ/du = b und dr/du = a dφ/du = ab
Die Weg- oder Bogenlänge kann auch angegeben werden, von 0 bis φ gilt dann w(φ)= a (φ (φ2 + 1)1/2 +arsinh(φ))/2
Es stellt sich nun die Frage, wie eine optimale Näherung aussieht.
Eine Forderung könnte zum Beispiel sein, daß die Qualität der Darstellung invariant unter Skalierung sein soll, daraus folgt dann, daß die ganze Kurve in äquidistante Segmente eingeteilt wird, die gleichen Winkelunterschieden entsprechen. Ist hingegen Skalierung bei der Darstellung als nicht relevant anzunehmen, täte man eher von einer konstanten Bogenlänge pro Segment ausgehen. Andererseits ist die Krümmung bei betragsmäßig kleinem r größer. Es reicht in dem Falle also, das Kurvensegment mit dem betragsmäßig kleinsten r sorgfältig zu wählen. Bei kleinen Winkeln wächst die Bogenlänge linear mit dem Winkel, bei großen Winkeln mit dem Quadrat des Winkels. Liegt das darzustellende Spiralfragment also unter 30 Grad, so können wiederum gleiche Winkelunterschiede gewählt werden, sonst ist es günstiger, die Winkelunterschiede pro Segment nur mit der Wurzel des Winkels zu vergrößern.

Eine Archimedische Spirale:
Archimedische Spirale mit gleichen Winkeldifferenzen (Quelltext Archimedische Spirale mit gleichen Winkeldifferenzen).

Und eine Variante mit einer Endpunktnotation, ähnlich wie für SVG-Kommandos oder einer Fortsetzung eines Pfades nützlich:
Archimedische Spirale mit gleichen Winkeldifferenzen, Endpunktnotation (Quelltext Archimedische Spirale mit gleichen Winkeldifferenzen, Endpunktnotation).

Ferner eine modifizierte Variante mit einer naheliegenden Parametrisierung, nicht zwangsläufig eine Archimedische Spirale, r und φ werden unabhängig voneinander parametrisiert:
Affine Spirale mit gleichen Winkeldifferenzen (Quelltext affine Spirale mit gleichen Winkeldifferenzen).

Dann die Variante mit einer Endpunktnotation, ähnlich wie für SVG-Kommandos oder einer Fortsetzung eines Pfades nützlich:
Affine Spirale mit gleichen Winkeldifferenzen, Endpunktnotation (Quelltext affine Spirale mit gleichen Winkeldifferenzen, Endpunktnotation).

Beim Lituus handelt es sich um eine Kurve vom Typ r (φ)= (a/φ)1/2 mit einer Konstante a, a und φ sind dann so zu wählen, daß es ein reelles Ergebnis gibt. Der Name kommt daher, daß die Kurve einem Kult- und Zeremonienstab der Pharaonen und antiker Wahrsager ähnelt, etwa denen römischer Auguren. Solche dekorativen Stäbe haben später dann auch Eingang in christliche Riten gefunden, die dies aus den älteren Religonen und Kulturen übernommen haben (Krummstab, Abtsstab, Hirtenstab, Pedum, Virga, Crosier).
Wenn man will, kann man die Vorzeichen von φ und a auch noch 'retten', indem man sie miteinander multipliziert und vor die Wurzel zieht, in der Wurzel nur den Betrag nimmt. φ=0 ist ein numerisches Problem, die Kurve geht einfach gegen die x-Achse. Man beachte auch, daß im Bereich von 0 bis etwa 30 Grad die Interpolationswinkel dichter liegen müssen als bei großen Winkeln, um eine passable Näherung hinzubekommen. Hier kann es sich auch wieder lohnen, die Winkeldifferenzen variabel zu wählen, wie im zweiten Beispiel:
Lituus-Spirale mit gleichen Winkeldifferenzen (Quelltext Lituus-Spirale mit gleichen Winkeldifferenzen).
Lituus-Spirale mit Winkel quadratisch zum Laufparameter (Quelltext Lituus-Spirale mit Winkel quadratisch zum Laufparameter).

Die logarithmische Spirale kommt häufig in der Natur vor, bei Wirbeln, Galaxien, Schneckengehäusen, beim Nautilus, bei diversen Pflanzen. Mit den Konstanten a und b ergibt sich r(φ) = a exp(b φ).
Mit der Funktion s(φ) = a (1 + b2)1/2 exp(b φ)/b ergibt sich die Bogenlänge als Differenz der s-Werte.
Logarithmische Spirale (Quelltext Logarithmische Spirale).

Die hyperbolische Spirale ist mit einer Konstante a vom Typ r(φ) = a/φ.
Bei φ = 0 ergibt sich wieder ein numerisches Problem wie beim Lituus, ebenso für den Bereich von 0 bis 30 Grad hinsichtlich der Interpolation. Hier kann es sich auch wieder lohnen, die Winkeldifferenzen variabel zu wählen, wie im zweiten Beispiel:
Hyperbolische Spirale mit gleichen Winkeldifferenzen (Quelltext hyperbolische Spirale mit gleichen Winkeldifferenzen).
Hyperbolische Spirale mit Winkel quadratisch zum Laufparameter (Quelltext hyperbolische Spirale mit Winkel quadratisch zum Laufparameter).

Eine weitere Form von Spirale mit eigenem Namen ist Fermats Spirale. Mit der Konstante a ergibt sich: r(φ) = (a φ)1/2.
Fermats Spirale (Quelltext Fermats Spirale).

Die meisten der bisher besprochenen Spiralen sind folglich von der Form einer Potenz r(φ) = (φ/a)p mit einer Konstante a und einem Exponenten p.
Spirale, Potenzform (Quelltext Spirale, Potenzform).

Die bereits kurz erwähnte Klothoide oder Spinnlinie hat einen ganz anderen Aufbau und erfordert eine numerische Integration, wird zudem eigentlich nicht in Polarkoordinaten angegeben:
x(t)= a π1/2 ∫(von 0 bis t) cos (π/2 u2) du
y(t)= a π1/2 ∫(von 0 bis t) sin (π/2 u2) du
Klothoiden kommen in der Optik im Rahmen der Beugung vor und sind auch als sogenannter Übergangsbogen im Verkehrswegebau sehr wichtig. Fährt ein Fahrzeug auf einer Klothoide, so vergrößert sich bei gleichbleibender Gewindigkeit der Einschlag des Lenkrades konstant, ermöglicht also eine komfortablere Kurvenfahrt, die Querbeschleunigung setzt also nicht ruckartig ein oder aus. Das liegt wiederum an bestimmten Eigenschaften dieser Kurve, etwa ist der Krümmungsradius umgekehrt proportional zur Länge L des Bogens, zudem gilt, wie man sich schnell überlegen kann: L = t a π1/2.
Klothoide (Quelltext Klothoide).
Den negativen Ast der Klothoide, der hier nicht dargestellt ist, erhält man einfach durch Punktspiegelung am Ursprung.

Kurven in Polarkoordinaten?

Setzt man obigen Ansatz der affinen Spirale fort in Richtung von Bézierkurven in Polarkoordinaten, so erhält man schöne Ergebnisse, die sich auch automatisch gut mit Bézierkurven in kartesischen Koordinaten nähern lassen. Leider bietet SVG 1.1 keine direkte Möglichkeit, Kurven direkt in Polarkoordinaten anzugeben. Es böte sich also an, in einer der kommenden Versionen von SVG einen Satz von entsprechenden Kommandos aufzunehmen. Es handelt sich in vielen Fällen nicht mehr um Spiralen, sondern um allgemeinere Kurven in Polarkoordinaten:
Quadratische Kurve in Polarkoordinaten (Quelltext, quadratische Kurve in Polarkoordinaten).
Kubische Kurve in Polarkoordinaten (Quelltext, kubische Kurve in Polarkoordinaten).
Einen Vorschlag für ein neues Element oder neue Kommandos bereite ich gerade vor, Vorschlag Element polarPath
Vorschlag Element polarPath (englische Version).

Das darin enthaltene Skript zur Simulation von solche Pfaden kann jedenfalls bereits jetzt verwendet werden, um solche Pfade einfach anzunähern:
Teste Element polarPath
(Teste Element polarPath (englische Version).
(Quelltext des zentralen Modules zur Erzeugung der Näherung des polarPath)