XHTML SVG CSS PHP

Dr. O. Hoffmann

SVG- und PHP-Labor

Vergnügen an Veränderung ist dem Menschen bleibend eigen.
Georg Christoph Lichtenberg

Skalierbare Vektorgraphik - Transformationen und IFS-Fraktale

Transformationen

Nicht nur, um die Dateigröße zu verkleinern, können verschiedene Elemente zu Gruppen (nicht im mathematischen Sinne, dort sind es eher Mengen) zusammengefaßt werden. Das g-Element vererbt auch Transformationen auf die von ihm umschlossenen Elemente, ebenso andere Eigenschaften. Für Transformationen von Elementen wird das Attribut transform verwendet. Der Wert ist eine leerzeichenseparierte Liste mit folgenden Operationen als mögliche Listenpunkte: matrix, translate, scale, rotate, skewX, skewY.

Bereits im vorherigen Abschnitt vorgestellte Grundformen sind ja alle noch sehr einfach. Mittels Transformationen wie Translation, Skalierung, Drehung und Scherung (oder Neigung) des Koordinatensystems kann man die Erscheinung von Elementen beeinflussen. Eine Skalierung mit einem negativen Faktor für x oder y ergibt eine Spiegelung. Statt der Einzelangaben ist auch die Angabe einer Transformationsmatrix möglich. Diese ist vor allem vorteilhaft, wenn Anfangs- und Endpunkte vorliegen, die ineinander transformiert werden sollen. Drei ansonsten frei wählbare Anfangspunkte dürfen dann nicht auf einer Geraden liegen, die drei Endpunkte sind beliebig wählbar. Zu jeder Kombination solcher 6 Punkte gibt es genau eine Matrix, welche die Anfangspunkte in die Positionen der Endpunkte transformiert. Zur normalen Schreibweise gehören für die Punkte Spaltenvektoren (x y 1). Mit der 1 als dritter Komponente wird bei den zweidimensionalen Vektorgraphiken die Translation in die Matrix hinein gemogelt. Ähnliche Matrizen werden auch Transfermatrizen genannt. Die Transformationsmatrix in der Kurzdarstellung matrix(a b c d e f) hat folgende Anordnung der Einträge:

ace
bdf
001

Als alternative Darstellung mit x' und y' den Koordinaten nach der Transformation und x und y davor ergibt sich:
x' = a x + c y +e
y' = b x + d y +f

Mit matrix(0 0 0 0 0 0) wird die Darstellung der zu transformierenden Elemente unterbunden.
Offenbar ist matrix(0 0 0 0 e f) eine Abbildung auf den Punkt (e f), das Ergebnis ist nicht sichtbar (in SVG tiny 1.2 gibt es die Möglichkeit, dies mit der Eigenschaft vector-effect = "non-scaling-stroke" zu ändern).
matrix(1 0 0 1 e f) = translate(e f) ist eine Translation.
matrix(1 0 0 1 e 0) = translate(e) ist eine Translation in x-Richtung.
matrix(a 0 0 a 0 0) = scale(a) ist eine Skalierung um a,
matrix(a 0 0 d 0 0) = scale(a d) ist eine Skalierung um (a d), entsprechend
matrix(-1 0 0 1 0 0) = scale(-1 1) eine Spiegelung an der x-Achse,
matrix(1 0 0 -1 0 0) = scale(1 -1) eine Spiegelung an der y-Achse,
matrix(-1 0 0 -1 0 0) = scale(-1 -1) eine Punktpiegelung am Ursprung,
matrix(cos(a) sin(a) -sin(a) cos(a) 0 0) = rotate(a 0 0) eine Drehung um den Winkel a,
matrix(1 0 tan(a) 1 0 0) = skewX(a) ist eine Neigung in x um den Winkel a,
matrix(1 tan(a) 0 1 0 0) = skewY(a) ist eine Neigung in y um den Winkel a
rotate(a b c) ist eine Drehung um den Winkel a um den Punkt (b c), das läßt sich natürlich auch als Matrix darstellen, ist aber kein so einfacher Ausdruck wie obige mehr, dazu ist es notwendig, eine Translation, eine Drehung und die Translation zurück auszuführen.

Nehmen wir als ein Beispiel an, wir wollen eine Transformation durchführen, welche (0 0) nach (q(x1) q(y1)), (x 0) nach (q(x2) q(y2)) und (0 y) nach (q(x3) q(y3)) abbildet, so rechnet man schnell aus, daß für die Transformationsmatrix matrix(a b c d e f) gilt:
(q(x1) q(y1)) = (e f) (Translation)
a= (q(x2) - q(x1))/x
b =(q(y2) - q(y1))/x
c= (q(x3) - q(x1))/y
d =(q(y3) - q(y1))/y

Dies und die Verallgemeinerung davon sind natürlich wieder eine ideale Anwendung von PHP - womit sich solche Matrizen schnell ausrechnen lassen und man die genaue Kontrolle über die Zielpunkte der Transformation hat.
Beispiel für eine Transformation mit einer Matrix (Quelltext zur Transformation mit einer Matrix).

Es handelt sich immer um affine Transformationen, diese erlauben es, ausgedehnte Objekte zu transformieren, in dem nur charakteristische Punkte etwa Ecken eines Objektes transformiert werden und dann wieder durch elementare Kurven oder Geraden miteinander verbunden werden können:
Weitere Beispiele zu Transformationen und Gruppen mit SVG (Quelltext zu Transformationen und Gruppen).

Man beachte dabei aber, daß das Ergebnis für den mit stroke gemalten Rand in der Regel ein anderes ist, wenn mittels transform eine Scherung durchgeführt wird als etwa mit PHP mit nachfolgender Darstellung des transformierten Pfades durch SVG. Letztere Darstellung kann in SVG tiny 1.2 ebenfalls mit vector-effect = "non-scaling-stroke" bei SVG-transformierten Objekten erreicht werden, wobei sich die Eigenschaft allerdings nicht vererbt, was einige Anwendungen etwas mühsam macht:
Beispiel non-scaling-stroke (Quelltext zu non-scaling-stroke).
Das gleiche Beispiel ohne non-scaling-stroke:
Beispiel ohne non-scaling-stroke (Quelltext zu Beispiel ohne non-scaling-stroke).

Abkürzungen

Um die Dateigröße zu verkleinern und allgemein zur effektiven Verwendung zu wiederholender Elemente gibt es auch verschiedene Möglichkeiten, Abkürzungen zu verwenden. Dies setzt nicht erst bei SVG an, sondern schon bei XML, wo der Autor durch eine Ergänzung der Dokument-Typ-Definition Abkürzungen einführen darf. SVG selbst bietet einige weitere Möglichkeiten mit Elementen wie defs, symbol, use, image. Zum Referenzieren wird nicht nur SVG verwendet, sondern ein weiteres XML-Format namens xlink, welches dann ebenfalls als Namensraum (xmlns) anzugeben ist.
Anmerkung: symbol ist nicht Bestandteil von SVG tiny und wird auch nicht von Opera 8 interpretiert. Stattdessen kann das Element g auch innerhalb von defs verwendet werden.
image mit SVG-Format ist ebenfalls nicht Bestandteil von SVG tiny (nur JPEG und PNG) und wird ebenfalls nicht von Opera 8 oder 9 interpretiert (nur JPEG und PNG). Integrierte svg-Elemente sind ebenfalls nicht Bestandteil von SVG tiny.

Abkürzungen und Transformationen (Quelltext zu Abkürzungen und Transformationen (1)).

Weiteres zu Abkürzungen und Transformationen (Quelltext zu Abkürzungen und Transformationen (2)).

Eingeschränkte Transformationen

In SVG tiny 1.2 gibt es die Möglichkeit, vorherige Transformationen wieder rückgängig zu machen und so Objekte mitten im Quelltext zu notieren, die effektiv das Koordinatensystem des Anzeigebereiches nutzen. Bei Gruppen können Kindelemente von dort aus wieder transformiert werden.
Die Einschränkung erfolgt mittels transform = "ref(svg)"
Eingeschränkte Transformation (Quelltext zur eingeschränkten Transformation).
Man beachte, daß nicht alle Transformationen invertierbar sind. Schlechte Implementierungen dieser Funktion können daher bei nicht invertierbaren Transformationen abstürzen. Bessere Implementierungen werden die betroffenen Objekte gar nicht erst transformieren, weswegen bei denen keine Rücktransformation notwendig ist, um den Effekt zu erreichen.

Mit einem Zusatz zweier Koordinaten transform = "ref(svg, -100,-100)" kann das Element zusätzlich verschoben werden. Alternativ kann man offenbar die Einschräkung auch auf ein Element g anwenden und darin weitere Transformationen anwenden.
Eingeschränkte Transformation mit Verschiebung (Quelltext zur eingeschränkten Transformation mit Verschiebung).

Diese neue Funktion kann es offenbar etwas erleichtern, mit den Einschränkungen der Zeichenreihenfolge zurechtzukommen. Ohne diese Funktion ist es erforderlich, die vorherigen Elemente und die nachfolgenden in Gruppen unterzubringen und getrennt zu transformieren und das verbleibende Element eben nicht. Zusätzlich ist dann die viewBox so anzugeben, daß keine Transformation dadurch stattfinden, dies wäre dann ebenfalls auf die Gruppen zu verschieben.

Fraktale oder iterierte Funktionensysteme (IFS)

Eine sehr interessante Anwendung von Transformationen sind IFS-Fraktale (IFS: Iterierte Funktionen-Systeme, iterated function system) oder Lindenmayer-Systeme. Durch die Anwendung von Transformationen und des use-Elementes ergibt sich eine sehr einfache Möglichkeit, diese zu realisieren. Ein paar sehr einfache Beispiele:
IFS-Fraktal mit SVG (a),
IFS-Fraktal mit SVG (b),
IFS-Fraktal mit SVG (c),
IFS-Fraktal mit SVG (d),
IFS-Fraktal mit SVG (e),
IFS-Fraktal mit SVG (f).
Je nach Rechenleistung und verwendetem Darstellungsprogramm kann es etwas dauern, bis das Fraktal angezeigt wird. Opera 8 und 9 scheinen da auch mit mäßig schnellen Prozessoren Beachtliches zu leisten.
KSVG 1 zeigt sich als prima Unterhalter und zeigt jeden Interationsschritt - das Bild baut sich also nach und nach auf - eine gute Idee eigentlich, da wird der Betrachter weniger ungeduldig, weil er sieht, daß es voran geht. Gerade bei Darstellungen im internet werden einige Nutzer ja schnell ungeduldig, obgleich hier einmal nicht das Laden der Datei Zeit dauert, sondern eher die Darstellung der Datei, nachdem sie bereits auf dem eigenen Rechner angekommen ist.

Relativ schnell wird man solche Fraktale mit einem PHP-Skript erstellen wollen.
PHP kann man etwa leicht anwenden, um einige Parameter zufällig zu variieren:
IFS-Fraktal mit SVG und PHP (Quelltext zum IFS-Fraktal mit SVG und PHP).

Durch eine einfache Rechnung kann auch abgeschätzt werden, wie groß das Fraktal maximal wird. Dazu geht man vereinfachend davon aus, daß nur jeweils um einen Faktor q <1 verkleinerte Stücke eindimensional an die Grundform der Länge a angesetzt werden.
Dann ergibt sich bei unendlich vielen Iterationen als geometrische Reihe eine Gesamtlänge von a /(1 -q).
Bei zweidimensionalen Objekten und zusätzlichen Drehungen ist es etwas komplizierter. Gehen wir davon aus, daß die Transformation im Ursprung beginnt und eine verkleinerte Kopie (Faktor q) im radialen Abstand r angefertigt wird, dann ergibt sich r/(1-q).
Für bessere Abschätzungen lohnt es sich, die genaue Grundform anzusehen und sich ein wenig in die Thematik von mathematischen Reihen einzulesen, vor allem auch, um eine gute Abschätzung für die minimale Größe zu erhalten.

Erste Versuche eines zufälligen und automatischen Strauch- oder Baumgenerators mit SVG-Fraktalen:
IFS-Strauch oder -Baum,
noch ein Strauch.
Es sehen noch nicht alle zufällig erzeugten Sträucher wirklich gut aus und der Preis für die einfachen IFS-Fraktale ist, daß in den Folgegenerationen kaum noch Zufall einzubauen ist - dafür muß man ein oder zwei Schritte zurück und flexibler arbeiten, was dann größere Dateien zur Folge hat, die dann aber eher nach natürlich gewachsenen Strukturen aussehen dürften. Hier jedenfalls könnte man bereits ganz gut gelungene Büsche per Hand weiterverarbeiten.

Einige weitere pflanzenartige Fraktale (Darstellung kann etwas länger dauern):
IFS-Fraktal mit SVG (g),
IFS-Fraktal mit SVG (h),
IFS-Fraktal mit SVG (i),
IFS-Fraktal mit SVG (j)

Darstellungen von Cantor-Mengen basieren auf den gleichen Mechanismen, wobei man bedenken muß, daß sie eigentlich gar keine flächigen, darstellbaren Objekte sind. Dargestellt wird vielmehr wie bei den Fraktalen auch das Ergebnis eines endlichen Iterationsschrittes.
Darstellung von Cantor-Mengen mit SVG (1).
Darstellung von Cantor-Mengen mit SVG (2).

Auch Zufallslinien oder Risse lassen sich so leicht erzeugen. Während ein richtiger Riß zumeist keine zwei vorgegebenen Enden hat, sondern sich von Anfang bis Ende zufällig ausbreitet, ist es für die Darstellung meist vorteilhafter, beide Enden festzulegen.
Die Linie dazwischen teilt man auf und setzt eine kürzere Strecke senkrecht auf den Trennpunkt, die Auslenkung. Damit ergeben sich zwei Strecken, die man erneut teilt, worauf man vier Strecken hat und so weiter. Ein Parameter ist also die Anzahl dieser Iterationen, ein weiterer, wo eine Strecke geteilt wird und noch ein weiterer, wie weit der Zwischenpunkt jeweils von der Verbindungslinie entfernt ist. Letztere beide Parameter wird man natürlich aus einem gewissen Bereich zufällig auswählen, um eine zufällige, unregelmäßige Struktur zu erreichen. Dabei kann man natürlich auch noch entscheiden, ob man die Richtung der Auslenkung zufällig wählt, oder immer in die gleiche Richtung.

Riß (1) (Quelltext zum Riß (1))
Parameter:
n: zwischen 2 und 12 für die Anzahl der Iterationen
abt: maximale relative Abweichung des Trennpunktes vom Mittelwert 0.5, zwischen 0 und 0.49
arp: maximale Auslenkung relativ zum Trennpunkt
ari: zufälliger Vorzeichenwechsel bei Auslenkung? 0 nein, 1 oder sonst ja

Nimmt man mehr als zwei Punkte entlang einer Kurve und legt man die Auslenkung von einem Trennpunkt auf der Kurve fest, lassen sich natürlich auch andere Formen realisieren, insbesondere geschlossene, um den Eindruck eines Loches zu erwecken.

Die lokale Korrelation zu benachbarten Punkten kann natürlich auch durch andere Arten von Iterationen erreicht werden. So kann man etwa in jedem Iterationsschritt über die Nachbarn mitteln und dazu einen Zufallswert addieren, der im Mittel null ist. Den Zufallswert kann man zudem mit jedem Iterationsschritt etwas verkleinern. Die relative Gewichtung zwischen Mittelwert und Zufallswert und der Verkleinerungsfaktor pro Iterationsschritt entscheiden dann über die Form der Zufallslinie.

Riß (2) (Quelltext zum Riß (2))
Parameter:
n: zwischen 5 und 2000 für die Anzahl der Iterationen
df: Dämpfungsfaktor pro Interation (0 > df > 1)
zuf: Gewicht Zufallsanteil zum Mittelwert der Nachbarpunkte bei der ersten Iteration (0 > zuf >= 1000, typisch 100)

Transformationen und Projektionen jenseits der Möglichkeiten von SVG mit PHP

Bei Transformationen in SVG handelt es sich immer nur um affine Abbildungen. Eine Transformation von einem problem-orientierten Koordinatensystem (Polarkoordinaten, Zylinderkoordinaten, hyperbolische Koordinaten etc oder auch Projektionstransformationen) in ein anderes oder umgekehrt ist leider nicht möglich. Dazu wäre dann immer PHP zu verwenden, um in die kartesischen Koordinaten von SVG umzurechnen. Zu beachten dabei ist, daß dies in der Regel nur punktweise korrekt ist, eine affine Funktion in Koordinaten des einen Koordinatensystems ist nicht auch eine in einem beliebigen anderen Koordinatensystem. Das kann höheren Rechenaufwand und Näherungen bei der Darstellung von ausgedehnten Objekten bedeuten, wo es eben nicht ausreicht, nur die Ecken zu transformieren.

Was ich bereits bei den Polygonen benutzt habe, sind Polarkoordinaten oder Kreiskoordinaten in der zweidimensionalen Ebene, rudimentär treten die in SVG auf bei Kreisen, Ellipsen und bei den Drehungen, wirklich durchgehend nutzen kann man sie in SVG allerdings nicht. Dazu ist eine Transformation in kartesische Koordinaten zum Beispiel mit PHP vor der Ausgabe erforderlich. Ist r die radiale Koordinate und φ die Winkelkoordinate, auch Polarwinkel, so ergibt sich mit den kartesischen Koordinaten x und y:
x = r cos(φ)
y = r sin(φ)
Durch Wahl oder Transformation des Vorzeichens von φ von positiv zu negativ kann zwischen einem Linkssystem und einem Rechtssystem gewechselt werden - SVG nutzt ohne zusätzliche Spiegelungen ein Linkssystem. Durch Addition eines Winkels zu φ kann offenbar das Koordinatensystem um diesen Winkel gedreht werden. Durch Addition von Konstanten kann der Ursprung verschoben werden, wie schon bei den kartesischen Koordinaten.
Bei Zylinderkoordinaten im Dreidimensionalen wählt man als dritte Koordinate h als Höhe des Zylinders, was keine Änderung gegenüber den kartesischen Koordinaten bedeutet, also
z = h.
Bei dreidimensionalen Koordinaten ist vor der Ausgabe mit SVG natürlich eine Projektion auf einen zweidimensionalen kartesischen Unterraum vorzunehmen.
Sphärische Koordinaten oder Kugelkoordinaten haben die drei Koordinaten r, φ und θ, wobei r wieder die radiale Koordinate ist und φ wie bei den Zylinderkoordinaten der Polarwinkel mit Werten zwischen 0 und 360 Grad. θ ist der Azimutwinkel, dieser wird von einer Achse senkrecht zu jener Ebene gemessen, welche die Polarwinkel aufspannen. Die Werte reichen von 0 bis 180 Grad. Es gilt also:
x = r cos(φ) sin(θ)
y = r sin(φ) sin(θ)
z = r cos(θ)
Auch hier ist vor der Ausgabe wieder eine Projektion auf einen zweidimensionalen kartesischen Unterraum vorzunehmen. Alternativ kann natürlich auch r festgehalten werden, dann erhält man eine Kugeloberfläche.

Bei Toruskoordinaten hat man in der Regel einen großen, polaren Radius R und einen kleinen r, einen Polarwinkel φ des großen, polaren Radius R und einen Winkel θ des kleinen r. Werden R und r festgehalten, so ergibt sich eine zweidimensionale Fläche in einem dreidimensionalen Raum:
x = (R + r cos(θ)) cos(φ)
y = (R + r cos(θ)) sin(φ)
z = r sin(θ)
Beide Winkel gehen von 0 bis 360 Grad. Je nach Wahl von R und r kann die Oberfläche sich selbst schneiden.

Hyperbolische Koordinaten verfügen gleich über mehrere Parametrisierungen, je nach Problemstellung. Ein Hyperboloid wird in kartesischen Koordinaten beschrieben durch die Gleichung:
(x/a)2 + (y/a)2 - (z/c)2 = 1.
Parametrisierungen der Fläche sind zum Beispiel:
x = a ( 1+ u2)1/2 cos(φ)
y = a ( 1+ u2)1/2 sin(φ)
z = c u
φ ist wieder ein Polarwinkel zwischen 0 und 360 Grad, u ein weiterer unbeschränkter Parameter, a und c feste Konstanten.
Andere Möglichkeit:
x = a (cos(φ) (-,+) u sin(φ)
y = a (sin(φ) (+,-) u cos(φ)
z = (+,-) c u
Bei den in Klammern stehenden Vorzeichen ist jeweils entweder das erste oder das zweite zu wählen. Die Konstruktion ist besonders schön, denn man kann sich die beiden Parameterscharen entstanden vorstellen aus einem Zylinder, bei welchem ein Ende polar verdreht wird.
Noch eine Möglichkeit:
x = a cosh(u) cos(φ)
y = a cosh(u) sin(φ)
z = c sinh(u)

Die einfachste Projektion oder Projektionstransformation besteht darin, einfach eine der drei Koordinaten x y z nicht darzustellen und die anderen beiden mit den zwei Koordinaten in der Ebene zu identifizieren. Das nennt sich Parallelprojektion, dabei wird eine der drei Achsen mit 0 multipliziert, also auf 0 abgebildet. Etwas eleganter und allgemeiner ist es, eine affine Abbildung auf den zweidimensionalen kartesischen Unterraum vorzunehmen, etwa durch eine Drehung im Raum in ein neues System x' y' z' mit anschließender Parallelprojektion von z' auf 0.
Drehungen um die z-Achse um den Winkel φ kennen wir bereits:
x' = cos(φ) x - sin(φ) y
y' = sin(φ) x + cos(φ) y
z' = z
Drehung um die x-Achse entsprechend
y' = cos(φ) y - sin(φ) z
z' = sin(φ) y + cos(φ) z
x' = x
und Drehung um die y-Achse
z' = cos(φ) z - sin(φ) x
x' = sin(φ) z + cos(φ) x
y' = y
Drehung um eine Achse (x, y, z) = (p, q, r) (Einheitsvektor):
x' = (t p p + c) x + (t p q - s r) y + (t p r + s q) z
y' = (t p q + s r) x + (t q q + c) y + (t q r - s p) z
z' = (t p r - s q) x + (t r q + s p) y + (t r r + c) z
mit s = sin(φ), c = cos(φ) und t = 1 - cos(φ)
Drehung einer Pyramide,
(Quelltext zur Drehung einer Pyramide):
Drehung eines Objektes (unregelmäßige dreieckige Pyramide) um den Mittelpunkt des Bildes um einen Vektor (x, y, z) = (p, q, r) um den Winkel o in Grad.
Im Ursprung ist eine Ecke der Pyramide, die anderen drei Punkte liegen auf den Achsen x, y und z. Das Verhältnis der Abstände auf diesen Achsen ist 1:2:3.
Gezeigt wird die x-y-Ebene, z wird auf 0 abgebildet.
o, p, q, r und ein Skalierungsfaktor s sind als GET-Parameter verfügbar.

Eine Skalierung entspricht einfach der Multiplikation einer Koordinate mit einem Wert ungleich 0. Die Multiplikation mit 0 kann als Projektion auf den zweidimensionalen Unterraum ohne diese Koordinate aufgefaßt werden. Skalierung:
x' = a x
y' = b y
z' = c z
Eine Verschiebung oder Translation um einen Vektor (u, v, w) entspricht einfach der Transformation:
x' = x + u
y' = y + v
z' = z + w

Andere Projektionen berücksichtigen die Sichtweise eines Malers mit einer Leinwand, des menschlichen Auges oder von Photoapparaten, wo auch weiter entfernt liegende Objekte kleiner erscheinen als nähere Objekte. Rechnerisch ist das die Projektion auf eine Fläche zwischen Objekt und Auge mittels des Strahlensatzes. Beim Malermodell ist die Fläche die Leinwand, das zu Malende befindet sich hinter der Leinwand, der Maler oder das Auge davor. Gemalt wird nur, was sich jenseits der Leinwand befindet. Beim Photoapparat ist die Fläche die Film- oder Chipebene, beim Auge die gekrümmte Rückseite des Augapfels, dargestellt wird eigentlich alles, was sich vor der Fläche befindet, also mehr als beim klassischen Malermodell, auch die Anordnung der Strahlen im Strahlensatz ist etwas anders. Auge und Kamera haben eine Linse, weswegen an sich nur eine Fläche, nicht der ganze Raum scharf dargestellt wird, das wird bei einer Projektion nicht berücksichtigt, was dann eher einer Lochkamera mit punktförmigem Loch entspricht, bei der allerdings praktisch kein Licht auf die Abbildungsfläche träfe oder bei einem großen Loch die Abbildung generell unscharf wäre. Das sind allesamt nur klassische Behandlungen, es wird also keine Beugung oder Interferenz berücksichtigt. An all diesen Punkten läßt sich erkennen, daß die Modelle alle nur einfache Näherungen dessen sind, was wirklich passiert, wenn Objekte mit dem Auge oder einer Kamera betrachtet werden.

Denken wir uns die Projektionsfläche am Ort z = a, das Auge des Betrachters bei z = 0 und das Objekt bei z = g (Gegenstandsweite). Ist die Größe des Objektes h, so erscheint ein Bild auf der Projektionsfläche der Größe h' = h a/g gleich groß. Das ergibt sich unmittelbar aus dem Strahlensatz. Man beachte, daß immer g > 0 gelten sollte. Bei g = 0 sitzt das Objekt offenbar im Auge des Betrachters, was sowohl bei der Projektion als auch im richtigen Leben ein fataler Unfall wäre und daher zu vermeiden ist. Für g < 0 ist das Objekt hinter dem Betrachter, der es dann nicht sehen kann.
Oft wird die z-Koordinate des Objektes als Gegenstandsweite angenommen. Das führt zu den Formeln der Zentralprojektion oder des Malermodells:
x' = x (a/z)
y' = y (a/z)

Um zu gucken, ob es reicht, nur die Eckpunkte oder eben die Stützpunkte eines Pfades zu transformieren, probieren wir das einfach aus, indem wir die vorherige Figur mit kubischen Bézierkurven realisieren (wobei das ein leichter Vorgriff auf einen der folgenden Abschnitte ist):
Drehung und Zentralprojektion,
(Quelltext zur Drehung und Zentralprojektion):
Drehung eines Objektes (unregelmäßige dreieckige Pyramide) um den Mittelpunkt des Bildes um einen Vektor (x, y, z) = (p, q, r) um den Winkel o in Grad.
Im Ursprung ist eine Ecke der Pyramide, die anderen drei Punkte liegen auf den Achsen x, y und z. Das Verhältnis der Abstände auf diesen Achsen ist 1:2:3.
Gezeigt wird die x-y-Ebene, z wird auf 0 abgebildet. o, p, q, r und ein Skalierungsfaktor s sind als GET-Parameter verfügbar.
Zentralprojektion: Das Objekt wird um t in z-Richtung verschoben, die Bildebene ist bei a, beides ist auch als GET-Parameter verfügbar.

Genaugenommen ist ein Objekt mit x oder y ungleich 0 natürlich auch weiter weg und somit im Modell eines Auges mit gekrümmter Abbildungsfläche kleiner darzustellen. Ein Photoapparat hat eine Ebene als Abbildungsfläche, aber auch teils kompensierte Linsenfehler, eine genaue Simulation ist daher schwierig und abhängig vom Auge oder dem jeweiligen Modell des Photoapparates samt Objektiv. Zudem beinhaltet die Abbildung etwas, was einer Punktspiegelung entspricht, was bei einer Projektion nicht relevant ist und durch ein leicht modifiziertes Modell zu berücksichtigen ist.
Nun kompensiert das Auge in gewissem Maße bereits den Effekt, daß etwa die Ränder einer Projektionsfläche weiter vom Auge weg sind und objektiv kleiner erscheinen. Möchte man dies aber korrekt berücksichtigen, so denkt man sich um das Auge herum eine Halbkugel im Abstand a als Projektionsfläche und bekommt mit der Hilfsgröße
H = a /(x2 + y2 + z2) 1/2
die Kugelprojektion:
x' = H x
y' = H y
Wie unmittelbar zu sehen ist, geht diese Formel für x und y gegen 0 in die Formel für die Zentralprojektion über. Die andere, dem Betrachter eigentlich nicht sichtbare Halbkugel könnte den Raum hinter dem Beobachter darstellen. Das Unfallrisiko, daß das Objekt im Auge steckt, reduziert sich damit auf einen Punkt, während es bei der Zentralprojektion noch die ganze Ebene war. Da häufig Objekte um den Ursprung herum definiert sind, ist daran zu denken, daß bei diesen Projektionstransformationen allerdings das Auge des Betrachters der Ursprung ist. Es ist also am Einfachsten, das Objekt zunächst in die passende Lage zu drehen, dann in z-Richtung auf einen geeigneten Abstand zu verschieben und anschließend erst zu projezieren.
Drehung und Kugelprojektion,
(Quelltext zur Drehung und Kugelprojektion):
Drehung eines Objektes (unregelmäßige dreieckige Pyramide) um den Mittelpunkt des Bildes um einen Vektor (x, y, z) = (p, q, r) um den Winkel o in Grad.
Im Ursprung ist eine Ecke der Pyramide, die anderen drei Punkte liegen auf den Achsen x, y und z. Das Verhältnis der Abstände auf diesen Achsen ist 1:2:3.
Gezeigt wird die x-y-Ebene, z wird auf 0 abgebildet. o, p, q, r und ein Skalierungsfaktor s sind als GET-Parameter verfügbar.
Kugelprojektion: Das Objekt wird um t in z-Richtung verschoben, die Bildebene ist bei a, beides ist auch als GET-Parameter verfügbar.
In diesem Falle ist es, wie bei genauen Hinsehen bemerkt werden kann so, daß Geraden in der Regel nicht exakt auf Geraden abgebildet werden. Immerhin sind die verwendeten kubischen Bézierkurven schon eine etwas bessere Näherung als gerade Linien. Noch besser ginge es, wenn die kubischen Bézierkurven erst nach der Transformation berechnet würden, was dem Leser zu Übung überlassen bleibt.

Eine weitere interessante Möglichkeit der Projektion ist, einfach zwei der Parameter des speziellen Koordinatensystems in kartesischen Koordinaten darzustellen, letztlich also die inverse Transformation vorzunehmen. Der Torus entspricht topologisch etwa einem endlichen rechteckigen Flächenstück, dessen gegenüberliegende Kanten wieder aneinandergefügt sind - nennt man auch periodische Randbedingungen, welche zum Beispiel gern für Hintergrundbilder auf internet-Seiten verwendet werden. Solche sich periodisch fortsetzenden Bilder entsprechen also eher der (verzerrten) lokalen Sicht auf einer Torusoberfläche, wenn man nicht in der Lage ist, dessen Krümmung in der zusätzlichen Dimension wahrzunehmen.

Nachzuprüfen ob folgende Behauptungen gelten, bleibt dem Leser zu Übung überlassen (Tip: oben sind bereits ein paar Beispiele angegeben):

  1. Drehung und Translation ändern die Form von Objekten nicht, nur die Orientierung und Positionierung im Raum.
  2. Bei Drehungen, Translationen, bei der Parallelprojektion und bei der Zentralprojektion werden Geraden auf Geraden oder Punkte abgebildet.
  3. Bei Drehungen, Translationen, bei der Parallelprojektion und bei der Zentralprojektion werden Vierecke auf Vierecke oder Geraden abgebildet.
  4. Wenn obige Behauptungen gelten, reicht es, die charakteristischen Eckpunkte eines Objektes mit geraden Kanten zu transformieren und erst im Anschluß daran die Verbindungslinien zu ziehen.
  5. Bei der Kugelprojektion werden in der Regel Geraden nicht auf Geraden abgebildet.

Beispiele zu Transformationen und Projektionen mit PHP

Weil es nett aussieht, gucken wir uns jetzt noch ein paar Objekte animiert an, die Animation ist recht einfach und kann mit dem Animationsabschnitt nachvollzogen werden. So animieren, transformieren und projezieren wir also erstmal Platonische Körper:
Tetraeder
Hexaeder
Oktaeder
Dodekaeder
Ikosaeder.

Bei so einfachen Objekten mit ebenen Oberflächen ist es auch recht einfach, nicht nur die Kanten darzustellen, sondern ebenso die Flächen. Bei mehrfarbigen Abbildungen von mehreren Objekten oder Flächen ist dann allerdings auch die Zeichenreihenfolge relevant. Diese ist ebenfalls zu bestimmen und kann dann berüchsichtigt werden, indem die Flächen mit use-Elementen referenziert werden und die Referenzierung dann eben in einer Animation so getauscht wird, daß immer von hinten nach vorne dargestellt wird:
Tetraeder - Flächendarstellung
Hexaeder - Flächendarstellung
Oktaeder - Flächendarstellung
Dodekaeder - Flächendarstellung
Ikosaeder - Flächendarstellung.

Weitere Beispiele:
Sterntetraeder
Sterntetraeder - Flächendarstellung
Hyperboloid
Punkte auf Kugel
Achtung, größere Dateien (um die 60 bis 400 kB): Torus 1, Torus 2, Torus 3, Torus 4, Torus 5