Diplomarbeit - Labor für künstliche Intelligenz

Diplomarbeit - Labor für künstliche Intelligenz
Evolution einer 3D-Beschreibung aus Bildern
mit Hilfe von Lindenmayer-Systemen
- Diplomarbeit zur Erlangung des akademischen Grades
Diplom-Informatiker (FH)
im
Fachbereich Informatik und Medien
der Fachhochschule Brandenburg
vorgelegt von
Jan Derer
14. Juni 2004
1. Gutachter :
Prof. Dr. F. Mündemann
2. Gutachter :
Dipl.-Inf. I. Boersch
Erklärung
Ich erkläre hiermit, dass die vorliegende Arbeit von mir selbst und ohne fremde Hilfe verfasst
wurde. Alle benutzten Quellen sind im Literaturverzeichnis angegeben. Die Arbeit hat in
gleicher oder ähnlicher Form noch keiner Prüfungsbehörde vorgelegen.
Brandenburg, 14. Juni 2004
Jan Derer
Kurzfassung / Abstract
Kurzfassung
Diese Arbeit beschäftigt sich mit den Lindenmayer-Systemen (kurz L-Systemen), deren Evolution und Anwendungsmöglichkeiten.
Dazu werden die theoretischen Grundlagen zu den L-Systemen und ihre wichtigsten Erweiterungen dargestellt. Des Weiteren werden einige Klassifikationen von L-Systemen präsentiert.
Außerdem wird auf die graphische Interpretation des Wortes eines L-Systems mit Hilfe der
Turtle-Interpretation eingegangen.
Für den Einstieg in die Evolution wird ein kurzer Überblick zu den Genetischen Algorithmen
und zur Genetischen Programmierung gegeben. Im Anschluss werden die Mutationskonzepte
besprochen und Anwendungsmöglichkeiten präsentiert.
Aus praktischer Sicht wird eine Applikation vorgestellt, die als Eingabe ein L-System bekommt und eine 3D-Grafik als Ausgabe liefert. Dabei ist die Applikation speziell auf die Bedürfnisse der Evolution zugeschnitten. Zusätzlich wird eine graphische Oberfläche für die
Applikation entwickelt, um 3D-Grafiken etwas komfortabler aus L-Systemen erzeugen zu
können.
Abstract
This thesis is part of an evolving bigger thesis. It will be concerned with LindenmayerSystems (or L-Systems) and their evolution. The most theoretical basics of L-Systems will be
shown as well as important additions to the basic L-System. From a theoretical perspective,
some classifications and an interpretation of L-Systems to generate a 3d-graphic are explained. As a basis for the following thesis about the evolution of L-Systems, some concepts
of mutation and possible applications will be discussed. .
From a practical point of view, an application will be presented, that gets a L-System as input
and provides a 3d-graphic as output. The program is especially designed for evolutionary
loops of L-Systems. Additionally to this commandline-based application, a GUI has been developed to generate the 3d-graphic of the L-System in a comfortable way.
Keywords:
L-Systems, simulated evolution, mutation, applications of L-Systems, Lparser,
POV-Ray
i
Inhaltsverzeichnis
Inhaltsverzeichnis
1
EINLEITUNG ZU DIESER DIPLOMARBEIT ........................................................... 1
1.1
Motivation zur Modellierung von Pflanzen mit L-Systemen ................................... 1
1.2
Aufgabenstellung.......................................................................................................... 3
1.3
Gliederung..................................................................................................................... 3
1.4
Anforderungen an den Leser ...................................................................................... 4
2
2.1
THEORETISCHE GRUNDLAGEN VON L-SYSTEMEN UND DEREN
EVOLUTION ................................................................................................................... 5
Kurzbiografien von wichtigen Personen .................................................................... 5
2.2
L-Systeme...................................................................................................................... 6
2.2.1
Wichtige Unterschiede zu den Chomskysprachen ................................................. 6
2.2.2
Das ursprüngliche L-System .................................................................................. 7
2.2.3
Übersicht der L-Systeme........................................................................................ 9
2.2.3.1 Basis L-System................................................................................................. 10
2.2.3.2 Deterministisches L-System............................................................................. 12
2.2.3.3 Propagierendes L-System................................................................................. 12
2.2.3.4 L-Systeme mit Terminalzeichen ...................................................................... 14
2.2.3.5 L-Systeme mit Tabellen ................................................................................... 16
2.2.3.6 L-Systeme mit Verzweigungen........................................................................ 18
2.2.3.7 Stochastische L-Systeme.................................................................................. 20
2.2.3.8 Parametrisierte L-Systeme ............................................................................... 21
2.2.3.9 Kontextsensitive L-Systeme............................................................................. 22
2.2.3.10
Weitere L-Systeme ....................................................................................... 23
2.2.3.11
Kombination von L-Systemen ..................................................................... 24
2.2.4
Wachstumsfunktion.............................................................................................. 25
2.2.5
Klassifikation / Einordnung ................................................................................. 26
2.2.6
Interpretation des Wortes ..................................................................................... 28
2.2.7
Übersicht über Anwendungsmöglichkeiten von L-Systemen.............................. 34
2.3
Alternativen zu L-Systemen ...................................................................................... 36
2.3.1
P-Systeme............................................................................................................. 37
2.3.2
Eco-Grammar Systeme ........................................................................................ 37
2.4
Genetische Algorithmen ............................................................................................ 39
2.5
Genetische Programmierung .................................................................................... 41
2.6
Mutation von L-Systemen ......................................................................................... 46
2.6.1
Mutation von L-Systemen unter Genetischen Algorithmen ................................ 46
2.6.2
Mutation von L-Systemen unter Genetischer Programmierung .......................... 54
2.6.3
Mutation der Interpretation .................................................................................. 56
ii
Inhaltsverzeichnis
2.7
Applikationsvision...................................................................................................... 56
2.7.1
Das Grundgerüst................................................................................................... 58
2.7.2
Die Anwendungsmöglichkeiten ........................................................................... 58
3
PROGRAMME, DIE L-SYSTEME SIMULIEREN/INTERPRETIEREN ............. 61
3.1
Lparser ........................................................................................................................ 61
3.2
L-System...................................................................................................................... 62
3.3
RayTraced Evolution ................................................................................................. 63
3.4
LinSys3D ..................................................................................................................... 64
3.5
Zusammenfassung und Übersicht............................................................................. 66
4
ENTWURF UND IMPLEMENTIERUNG DER FASSADE ..................................... 69
4.1
Der Begriff der Fassade ............................................................................................. 69
4.2
Einführung zum Entwurf .......................................................................................... 69
4.3
Die Entwicklungsumgebung...................................................................................... 70
4.4
Modulübersicht........................................................................................................... 70
4.5
Vereinfachungen und die entsprechende Modulübersicht..................................... 72
4.6
Lparser ........................................................................................................................ 75
4.7
Persistence of Vision-Ray .......................................................................................... 77
4.8
Schnittstelle der Fassade............................................................................................ 80
4.9
Ablauf der Fassade..................................................................................................... 82
4.10
Implementierung der Fassade................................................................................... 86
5
DIE GRAPHISCHE OBERFLÄCHE.......................................................................... 91
5.1
Einführung zur graphischen Oberfläche ................................................................. 91
5.2
Die Entwicklungsumgebung für die graphische Oberfläche.................................. 91
5.3
Externe Bibliotheken und Komponenten................................................................. 91
5.4
Entwurf der graphischen Oberfläche....................................................................... 92
5.4.1
Das Hauptformular - TMDIMain......................................................................... 92
5.4.2
Das Kindfenster - TMDIChildMain..................................................................... 94
5.4.3
Einstellungs-Dialog - TCFGForm........................................................................ 95
5.4.4
Die 3D-Ansicht - TDirect3DForm ....................................................................... 96
iii
Inhaltsverzeichnis
6
TESTS UND AUSWERTUNG DER ENTWICKELTEN SOFTWARE .................. 98
6.1
Test der Fassade ......................................................................................................... 98
6.1.1
Testumgebung ...................................................................................................... 98
6.1.2
Testumfang........................................................................................................... 98
6.1.3
Testdurchführung ................................................................................................. 98
6.1.4
Auswertung .......................................................................................................... 99
6.1.4.1 Performancetest................................................................................................ 99
6.1.4.2 Belastungstest................................................................................................. 101
6.1.4.3 Funktionstest .................................................................................................. 101
6.2
Test der graphischen Oberfläche............................................................................ 103
6.2.1
Testumfang......................................................................................................... 103
6.2.2
Testdurchführung ............................................................................................... 103
6.2.3
Auswertung ........................................................................................................ 103
6.2.3.1 Funktionstest .................................................................................................. 103
6.2.3.2 Usability-Studie.............................................................................................. 104
7
ZUSAMMENFASSUNG UND AUSBLICK ZU DIESER ARBEIT ....................... 105
7.1
Zusammenfassung.................................................................................................... 105
7.2
Ausblick..................................................................................................................... 106
A
ABBILDUNGSVERZEICHNIS ................................................................................. 107
B
TABELLENVERZEICHNIS ...................................................................................... 109
C
LITERATURVERZEICHNIS .................................................................................... 110
D
ZUSAMMENFASSUNG DER WICHTIGSTEN LITERATURQUELLEN ......... 118
E
SOFTWAREQUELLEN ............................................................................................. 119
F
BEDIENUNGSANLEITUNG FÜR VISUAL L ........................................................ 121
G
CD-ROM INHALT ...................................................................................................... 123
H
QUELLTEXTE ............................................................................................................ 124
iv
1
Einleitung zu dieser Diplomarbeit
1 Einleitung zu dieser Diplomarbeit
1.1 Motivation zur Modellierung von Pflanzen mit L-Systemen
Die Vielfalt von Pflanzen und deren komplexe (variantenreiche) Strukturen scheinen nur
schwer beschreibbar zu sein. Wenn man sich zum Beispiel die Anordnung der Samen, auch
Phyllotaxis genannt, einer Sonnenblume betrachtet, ist auf den ersten Blick zu erkennen, dass
diese kreisförmig angeordnet sind und es dadurch erschwert wird, eine genau Beschreibung
zu erstellen. Wenn man jedoch einer streng mathematischen Beschreibung folgt, ermöglicht
es diese, eine genaue Anordnung der Samen wiederzugeben. Es hat sich gezeigt, dass der Verteilung der Samen ein Optimierungsprozess zugrunde liegt, der die runde Fläche am besten
ausnutzt. Dabei folgt die mathematische Beschreibung der Anordnung des goldenen Schnittes. [De03]
Abbildung 1.1: Phyllotaxis der Sonnenblume [De03]
Ebenso verhält es sich mit der Struktur von Pflanzen. Die Verästelung der Zweige geschieht
nicht willkürlich, sondern ebenfalls nach einer bestimmten Gesetzmäßigkeit.
So findet man bei der Knospe drei verschiedene Blattstellungen.
Dabei nennt man den ringförmigen Bereich, an dem die Knospe entsteht, Knoten (Nodus).
Zwischen zwei Knoten liegt ein Bereich, der Internodium heißt, an dem keine Knospe entsteht.
1
1
Einleitung zu dieser Diplomarbeit
Die erste Stellung (vgl. Abbildung 1.2 a) nennt man Distichie. Diese Gesetzmäßigkeit sagt
aus, dass pro Knoten nur eine Knospe existiert und dass die Knospen immer um 180° versetzt
angeordnet sind.
Die zweite Stellung (vgl. Abbildung 1.2 b) ist die Dispersion. In diesem Fall sind die Knospen
schraubenförmig angeordnet und haben einen Divergenzwinkel, die den goldenen Schnitt
approximiert.
Die letzte Stellung (vgl. Abbildung 1.2 c) ist die Dekussation. Dabei können mehrere Knospen an einem Knoten entstehen. Die Folgeknospen sind so angeordnet, dass sie in der Lücke
zwischen zwei Knoten liegen. [De03]
Abbildung 1.2: Stellungen der Knospen [De03]
Dadurch, dass diese Gesetzmäßigkeiten bei Pflanzen existieren, kann man ein mathematisches
Modell aufstellen, welches eine bestimmte Pflanze wiedergibt.
In der Computervisualistik hat es sich gezeigt, dass sich die L-Systeme als mathematisches
Modell zur Pflanzenmodellierung hervorragend eignen.
Dieses mathematische Modell kann nun mit anderen Elementen der Informatik verknüpft
werden. Da Pflanzen einer natürlichen Evolution unterliegen, ist dies eine mögliche Schnittstelle zur künstlichen Intelligenz. So könnte zum Beispiel die Genetische Programmierung
genutzt werden, um die natürliche Evolution zu simulieren. Wenn man von einem generierten
Wort eines L-Systems ausgeht, könnte das Wachstum rückwärts verfolgt werden, um die
Entwicklung einer Pflanze nachzuvollziehen. Das könnte so weit reichen, dass Aussagen darüber gemacht werden können, welchen Bedingungen ein Baum im Laufe eines Jahres ausgesetzt war.
Diese Arbeit ist der Anfang von mehreren Arbeiten, die zusammen ein Konzept ergeben, welches L-Systeme und die künstliche Evolution miteinander verknüpft. Dabei werden Ideen für
das Gesamtkonzept in dieser Diplomarbeit behandelt.
2
1
Einleitung zu dieser Diplomarbeit
1.2 Aufgabenstellung
Ziel der Arbeit ist eine Untersuchung zur automatischen Erstellung von Objektbeschreibungen aus vorgegebenen Quellbildern.
Die Objektbeschreibung erfolgt in Form von L-Systemen, die passend zu Quellbildern erzeugt
werden. Hierzu wird ein evolutionärer Prozess auf die L-Systeme angewendet. Fitnessfunktion sei die Übereinstimmung der Ansichten der erzeugten L-Systeme aus verschiedenen Kameraperspektiven mit den Originalbildern eines einfachen Gegenstandes.
Die Arbeit soll die theoretischen Grundlagen dieses Ansatzes darlegen (insbesondere Einordnung und Klassifizierung von L-Systemen und ihre Evolutionsmöglichkeiten), eine Systemarchitektur vorschlagen und in Teilen prototypisch implementieren. Bei der Implementation soll
besonderer Wert auf die Weiternutzbarkeit der erstellten Module und die Definition externer
Schnittstellen gelegt werden. Die Funktionsfähigkeit des Systems ist durch geeignete Teststellungen zu evaluieren.
1.3 Gliederung
Im folgenden Kapitel werden die theoretischen Grundlagen für diese Arbeit erläutert. Dazu
wird das ursprünglich mathematische Modell von Lindenmayer vorgestellt und dessen wichtigste Unterschiede zu den Sprachen der Chomskyhierarchie aufgezeigt. Des Weiteren werden
die wichtigsten L-Systeme besprochen, wobei kurz auf einige spezielle L-Systeme eingegangen wird. Danach werden die Klassifikationen besprochen sowie die Interpretation des Wortes in graphische Kommandos. Außerdem wird gezeigt, wie L-Systeme eingesetzt werden.
Auch auch alternative Sprachen zu den L-Systemen werden kurz vorgestellt. .
Zusätzlich zu den Grundlagen der L-Systeme wird eine Übersicht zu den Genetischen Algorithmen und zur Genetischen Programmierung gegeben, damit später die Konzepte zur Mutation von L-Systemen diskutiert werden können.
Zum Abschluss des Kapitels werden die Anwendungsmöglichkeiten dieser Arbeit besprochen.
Das dritte Kapitel gibt einen Überblick über vorhandene Applikationen zu L-Systemen, wobei
einige kurz erläutert werden. Am Ende werden tabellarisch die wichtigsten Informationen
zusammengestellt.
Das grundlegende Konzept und der Entwurf für die Software der Diplomarbeit sind Inhalt des
vierten Kapitels. Hier wird die Entwicklungsumgebung und die benutzten Tools vorgestellt.
Einige Gesichtspunkte aus der Sicht des Software-Engineerings werden dabei genauer betrachtet. Außerdem wird die Implementierung der Software beschrieben.
Die graphische Oberfläche wird im fünften Kapitel dargestellt. Dabei werden die wichtigsten
Formulare und die externen Bibliotheken vorgestellt.
Im sechsten Kapitel wird der Schwerpunkt auf die Tests gelegt. Es wird nicht nur die Applikation selbst auf ihre Tauglichkeit getestet, sondern auch deren Performance und die Belastung für den Computer.
Den Abschluss der Arbeit bildet das siebente Kapitel. In diesem wird eine Zusammenfassung
der Arbeit vorgenommen und ein Ausblick auf die mögliche Erweiterung der Software gegeben.
3
1
Einleitung zu dieser Diplomarbeit
Im Anhang befinden sich das Literaturverzeichnis, das Abbildungsverzeichnis, das Tabellenverzeichnis und ein Verzeichnis über die Softwarequellen. Des Weiteren ist eine Übersicht
der Baumstruktur der CD-ROM enthalten.
1.4 Anforderungen an den Leser
Im Rahmen dieser Arbeit werden einige Anforderungen an den Leser gestellt, denn sie reißt
viele Gebiete der Informatik an, auf die aus Platzgründen im Einzelnen nicht eingegangen
werden kann.
Hierzu zählt ein gewisser Grad an mathematischem Verständnis, der spätestens mit dem Erreichen des Vordiploms erworben sein sollte. Zum Nachschlagen sei auf folgende Quellen
verwiesen [SGT99] [SD01] [Gö99].
Der Leser sollte über die Grundlagen der Formalen Sprachen verfügen und die Chomskyhierarchie sowie die Grundlagen zur Automatentheorie kennen. Zum Nachlesen empfiehlt sich
folgende Literatur [HU00] [Wä99].
Kenntnisse über Datenstrukturen sollten auch vorhanden sein und können in [Se92] [OW02]
nachgeschlagen werden.
Für den praktischen Teil der Arbeit werden allgemeine Kenntnisse der Programmierung vorausgesetzt, insbesondere Kenntnisse der Programmiersprachen C und Delphi. Folgende Literatur kann genutzt werden, um das nötige Wissen zu erhalten [KR90] [PP99] [Wi95] [DK00]
[Lo00] [Eb02]. Nicht nur als Übungsbuch für Delphi, sondern auch als Lehrbuch für bessere
und fortgeschrittene Programmiertechniken ist [El00] [El01] zu empfehlen.
Eine weitere notwendige Grundlage ist das Wissen über Software-Engineering, das zum Beispiel aus [Ba98] [Ba99] [Ba00] entnommen werden kann.
Grobe Grundlagen der graphischen Datenverarbeitung, im Speziellen von 3D-Grafiken in
Verbindung mit Ray-Tracing, sind vorteilhaft und können in [ESK96] [ESK97] [GFMP01]
nachgelesen werden.
4
2
Theoretische Grundlagen von L-Systemen und deren Evolution
2 Theoretische Grundlagen von L-Systemen und deren
Evolution
Dieses große und umfassende Kapitel vermittelt die theoretischen Grundlagen, die für diese
Arbeit nötig sind. Es wird nicht nur auf die verschiedenen L-Systeme eingegangen, sondern
auch auf Mutationskonzepte von L-Systemen unter Nutzung von Genetischer Programmierung und Genetischen Algorithmen. Das Kapitel zeigt auch Alternativen zu L-Systemen auf
sowie die Anwendungsmöglichkeiten von L-Systemen in Kombination mit der Evolution.
Zuvor werden jedoch kurz die drei Personen vorgestellt, die für diesen Bereich prägend sind.
2.1 Kurzbiografien von wichtigen Personen
Aristid Lindenmayer: [FMF04]
Aristid Lindenmayer kommt am 17. November 1925 in Budapest zur Welt. Dort studiert er
auch und erhält seinen Abschluss in Chemie. Er wandert in die USA aus und wird wissenschaftlicher Mitarbeiter an der Universität Michigan, wo er in Biochemie promoviert. Danach
verlässt er Michigan und ist zeitweise an der Universität von Pennsylvania und am Queens
College in New York tätig. Am 23. August 1967 schreibt Lindenmayer den Artikel über sein
mathematisches Modell, welches dann nach ihm benannt wird, das Lindenmayer-System
[Li68a], [Li68b]. Diesen Artikel veröffentlicht er noch am Queens College. 1968 geht er dann
in die Niederlande, um die Position des Direktors für Theoretische Biologie an der Universität
Utrecht zu übernehmen. Dieses Amt übt er bis zu seinem Tod im Jahre 989 aus.1
Przemyslaw Prusinkiewicz: [Si97]
Sein Geburtsort und das Geburtsdatum sind unbekannt. 1974 schließt Prusinkiewicz sein Studium mit dem Master of Science an der Technischen Universität Warschau ab. Hier bleibt er
als wissenschaftlicher Mitarbeiter bis zu seiner Promotion im Jahre 1979. Danach geht er für
drei Jahre nach Algerien, wo er an der Universität von Algier arbeitet. 1982 wandert er in die
USA aus und wird wissenschaftlicher Mitarbeiter an der Universität Regina. Während dieser
Zeit arbeitet Prusinkiewicz an der Visualisierung von Strukturen und demWachstum von
Pflanzen. 1986 präsentiert er zum ersten Mal seine Ergebnisse auf der Konferenz „Graphics
Interface“ [Pr86]. Seit 1991 lebt er in Kanada. Dort ist er Professor an der Universität Calgary
im Fachbereich Informatik. . Außerdem ist er Gastprofessor und Gastforscher an vier Universitäten.
Grzegorz Rozenberg: [Ro04]
Auch der Geburtsort und das Geburtsdatum von Rozenberg sind unbekannt. Wie Prusinkiewicz studiert Rozenberg an der Technische Universität Warschau. Er schließt sein Studium
1965 mit dem „Master and Engineer degree in computer science“ ab. Drei Jahre später erhält
er den Doktortitel an der „Polish Academy of Sciences“ in Warschau. Bis 1979 ist er an vier
Universitäten Professor, seit diesem Zeitpunkt ist er Professor an der Universität Leiden im
Fachbereich Informatik und lebt auch dort. Er erhält 1997 den Gödel Preis und ist an zwei
Universitäten Honorar-Doktor, unter anderem an der TU Berlin. Rozenberg entwickelte zu
den L-Systemen viele theoretische Grundlagen und brachte dadurch die L-Systeme entschieden weiter.
1
Es sei darauf hingewiesen, dass viele Angaben zur Person im Internet falsch sind. Sei es z. B. sein Geburtsland,
Sterbe- oder Geburtsdatum.
5
2
Theoretische Grundlagen von L-Systemen und deren Evolution
2.2 L-Systeme
Es werden nun die wichtigsten Aspekte der L-Systeme dargestellt. Dazu gehören die wichtigsten L-Systeme selbst sowie die Interpretation des Wortes eines L-Systemes in graphische
Kommandos. Außerdem wird ein Blick auf die Klassifikation von L-Systemen in Bezug auf
die Chomskyhierarchie gegeben sowie ein Entscheidungsautomat für die Computergrafik präsentiert. Das Kapitel endet mit einem kurzen Überblick über die Anwendungsmöglichkeiten
von L-Systemen.
2.2.1 Wichtige Unterschiede zu den Chomskysprachen
Um einen Einstieg in die L-Systeme zu bekommen wird gezeigt, welche Unterschiede zwischen den L-Systemen und den Sprachen der Chomskyhierarchie existieren. Deshalb wird
hier auf Kapitel 2.2.3 vorgegriffen und ein Beispiel für ein L-System präsentiert. Die genaueren Definitionen folgen dann dort.
Im Beispiel 2.1 wird eine Grammatik G für ein L-System definiert. Dabei steht Σ für die
Menge aller Symbole, die in dieser Grammatik verwendet werden, P für die Menge aller Produktionen und ω für das Axiom, mit dem gestartet wird.
Beispiel 2.1 für ein einfaches L-System:
Das Beispiel zeigt ein einfaches, triviales L-System.
G = ( Σ = { a },
P = { a → a a },
ω= a
)
Die Ableitung der ersten fünf Schritte, entwickelt folgendes Wort:
4
a ⇒ aa ⇒ aaaa ⇒ aaaaaaaa ⇒ aaaaaaaaaaaaaaaa oder auch kurz a ⇒ a 16 .
L-Systeme haben zwei wichtige Eigenschaften, welche die wesentlichen Unterschiede zu den
Chomskysprachen beinhalten.
Einer dieser Unterschiede ist, dass L-Systeme nicht zwischen Terminalzeichen und Nichtterminalzeichen unterscheiden (mit Ausnahme der E0L-Systeme). Das Wichtige an den Zeichen
ist, dass diese nur enthalten sein dürfen, wenn für sie eine Ableitung existiert.
Der zweite Unterschied liegt bei den Ableitungen. Bei Sprachen der Chomskyhierarchie ist
die Ableitung sequenziell. Jeder Ableitungsschritt besteht nur aus der Ableitung einer Produktion. Die L-Systeme führen Ableitungen parallel aus. So wird bei jedem Ableitungsschritt für
jedes Zeichen eine Produktion ausgeführt. Damit einher geht auch das Problem des Wachstums. Das Wachstum eines zu generierenden Wortes nimmt bei L-Systemen, die komplexe
Objekte beschreiben sollen, in der Regel mindestens exponentiell zu.
An Beispiel 2.1 wird deutlich, dass die Berechnungskomplexität für ein triviales Beispiel
schon bei O(n ) = 2 n liegt. Bei der Modellierung von Pflanzen kann die Berechnungskomplexität noch um vieles höher sein. Daher muss die Anzahl der Ableitungsschritte bei der synthetischen Modellierung von Pflanzen gut durchdacht sein, damit der Computer in einer endlichen Zeit terminiert. Das Problem der Komplexität von L-Systemen ist nicht immer so einfach zu lösen. Alternativ sei auf folgende Quelle [Ke86] verwiesen, in der das Thema ausführlicher behandelt wird. Sie enthält auch weitere Quellen zum Thema Komplexität und LSysteme.
6
2
Theoretische Grundlagen von L-Systemen und deren Evolution
L-Systeme in eine bestimmte Sprachhierarchie einzugliedern erweist sich als sehr schwer. Es
existieren einige Dissertationen [Gä96], die einzelne L-System in die Chomskyhierarchie einzuordnen versuchen. Diese Einordnung kann jedoch nur für wenige L-Systeme (E0LSysteme) problemlos erfolgen, bei anderen (S0L-Systeme) ist dies nur sehr schwer möglich.
Hopcroft und Ullman [HU00] ordnen die L-Systeme den indizierten Sprachen zu.
2.2.2 Das ursprüngliche L-System
Nach vielen Jahren der Forschung über Bäckerhefe, siehe Referenzliste der Publikationen von
Lindenmayer in [RS86], stellte Aristid Lindenmayer 1966 auf dem vierten Symposium für
Biomathematik und Informatik in Biowissenschaften in Houston sein mathematisches Modell
vor. 1968 wird sein Artikel zum mathematischen Modell gedruckt, auf das nun eingegangen
werden soll. Von Interesse ist in diesem Zusammenhang, dass Lindenmayer erst 1971 in
„Developmental Systems without cellular interactions, their languages and grammars“ die
Grammatiken für sein Entwicklungssystem eingeführt [RS86]. Das ursprüngliche Modell basiert auf Automaten und noch nicht auf Grammatiken.
Als Lindenmayer an seinem mathematischen Modell arbeitet, sind die aktuellen Themen in
der Entwicklungsbiologie die Kontrolle der Zellteilung, die Differenzierung von Zellen und
die Zellausdehnung. Getrieben von dem Gedanken, dass diese Operationen vom gesamten
Organismus selbst kontrolliert werden können, entwickelt er sein mathematisches Framework.
Lindenmayer erkennt dass es nicht mehr möglich ist, alle Kombinationen zu beherrschen,
wenn man über die Entwicklung von einer Hand voll Zellen hinausgehen möchte.
Das erste Beispiel zeigt einen endlichen Automaten, kurz EA, der ein lineares Array von Zellen verarbeiten soll.
Beispiel 2.2 für EA mit einseitiger Eingabe ohne Wachstum [Li68a]:
Das lineare Array von Zellen sieht im Anfangszustand wie folgt aus.
01100
Der EA hat nur zwei Zustände, 0 und 1, wobei die Ausgabe gleich dem Zustand ist.
Der Anfangszustand des EA ist abhängig von dem Zustand der ersten Zelle auf der
linken Seite. Endzustände sind sowohl 0 als auch 1. Das Eingabealphabet besteht ebenfalls aus 0 und 1. δ ist in der Tabelle 2.1 und im Transitionsdiagramm in Abbildung 2.1 beschrieben.
Eingabe
0
1
0
1
1
0
Zustände
0
1
Tabelle 2.1: Übergangsmatrix für den EA
0
0
1
1
0
1
Abbildung 2.1: Transitionsdiagramm für den EA
7
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Um den Automaten zu betreiben, wird noch eine willkürliche linke Eingabe benötigt.
Lindenmayer nennt diese Eingabe „environmental input“. In diesem Beispiel soll es 1
sein. Es ergibt sich folgende Zustandsänderung des Zellenarrays.
Abbildung 2.2: Übergang vom Anfangszustand zum ersten Folgezustand
Das Beispiel zeigt ein einfaches Verhalten für Zellenarrays. Das Problem ist, dass diese nur
zwischen zwei Zuständen wechseln können. Die Zellen können sich nicht teilen und auch
nicht absterben. Das Zellenarray kann zwar nicht sterben, aber sich auch nicht weiterentwickeln. Das nächste Beispiel zeigt, wie durch eine kleine Veränderung die Möglichkeit der
Zellteilung hinzugefügt wird.
Beispiel 2.3 für EA mit einseitiger Eingabe und mit Wachstum [Li68a]:
Alle Eigenschaften des EA aus dem Beispiel 2.1 werden übernommen, bis auf die Übergangsfunktion. Diese wird in Tabelle 2.2 beschrieben.
Zustände
0
1
Eingabe
0
1
0
1
11
0
Tabelle 2.2: Übergangsmatrix für den EA
In der folgenden Abbildung wird der Übergang vom Anfangszustand in den Folgezustand illustriert.
8
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.3: Einführung der Zellteilung und der entsprechende Folgezustand
Die EA aus den Beispielen ähneln dem Verfahren für die zellulären Automaten (weitere Informationen dazu in [Ja97] [De03]), haben aber gravierende Unterschiede. So hat das Array
keine feste Größe, sondern kann wachsen aber auch schrumpfen. Des Weiteren ist die Zustandsänderung bei einseitiger Eingabe nur abhängig von dem linken Kontext und nicht, wie
bei zellulären Automaten, abhängig vom linken und rechten Kontext. Außerdem ist der „environmental input“ ausschlaggebend für die Entwicklung der Zellen. Solch eine Eingabe existiert bei den zellulären Automaten ebenfalls nicht.
Diese beiden Beispiele zeigen die einfachste Implementierung des mathematischen Modells.
Ausgelegt sind diese Beispiele für einseitige Eingaben. Aus biologischer Sicht haben eindimensionale, fadenförmige Organismen zwei Seiten, von denen aus ein Organismus Eingaben
wahrnehmen kann. Dazu führt Lindenmayer die Vektorpfeile über der Übergangs- und Aus→
←
gabefunktion ein. So gilt δ für eine linksseitige Übergangsfunktion und δ für eine rechtsseitige Übergangsfunktion. Analog gilt es auch für die Ausgabefunktion λ . Lindenmayer führt
auch die Verzweigung in sein Entwicklungssystem ein. Um den Anfang einer Verzweigung
zu beschreiben, wird das Symbol „[“ verwendet. Das Ende einer Verzweigung wird entsprechend mit „]“ beschrieben. [Li68b]
2.2.3 Übersicht der L-Systeme
Nachdem das ursprüngliche mathematische Modell vorgestellt ist, werden jetzt die geläufigen
L-Systeme erläutert. Die L-Systeme sind als Grammatik definiert und nicht, wie ursprünglich,
als Automat. Es werden die wichtigsten L-Systeme besprochen und Beispiele für deren
Grammatik und Anwendung gegeben. Einige spezielle L-Systeme, die nicht unbedingt interessant für die Computergrafik sind, werden nur angerissen.
9
2
Theoretische Grundlagen von L-Systemen und deren Evolution
2.2.3.1 Basis L-System
Das 0L-System ist die Basis aller kontextfreien L-Systeme. Dabei steht die Null, nicht O (der
Buchstabe), wie in vielen Quellen falsch angegeben, vor dem L für ein kontextfreies LSystem, bzw. für ein L-System ohne Interaktion. Es folgen die wichtigsten Definitionen für
0L-Systeme, die auch für alle anderen kontextfreien L-Systeme gelten, es sei denn, es wird
explizit darauf hingewiesen, dass andere Definitionen für ein anderes L-System gelten.
Definition 2.1 für ein 0L-Schema [HR75]:
Ein 0L-Schema ist ein Paar S = Σ, P , wo Σ (das Alphabet von S) eine endliche, nicht
leere Menge ist und P (die Menge der Produktionen von S) eine endliche, nicht leere
Teilmenge von Σ × Σ * ist, so dass
(∀a )Σ (∃α )Σ* ( a,α ∈ P ) .
(2.1)
Jedes Symbol aus Σ muss mindestens eine Produktion in der Menge der Produktionen P aufweisen. Dabei ist es ausgeschlossen, dass ε, das leere Wort, auf der linken Seite einer Produktion steht.
Definition 2.2 für die direkte Ableitung [HR75]:
Sei S = Σ, P ein 0L-Schema, sei x = a1 ...a m mit m > 0 und a j ∈ Σ für j = 1,..., m
und sei y ∈ Σ * . Dann sagt man, dass x direkt y (in S) ableitet, und deutet es mit x ⇒ y
S
an, aber auch nur genau dann, wenn
(∃α 1 ,...,α m )Σ* a1 →α 1 ,..., a m →α m und ( y = α 1 ,...,α m ) .
((
P
)
P
)
(2.2)
Definition 2.3 für die endliche Sprache [HR75]:
Für ein 0L-Schema S = Σ, P , für ein Wort x in Σ * und für eine nicht negative ganze
Zahl n wird die endliche Sprache Ln (S , x ) durch Induktion über n definiert.
L0 (S , x ) = {x},
{
(
)}
Ln +1 (S , x ) = y (∃z ) z ∈ Ln (S , x ) and z ⇒ y .
S
(2.3)
(2.4)
Definition 2.4 für ein 0L-System [HR75]:
Ein 0L-System ist ein Tripel G = Σ, P, ω , wo S = Σ, P das 0L-Schema ist (auch
das Schema von G genannt) und ω (das Axiom von G) ist ein Wort über Σ .
Definition 2.5 für L(G) [HR75]:
Sei G = Σ, P, ω ein 0L-System. Die Sprache, die G generiert (oder einfach die Sprache von G), bezeichnet mit L(G ) , ist definiert als
*
⎧
⎫
L(G ) = ⎨ x w ⇒ x ⎬ .
G
⎩
⎭
(2.5)
Es folgt ein Beispiel zum besseren Verständnis von 0L-Systemen. Dabei steht das Symbol |
für eine alternative Auswahl und ist nicht Bestandteil des Alphabetes Σ.
10
2 Theoretische Grundlagen von L-Systemen und deren Evolution
Beispiel 2.4 für ein 0L-System:
Für ein 0L-System sei folgende Grammatik definiert.
G = ( Σ = { a, b, c, d , S , ε },
P = { S → ab,
a → a bc,
b → bda d ,
c → ε,
},
d → ab a
)
ω= S
Für die ersten drei Ableitungsschritte ergibt sich folgender Ableitungsbaum.
Abbildung 2.4: Ableitungsbaum zum Beispiel 2.4
Dementsprechend ist das Wort, welches aus diesem 0L-System generiert wird,
dε b d a a b a a = d b d a a b a a .
Anwendungsbereiche:
Diese einfache Familie der L-Systeme eignet sich für jegliche Art von fadenförmigen Organismen [Li68a] [Li68b]. Das hängt damit zusammen, dass Lindenmayer dieses mathematische
Modell für solche Organismen entwickelt hat. So kann jedes Zeichen aus dem Alphabet der
Grammatik eine Zelle in einem bestimmten Zustand abbilden, oder jedes Zeichen eine andere
Art von Zelle darstellen. Mit jedem Ableitungsschritt erkennt man, wie sich der Organismus
weiterentwickelt. Dabei kann sich eine Zelle teilen, die Zelle bleibt erhalten oder eine Zelle
stirbt ab. Das Absterben kann mit dem leeren Wort simuliert werden. Es ist klar, dass bei fadenförmigen Organismen die Betrachtung in der ersten Dimension geschieht. Daher kann das
Wort direkt interpretiert werden und ist auch leicht verständlich. Mit der späteren Einführung
einer Interpretation des Wortes (vgl. Kapitel 2.2.4) können andere Objekte betrachtet werden,
wie zum Beispiel Pflanzen, Gegenstände und Kreaturen [Pr93] [Pr86] [PL90] [PJM94]
[PHHM96b] [PHHM96a] [PHHM95] [BPFGK03] [HLP01] [Ho03] [HP01a] [HP01b]
[HP01c] [HP02] [GR92].
11
2
Theoretische Grundlagen von L-Systemen und deren Evolution
2.2.3.2 Deterministisches L-System
Das D in solch einem D0L-System steht für deterministic und bedeutet, dass für jede Ableitung eines Zeichen nur eine Möglichkeit der Ableitung existiert.
Definition 2.6 für ein D0L-Schema [HR75]:
Ein 0L-Schema S = Σ, P ist deterministisch, wenn für alle a in Σ exakt ein α in Σ *
der Form a →α existiert. Andernfalls, nennt sich S nicht deterministisch.
P
Definition 2.7 für ein D0L-System [HR75]:
Ein 0L-System G = Σ, P, ω ist deterministisch genau dann, wenn S deterministisch
ist.
Das 0L-System aus dem Beispiel 2.4 ist nicht deterministisch, weil für die Zeichen a und b
mehrere Möglichkeiten der Ableitung existieren. So kann schon nach dem zweiten Ableitungsschritt ein Wort wie ad entstehen, weil statt der Produktion a → bc die Produktion
a → a gewählt wurde und statt der Produktion b → bda die Produktion b → d .
Beispiel 2.5 für ein D0L-System [HR75]:
Für G sei folgendes D0L-System definiert.
G = ( Σ = { a, b, c, d , S , ε },
P = { S → ab,
a → bc,
b → bda,
c → ε,
},
d → ab a
ω= S
)
Auswirkungen:
Ist ein 0L-System deterministisch, so bedeutet dies, dass eine Zelle nicht mehrere Aktionen
ausführen kann. Um zum Beispiel abzusterben, muss sich die Zelle mittels einer Produktion in
eine andere Art von Zelle transformieren, die als einziges Zeichen auf der linken Seite das ε
hat.
2.2.3.3 Propagierendes L-System
Das P in einem P0L-System steht für propagating. Damit ist gemeint, dass in keiner Produktion des L-Systems das ε auftauchen darf.
Definition 2.8 für ein P0L-Schema [HR75]:
Ein 0L-Schema S = Σ, P heißt propagierend, wenn es keine Produktion in P der
Form a → ε gibt. (Eine Produktion dieser Form nennt man löschende Produktion.)
Andernfalls nennt man S nicht propagierend.
12
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Definition 2.9 für ein P0L-System [HR75]:
Ein 0L-System G = Σ, P, ω ist propagierend genau dann, wenn S propagierend und
ω ≠ ε ist.
Die in Beispiel 2.4 definierte Grammatik G für ein 0L-System ist nicht propagierend, weil das
Zeichen c eine löschende Produktion darstellt, welches gegen die Definition 2.8 verstößt. Folgendes Beispiel stellt ein P0L-System dar.
Beispiel 2.6 für ein P0L-System:
G = ( Σ = { a, b, c, d , S , ε
P = { S → ab,
},
a → a bc,
b → bda d ,
ω=
c → c,
d → ab a
S
},
)
Satz 2.1 über den Zusammenhang der L-Systeme:
Der gerichtete Graph soll folgenden Sachverhalt zeigen:
F (PD0 L ) ⊂ F (D0 L ), F (D0 L ) ⊂ F (0 L ),
F (PD0 L ) ⊂ F (P0 L )und F (P0 L ) ⊂ F (0 L )
Zwei Sprachfamilien, die nicht über einen Pfad erreichbar sind, sind zueinander inkompatibel aber nicht disjunkt.
Beweis:
[HR75]
Auswirkungen:
Ein P0L-System ist ein lebendes System, das heißt kein Zeichen kann durch die Anwendung
einer Produktion aus dem Wort gelöscht werden. Für einen fadenförmigen Organismus heißt
es, dass Zellen sich nur teilen können oder bestehen bleiben. Keine Zelle kann in einem lebenden System absterben. Der Organismus entwickelt sich immer weiter.
13
2 Theoretische Grundlagen von L-Systemen und deren Evolution
2.2.3.4 L-Systeme mit Terminalzeichen
Ein E vor einem E0L-System, steht für Extension.
Die Extension in solch einem 0L-Systeme ist die Einführung von Terminalzeichen. Durch die
E0L-Systeme erhält man eine Schnittstelle zu den Sprachen der Chomskyhierarchie. Im Gegensatz zu den Sprachen der Chomskyhierarchie können Terminalzeichen weiter abgeleitet
werden. Des Weiteren ist ein Wort in einer Sprache nur enthalten, wenn in einem Ableitungsschritt alle Zeichen des Wortes Terminalzeichen sind.
Definition 2.10 für ein E0L-System [HR75]:
Ein E0L-System ist ein Quadrupel der Form G = Σ, P, ω , ∆ , wo G = Σ, P, ω ein
0L-System ist und ∆ ⊂ Σ . Die Sprache von G (bezeichnet mit L = L(G ) ) ist definiert
durch L(G ) = L(G ) ∩ ∆* . In solch einem Fall bezeichnet L(G) eine E0L-Sprache.
Definition 2.11 für ein volles E0L-System [HR75]:
Ein E0L-System G = Σ, P, ω , ∆ und seine Sprache L(G) wird voll genannt genau
dann, wenn ∆ = Σ .
Beispiel 2.7 für ein E0L-System angelehnt an [HR75]:
Die Grammatik eines PE0L-System sei wie folgt definiert:
G = ( Σ = { S , A, B, A' , B' , F , a, b },
P = { S → AB,
A → AA' a,
B → BB' b,
A' → A' a,
B'→ B' b,
ω=
∆={
F → F,
a → F,
b→F
S,
a, b
},
} )
Es generiert die Wörter der Sprache L = {a n b n n > 0}.
Es werden zwei Ableitungsbäume für die ersten 3 Ableitungen gezeigt.
14
2 Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.5-a: Ableitungsbaum für Beispiel 2.7
Im dritten Ableitungsschritt hat das E0L-System das gültige Wort aabb generiert. Es
folgt eine alternative Ableitung.
Abbildung 2.5-b: Ableitungsbaum für Beispiel 2.7
Zu keinem Zeitpunkt ist nach einer Ableitung ein Wort erstellt worden, das nur aus Terminalzeichen besteht, daher wurde kein gültiges Wort für diese Sprache entwickelt.
15
2 Theoretische Grundlagen von L-Systemen und deren Evolution
2.2.3.5 L-Systeme mit Tabellen
Das T in einem T0L-System steht für table.
Bis jetzt waren alle 0L-Systeme in einer idealisierten Umgebung. Die Zellen eines Organismus haben sich geteilt, sind abgestorben oder einfach erhalten geblieben. Dabei spielte die
Umgebung, in der sie leben, keine Rolle. In der realen Welt verhält es sich anders. Hier entwickelt sich jeder Organismus so, wie seine Umgebung es ihm vorgibt. So wächst eine Pflanze beispielsweise nicht, wenn sie kein Wasser oder keine Sonnenstrahlen erhält. ,
Um verschiedene Umgebungssituationen in L-Systemen zu berücksichtigen, wurden T0LSysteme eingeführt. Jede Tabelle eines T0L-Systems steht für eine bestimmte Umgebungssituation, zum Beispiel für eine Umgebung mit Produktion, wenn es warm ist, kalt, hell oder
dunkel. Dabei kann zu jedem Ableitungsschritt nur die Produktionen einer Tabelle benutzt
werden.
Durch das Einführen von T0L-Systemen müssen einige Definition für die L-Systeme angepasst werden.
Definition 2.12 für ein T0L-Schema [HR75]:
Ein T0L-Schema ist ein Paar S = Σ, P , wo Σ (das Alphabet von S) eine endliche,
nicht leere Menge und P (die Menge an tables von S) eine endliche, nicht leere Menge
ist. Jedes Element P von P (auch table genannt) ist eine endliche, nicht leere Teilmenge von Σ × Σ * sodass
(∀a )Σ (∃α )Σ* ( a,α ∈ P ) .
(2.6)
Definition 2.13 für die direkte Ableitung [HR75]:
S = Σ, P sei ein T0L-Schema, x = a1 ,..., a m sei mit m > 0 und a j ∈ Σ für j = 1, ...,
m und y ∈ Σ * . Dann sagt man, dass x direkt y (in S) ableitet, und deutet es mit x ⇒ y
S
an, aber auch nur genau dann, wenn
(∃P )P (∃α 1 ,...,α m )Σ* a1 →α 1 ,..., a m →α m und ( y = α 1 ,...,α m ) .
((
P
P
)
)
(2.7)
Definition 2.14 für die endliche Sprache [HR75]:
Für ein T0L-Schema S = Σ, P , für ein Wort x in Σ * und für eine nicht negative
ganze Zahl n wird die endliche Sprache Ln (S , x ) durch Induktion über n definiert.
L0 (S , x ) = {x},
(2.8)
{
}
Ln +1 (S , x ) = y es existiert ein z in LN (S , x )und ein P in P so das z ⇒ y .
P
(2.9)
Definition 2.15 für ein PT0L-Schema [HR75]:
Ein T0L-Schema S = Σ, P heißt propagierend, wenn es keine Produktion in jedem
P von P der Form a → ε gibt (eine Produktion dieser Form nennt man löschende
Produktion). Andernfalls nennt man S nicht propagierend.
Definition 2.16 für ein DT0L-Schema [HR75]:
Ein T0L-Schema S = Σ, P ist deterministisch, wenn für alle P in P und für alle a in
Σ exakt ein α in Σ * der Form a →α existiert. Andernfalls nennt sich S nicht determiP
nistisch.
16
2 Theoretische Grundlagen von L-Systemen und deren Evolution
Definition 2.17 für ein T0L-System [HR75]:
Ein T0L-System ist ein Tripel G = Σ, P , ω , wo S = Σ, P
das T0L-Schema ist
(auch das Schema von G genannt) und ω (das Axiom von G) ein Wort über Σ ist. G ist
propagierend genau dann, wenn S propagierend ist und ω ≠ ε . G ist deterministisch
genau dann, wenn S deterministisch ist.
Definition 2.18 für L(G) [HR75]:
Sei G = Σ, P , ω ein T0L-System. Die Sprache, die G generiert (oder einfach die
Sprache von G), bezeichnet mit L(G ) , ist definiert als
*
⎧
⎫
L(G ) = ⎨ x w ⇒ x ⎬ .
G
⎩
⎭
(2.10).
Wie schon zu Anfang erwähnt, besteht der Unterschied zwischen einem T0L-Schema und
einem 0L-Schema darin, dass mehrere tables zur Verfügung stehen und benutzt werden können. Es wird nun definiert, wie dies geschieht.
Definition 2.19 für die Ableitungsfunktion eines T0L-Schemas [HR75]:
Sei S = Σ, P ein T0L-Schema. Eine Ableitung in S ist ein Tripel D = O, v, p , wo
O eine Menge von geordneten Paaren nicht negativer ganzer Zahlen ist (das Vorkommen in D), v ist eine Funktion von O in Σ (v(i, j) ist der Wert von D am Vorkommen
i, j ) und p ist eine Funktion von der Teilmenge O in P × (U P∈P P ) (p(i, j) ist der
table-Produktions Wert von D am Vorkommen i, j ), die folgenden Bedingungen befriedigen. Es existiert eine Sequenz von Worten ( x0 , x1 ,..., x n ) in Σ * (auch genannt der
trace von D und bezeichnet mit tr(D)) so dass
1. O = i, j 0 ≤ i ≤ f ,1 ≤ j ≤ xi ,
{
}
2. v(i, j) ist das jte Zeichen in xi ,
{
}
3. die Domäne von p ist i, j 0 ≤ i ≤ f ,1 ≤ j ≤ xi ,
4. für 0 ≤ i ≤ f , existiert ein P in P , sodass für 1 ≤ j ≤ xi ,
p(i, j ) = P, v(i, j ) → α j , wo v(i, j ) →α j und α 1α 2 ...α xi = xi +1 .
P
In solch einem Fall wird gesagt, dass D eine Ableitung über x f von x0 und f die Höhe
von der Ableitung D ist.
Die wichtigsten Eigenschaften sollen nun direkter formulieren werden. O bildet Zahlenpaare,
wobei die erste Zahl die Ableitung kennzeichnet und die zweite Zahl die Stelle des Wortes
(zum Beispiel im Wort aba ist an der zweiten Stelle b). Während O nur abstrakte Zahlenpaare
darstellt, kann O in v eingesetzt werden, um das Zeichen einer Stelle im Wort zu einer bestimmten Ableitung zu bestimmen (beim Beispiel aba wäre v(0, 2) = b). In p wird auch ein
Zahlenpaar von O eingesetzt, um die Ableitung für ein bestimmtes Symbol zu erhalten.
17
2 Theoretische Grundlagen von L-Systemen und deren Evolution
Beispiel 2.8 für ein T0L-System [HR75]:
Dieses Beispiel zeigt ein T0L-Schema und die ersten zwei Ableitungsschritte.
Sei S {a, b}, {P1 , P2 } , wobei P1 = a → a 2 , b → b 2 , P2 = a → a 3 , b → b 3 . Sei
{
}
{
}
D = O, v, p , wobei O = { 0,1 , 0,2 }∪ { 1, j 1 ≤ j ≤ 4}∪ {2, j 1 ≤ j ≤ 12},
v(0,1) = a, v(0,2 ) = b,
v(1, j ) = a für 1 ≤ j ≤ 2 , v(1, j ) = b für 3 ≤ j ≤ 4 ,
v(2, j ) = a für 1 ≤ j ≤ 6 , v(2, j ) = b für 7 ≤ j ≤ 12 ,
p(0,1) = P1 , a → a 2 , p(0,2) = P1 , b → b 2 ,
p(1, j ) = P2 , a → a 3 für 1 ≤ j ≤ 2 , p(1, j ) = P2 , a → b 3 für 3 ≤ j ≤ 4 .
D ist eine Ableitung über a 6 b 6 von ab. Abbildung 2.6 zeigt den Ableitungsbaum.
Abbildung 2.6: Ableitungsbaum vom Beispiel 2.8
Definition 2.20 für die Ableitungen eines T0L-Systems [HR75]:
Wenn G = Σ, P , ω ein T0L-System ist und S = Σ, P ein T0L-Schema, dann ist
eine Ableitung in S auch eine Ableitung in G. Im Einzelnen, eine Ableitung mit dem
trace x0 ,..., x f in G, derart das x0 = ω , ist eine Ableitung über x f in G.
2.2.3.6 L-Systeme mit Verzweigungen
Schon im „Ur-L-System“ führt Lindenmayer Verzweigungen ein. Bis jetzt wurde darauf noch
nicht weiter eingegangen. Das B in diesen B0L-Systemen steht für branching oder auch für
bracketed, weil die eckigen Klammern diese Verzweigungen darstellen. Viele Autoren nehmen die Verzweigungen in ein L-System mit auf und bezeichnen dieses L-System nicht explizit mit einem B. Ein Autor hat in einem Artikel [Ke86] sehr eng mit Lindenmayer zusammengearbeitet, als es um Verzweigungen in L-Systemen ging und diese entsprechend definiert. Daher soll zur Vervollständigung seine Generalisierung mit aufgeführt werden. Dabei
stehen die Zeichen „[“ für den Anfang einer Verzweigung und „]“ für das Ende, wie auch in
dem ursprünglichen L-System.
18
2 Theoretische Grundlagen von L-Systemen und deren Evolution
Definition 2.21 für ein B0L-Schema [Ke86]:
Ein B0L-Schema S = Σ ∪ {[, ]}, P ist ein 0L-Schema, wenn
1. die Zeichen [, ] nicht in Σ enthalten sind,
2. eine Produktion der Form [→ [ und ] →] existiert
3. und alle Wörter, die über die Produktionen erzeugt werden, wohlgeformte
Klammerausdrücke enthalten, falls Klammern enthalten sind.
Definition 2.22 für ein B0L-System [Ke86]:
Ein B0L-System ist ein Tripel G = Σ ∪ {[, ]}, P, ω , wo S = Σ ∪ {[, ]}, P das B0L-
Schema ist (auch das Schema von G genannt) und ω (das Axiom von G) ist ein Wort
über Σ .
Beispiel 2.9 für ein B0L-System:
Folgendes B0L-System sei definiert:
G = ( Σ = { a, ε } ∪ { [, ]
},
P = { a → a aa [a ] ε ,
ω=
[→ [,
]→]
a
},
)
Die ersten drei Ableitungsschritte illustriert die Abbildung 2.7.
Abbildung 2.7: Ableitungsbaum für das B0L-System
Auswirkungen:
Mit der Einführung von Verzweigungen ist es möglich, nicht nur eindimensionale fadenförmige Organismen, sondern auch zweidimensionale fadenförmige Organismen zu modellieren.
Man stelle sich vor, dass die Symbole Zellen darstellen und dabei Stränge bilden. Illustriert
wird dies in Abbildung 2.7. Die Auswirkungen sind weitreichend. Es können nun zum Beispiel Pflanzen modelliert werden, aber nur bis zur zweiten Dimension. Das reicht jedoch aus,
um die topologische Struktur von Pflanzen zu erzeugen. Es sei darauf hingewiesen, dass noch
keine graphische Interpretation des Wortes vorliegt, sondern nur das Wort selbst. Daher ist die
Interpretation in Abbildung 2.7 eine von vielen möglichen.
19
2 Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.8: Zweidimensionaler fadenförmiger Organismus
2.2.3.7 Stochastische L-Systeme
In der Natur ist es fast ausgeschlossen, dass komplexe Strukturen, wie zum Beispiel zwei
Bäume, identisch aussehen. Um eine gewisse Zufallskomponente in L-Systeme einzugliedern,
wurden die stochastischen 0L-Systeme eingeführt. Daher steht das S auch für stochastic. Im
frühen Stadium hießen diese L-Systeme probabilistic [JM86].
Bei einem „echten“ S0L-System existieren für jedes Symbol mehrere Produktionen. Jede dieser Produktionen bekommt eine Wahrscheinlichkeit von 0 bis 1 zugewiesen, Null selbst ist
aber ausgeschlossen, wobei die Summe aller Wahrscheinlichkeiten für alle Produktionen eines Symbols 1 ergeben muss. Somit ist es aus praktischer Sicht ausgeschlossen, dass ein S0LSystem deterministisch ist. Ein S0L-System mit nur einer Produktion für jedes Symbol ist
gleich einem D0L-System. Die Definition schließt nicht aus, dass S0L-Systeme einem D0LSystem entsprechen können.
Definition 2.23 für ein S0L-System [PL90]:
Ein S0L-System ist ein geordneter Quadrupel G = Σ, P, ω , π . Das Alphabet Σ, das
Axiom ω und die Menge der Produktionen P sind definiert wie in einem 0L-System.
Die Funktion π : P → (0,1] , bezeichnet als Wahrscheinlichkeitsverteilung, bildet die
Menge der Produktionen auf die Menge der Produktionswahrscheinlichkeiten ab. Man
kann davon ausgehen, dass für alle Symbole a ∈ Σ die Summe der Wahrscheinlichkeiten aller Produktionen mit der gleichen linken Seite gleich eins ist.
Beispiel 2.10 für stochastische Produktionen:
Es werden Beispiele für Produktionen mit Angabe von deren Wahrscheinlichkeit gegeben.
0.25
a → aba,
0.25
a → a,
0.5
a → bbba,
1
b → abc,
0.85
c → ccba,
0.15
c →ε
20
2 Theoretische Grundlagen von L-Systemen und deren Evolution
2.2.3.8 Parametrisierte L-Systeme
Parametrisierte 0L-Systeme bieten die Möglichkeit, bestimmte Regeln nur dann auszuführen,
wenn eine bestimmte arithmetische Bedingung erfüllt ist. Dabei muss gesichert sein, dass zu
jedem Zeitpunkt eine Ableitung für jedes Symbol in Σ existiert. Damit ist ein „echtes“ parametrisiertes L-System nie deterministisch. Wenn es nur eine Produktion für jedes Symbol in Σ
gibt, dann muss zwangsweise der Bedingungsteil immer wahr sein. Damit wäre es einem
D0L-System gleichzusetzen. Die Definition schließt nicht aus, dass parametrisierte 0LSysteme einem D0L-System entsprechen können. Mit der Einführung dieser L-Systeme wollten Prusinkiewicz und Lindenmayer beweisen, dass auch komplexe Strukturen mit LSystemen erzeugt werden können [De03]. Bis dahin waren Aono und Kunii [De03] davon
überzeugt, dass nur ihr dreidimensionales prozedurales Modell dazu in der Lage sei, jedoch
nicht die L-Systeme.
Formal sieht eine Produktion in einem parametrisierten 0L-System wie folgt aus:
Formalparameter
Bedingung
Aktualparameter
A( x, y ) : y ≤ 3 → A(x * 2, x + y )
Abbildung 2.9: Struktur einer Produktion eines parametrisierten 0L-Systems
Das Symbol A hat die Formalparameter x und y, denen ein Wert übergeben werden muss. Die
Produktion wird aber nur ausgeführt, wenn die Bedingung y ≤ 3 erfüllt ist. Den Symbolen auf
der rechten Seite der Produktion werden ein oder mehrere Aktualparameter übergeben, entsprechend der Definition der Formalparameter.
Definition 2.24 für parametrisiertes 0L-System [PL90]:
Ein parametrisiertes 0L-System ist definiert als ein geordneter Quadrupel
G = Σ, P, ω , Λ , wobei gilt
Σ ist das Alphabet des Systems,
*
P ⊂ Σ × Λ* × C (Λ ) × (Σ × E (Λ )) ist eine endliche Menge von Produktionen,
(
)
ω ∈ (Σ × ℜ )
* +
ist ein nicht leeres parametrisiertes Wort, genannt Axiom,
Λ ist die Menge von formalen Parametern.
In der Definition steht C(Λ) (Condition) für die Bedingung in der Produktion und E(Λ) (Expression) für den Ausdruck, der eingesetzt wird.
Beispiel 2.11 für ein parametrisiertes P0L-System [PL90]: 2
Das folgende parametrisierte P0L-System soll diese L-Systeme illustrieren. Dabei ist
Σ = {A, B, C}, ω = B(2 )A(4,4 ) und
P ={
A( x, y ) : y ≤ 3 → A( x * 2, x + y ),
A( x, y ) : y > 3 → B(x )A( x / y,0 ),
B(x )
: x < 1 → C,
B(x )
: x ≥ 1 → B( x − 1),
}
→C
C
2
Beispiel in der Quelle ist fehlerhaft.
21
2 Theoretische Grundlagen von L-Systemen und deren Evolution
Die Symbole x und y bilden Variablen, die beim Aufruf der Produktion ihre Werte übergeben bekommen und diese auch als Werte für die Aktualparameter weitergeben
können. Es ist eine Voraussetzung von parametrisierten L-Systemen, dass die Variablen auf der rechten Seite für die linke Seite benutzt werden oder für die Bedingung. Es
folgt der Ableitungsbaum für die ersten vier Ableitungsschritte.
Abbildung 2.10: Ableitungsbaum für das Beispiel 2.11
2.2.3.9 Kontextsensitive L-Systeme
Eine weitere Erweiterung der L-Systeme ist die Kontextsensitivität. Für diese Erweiterung,
für ein L-System mit interaction, steht das I in dem Begriff IL-System. Statt I kann auch <k,
l> stehen, wobei mit k die maximale Sensitivität der linken Seite bezeichnet wird und mit l
die der rechten Seite. Alternativ kann für <k, l> auch die Summe von k und l stehen. Wenn
zum Beispiel k = 1 und l = 0 ist, dann könnte man <1, 0> L-System schreiben, aber auch 1LSystem.
Neben den vielen Möglichkeiten für die Bezeichnung solcher L-Systeme gibt es auch zwei
Formen der Bezeichnungen für das eigentliche L-System, im Speziellen für die Produktionen.
Diese Unterschiede kommen zustande, weil zwei wichtige Forscher, Rozenberg und Prusinkiewicz, in ihren Büchern und Artikeln zwei verschiedene Notationen benutzen. Weil sich die
vorliegende Arbeit mehr in die Richtung Computergrafik bewegt, wird der Notation von Prusinkiewicz der Vorzug gegeben. Der Unterschied liegt in der Beschreibung der Produktionen.
Die Produktion eines IL-Systems bei Prusinkiewicz hat die Form [PL90]
al < a > a r → α .
Dabei steht al für den Kontext auf der linken Seite und a r für den Kontext auf der rechten
Seite. al und a r können auch eine Länge größer eins besitzen oder nicht vorhanden sein. Ist
al für eine Produktion nicht vorhanden, kann auch die Schreibweise
a > ar → α
verwendet werden. Analog gilt für a r die Schreibweise
al < a → α .
Die Produktion eines IL-Systems bei Rozenberg hat die Form [HR75]
< al , a, a r >→ α .
22
2 Theoretische Grundlagen von L-Systemen und deren Evolution
Bei einem IL-System ist es auch Voraussetzung, dass zu jedem Zeitpunkt jedes Symbol in Σ
abgeleitet werden kann. Zu diesem Zweck können auch kontextfreie Produktionen in solch
einem L-System enthalten sein. Sollte zu einem Zeitpunkt für ein Symbol sowohl eine kontextfreie als auch eine kontextsensitive Produktion zutreffen, wird der kontextsensitiven Produktion der Vorzug gegeben.
Die Anwendung für diese L-Systeme ist die Nachbildung des Signalflusses von Organismen.
Dadurch ist es möglich, diverses Verhalten so zu beschreiben, dass es Schritt für Schritt verfolgbar ist. Dies ermöglicht es, das Aufblühen einer Blüte oder das Absterben von Pflanzenteilen schrittweise zu modellieren. Dadurch gibt es einen harmonischen Übergang und kein
„plötzliches Erscheinen“ mehr.
Beispiel 2.12 für ein PIL-System [PL90]:
Das Beispiel zeigt an einem trivialen PIL-System, wie Signale weitergegeben werden
können. Sei Σ = {a, b}, P = {b < a → b, b → a, a → a} und ω = baaaaaaaa . Der Ableitungsbaum für die ersten vier Ableitungen zeigt Abbildung 2.10.
Abbildung 2.11: Signalverlauf in einem PIL-System
2.2.3.10 Weitere L-Systeme
Die bisher besprochenen L-Systeme stellen nur ein Bruchteil der L-Systeme dar, die noch
existieren. Einige der noch nicht besprochenen L-Systeme sollen hier lediglich angerissen
werden, um die Mächtigkeit und die Vielfalt der L-Systeme zu zeigen.
Map L-System:
Map L-Systeme wurden entwickelt, um die Modellierung von zellularen Ebenen zu ermöglichen. Indem man L-Systeme mit planaren Graphen und Zyklen kombiniert, können komplexe
topologische Strukturen von Zellen modelliert werden. Mit Hilfe des Graphen erhält man eine
Karte, in der die Kanten Zellwände darstellen und Regionen die Zellen. Dabei gilt, dass keine
Inseln innerhalb einer Region existieren dürfen. Kanten selbst können auch Zyklen bilden.
[PL90]
23
2 Theoretische Grundlagen von L-Systemen und deren Evolution
Timed L-System:
Aus der Sicht der Computergrafik, interessiert man sich nicht nur für Bilder, sondern auch für
Animationen. Sie werden mit diesen „getakteten“ L-Systeme ermöglicht. Dazu werden jedem
Symbol Zahlen zugewiesen. Dabei bedeutet die Zahl auf der linken Seite das Sterbealter und
die auf der rechten Seite das Initialalter. Beim Ablauf einer Animation wird eine globale Zeitvariable hochgezählt. Erreicht eine Produktion ihr Sterbealter, wird diese Produktion angewendet und fängt mit dem initialen Alter an zu leben. Ein Problem bei diesem Modell ist, dass
es kontextfrei ist. Somit kann man zwar das Wachstum animieren, aber nicht mit der Umgebung interagieren. [PL90]
dL-System:.
Das Problem der Timed L-Systeme kann durch ein Hybrid Modell beseitigt werden. Dabei
erzeugt das L-System die topologischen Informationen, der eigentliche Animationsprozess
wird durch Differentialgleichungen gesteuert. [De03]
Unary L-System:
Das Besondere an diesen L-Systemen ist, dass sie unär sind. In ihrem Alphabet Σ ist lediglich
ein Symbol enthalten. Diese L-Systeme finden eher Anwendung im theoretischen Bereich. Sie
sind nicht so mächtig wie die anderen L-Systeme, bieten aber die Möglichkeit einer kompletten Charakterisierung der Grammatik. [HR75]
Weitere L-Systeme, die eher theoretischen Charakter haben, sind zum Beispiel A0L-Systeme
[KR92] oder Partitions-limitierte L-Systeme [Gä96]. Andere spezielle L-Systeme sind Value
L-Systeme oder „forgetful“ L-Systeme [CJ92] [KPM92].
Das eigentliche Problem ist die Bezeichnung von L-Systemen. Es gibt eine große Anzahl von
L-Systemen, so dass einige Buchstaben doppelte Bedeutungen aufweisen. Beispielsweise hat
Rozenberg das S für symmetrische L-Systeme verwendet. Das S steht aber auch für stochastische L-Systeme.
2.2.3.11 Kombination von L-Systemen
An einigen Beispielen wurde bereits gezeigt, dass sich die L-System-Erweiterungen (T0L,
E0L) und Beschränkungen (D0L, P0L) miteinander kombinieren lassen. Dabei sind fast alle
erdenklichen Kombinationen möglich, zum Beispiel ET0L-System, PD2L-System oder
PSTEIL-System.
Nur sehr wenige L-Systeme lassen sich nicht mit anderen kombinieren, weil es deren Definition nicht zulässt. Ein Beispiel sind die Timed L-Systeme. In ihrer Definition werden diese
genauer als Timed D0L-Systeme definiert.
Die Buchstaben der einzelnen L-Systeme müssen nicht in einer bestimmten Ordnung stehen.
So kann ein L-System PT0L-System oder auch TP0L-System heißen. Es ist aber üblich, dass
die Symbole 0, 1, 2, ... bis I vor dem L stehen. Sollten Tabellen genutzt werden, dann käme
als Nächstes ein T links vom Kontext. Wenn Extentions verwendet werden, dann folgt nun
ein E links vom T oder links vom Kontext. Links außen steht ein P, wenn es propagierend ist,
gefolgt von einem D, wenn es deterministisch ist. Alle anderen Symbole liegen in der Regel
zwischen den Bereichen, die von den Zeichen P und D sowie E, T und Kontext gebildet werden.
Abschließend werden die besprochenen L-Systeme noch einmal in einer Übersichtstabelle
zusammengefasst.
24
2 Theoretische Grundlagen von L-Systemen und deren Evolution
L-System
0L-System
D0L-System | deterministic
P0L-System | propagating
E0L-System | Extension
T0L-System | table
B0L-System | branching
S0L-System | stochastic
Parametrisierte 0L-System
IL-System | interaction
Map L-System
Timed L-System
dL-System | differential
U0L-System | unary
Bedeutung
Basis Lindenmayer System
Für jedes Symbol existiert nur
eine Produktion
ε ist nicht Bestandteil einer
Produktion
Einführung von Terminal- und
Nichtterminalzeichen
Einführung von mehreren
Mengen von Produktionen
Einführung von Verzweigungssymbolen
Zuweisung der Produktionen
einer Wahrscheinlichkeit für
deren Ausführung
Erweiterung der Produktionen
um eine Bedingung und um
Parameter, die übergeben werden können
Erweiterung der L-Systeme um
Kontextsensivität
Graphenbasierte L-Systeme
Produktionen werden um Initial- und Sterbealter erweitert,
um eine Animation zu realisieren
Animation einer Szene von LSystemen durch Differentialgleichungen
Beschränkung des Alphabetes
Σ auf ein Symbol.
Anwendung
Siehe Kapitel 2.1.7
Zum automatischen Entwickeln eines Wortes
Entwicklung ohne Absterben
von Teilen des Wortes
Schnittstelle zur Chomskyhierarchie
Verschiedene Umgebungen
modellieren (warm, kalt, ...)
Mehr dimensionale Strukturen
Automatische Entwicklung
eines Wortes, wobei unterschiedliche Worte generiert
werden können
Realisierung von komplexen
Strukturen.
Modellierung von Signalverläufen
Zellebenen modellieren
Animation eines einzelnen LSystems
Animation mehrerer LSysteme mit Interaktion ihrer
Umgebung
Komplette Charakterisierung
der L-Systeme
Tabelle 2.3: Übersichtstabelle der besprochenen L-Systeme
2.2.4 Wachstumsfunktion
Im Beispiel 2.1 wurde demonstriert, dass das Wort eines L-Systems schnell anwachsen kann.
Um dieses Wachstum mathematisch zu beschreiben, wurden Wachstumsfunktionen eingeführt. Die Wachstumsfunktion ist eine Funktion, die zu jedem Zeitpunkt die Anzahl der Symbole eines Wortes wiedergeben kann.
Bei einem D0L-System ist zu beobachten, dass die Wachstumsfunktion nicht abhängig von
der Anordnung der Symbole im Wort ist und auch nicht von den abgeleiteten Wörtern ist.
Daher ergibt sich eine Relation zwischen dem Vorkommen der Anzahl der Symbole und den
zwei Wörtern µ und υ, wobei µ das abzuleitende Wort ist und υ das abgeleitete Wort darstellt.
Diese Relation lässt sich durch eine Matrix beschreiben.
25
2 Theoretische Grundlagen von L-Systemen und deren Evolution
Es sei ein D0L-System gegeben, dessen Alphabet Σ geordnet ist, Σ = {a1 ,..., a m }, dabei ist m
eine nicht negative ganze Zahl, die größer Null ist. Die Matrix Qm×m ist so aufgebaut, dass
jedes Element qi , j das Vorkommen eines Symbols a j auf der rechten Seite einer Produktion
ai enthält. Sei aik die Anzahl des Vorkommens eines Symbols ai in einem generierten Wort
x und k die Anzahl der entsprechenden Ableitungsschritte. Es lässt sich nun eine Matrix für
die direkte Ableitung eines Wortes auf der Grundlage eines D0L-Systems erzeugen. [PL90]
q12
q1m ⎤
L
⎡q11
⎢q
q 22
q 2 m ⎥⎥
L
21
k
k
⎢
a1
am
L
= a1k +1 L
a mk +1
⎥
⎢ M
⎥
⎢
qm2
q mn ⎦
L
⎣q m1
[
]
[
]
Beispiel 2.13 für die Wachstumsfunktion [PL90]:
Es sei folgendes PD0L-System gegeben.
},
G = ( Σ = { a, b
P = { a → ab,
},
b→a
)
ω= a
Die entsprechende Matrix lautet
1⎤
⎡1
k +1
ak
bk
b k +1
⎢1
⎥= a
0
⎣
⎦
oder alternativ
a k +1 = a k + b k = a k + a k −1 .
Lässt man nun einige Ableitungsschritte durchführen (für k = 1, 2, 3, ...), erhält man
für die Wachstumsfunktion die Fibonacci-Reihe: 1, 1, 2, 3, 5, 8, ... .
[
]
[
]
Rozenberg und Salomaa zeigen in „The Mathematical Theory of L Systems“ [PL90], dass die
Wachstumsfunktion eines jeden D0L-Systems eine Kombination aus einer exponentialen und
ganz rationalen Funktion ist.
Aus der Sicht der realistischen Pflanzenmodellierung sind solche Wachstumsfunktionen nicht
verwendbar, weil eine Pflanze nicht exponentiell wächst. Um das Pflanzenwachstum realistisch zu modellieren, wäre daher die Nutzung einer Sigmoidfunktion für D0L-Systeme oder
die Nutzung der Funktion einer Quadratwurzel für IL-Systeme angebracht. [PL90]
In diesem Zusammenhang sei noch erwähnt, dass Wachstumsfunktionen einen eher theoretischen Charakter haben. In der Praxis ist es nur sehr schwer möglich, eine Wachstumsfunktion
für ein gegebenes L-System zu finden. Eine differenzierte Darstellung der Materie ist zu finden bei. [HR75]
2.2.5 Klassifikation / Einordnung
L-Systeme in Verbindung zu setzen oder zusammenzubringen ist keine leichte Aufgabe. Weil
der Schwerpunkt dieser Arbeit nicht auf der Klassifikation von L-Systemen liegt, werden die
L-Systeme größtenteils in einen bestimmten Kontext eingeordnet. Da die Grafiken sehr groß
sind, werden sie ausgegliedert und auf ein Poster gedruckt. Wie alle anderen Abbildungen
liegen aber auch sie auf der CD-ROM vor.
26
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Zeitstrahl:
Die wichtigsten besprochenen L-Systeme werden auf einen Zeitstrahl gesetzt und chronologisch nach dem Datum ihrer Veröffentlichung eingeordnet. Dabei wird zu jedem L-System
auch die entsprechende Quelle angegeben. Der Zeitstrahl fängt beim ursprünglichen mathematischen Modell im Jahre 1968 an und endet 1993 mit den differentiellen L-Systemen zur
Realisierung von Animationen. Seit 1993 sind keine weiteren wichtigen L-Systeme dazugekommen, bzw. bestehende unbedeutende haben sich nicht zu bedeutsamen L-Systemen qualifiziert [HR75] [PL90] [De03] [RS86].
Eigenschaften von L-Systemen:
Die verschiedenen Eigenschaften von L-Systemen lassen sich stark vereinfacht in zwei Klassen einordnen.
In die erste Klasse lassen sich die Beschränkungen einordnen. Basierend auf einem Ausgangssystem, in diesem Fall das 0L-System, wird untersucht, welche Beschränkungen für
dieses System vorliegen, beispielsweise ob es deterministisch ist oder propagierend, wobei die
beschränkten L-Systeme eine echte Teilmenge zum Ausgangssystem bilden [HR75]. Je gravierender die Beschränkung ist, desto weiter weg steht das L-System vom Ausgangssystem.
Da einem P0L-System nur ein Zeichen fehlt, bildet es keine große Beschränkung. Eine größere Beschränkung ist dagegen, dass für jedes Symbol in Σ nur eine Produktion existieren darf.
Die größte Beschränkung ist jedoch, das Σ nur ein Symbol enthalten darf, wie bei den U0LSystemen. All diese L-Systeme stellen eine beschränkte Eigenschaft dar und reduzieren die
Mächtigkeiten der Sprache.
In die zweite Klasse lassen sich die Erweiterungen der L-Systeme einordnen. Je weiter weg
eine Erweiterung ist, desto größer ist die Leistung, die diese Erweiterung mit sich bringt.
Das einfache getrennte Hinzufügen von zwei Klammern bei den B0L-Systemen stellt dabei
die niedrigste Erweiterung dar. Die Klammern sind zwar für die graphische Interpretation
wichtig, aber nicht für die reine Mächtigkeit der Sprache. Timed L-Systeme und S0L-Systeme
bilden eine größere Erweiterung zum Ausgangssystem. S0L-Systeme ermöglichen eine kontrollierte automatische Ableitung von nicht deterministischen Produktionen, während Timed
L-Systeme den Produktionen ein Initial- und Sterbealter zuweisen, um festzulegen, wann diese angewandt werden. Die mächtigsten Erweiterungen bilden die T0L, IL, E0L, dL und die
parametrisierten L-Systeme. Mit den T0L-Systemen ist die Modellierung von mehreren Umgebungen möglich. Durch IL-Systeme lassen sich Signalflüsse modellieren. Die E0LGrammatik führt ein zweites Alphabet ein, das der Terminale. Die dL-Systeme bieten die
Möglichkeit der Animation von ganzen Szenarien, bzw. das kontrollierte Wachstum solcher
Szenarien. Mit parametrisierten L-Systemen ist die Modellierung sämtlicher komplexer Objekte möglich. Alle diese L-Systeme erweiterten das 0L-System um zusätzliche Mächtigkeit.
Schnittstellen zur Chomskyhierarchie:
Hierfür wurde auf [HR75] zurückgegriffen, Theorem 10.10. Dabei stellt eine gerichtete Kante
dar, dass die eine Sprachfamilie in der anderen enthalten ist. Wenn zwei Sprachfamilien durch
einen Pfad in diesem gerichteten Graphen nicht erreichbar sind, sind diese inkompatibel aber
nicht disjunkt. Dabei ergibt sich Folgendes:
Klassen sind nicht disjunkt
(Lemma 10.1 [HR75]).
F (RG ) ⊂ F (CF )
(Theorem1.1 [HR75]),
F (0 L ) ⊂ F (T 0 L )
(Lemma 10.2 [HR75]),
F (0 L ) ⊂ F (IL )
(Lemma 10.2 [HR75]),
F (0 L ) ⊂ F (E 0 L )
(Lemma 10.2 [HR75]),
F (CF ) ⊂ F (E 0 L )
(Corollary 10.1 [HR75]),
F (T 0 L ) ⊂ F (ET 0 L )
(Lemma 10.2 [HR75]),
27
2
Theoretische Grundlagen von L-Systemen und deren Evolution
F (IL ) ⊂ F (EIL )
F (E 0 L ) ⊂ F (ET 0 L )
F (ET 0 L ) ⊂ F (CS )
F (CS ) ⊂ F (RE )
F (EIL ) ⊂ F (RE )
F (RG ) ⊂ F (T 0 L )
F (RG ) ⊂ F (IL )
F (0 L ) ⊂ F (CF )
F (T 0 L ) ⊂ F (IL )
F (T 0 L ) ⊂ F (E 0 L )
F (IL ) ⊂ F (CS )
(Lemma 10.2 [HR75]),
(Lemma 10.2 [HR75]),
(Theorem 10.5 [HR75]),
(Theorem 1.1 [HR75]).
(Theorem 10.6 [HR75]).
(Lemma 10.1 [HR75]),
(Lemma 10.1 [HR75]),
(Theorem 10.4 [HR75]),
(Theorem 10.9 [HR75]),
(Theorem 10.8 [HR75]),
(Theorem 10.7 [HR75]).
Entscheidungsautomat:
Die wichtigsten L-Systeme für die Computergrafik werden zu einem Automaten zusammengefügt. Der Automat erzeugt eine Zeichenkette, die ein L-System darstellt, das die Eigenschaften aufweist, die zur Modellierung benötigt werden. Dazu wird in jedem Zustand eine
Frage gestellt, die mit „J“ (Ja) oder „N“ (Nein) beantwortet werden muss. Zu jeder Frage gibt
es ein Beispiel, das die Frage verständlicher machen soll.
2.2.6 Interpretation des Wortes
1986 stellt Prusinkiewicz [Pr86] eine Möglichkeit vor, wie man das Wort eines L-Systems
graphisch interpretieren könnte. Dabei zeigt er, wie man die Turtle-Interpretation für Wörter
der L-Systeme nutzen könnte. Szilard und Quinton zeigen schon 1979, wie die TurtleInterpretation auf D0L-Systemen genutzt werden könnte, aber erst Prusinkiewicz verallgemeinerte dies. [Ja97] Hinter dieser Entwicklung steht die Idee, das Verhalten einer Schildkröte bei der Bewegung zu imitieren (sie läuft so lange geradeaus ohne die Richtung zu ändern,
bis sie gestört wird oder durch ein anderes Bedürfnis dazu veranlasst wird).
Prusinkiewicz definiert die Turtle-Interpretation wie folgt.
Definition 2.25 für die Turtle-Kommandos im 2D-Raum [Pr86]:
Der Zustand einer Schildkröte ist ein Tripel (x, y, α), wobei die Koordinaten (x, y), die
Position der Schildkröte repräsentieren und der Winkel α, auch Ausrichtung der
Schildkröte genannt, die Richtung angibt, in die die Schildkröte blickt. Des Weiteren
soll d die Schrittweite eines Schrittes sein und δ die Schrittweite, die bei einer Veränderung des Winkels α benutzt wird. Es seien folgende Kommandos für die Schildkröte
definiert:
F
Die Schildkröte bewegt sich um die Schrittweite d vorwärts und zeichnet eine
Gerade über die Strecke, die sie zurücklegt. Dabei verändert sich der Zustand
der Schildkröte wie folgt:
(x, y,α ) → (x + d cos α , y + d sin α ,α )
f
Die Schildkröte bewegt sich um die Schrittweite d vorwärts und zeichnet keine
Gerade. Dabei verändert sich der Zustand der Schildkröte wie folgt:
(x, y,α ) → (x + d cos α , y + d sin α ,α )
+
Die Richtung der Schildkröte wird um die Schrittweite δ weiter nach links ausgerichtet. Der Zustand der Schildkröte verändert sich wie folgt:
( x , y , α ) → ( x, y , α + δ )
28
2
Theoretische Grundlagen von L-Systemen und deren Evolution
-
Die Richtung der Schildkröte wird um die Schrittweite δ weiter nach rechts
ausgerichtet. Der Zustand der Schildkröte verändert sich wie folgt:
( x , y , α ) → ( x, y , α − δ )
Alle anderen Symbole werden ignoriert.
Definition 2.26 für die Turtle-Interpretation im 2D-Raum [Pr86]:
Sei ν eine Zeichenkette, (x 0 , y 0 , α 0 ) der Startzustand der Schildkröte sowie d und δ
feste Parameter. Basierend auf die Kommandos, die in der Zeichenkette ν enthalten
sind, wird das Bild von der Schildkröte gezeichnet. Dies nennt man TurtleInterpretation von ν im zweidimensionalen Raum.
Beispiel 2.14 für die quadratische Koch Insel [Pr86]:
Es sei folgendes PD0L-System definiert.
},
G = ( Σ = { F ,−,+
P=
F → F + F − F − FF + F + F − F ,
ω = F+F+F+F
)
Des Weiteren sei δ = 90°. Es folgt die Turtle-Interpretation für die ersten drei generierten Worte. Sie bildet die quadratische Koch Insel.
Abbildung 2.12: Die graphische Interpretation der ersten vier generierten Worte
Um nicht nur eine starre Folge von aneinander gereihten Linien zu erhalten, werden zur Turtle-Interpretation zwei weitere Symbole hinzugefügt. Diese Symbole helfen Verzweigungsstrukturen miteinzubeziehen. In Anlehnung an Lindenmayer werden dazu die Symbole „[“
und „]“ hinzugenommen.
29
2
[
]
Theoretische Grundlagen von L-Systemen und deren Evolution
Lege den aktuellen Zustand der Schildkröte auf den Stack. Es können auch optional
weitere Attribute (wie die Farbe) oder Parameter (wie d) mit abgelegt werden.
Hole einen Zustand vom Stack und setze die Schildkröte an die Position, die der Zustand beschreibt. Zeichne beim Umsetzen der Schildkröte keine Linie. Sollten weitere
Attribute vorliegen (wie Farbe) oder Parameter (wie d), dann werden die derzeitigen
Attribute und Parameter mit den neuen aktualisiert.
Sollte ein Zustand vom Stack geholt werden und dieser ist leer, dann gibt es für das Wort υ
keine Interpretation. In diesem Fall wird eine Fehlermeldung ausgegeben.
Beispiel 2.15 für eine einfache Pflanze [Pr86]:
Durch die Bedeutung der Symbole lassen sich zum Beispiel Pflanzen beschreiben. Die
graphische Interpretation dieses Beispiels soll eine Pflanze darstellen. Gegeben sei
folgendes PDB0L-System.
G = ( (Σ = { F , X ,−,+}) ∪ {[, ]},
P ={ X
→ F − [[ X ] + X ] + F [+ FX ] − X ,
F
→ FF ,
[
→ [,
},
]
→]
)
ω=X
Des Weiteren sei δ = 22,5°. Die folgenden Abbildungen zeigen die Ergebnisse nach
drei, vier, fünf und sechs Ableitungen. Die Bilder aus den Beispielen 2.14 und 2.15
wurden mit dem Applet LSys.Class generiert. [[Lap]]
Abbildung 2.13: Die Entwicklung einer Pflanze mit einem L-System
30
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Eine zweidimensionale Grafik, welche die topologische Struktur der Pflanze visualisiert,
reicht nicht aus, um realistische Pflanzen zu modellieren. . Hierzu muss die TurtleInterpretation in den 3D-Raum überführt werden.
Zu diesem Zweck wird zu der x- und y-Koordinate noch die z-Koordinate hinzugefügt sowie
eine Rotationsmatrix M. Mit Hilfe der Rotationsmatrix kann sich die Schildkröte um jede
Achse drehen, dazu muss nur die Rotationsmatrix mit einer entsprechenden Matrix verknüpft
werden. Die Abbildung 2.13 verdeutlicht dies.
z
x
y
Abbildung 2.14: Turtle-Interpretation im dreidimensionalen Raum
Definition 2.27 für die Turtle-Kommandos im 3D-Raum [De03]:
Der Zustand einer Schildkröte ist ein Quadrupel (x, y, z, M), wobei die Koordinaten
(x, y, z) die Position der Schildkröte repräsentieren. M ist die Rotationsmatrix, die die
Ausrichtung der Schildkröte im dreidimensionalen Raum beschreibt, also wohin die
→
Schildkröte blickt. Des Weiteren soll d die Schrittweite eines Schrittes sein und δ die
Schrittweite, die bei einer Veränderung der Rotationsmatrix M benutzt wird. Außerdem seien folgende Rotationsmatrizen definiert, die angewandt werden, wenn auf einer entsprechenden Achse rotiert wird.
0
0 ⎞
⎛1
⎟
⎜
R x (δ ) = ⎜ 0 cos δ − sin δ ⎟
⎜ 0 sin δ cos δ ⎟
⎠
⎝
⎛ cos δ
⎜
R y (δ ) = ⎜ 0
⎜ sin δ
⎝
0 − sin δ ⎞
⎟
1
0 ⎟
0 cos δ ⎟⎠
⎛ cos δ sin δ 0 ⎞
⎟
⎜
R z (δ ) = ⎜ − sin δ cos δ 0 ⎟
⎜ 0
0
1 ⎟⎠
⎝
31
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Es seien folgende Kommandos für die Schildkröte definiert:
F
Die Schildkröte bewegt sich um die Schrittweite d vorwärts und zeichnet eine
Gerade über die Strecke, die sie zurücklegt. Dabei verändert sich der Zustand
der Schildkröte wie folgt:
→
→
→
(x, y, z, M ) → ⎛⎜⎜ x + ⎛⎜ M d ⎞⎟ , y + ⎛⎜ M d ⎞⎟ , z + ⎛⎜ M d ⎞⎟ , M ⎞⎟⎟
⎝
⎠1
⎝
⎠2
⎝
⎠3
⎠
⎝
f
Die Schildkröte bewegt sich um die Schrittweite d vorwärts und zeichnet keine
Gerade. Dabei verändert sich der Zustand der Schildkröte wie folgt:
→
→
→
(x, y, z, M ) → ⎛⎜⎜ x + ⎛⎜ M d ⎞⎟ , y + ⎛⎜ M d ⎞⎟ , z + ⎛⎜ M d ⎞⎟ , M ⎞⎟⎟
⎝
⎠1
⎝
⎠2
⎝
⎠3
⎠
⎝
+
rotiert die Schildkröte um die Schrittweite δ auf der y-Achse. Der Zustand der
Schildkröte verändert sich wie folgt:
(x, y, z, M ) → (x, y, z, M ⋅ R y (δ ))
rotiert die Schildkröte um die Schrittweite -δ auf der y-Achse. Der Zustand der
Schildkröte verändert sich wie folgt:
(x, y, z, M ) → (x, y, z, M ⋅ R y (− δ ))
&
^
\
/
|
rotiert die Schildkröte um die Schrittweite δ auf der x-Achse. Der Zustand der
Schildkröte verändert sich wie folgt:
(x, y, z, M ) → (x, y, z, M ⋅ Rx (δ ))
rotiert die Schildkröte um die Schrittweite -δ auf der x-Achse. Der Zustand der
Schildkröte verändert sich wie folgt:
(x, y, z, M ) → (x, y, z, M ⋅ Rx (− δ ))
rotiert die Schildkröte um die Schrittweite δ auf der z-Achse. Der Zustand der
Schildkröte verändert sich wie folgt:
(x, y, z, M ) → (x, y, z, M ⋅ Rz (δ ))
rotiert die Schildkröte um die Schrittweite -δ auf der z-Achse. Der Zustand der
Schildkröte verändert sich wie folgt:
(x, y, z, M ) → (x, y, z, M ⋅ Rz (− δ ))
dreht die Schildkröte um 180° um die y-Achse. Der Zustand der Schildkröte
verändert sich wie folgt:
(x, y, z, M ) → (x, y, z, M ⋅ R y (180°))
Alle anderen Symbole werden ignoriert.
Die eckigen Klammern behalten ihre Funktion bei und verändern sich nicht bei der
Turtle-Interpretation im dreidimensionalen Raum.
Definition 2.28 für die Turtle-Interpretation im 3D-Raum:
Sei ν eine Zeichenkette, (x 0 , y 0 , z 0 , M 0 ) der Startzustand der Schildkröte sowie d und
δ feste Parameter. Das Bild, das von der Schildkröte auf der Basis der Kommandos
gezeichnet wurde, die in der Zeichenkette ν enthalten sind, nennt man TurtleInterpretation von ν im dreidimensionalen Raum.
Im Rahmen der Erläuterung der L-Systeme wurden bereits parametrisierte L-Systeme vorgestellt. Diese Parametrisierung kann auch auf die Kommandos der Schildkröte ausgeweitet
werden. Da es nur zwei Arten von Kommandos gibt, bewegen und rotieren, soll exemplarisch
für jeweils eines dieser beiden Kommandos die parametrisierte Form angegeben werden. Für
die anderen ist es dann analog umzusetzen.
32
2
F
+
Theoretische Grundlagen von L-Systemen und deren Evolution
Die Schildkröte bewegt sich um die Schrittweite d, welche einen Parameter w übergeben bekommt, vorwärts und zeichnet eine Gerade über die Strecke, die sie zurücklegt.
Dabei verändert sich der Zustand der Schildkröte wie folgt:
→
→
→
(x, y, z, M ) → ⎛⎜⎜ x + ⎛⎜ M d (w)⎞⎟ , y + ⎛⎜ M d (w)⎞⎟ , z + ⎛⎜ M d (w)⎞⎟ , M ⎞⎟⎟
⎝
⎠1
⎝
⎠2
⎝
⎠3
⎠
⎝
rotiert die Schildkröte um die Schrittweite δ, die einen Parameter w übergeben bekommt, auf der y-Achse. Der Zustand der Schildkröte verändert sich wie folgt:
(x, y, z, M ) → (x, y, z, M ⋅ R y (w))
w kann dabei ein Wert aus dem Bereich der reellen Zahlen sein, entsprechend dem Wert in
einem parametrisierten L-System.
Neben diesen Standardkommandos gibt es noch einige zusätzliche Kommandos, die zur realistischen Modellierung von Pflanzen hinzugefügt werden. [PL90] [PHM]
So gelten die Punkte in den geschweiften Klammern ( { } ) als Punkte für ein Polygon, das
bei der graphischen Interpretation eine geschlossen Fläche darstellt. Damit lassen sich zum
Beispiel Blätter darstellen. Mit dem Ausrufezeichen ( ! ) kann der Astdurchmesser vermindert
werden und mit dem ' Zeichen kann die Farbe gesteuert werden.
Es gibt aber keinen standardisierten Satz an Kommandos. Daher können weitere Kommandos,
die nicht zum besprochenen Vorrat der Kommandos zählen, in Applikationen verwendet werden. Im folgenden Beispiel werden einige dieser proprioritären Kommandos benutzt. Die Applikation, die diese 3D-Grafik erstellt hat, heißt L-System 4 und ist auf der CD-ROM mit enthalten. Zur Interpretation dieser zusätzlichen Kommandos sei auf die Hilfe-Datei verwiesen.
Beispiel 2.16 für einen mit L-System 4 [[LS4]] erzeugten Baum:
Es sei folgendes parametrisiertes PDB0L-System gegeben, welches eine 3D-Grafik erzeugt, die in Abbildung 2.14 zu sehen ist.
G = ( (Σ = {A, B, C , D, F , L, T , c, f , g , t , z ,&,$, ' ~, _, >,−,+}) ∪ {[, ]},
P = { T → CCA,
A → CBD > (94)CBD > (132) BD,
B → [&CDCD$A],
D → [ g (50) Lg (50) Lg (50) Lg (50) Lg (50) Lg (50) L],
C → !(.95)~ (5)tF ,
F → ' (1.25) F' (.8),
L → [~f (200)c(8){+(30) f (200)-(120) f (200)-(120) f (200)}],
f → z,
z → _,
[ → [,
},
] →]
)
ω = c(12)T
Des Weiteren soll δ = 20° sein und der Astdurchmesser = 50. Für die Abbildung 2.14
wurden 15 Ableitungsschritte durchgeführt. Das Wort enthält 1.337.641 Zeichen und
17.494 3D-Primitive.
33
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.15: Die 3D-Grafik zum Beispiel 2.16
Anzumerken ist, dass es auch alternative graphische Interpretationen gibt. Diese haben sich
gegenüber der Turtle-Interpretation jedoch nicht durchsetzten können, vergleiche dazu
[HR75].
2.2.7 Übersicht über Anwendungsmöglichkeiten von L-Systemen
Bisher wurden zahlreiche L-Systeme sowie ihre Interpretation vorgestellt. Auch einige Stichworte zu den Anwendungsmöglichkeiten von verschiedenen L-Systemen wurden bereits genannt. Dieses Kapitel stellt einige weitere Anwendungsmöglichkeiten vor. Dabei werden die
Anwendungen in drei Kategorien eingeteilt.
Computergrafik:
Pflanzenmodellierung
Durch die Arbeiten von Prusinkiewicz ist die Pflanzenmodellierung einer der Hauptanwendungsbereiche für L-Systeme [PL90].
Modellierung auf zellularer Ebene
Prusinkiewicz [PL90] nutzte L-Systeme auch zur Visualisierung ihrer ursprünglichen
Funktion. Dazu kombiniert er Map L-Systeme, die die topologische Struktur beschreiben,
mit einer graphischen Interpretation, die auf [NLA86] basiert. Hierbei ist das Axiom ein
reguläres Polynom, das die Anfangskarte mit deren Grenzen enthält. Die Zellwand ist eine
Linie, entsprechend den Kanten des Graphen. Wenn eine Wand durch eine Produktion ge34
2
Theoretische Grundlagen von L-Systemen und deren Evolution
teilt wird, dann werden alle Folgewände auf die gleiche Länge ausgerichtet. Die Position
einer Wand basiert auf der Position zweier passender Marker.
Fraktale
Eine weitere Anwendung finden L-Systeme in der Beschreibung und graphischen Interpretation von Fraktalen. So erzeugt ein L-System mit dem Axiom ω = F-F-F-F und der
Produktion P = F → F − F + F + FF − F − F + F eine quadratische Koch Insel. Dabei
wird ein Winkel von δ = 90° angenommen [PL90]. Die Arterien eines Menschen sind ebenfalls fraktalartig aufgebaut. In [Za01] werden Arterien mit L-Systemen modelliert.
Gebirge und Gegenstände
ELSYS ist ein Programm, das für Sun Workstations entwickelt wurde. Es enthält einen LCompiler, ein Grafikmodul und ein User Interface. Der L-Compiler übersetzt eine Sprache
in eine Grafiksequenz für das Grafikmodul. Dabei besteht die Sprache aus einer Mischung
aus Hochsprache, L-Systemen und Erweiterungen für L-Systeme. So können Gebirge auf
der Basis von Fraktalen erzeugt werden, die mit L-Systemen beschrieben wurden. [GR92]
Kreaturen und Gegenstände
Hornby nutzt L-Systeme in Verbindung mit den Genetischen Algorithmen. Dabei wird eine Initialpopulation erzeugt, die aus Produktionen von L-Systemen besteht. Dieser Population wird mit Hilfe von Genetischen Algorithmen und deren Operatoren verändert. So
werden, neben Kreaturen, Gegenstände wie Tische oder Bauanleitungen auf der 2D-Ebene
für Roboter entwickelt. [HLP01] [HP01a] [HP01b] [HP01c] [HP02]
Städtebau
Das Programm City-Engine wird zur kompletten Entwicklung von Städten genutzt, wobei
L-Systeme zur Beschreibung von vollständigen Straßenzügen und allen Gebäuden verwendet werden [Bü03].
Federn
Federn weisen eine ähnliche Struktur wie Blätter auf. Deshalb kann vom Wissen der
Pflanzenmodellierung profitiert werden, um auch Federn zu modellieren. [Gw03]
Theoretische Informatik:
Einordnung in die Chomskyhierarchie
Rozenberg zeigt in [HR75], wie mit Hilfe der E0L-Systeme eine Verbindung zur Chomskyhierarchie aufgebaut werden kann. Weiterhin wird versucht, wie in [Gä96], L-Systeme
so weit wie möglich in die Chomskyhierarchie einzugliedern. Weitere Quelle zum Thema.
[Wa01]
Komplexität von L-Systemen
Für komplexere L-Systeme ist es schwer, wenn nicht unmöglich, die Berechnungskomplexität zu bestimmen. Wie schon im Kapitel über die Wachstumsfunktionen erwähnt, haben Rozenberg und Salomaa in „The Mathematical Theory of L Systems“ gezeigt, dass die
Wachstumsfunktion von D0L-Systemen allgemein eine Kombination aus exponentieller
und polynominaler Funktion ist. Weitere Quellen zur Komplexität von L-Systemen.
[LS92] [Ke86]
Zugehörigkeitsproblem
Eine zentrale Frage im Bereich der Formalen Sprachen ist, ob ein willkürliches Wort zur
Sprache einer Grammatik gehört. Für grundlegende 0L-Systeme wird das Problem in
[HR75] behandelt.
Äquivalenzproblem
Eine weitere Forschungsrichtung in diesem Bereich setzt sich mit der Frage auseinander,
ob zwei willkürliche Grammatiken von L-Systemen dieselbe Sprache erzeugen. Auch hier
hat Rozenberg in seinem Buch [HR75] die Grundlagen für typische L-Systeme gelegt.
Weitere Quellen zum Äquivalenzproblem. [Ru86] [CK86] [Hon01] [Wa01]
35
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Sonstige Anwendungen:
Routing Tabellen für Computernetze
In [LT86] wird gezeigt, wie man das Problem der anwachsenden Routing Tabellen alternativ lösen kann. Umsetzungen lassen sich über Table L-Systeme oder auch mit Map LSystemen realisieren.
Parallele Kommunikationssysteme
In [Pa92] wird gezeigt, dass L-Systeme auch die Rolle eines parallelen Kommunikationssystems annehmen können.
Modellierung der Netzhaut
GREDEA ist ein Monitor-Programm zur Prüfung der Blutzirkulation der Netzhaut. Dabei
werden zur Beschreibung der Netzhaut parametrisierte L-Systeme verwendet. [KTV99a]
[KTV99b]
Erzeugung von Musik
Tom Johnson nutzte L-Systeme zur Generierung von Musikstücken. Dabei wird ein LSystem erzeugt und n mal abgeleitet. Jedes erzeugte Wort bildet eine Stimme. Das erste
Wort die tiefste und das letzte erzeugte Wort die höchste Stimme. Alle Stimmen werden
auf ein Tempo befördert, indem der Tonwechsel bei der ersten Ableitung am langsamsten
und bei der letzten Ableitung am schnellsten ist [Be01]. Informationen zur Erzeugung von
Musik mit L-Systemen als Beschreibung und mit Genetischen Algorithmen zur Generierung findet man bei [Fo00]. Andere Anwendungen in der Musik findet man in [DB03].
Evolution von Neuronalen Netzen
In [Sc02] werden einfache Beispiele dafür gezeigt, wie die Beschreibung von L-Systemen
genutzt werden kann, um Neuronale Netze wachsen zu lassen. So kann mit Genetischen
Algorithmen die Produktionen eines L-Systems verändert werden und anschließend ein
Wort generiert werden, welches als Neuronales Netz interpretiert werden kann.
Analyse von Gehölzen
L-Systeme können aber auch zur Analyse von Gehölzen benutzt werden. So können der
Saftfluss oder die Auswirkungen von Parasiten und Insekten auf die Holzqualität verfolgt
werden. Spezialist auf diesem Gebiet ist Prof. Dr. Winfried Kurth von der BTU Cottbus.
In seiner Monografie [Ku99] geht er auf Analysemöglichkeiten mit seinem Programm
GROGRA ein.
2.3 Alternativen zu L-Systemen
L-Systeme stellen ein diskretes mathematisches Modell dar. Biologen nutzen alternativ zu LSystemen auch ein kontinuierliches mathematisches Modell, um das Verhalten von Zellen zu
modellieren, vergleiche dazu [Ab86].
In diesem Kapitel werden nur die Modelle vorgestellt, die auch auf Grammatiken basieren
und eine gewisse Ähnlichkeit zu L-Systemen aufweisen, bzw. L-Systeme wesentlich erweitern.
Einen Kompromiss zwischen L-Systemen und den Sprachen der Chomskyhierarchie stellen
die Indian Parallel Systems dar. Hierbei wird, wie bei den Sprachen der Chomskyhierarchie,
eine Produktion ausgesucht. Diese Produktion wird dann auf alle Symbole angewandt, die zu
der Produktion passen. [Kud86]
36
2
Theoretische Grundlagen von L-Systemen und deren Evolution
2.3.1 P-Systeme
P-Systeme basieren auf einem weiteren mathematischen Modell, das sich an die Natur anlehnt. Eingeführt wurden die P-Systeme von Gheorghe Paun in [Pa98]. Das biologische Vorbild ist die Membrane, statt von P-Systemen spricht man deshalb auch von „Membrane Computing“. Dabei sollen P-Systeme biologische Membranen nicht modellieren, sondern einige
Eigenschaften dieser Membranen nutzen.
ElementarMembran
Umgebung
Haut
Membran
Region
Abbildung 2.16: Beispiel einer Membran
Das System besteht aus einer Basismembran, deren Kanten die Haut darstellen. Auf diese
Weise wird die Basismembran von ihrer Umgebung getrennt. Die Basismembran kann weitere Membranen enthalten. Somit erhält man eine hierarchische Struktur. Sind in der Region
einer Membran keine weiteren Membranen enthalten, wird sie als Elementarmembran bezeichnet. Jede Membran hat eine Region, in der sich Objekte und Regeln befinden. Dabei
stellen die Objekte Buchstaben eines Alphabetes dar und die Regeln die Produktionen. Die
Anwendung der Produktion findet im Basismodel, wie in einem 0L-System, parallel und nicht
deterministisch statt. .
Objekte können von einer Membran zu einer anderen weitergereicht werden. Membranen
selbst können ihre Durchlässigkeit verändern, sich aufblähen und teilen.
Für weitere Informationen, empfiehlt es, sich die Homepage der P-Systeme zu besuchen,
http://psystems.disco.unimib.it/ (Stand: 05/2004). [PR01] [Pa00]
2.3.2 Eco-Grammar Systeme
Eco-Grammar Systeme werden 1994 in „Eco (grammar) systems“ von Păun, Kelemenová,
Kelemen und Csuhaj-Varjú [Cs02] eingeführt. Die folgende Abbildung illustriert ein solches
Eco-Grammar System.
37
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.17: Ein vollständiges Eco-Grammar System [CPS95]
Die Idee, die hinter den Eco-Grammar Systems steckt, ist die Modellierung eines Ökosystems. In einem Eco-Grammar System gibt es zwei Rollen, die eines Agenten (Agents) und die
Rolle der Umgebung (Environment). Dabei darf es eine beliebige Menge von Agenten geben,
aber nur eine Umgebung. Alle Agenten leben in einer gemeinsamen Umgebung.
Die Umgebung kann sich selbst unabhängig von den Agenten weiterentwickeln, die Agenten
dagegen entwickeln sich abhängig von der Umgebung. Dadurch nehmen die Agenten die
Umgebung wahr. Die Umgebung selbst muss die Agenten nicht wahrnehmen, sie kann aber
durch die Agenten verändert werden. Der Zustand eines jeden Agenten und der der Umgebung wird durch eine Zeichenkette repräsentiert. Die Agenten und die Umgebung stellen jeweils ein L-System dar, welches für sich selbst definiert ist. Außerdem existiert eine globale
Zeit für das Ökosystem. Zu jedem Zeitschritt führen die Agenten und die Umgebung eine
Ableitung ihres Zustandes aus. Für die Agenten ist immer nur eine Teilmenge der Produktionen „aktiv“, dies ist abhängig vom Zustand der Umgebung. Mit diesen Produktionen leiten
die Agenten ihren neuen Zustand ab.
Im zweiten Schritt agieren die Agenten auf die Umgebung mit speziellen „action rules“. Dabei kann ein Agent nur eine Produktion in einem Zeitschritt auswählen. Die Auswahl ist dabei
abhängig vom Zustand des Agenten. Die Aktion, die ein Agent mit der Produktion auf die
Umgebung ausübt, hat Priorität gegenüber der Entwicklung der Umgebung. Die Symbole der
Umgebung, die durch diese Aktion nicht berührt wurden, entwickeln sich weiter, indem sie
eine Produktion aussuchen und diese anwenden. Die „action rules“ wirken sich auch auf die
Zustände der anderen Agenten aus. [CPS95] [Cs02]
38
2
Theoretische Grundlagen von L-Systemen und deren Evolution
2.4 Genetische Algorithmen
Im Folgenden wird eine kurze Übersicht zum Thema Genetische Algorithmen gegeben. Sie
stellen die Basis dar, auf der später die Mutation von L-Systemen besprochen werden. Ausführliche Literatur zu dem Thema ist in [Mi99] [Ja97] zu finden.
1975 führte John Holland in „Adaption in Natural and Artificial Systems“ die Genetischen
Algorithmen ein.
Die genetischen Algorithmen kommen aus der Genetik. Dabei enthält ein Chromosom, auch
Genotyp genannt, die Erbinformationen. Das Individuum, das entsteht, wird Phänotyp genannt.
Genetische Algorithmen werden benutzt, um für ein Problem, für das auf üblichen Wegen
keine Lösung gefunden wird, in einer akzeptablen Zeit eventuell doch noch eine Lösung zu
finden.
In GA werden die Chromosomen durch Bit-Strings repräsentiert, wobei jedes Bit den Wert 0
oder 1 einnehmen kann. In einer Programmiersprache wie Pascal würde man ein boolesches
Array zur Repräsentation eines Chromosoms nehmen.
Der prinzipielle Ablauf eines GA ist in Abbildung 2.17 illustriert.
Abbildung 2.18: PAP eines GA [Sch03]
39
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Am Anfang wird eine zufällige Anfangspopulation erzeugt, das heißt eine nicht leere Menge
von Chromosomen, und geprüft, ob diese schon das Problem löst. Sollte dies nicht der Fall
sein, dann wird die Fitness eines jeden Individuums der Population bestimmt. Dabei wird jedem Individuum ein Maß zugeschrieben, das die Nähe zur Lösung beschreibt.
Per Zufall wird eine Operation ausgewählt. Entsprechend der Operation werden zwei Individuen oder nur ein Individuum benötigt. Um nun diese Individuen aus der Population herauszusuchen, wird eine Selektion durchgeführt. Bei dieser Selektion sind mehrere Varianten
möglich.
Tournament:
Eine bestimmte Anzahl von willkürlich gewählten Individuen wird zu einer Gruppe
zusammengestellt. Auf der Basis der Fitness der Individuen in der Gruppe werden,
entsprechend der Operation, ein oder zwei Individuen selektiert.
Roulette:
Die Auswahlwahrscheinlichkeit steigt proportional zur Fitness.
Overselection:
Die Auswahlwahrscheinlichkeit wird so manipuliert, dass das stärkste Drittel der Population eine Gewichtung von 80 % und der Rest eine Gewichtung von 20 % bekommt.
Nun wird der Operator auf die Individuen angewandt. Bei den GA existieren drei verschiedene Operatoren, die sich an die Natur anlehnen.
Der erste GA-Operator ist die Reproduktion. Bei der Reproduktion wird das Individuum nicht
verändert und in die neue Population eingefügt.
Beim Operator Crossover werden zwei Individuen verändert und in die neue Population eingefügt. Dabei wird ein Teil des Bit-Strings zwischen den beiden Individuen vertauscht. Beide
Bit-Strings müssen aber die gleiche Länge aufweisen.
Abbildung 2.19: Crossover in GA
Der Crossover Operator kann so modifiziert werden, dass mehr als zwei Individuen benutzt
werden, dass mehrere Teile des Bit-Strings der Individuen vertauscht werden oder dass beides
gleichzeitig stattfindet.
Der letzte Operator ist die Mutation. Bei der Mutation werden an zufälligen Stellen des BitStrings die Bits gekippt. Die Anzahl der zu kippenden Bits muss vorher festgelegt werden.
Das mutierte Individuum wird dann in die neue Population eingefügt.
40
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.20: Mutation in GA
Der Genetische Algorithmus bricht idealerweise bei einer Lösung ab. Es sollte dem Anwender
trotzdem jederzeit die Möglichkeit zur Verfügung stehen, den GA abzubrechen und auf die zu
diesem Zeitpunkt beste Lösung zurückzugreifen, denn die Laufzeit des GA kann sehr lang
sein. [Bo04] [Ja97] [Mi99] [Sch03] [Ban03]
2.5 Genetische Programmierung
In diesem Kapitel wird nur so weit ein Überblick über die Genetische Programmierung gegeben, wie es für das Verständnis der Mutation von L-Systemen notwendig ist. Eine ausführlichere Erörterung dieses Themas findet sich bei [BNKF98].
Die früheste Form der Genetischen Programmierung geht auf Smith (1980) zurück. Er arbeitete mit Bit-Strings, wie es bei den Genetischen Algorithmen der Fall ist, mit dem Unterschied, dass die von ihm angewendeten Bit-Strings eine variable Länge hatten. In den Folgejahren werden weitere frühe Formen der Genetischen Programmierung entwickelt, bis Koza
1992 die jetzige Form der Genetischen Programmierung einführte.
Wie die GA wird auch die Genetische Programmierung benutzt, um ein Problem zu lösen,
wenn dies mit herkömmlichen Lösungsmethoden nicht oder nicht in einer akzeptablen Zeit
erreicht werden kann. Beispiele für den Anwendungsbereich sind Intrusion Detection, Evolution von Hardware-Eigenschaften und Klassifikation von Bildern in Geo-Systemen.
Bei der Genetischen Programmierung werden Programme einer Evolution unterzogen. Die
Population ist eine Menge von Programmen. Da sich ein Programm nicht in Bit-Strings formulieren lässt, müssen dazu andere Datenstrukturen eingesetzt werden.
Abbildung 2.21: Automat als Datenstruktur
41
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Die drei gängigsten Datenstrukturen sind der Baum, eine lineare Sequenz von Befehlen und
ein Automat. Beim Einsatz von Automaten kann man sich drei Bereiche vorstellen, eine CPU,
einen Stackspeicher und einen indizierten Speicher. Dabei ist die CPU der eigentliche Automat, in dem ein Start- und ein Endzustand existiert sowie Zustände, die eine arithmetische
oder logische Funktion ausführen. Ein Zustand kann auch ein Unterprogramm darstellen.
Durch die Verbindung von Zuständen mit Kanten wird ein Programm erzeugt. Ein GP-System
auf der Basis von Automaten ist PADO. Leider existiert zu PADO keine Web-Präsenz, daher
werden alternativ die Links zu Astro Teller ( http://www-2.cs.cmu.edu/~astro/astropapers.html (Stand: 06/2004) ) und Manuela M. Veloso ( http://www-2.cs.cmu.edu/~mmv/
(Stand: 06/2004) ) angeboten, beide sind die Entwickler von PADO.
Abbildung 2.22: Lineare Struktur
Bei der linearen Sequenz von Befehlen wird ein Computer nachgebaut. So besteht ein Programm aus einer CPU mit einer bestimmten Anzahl von Registern, die eine lineare Sequenz
von Befehlen abarbeitet. In den Befehlen sind nur arithmetische und logische Operationen
möglich. AIMGP war solch ein GP-System. Aus AIMGP ist Discipulus entstanden. Weitere
Informationen sind unter http://www.aimlearning.com (Stand: 06/2004) zu finden.
42
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.23: Bäume als Datenstruktur
Bäume als Datenstruktur für Programme sind die gebräuchlichste Lösung. Dabei stellen die
Blätter Variablen oder Konstanten dar und die restlichen Knoten arithmetische oder logische
Operationen. Der Vorteil von Bäumen ist, dass auch ein IF THEN/ELSE Konstrukt verwendet
werden kann. Die Bäume sind dabei die Repräsentation eines Ausdruckes in Präfix-Notation.
So steht der Baum in Abbildung 2.22 für den Ausdruck
( + 1 ( IF ( > TIME 10 ) 3 4 ) )
in Präfix-Notation. Der Ausdruck in imperativer Form ist
if (TIME > 10) then
return 1 + 3;
else
return 1+4;
Diese Bäume nennt man auch S-Expression. Die Kombination aus S-Expression und GA ergibt dann GP. Daher ist der prinzipielle Programmablauf identisch mit dem der GA, siehe
Abbildung 2.17. Der Unterschied liegt in den Operatoren und in deren Ausführung auf die
Datenstruktur.
Die Reproduktion eines Individuums ist einfach. Eine Kopie des Individuums wird in die
nächste Population eingefügt.
Auch der Crossover-Operator bei der linearen Sequenz von Befehlen ist unproblematisch. Da
alle Programme auf der gleichen CPU laufen, können Sequenzen einfach vertauscht werden.
Bei Bäumen wird ein Teilbaum von zwei Individuen selektiert und vertauscht.
43
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.24: Crossover in GP – Lineare Befehlssequenz
Abbildung 2.25: Crossover in GP - Baum
44
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Problematischer wird es bei der Mutation von Bäumen. Hier gibt es zwei Möglichkeiten der
Mutation. Bei der Tree Mutation wird ein bestehender Teilbaum durch einen neuen zufälligen
Teilbaum ersetzt. Die zweite Möglichkeit ist die Linear Mutation. Dabei wird ein Knoten des
Baumes ausgewählt und durch einen neuen zufälligen Knoten ersetzt, wobei der neue Knoten
sich in die Präfix-Notation eingliedern lassen muss. So kann ein einzelner Operand nicht
durch eine einzelne Operation ersetzt werden und umgekehrt.
Abbildung 2.26: Mutation beim Baum
Bei der Mutation von linearen Befehlssequenzen wird zu Anfang eine komplette Sequenz
herausgesucht. Im Anschluss wird per Zufall eine der drei folgenden möglichen Mutation
gewählt.
1. Ein Register wird durch ein anderes existierendes Register ersetzt.
2. Eine Konstante wird durch eine andere im Wertebereich liegende Konstante geändert.
3. Die Operation wird durch eine andere erlaubte Operation ersetzt.
45
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Beispiel 2.17:
Folgende Sequenz soll mutieren, wobei die CPU die Register R0, R1 und R2 besitzt und
die arithmetischen Operationen +, -, MUL und DIV versteht sowie die bit-orientierten logischen Operationen AND, OR und NOT. Der Wertebereich für die Konstanten liegt im
Wertebereich der ganzen nicht negativen Zahlen.
R0 = R1 + 4
Mögliche Mutationen:
R1 = R1 + 4
R0 = R2 + 4
R0 = R1 + 8
R0 = R1 MUL 4
(1.
(1.
(2.
(3.
Fall)
Fall)
Fall)
Fall)
Beim Umgang mit den Populationen gibt es zwei Ansätze. Entweder wird bei jedem Durchlauf des Algorithmus durch Selektion und die Anwendung eines Operators eine komplett neue
Population erzeugt oder das neue Individuum wird in die bestehende Population eingegliedert,
wofür ein anderes Individuum weichen muss. Den zweiten Ansatz nennt man auch steadystate. Er hat den Vorteil, dass zu jedem Zeitpunkt die bisher beste Lösung verfügbar ist. Bei
der Erzeugung einer neuen Population kann die bisher beste Lösung verloren gehen. Diese
Ansätze gelten auch für die GA. Wie die GA ist auch die GP sehr rechenaufwendig.
[BNKF98] [Ban03] [Bo04] [Ja97]
2.6 Mutation von L-Systemen
Hier sollen einige Gedanken zu der Frage aufgegriffen werden, wie L-Systeme mutieren können. Dabei werden zwei Ansätze betrachtet, der Einsatz von GA und der Einsatz von GP.
Gerade L-Systeme, die eine Erweiterung zum 0L-System darstellen, sind sehr interessant für
die Mutation. So könnte bei einem T0L-System durch Mutation die Umgebung manipuliert
werden. Bei stochastischen L-Systemen könnte die Wahrscheinlichkeitsverteilung auf die
Regeln variiert werden. Bei IL-Systemen könnte der Kontext verändert werden. In der Praxis
werden zur Mutation am häufigsten parametrisierte PDB0L-Systeme verwendet, weil sich
hiermit komplexere Objekte realisieren lassen. Im praktischen Einsatz ist es empfehlenswert,
ein deterministisches L-System oder ein stochastisches zu verwenden, damit der Anwender
nicht in jeden Ableitungsschritt eingreifen muss.
2.6.1 Mutation von L-Systemen unter Genetischen Algorithmen
Bei der Anwendung von GA mit L-Systemen muss erst bestimmt werden, was ein Chromosom ist. Entgegen der eigentlichen Definition eines Chromosoms, dass es ein Bit-String mit
einer fixen Länge ist, stellt eine Produktion ein Chromosom dar. Dieses Chromosom hat keine
feste Länge. Viele Autoren (zum Beispiel Fox [Fo00] und Mock [Mo96]) sprechen trotzdem
von GA, nur wenige sprechen von Evolutionären Algorithmen (zum Beispiel Kókai
[KTV99a] und Hornby [Ho03]). Da die Idee auf GA basiert, wird auch hier von GA gesprochen.
46
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.27: Eine Produktion als Chromosom
Die Abbildung von Evolution auf L-Systeme unter der Nutzung von GA hat sich als die gängigste Variante herausgestellt. Dabei werden am häufigsten parametrisierte PDB0L-Systeme
verwendet. Daher werden die folgenden Mutationsmöglichkeiten auch an solchen Produktionen gezeigt. Die Erweiterung der Mutation auf andere L-Systeme sollte dann nicht mehr
schwer fallen.
Point Mutation:
Hierbei wird nur ein einzelnes Symbol oder eine Zahl eines L-Systems angefasst. Es können
auch mehrere einzelne Symbole oder Zahlen verändert werden, sie stehen bei dieser Mutationsart nur nicht in einem bestimmten Kontext.
Bezogen auf die Argumente, die in parametrisierten L-Systemen übergeben werden, heißt es,
das Zahlen verändert werden können oder eine Variable durch eine andere ersetzt werden
kann. Es sei daran erinnert, dass die Variablen in einer Produktion benutzt werden müssen,
die als Formalparameter angegeben wurden, ansonsten wäre die Produktion ungültig. Neben
dem Ersetzen können auch Argumententeile gelöscht werden. Dabei muss der Operator mitgelöscht werden. In den Argumenten können auch neue Zahlen oder vorhandenen Variablen
mit einem Operator neu hinzugefügt werden. Das Verändern der Anzahl der Argumente ist
eine problematische Aufgabe. Denn dadurch müssen alle anderen Produktionen, die auf diese
Produktion zugreifen, entsprechend auch verändert werden. Auch ein gültiger Ausdruck für
die Formalparameter muss der veränderten Produktion übergeben werden.
Das Einfügen, Löschen und Ersetzen kann man auch auf einzelne Symbole der Produktion
beziehen. Dabei ist zu beachten, dass beim Löschen auch der Argumententeil mitgelöscht
wird. Beim Ersetzen eines Symbols durch ein anderes muss gesichert werden, dass die Argumente auch zur Produktion des Symbols passen. Sollte ein Symbol in eine Produktion eingefügt werden, dann muss auch ein entsprechender Argumententeil generiert werden. Das Ersetzen eines Symbols auf der linken Seite einer Produktion würde in einem deterministischen
System bedeuten, dass zwei Symbole vertauscht werden und dass die jeweilige rechte Seite
geprüft werden muss, wenn die Anzahl der Argumente beider Symbole ungleich ist. Dementsprechend müssten auch alle anderen Produktionen, die das Symbol enthalten, verändert werden.
Insgesamt ist es nicht ratsam, die Anzahl von Argumenten oder das Symbol auf der linken
Seite einer Produktion zu mutieren. Es ist auch nicht ratsam, eckigen Klammern einer Point
Mutation zu unterziehen, denn ein Wort ist nur interpretierbar, wenn die Anzahl der offenen
und geschlossenen Klammern gleich ist. Zu überprüfen, ob das Verändern einer Klammer
Auswirkungen hat, ist nur sehr schwer möglich.
47
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.28: Beispiele für Point Mutation
Die folgenden Mutationsarten beziehen sich immer auf die linke Seite der Produktion, mit
Ausnahme der letzten.
Inversion:
Hierbei wird ein zusammenhängender Teil der Produktion herausgenommen, die Reihenfolge
der Symbole umgekehrt und wieder an die gleiche Stelle eingefügt. Das Einbeziehen von eckigen Klammern sollte dabei verhindert werden. Dadurch ist die Wahrscheinlichkeit sehr
hoch, dass ein ungültiges Wort generiert wird. Argumente können innerhalb ihres Argumententeils invertiert werden, es ist dabei nur zu beachten, dass alle Operanden mit einem Operator gültig verknüpft sind und dass ein gültiger Ausdruck entsteht.
48
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.29: Inversion einer Produktion
Deletion:
Bei der Deletion wird ein zusammenhängender Teil der Produktion aus mehreren Symbolen
entfernt. Eckige Klammern können mitgelöscht werden, wenn es paarweise geschieht, um
sicherzustellen, dass das generierte Wort gültig ist. Beim Anwenden von Deletion auf einen
Argumententeil eines Symbols ist darauf zu achten, dass der Argumententeil gültig bleibt.
Abbildung 2.30: Deletion bei einer Produktion
Duplication:
Ein zusammenhängender Teil der Produktion wird dupliziert und an einer zufälligen Stelle
der Produktion eingefügt. Auch hier ist nur das paarweise Duplizieren von eckigen Klammern
zulässig, um die Gültigkeit des Wortes zu garantieren. Das Duplizieren eines Argumententeils
und das Einfügen in einem Argumententeil ist nicht ratsam, weil dadurch die komplette Produktion verändert werden müsste.
49
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.31: Beispiel für Duplication an einer Produktion
Translocation:
Hierbei wird ein zusammenhängender Teil der Produktion herausgelöst und an einer zufälligen Stelle in der Produktion eingefügt. Auch hier ist nur das paarweise Verschieben von eckigen Klammern erlaubt, um die Gültigkeit des Wortes zu garantieren. Eine Translocation von
einem Argumententeil ist nicht möglich, weil dann ein neuer Argumententeil für ein Symbol
generiert werden müsste. Das komplette Vertauschen zweier Argumententeile wäre schon
eher denkbar wenn gesichert wird, dass die Argumententeile zu den Symbolen passen. Denkbar wäre auch das Vertauschen von Teilen der Produktion.
Abbildung 2.32: Mutation einer Produktion mit Translocation
Hinzufügen eines neuen Symbols:
In einer Produktion wird ein Symbol hinzugefügt, das bisher noch nicht in diesem L-System
existiert. Das hat zur Folge, dass eine entsprechende Produktion generiert werden muss.
50
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.33: Hinzufügen eines neuen Symbols
Mutation der Bedingung:
Es ist auch denkbar, dass die Bedingung einer Produktion mutiert werden kann. Das Problem
hierbei ist zu gewährleisten, dass zu jedem Zeitpunkt eine Produktion für ein Symbol greift.
Ansonsten wäre es denkbar, jegliche Bedingung mutieren zu lassen.
Das Problem der eckigen Klammern könnte bei der Mutation auch vernachlässigt werden,
wenn vor der graphischen Interpretation das Wort geprüft wird und entsprechende eckige
Klammern nachträglich an sinnvollen Stellen hinzugefügt werden.
Wie deutlich wird, unterscheiden sich die Arten der Mutation anhand ihrer Auswirkungen auf
das Chromosom. So zählt die Point Mutation zur Gruppe der Genmutationen. Ein Gen ist in
diesem Fall ein Symbol mit seiner Parameterliste in der Produktion. Eine Ausnahme bildet
der Bedingungsteil einer Produktion. Dieser wird als Einheit betrachtet, auch wenn die Bedingung noch so komplex ist. Daher würde eine Mutation auf die Bedingung auch zur Genmutation zählen.
Eine weitere Gruppe bildet die Chromosomenmutation. Zu dieser Gruppe gehören alle Mutationsarten, die sich auf mehrere Gene auswirken. Damit zählen die Inversion, Deletion,
Duplication und die Translocation auch zur Gruppe der Chromosomenmutation.
Die letzte Gruppe wird als Genommutationen bezeichnet. In diesem Fall bildet das komplette
L-System das Genom. Eine Mutation gehört in diese Gruppe, wenn die Anwendung des Mutationsoperators die Anzahl der Chromosomen verändert. Dazu zählt auch der Operator Hinzufügen eines neuen Symbols, der die Anzahl der Chromosomen erhöht. Denkbar als Mutationsoperator wäre auch das Löschen eines Chromosoms. Dies würde eine Produktion löschen
und alle Symbole aus den noch vorhandenen Produktionen entfernen, die auf die zu löschenden Produktionen verweisen. Allgemein besteht ein komplexeres L-System aus polyploiden
Chromosomen, das heißt die Chromosomenanzahl ist größer 2 (Produktionen). Genome mit
einem Chromosom werden als haploide Chromosomen und Genome mit zwei Chromosomen
als diploide Chromosomen bezeichnet.
Die Auswirkung von Mutationen wird am Beispiel der Software L-System gezeigt, da es ein
Modul zur Mutation enthält. Die Software selbst wird später noch genauer in Kapitel 3.2 besprochen, zu Demonstrationszwecken wird an dieser Stelle nur kurz darauf eingegangen.
[[LS4]]
51
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.34: Der Mutator von L-System
Der Mutator beim L-System bietet die Möglichkeit, L-System zu mutieren und für die Mutation diverse Parameter festzulegen. Dabei werden folgende Mutationsarten unterstützt.
Recursion
Veränderung der Rekursionstiefe.
Angle
Mutation des Basiswinkels.
Thickness
Die Startdicke für die 3D-Grafik.
Deletions
Das Löschen von Teilen der Produktion.
Additions
Das Hinzufügen von Teilen in die Produktion.
Substitutions
Ersetzen von Teilen der Produktion.
Die Stärke jeder Mutationsart lässt sich gesondert einstellen. Außerdem kann über Radiation
die globale Stärke der Mutation geregelt werden. Ansonsten gibt es einen Zufallsgenerator
(Scramble), der die Verteilung der Stärken vornimmt.
Im Folgenden werden die Möglichkeiten der Mutation an einigen Beispielen verdeutlicht. Das
ursprüngliche L-System heißt FLOWER1.LS und die normal erzeugte 3D-Grafik ist in Abbildung 2.34 zu sehen.
52
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.35: Die nicht mutierte Pflanze
Die Blume wurde zwei Mutationsschritten unterzogen. Dabei wurden nur zwei Mutationsarten aktiviert, Additions, volle Stärke, und Substitutions, mittlere Stärke.
Abbildung 2.36: Eine Mutation der Blume
Erneut wurden zwei Mutationsschritte durchgefüher, nur dieses Mal wurden Angle, Deletions
und Substitutions sehr hoch angesetzt und Additions sehr niedrig.
53
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.37: Eine zweite Mutation der Blume
Ein weiterer interessanter Aspekt wäre es, die Mutation dynamisch zu erstellen. Der übliche
Ansatz ist eine statische Mutation, das heißt ein L-System erlebt vor Generierung des Wortes
eine Mutation. Alternativ kann man auch während der Wortgenerierung nach drei Ableitungsschritten eine Mutation an einer Produktion vornehmen.
[Ja97] [Ho03] [HLP01] [HP01a] [HP01b] [HP01c] [HP02] [KTV99a] [KTV99b] [Hol01]
2.6.2 Mutation von L-Systemen unter Genetischer Programmierung
Bei der GP werden die L-Systeme als Bäume dargestellt, siehe Abbildung 2.37.
Der Baum ist so organisiert, dass von der Wurzel aus das Axiom und die Produktionen abgehen. Der Knoten Axiom enthält als Blätter den Ausdruck des eigentlichen Axioms. Der Knoten Produktionen verzweigt in alle Produktionen, die das L-System hat. Eine einzelne Produktion wird wiederum in eine linke Seite (auch predecessor genannt) und eine rechte Seite (auch
successor genannt) aufgeteilt. Die linke Seite enthält als Blatt das Symbol und seine Parameterliste. Die rechte Seite geht in eine Sequenz über. Um die GP effektiv bei L-Systemen anzuwenden, ist die Strukturierung der rechten Seite der Produktion im Baum sehr wichtig. Für
die GP ist eine zu flache Hierarchie nicht sinnvoll, wenn man auch Teilbäume manipulieren
möchte. Daher gibt es so genannte Sequenzen, die der rechten Seite eine Hierarchie aufzwingen sollen. Eine Sequenz besteht wieder aus n Blättern (in Abbildung 2.37 ist n = 3). Sollte
die rechte Seite nur n Symbole enthalten, wäre die Verzweigung der Symbole hier beendet
und die Blätter wären mit den Symbolen gefüllt. Sind mehrere Symbole vorhanden, muss ein
Blatt durch eine weitere Sequenz ersetzt werden, bis alle Symbole einem Blatt zugeordnet
sind. Bei der GP dürfen eckige Klammern nur paarweise auftauchen. Sind trotzdem ein paar
eckige Klammern in der Produktion vorhanden, so wird der Inhalt der eckigen Klammern in
einem Teilbaum dargestellt. Der Knoten wird mit Stack bezeichnet und erzeugt die gleiche
Anzahl an Blättern wie die Sequenz.
54
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.38: Ein L-System als Baum dargestellt
In einem L-System-Baum können nur die Blätter und die Knoten Stack und Sequenz eine Mutation erleben. An Mutationsmöglichkeiten bietet GP für L-Systeme sieben Möglichkeiten an.
Abbildung 2.39: Mutationsmöglichkeiten unter GP [Ja03]
Diese Möglichkeiten werden hier kurz erläutert.
Point Mutation
Es wird ein einzelnes Blatt mutiert.
Permutation
Zwei Teilbäume werden vertauscht.
55
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Hoist
Dem nächst höheren Knoten werden die Teilbäume abgeschnitten und nur eine Anzahl der
Blätter der Teilbäume seinen Blättern übergeben.
Shrinking
Ein Teilbaum wird durch ein Blatt ersetzt.
Expansion
Ein Blatt wird durch einen neu generierten Teilbaum ersetzt.
Duplication
Ein bestehender Teilbaum wird dupliziert und an Stelle eines Blattes hinzugefügt.
Tree Mutation
Die ursprünglichen Blätter bleiben erhalten, nur zwischen den Blättern und den ursprünglichen Knoten wird ein neues Stück Teilbaum hinzugefügt. Dabei kann der ursprüngliche
Knoten selbst auch mutiert werden.
Wie die GA lassen sich auch die Mutationsarten in die bekannten Mutations-Gruppen einordnen. Die Point-Mutation zählt zu den Genmutationen und alle anderen zu den Chromosomenmutationen. Da bei der GP ein L-System das Genom und Chromosom darstellt, ist das LSystem ein haploides Chromosom. Deshalb sind Genommutation ausgeschlossen.
[Ja03] [Ja97] [Ja95] [Ban03] [BNKF98] [Qu02]
2.6.3 Mutation der Interpretation
Eine weitere interessante Möglichkeit der Mutation ist das Verändern der graphischen Interpretation. Bei der Turtle-Interpretation ist es möglich, die Bedeutung von zwei Symbolen zu
vertauschen. So könnte ein F die Bedeutung eines + bekommen und umgekehrt. Alternativ
könnten ganz andere Symbole die Bedeutung der bestehenden graphischen Interpretation übernehmen.
Diese Mutationsmöglichkeit setzt aber voraus, dass das Programm für die graphische Interpretation so flexibel ist, dass es notfalls von selbst noch fehlende eckige Klammern hinzufügt.
Außerdem müsste die Evolution, wenn sie auf der genetischen Programmierung basiert, auch
die neuen Symbole für den Stack berücksichtigen und den Baum entsprechend ausrichten.
2.7 Applikationsvision
Im Folgenden sollen einige Anwendungsmöglichkeiten zu L-Systemen und deren Evolution
vorgestellt werden. Es wird eine grobe Struktur präsentiert sowie die Möglichkeiten oder Ideen, die mit dieser Struktur verbunden sind. Praktischer Bestandteil diese Arbeit ist die Bildgenerierung aus der Grammatik. Zum Evolutionsteil steuert diese Arbeit die Möglichkeiten
der Mutation von L-Systemen bei. Soweit es den Anwendungsteil betrifft werden im Wesentlichen Entwürfe angerissen, wie der Anwendungsteil aussehen könnte. Außerdem wird erläutert, wozu solch ein System gebaut wird.
Wie in Abbildung 2.39 zu sehen ist, besteht das System aus drei Teilen. Zwei dieser Teile
bilden das Grundgerüst. Die eigentliche Anwendung des Systems ist unabhängig vom Grundgerüst.
56
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Abbildung 2.40: Grobe Struktur des Systems
57
2
Theoretische Grundlagen von L-Systemen und deren Evolution
2.7.1 Das Grundgerüst
Das Grundgerüst wird in zwei Hälften unterteilt, in die Bildgenerierung aus einem vorgegebenen L-System und in die Evolution.
Die Bildgenerierung bekommt als Eingabedaten ein L-System und die graphische Interpretation. Dabei basiert die graphische Interpretation auf der Turtle-Interpretation. Die Grammatik
dient als Eingabe für ein Programm zur Ableitung eines Wortes. Das generierte Wort wird mit
der graphischen Interpretation einem Modeller übergeben. Der Modeller selbst verhält sich
wie die Schildkröte im dreidimensionalen Raum und erzeugt ein 3D-Modell, das sich aus den
Eingabedaten herleitet. Im Anschluss wird das allgemeine 3D-Modell in ein spezielles 3DModell für ein bestimmtes Ray-Tracing-Programm überführt. Diese Arbeit übernimmt ein
Adapter. Dadurch ist es möglich, ein beliebiges Ray-Tracing-Programm zu nutzen. Ausgetauscht werden muss lediglich der Adapter. Sollte aber das allgemeine 3D-Modell schon von
einem Ray-Tracing-Programm verstanden werden, ist der Schritt über den Adapter nicht nötig. Im letzten Verarbeitungsschritt zum Bild wird dem Ray-Tracing-Programm das 3DModell übergeben, aus dem das Bild erzeugt werden soll. Diesen Vorgang nennt man Rendering, deshalb werden Ray-Tracing-Programm auch kurz Renderer genannt. Gegebenenfalls
wird vom Renderer nicht nur ein Bild erzeugt, sondern mehrere Bilder des gleichen Objektes,
nur aus verschiedenen Kameraeinstellungen.
Im ersten Schritt der Evolution bekommt das Programm zur Fitnessbestimmung die erzeugten
Bilder aus der Grammatik. Die Fitnessbestimmung hängt größtenteils von der Eingabe des
Anwendungsteils ab. In Abhängigkeit davon, was von der Anwendungsseite zur Fitnessbestimmung übergeben wird, erzeugt die Fitnessbestimmung eine Ausgabe. Diese Ausgabe
kann Verschiedenes enthalten, dies wird bei den Anwendungsmöglichkeiten näher erläutert.
Gleiches gilt auch für die Spezifizierung der Eingabe. Nachdem die Fitness bestimmt ist, werden die Grammatik und die graphische Interpretation einer Evolution unterzogen. Beides
bringt wiederum eine neue Grammatik und eine neue graphische Interpretation zum Vorschein, die wiederum direkt dem Bilderzeugungsprozess übergeben wird. Das beste Individuum sollte dabei immer zwischengespeichert werden, damit es nicht verloren geht. Falls die
graphische Interpretation einer Mutation unterzogen wurde kann es passieren, dass die neue
Grammatik viel weiter von der Lösung entfernt ist. Sollte sich das Ergebnis nach mehreren
Evolutionsschritten nicht verbessern, kann eine Kopie des besten Individuums und seiner Interpretation der Evolution übergeben werden. Die andere Grammatik und Interpretation können dann verworfen werden.
2.7.2 Die Anwendungsmöglichkeiten
Applikation: Imogene
Als Vorbild dient das Programm Imogene [[Imo]], das auch auf der CD-ROM beiliegt. Imogene erzeugt Bilder, die manchmal eine Struktur erkennen lassen, manchmal aber auch wie
ein „Ameisenbild“ aussehen. Der Anwender kann aus neun Bildern das auswählen, das ihm
am besten gefällt. Anhand des ausgesuchten Bildes wird eine neue Generation von Bildern
erzeugt.
Eine Anwendungsmöglichkeit wäre ein Programm in der Art von Imogene. Dabei könnte man
als Initialpopulation neu generierte L-Systeme verwenden oder auf eine bestehende Basis von
L-Systemen aufsetzen. Diese Neugenerierung kann zu einer Art von abstraktem Bild führen.
Die bestehende Basis von L-Systemen stellt eine Menge dar, aus der einige L-Systeme gewählt werden, die dann die Anfangspopulation darstellen. So könnte es Mengen von LSystemen geben, die Bäume, Pflanzen, Häuser oder Gebirge darstellen.
58
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Der Anwender bestimmt dabei immer noch, welches erzeugte Bild ihm am besten gefällt. Die
Basis für die Evolution bildet das L-System, das dahinter steht. Aus dem L-System werden
nun durch die Evolution so viele neue Individuen erzeugt, bis die Population vollständig ist.
Erneut kann der Anwender aus der neuen Population seinen subjektiven Favoriten auswählen.
Diese Schleife wird so lange fortgeführt, bis der Anwender kein Interesse mehr an der Fortsetzung hat. Es liegt nahe, dass auch nur eine Grammatik für die Evolution zur Verfügung
steht, wenn nur ein Bild ausgewählt wird. Damit wäre nur die Mutation als Evolution sinnvoll. Eine Reproduktion wäre aus der Sicht des Anwenders wahrscheinlich nicht wünschenswert, da er nicht das gleiche Bild erneut erwartet.
Durch die Auswahl des Bildes legt der Anwender auch die Eingabe für die Fitnessbestimmung fest und damit auch gleichzeitig die Bestimmung des Individuums, das einer erneuten
Evolution unterzogen werden soll. Als Ausgabe erhält der Anwender die generierten Bilder.
Applikation: Alternatives L-System
Das Einsatzgebiet von GA und GP sind Probleme, die nicht mit herkömmlichen Lösungsmethoden lösbar sind. Man könnte nun folgendes Problem definieren.
Auf der Basis eines L-Systems wird ein Bild erzeugt. Nun soll über ein Programm das
L-System gefunden werden, das solch ein Bild erzeugt. Das L-System, welches das
Bild erzeugt hat, steht zur Findung nicht zur Verfügung.
Interessant ist, ob dieses L-System gefunden wird oder ein anderes L-System, welches das
gleiche Bild erzeugt. Dabei kann das andere L-System vielleicht mehr oder weniger Produktionen und kürzere oder längere Produktionen aufweisen. Wenn ein anderes L-System gefunden wurde, das auch das Bild erzeugt, ist weiterhin interessant, ob das Wort das gleiche ist
oder nicht.
In diesem Zusammenhang spricht man auch vom Inferenzproblem für L-Systeme.
„Das Inferenzproblem für L-Systeme besteht darin, ein geeignetes Axiom und entsprechende Produktionen zu finden, so daß eine gegebene Struktur oder ein bestimmter
Wachstumsprozeß nachgebildet werden.“
[Ja97]
Als Eingabe für die Fitnessbestimmung gibt es einmal das Bild, für das ein L-System gesucht
wird, und ein Bild, von dem/den L-System/en das/die nach dem L-System sucht/suchen. Über
die Ähnlichkeit der Bilder lässt sich dann die Fitness bestimmen.
Als Ausgabe erhält der Anwender entweder das gefundene L-System, welches das Bild erzeugen kann, oder das L-System, das dem Ergebnis am nächsten kommt. Dabei sollten die
Fitnessergebnisse dem Anwender auch zukommen.
Interessant ist auch die Frage, wie viel Zeit für die Findung eines L-Systems benötigt wird,
das möglichst nahe an die Lösung kommt.
Applikation: Effektive Speicherung von Pflanzen
Prusinkiewicz zeigt in seinen Veröffentlichungen [PL90] [Pr86] [Pr93] [PJM94] [PHM]
[PHHM96a] [PHHM96b] [PHHM95] [BPFGK03], dass alle erdenklichen Pflanzen mit LSystemen für die Computergrafik beschrieben werden können. Dieser Prozess könnte auch
umdreht werden indem versucht wird, von einem Bild ein L-System zu generieren, das möglichst nahe an das Originalbild der Pflanze kommt. Dadurch könnte eine Pflanze sehr effektiv
gespeichert werden.
59
2
Theoretische Grundlagen von L-Systemen und deren Evolution
Als Eingabe für die Fitnessbestimmung steht das Foto des Objektes zur Verfügung sowie die
generierten Bilder der L-Systeme. Über die Ähnlichkeit kann dann die Fitness bestimmt werden. Als Ausgabe erhält man das L-System, das dem Foto am nächsten kommt.
Applikation: Pflanzenerkennung
Mittlerweile gibt es eine große Menge von L-Systemen, die reale Pflanzen erzeugen. Ein Teil
dieser L-Systeme könnte in einer Datenbank abgelegt werden. Diese Datenbank würde eine
Basis von repräsentativen Pflanzen bilden. Diese L-Systeme könnten nun aus der Datenbank
entnommen werden und mit Hilfe von GA oder GP einer Evolution unterzogen werden, um
auf einem Foto eine Pflanze zu erkennen. Wird beispielsweise das Foto einer Rose aufgenommen und in der Datenbank liegt auch ein repräsentatives Modell einer Rose, dann wird
vom Programm zunächst dieses Modell genommen. Nach zahlreichen Evolutionsschritten
entsteht so eine Rose, die dem Foto sehr nahe kommt. Das Programm entschließt, dass auf
dem Foto eine Rose ist und teilt das dem Anwender mit.
Dieser grobe Überblick soll noch ein etwas spezifiziert werden. Als Eingabe erhält der Evolutionsprozess das Foto einer Pflanze sowie die Datenbank mit den repräsentativen Pflanzen
(Eine Auswahl repräsentativer Modelle zu den wichtigsten Bäumen ist in [De03] zu finden).
Das Programm entnimmt nun ein Modell aus der Datenbank und bestimmt die Ähnlichkeit
des Modells mit dem Foto. Diese Ähnlichkeit des Modells zum Foto ist die Fitnessbestimmung. Nun durchläuft das Modell eine bestimmte, vorher festgelegte, Anzahl von Evolutionsschritten. Nach jedem Evolutionsschritt wird die Fitness des neuen Modells bestimmt. Sollte
eine bestimmte Ähnlichkeitsschwelle überschritten werden, so erhält die Pflanze auf dem Foto die Bezeichnung des Modells. Bevor festgelegt wird, dass die Pflanze eine Alternative zum
Modell ist, muss eine Ähnlichkeitsprüfung zwischen dem Modell und dem generierten Bild
vorgenommen werden. Damit soll sichergestellt werden, dass durch die Evolutionsschritte aus
einem ursprünglichen Modell, wie zum Beispiel aus einer Rose, nicht ein vollkommen anderes Objekt entsteht, beispielsweise ein Haus. Konnte diese Schwelle mit dem ersten Modell
nicht überschritten werden, dann wird ein anderes Modell aus der Datenbank entnommen.
Das geschieht so lange, bis alle Modelle geprüft sind.
Wenn kein Modell der Pflanze auf dem Foto entspricht, dann könnte die Applikation versuchen, ein neues Modell aufzustellen, um die neue Pflanze in die Datenbank aufzunehmen.
Eine Einstiegshilfe könnte das erste neue Modell sein, was bei den vorherigen Durchläufen
die beste Fitness aufweisen konnte. Natürlich kann auch ein komplett neues Modell per Zufall
generiert und der Evolution unterworfen werden.
Wurde ein entsprechendes Modell für die neue Pflanze gefunden, könnte das Wort, das die
Pflanze erzeugt, einem intelligenten Analyseunterprogramm gereicht werden. Dieses könnte
versuchen, aus der Struktur, die das Wort repräsentiert, eine Zuordnung zu einer groben
Pflanzengattung machen, beispielsweise Blume oder Baum. Dazu kann das Analyseunterprogramm auf Merkmale der verschiedenen Pflanzengattungen zurückgreifen, bei einem Baum
beispielsweise auf die Merkmale sehr hoher Stamm und eine große Anzahl von Verzweigungen. Repräsentiert werden könnte das Analyseunterprogramm zum Beispiel durch ein Neuronales Netz.
60
3
Programme, die L-Systeme simulieren/interpretieren
3 Programme, die L-Systeme simulieren/interpretieren
In diesem Kapitel wird ein Überblick über die vorhandenen Applikationen zu L-Systemen
gegeben und ihre unterschiedliche Eigenschaften zu verdeutlichen.
3.1 Lparser
Das Softwarepaket Lparser [[LP4]] liegt in der Version 4 vor und stammt hauptsächlich vom
Autor Laurens Lapre. Es ist zwar schon älter (1995), aber sehr verbreitet bei WindowsAnwendern.
Lparser besteht stark vereinfacht aus drei Komponenten, aus einem Programm, das ein Wort
aus einem L-System generiert und als 3D-Modell abspeichert, aus einem Programm zum Betrachten des 3D-Modells als Drahtgittermodell und aus einem Programm zum Konvertieren
des 3D-Modells in eine POV-Ray Datei. Das Konvertierungsprogramm wurde von Cees van
der Mark jr. geschrieben.
Das Programm zum Erzeugen eines 3D-Modells nennt sich Lparser und ist ein Kommandozeilenprogramm. Dieses bekommt als Eingabe eine Datei (*.LS), in der ein L-System definiert ist. Dabei unterstützt Lparser PDB0L-Systeme, lediglich die Turtle-Kommandos können
parametrisiert werden. Neben dem L-System selbst können auch Optionen über die Kommandozeilenargumente übergeben werden. Außer dem POV-Ray Format werden die Formate
DXF (AutoCAD), RAW, WRL (VRML), RDF (RenderStar) und BLB (Blob Sculptor) unterstützt. Eine weitere Eigenschaft ist die Möglichkeit, ein L-System mutieren zu lassen, wobei
als Parameter nur die Anzahl der Mutationsdurchläufe angegeben werden kann. Die Mutation
lässt die einzelnen Produktionen mutieren, entspricht also dem Vorbild von GA (vgl. Kapitel
2.3).
Das Anzeigeprogramm LViewer bietet die Möglichkeit, sich das erzeugte 3D-Modell anzeigen zu lassen. Angezeigt wird ein Drahtgittermodell, das rotiert und skaliert werden kann. Die
Änderungen werden dabei nicht im POV-Ray 3D-Modell gespeichert. Lediglich für RenderStar 3D-Modelle können die Einstellungen gespeichert werden. Für alle andere 3DModelle kann eine INFO.TXT Datei generieren werden, mit den Informationen über die Kameraeinstellungen.
Das Konvertierungsprogramm heißt LV2POVID. Dabei wird nicht nur POV-Ray unterstützt
sondern auch ViVid. Das Programm erzeugt für POV-Ray eine Szene, die kompatibel zur
Version 2.X ist. Die einzige Änderung, die im POV-Ray Editor ausgeführt werden muss, um
zur Version 3.X kompatibel zu sein, ist, dass die declare-Anweisungen ein Semikolon benötigen, weil diese eine Aufzählung darstellen. Ansonsten kann die Szene sofort gerendert werden. Neben der LPAR2POV.POV Datei erzeugt das Programm eine Include-Datei,
LPAR2POV.INC, in der die Daten der 3D-Primitiven beschrieben sind.
61
3
Programme, die L-Systeme simulieren/interpretieren
Abbildung 3.1: Baum, erzeugt von Lparser und gerendert mit POV-Ray
3.2 L-System
Das Programm L-System wurde von Timothy C. Perz [[LS4]] 1999 entwickelt und liegt in der
Version 4.01 vor. L-System basiert dabei auf Lparser mit dem Unterschied, dass es eine vollständige Windows-Applikation mit einer graphischen Oberfläche ist. So bindet L-System alle
einzelnen Komponenten zu einem Programm zusammen und erhöht damit die Transparenz
und Benutzerfreundlichkeit. Weil es auf Lparser basiert, sind alle *.LS, die unter Lparser laufen, auch unter L-System ausführbar.
Gegenüber Lparser enthält L-System einige Erweiterungen. Eine wichtige ist die Unterstützung von parametrisierten L-Systemen. Damit können auch komplexe Modelle erzeugt werden.
Außerdem wurden die Mutationsmöglichkeiten gegenüber Lparser stark erweitert. Für die
Mutation wurde ein Mutator entwickelt. Über ihn lassen sich verschiedene Mutationsarten
regeln (siehe Kapitel 2.5.1). Bei der Anwendung entsteht jedoch der Eindruck, als ob sowohl
bei Lparser als auch bei L-System die Mutation noch nicht ausgereift ist, weil die Ergebnisse
deterministisch sind bzw. nicht stark genug hervorkommen. Wird beispielsweise das gleiche
L-System zweimal mit derselben Gewichtung bei den Mutationsfaktoren mutiert, so wirkt das
Ergebnis nahezu identisch.
Eine andere Erweiterung von L-System ist der Random-Generator. Über eine Vielzahl von
Parametern, lassen sich willkürliche L-Systeme generieren. Erzeugt diese generierte Grammatik ein langes Wort, dann stürzt L-System ab.
Das Anzeigeprogramm von L-System ist gegenüber LViewer in einem Teil schwächer. So
lassen sich die 3D-Modelle nur im DXF-Format speichern.
Die Grafiken lassen sich skalieren und rotieren. Dabei wird die Kameraeinstellung mitgespeichert. Außerdem kann neben dem Drahtgittermodell auch ein komplettes 3D-Modell mit und
ohne Texturen angezeigt werden. Die Texturen und Farben können im Anzeigeprogramm
verändert werden. Vor der Anzeige wird das Wort daraufhin überprüft, ob es überhaupt interpretierbar ist.
62
3
Programme, die L-Systeme simulieren/interpretieren
Abbildung 3.2: Beispiel für eine 3D-Grafik von L-System
3.3 RayTraced Evolution
RayTraced Evolution wurde von Nguyen Duc Cuong an der TU Ilmenau entwickelt [[RTE]].
Die vorliegende Version 1.1 wurde 1997 erstellt.
Das Programm erzeugt aus einer Beschreibungs-Datei (*.RTE) ein 3D-Modell für einen speziellen Renderer und speichert es in einer Datei ab. Dabei wird bei der Ausgabe auf einen
Treiber für den entsprechenden Renderer zurückgegriffen. So kann durch Austausch des Treibers das 3D-Modell für einen anderen Renderer benutzt werden. Als Standard ist eine POVRay Unterstützung integriert. Um eine Kollision der Turtle-Bewegungen zu vermeiden, wurde
der GX/GENETIC Ray-Tracing Kernel eingebaut. Sollte die Schildkröte irgendwo kollidieren, können verschiedene Aktionen ausgeführt werden, die vom „Sterben“ bis zur „Reinkarnation“ reichen. Die generierte Szene kann ohne Veränderung direkt mit POV-Ray gerendert
werden.
Eine weitere mächtige Eigenschaft von RayTraced Evolution ist die Beschreibungssprache für
eine Szene. Sie ist in zwei Sprachen unterteilt, in eine C-ähnliche Hochsprache und in die LSysteme. Seitens der Hochsprache werden dabei auch Datentypen wie Vektoren und Matrizen
unterstützt sowie die meisten Operatoren, die aus C bekannt sind. Viele Bibliotheken unterstützen dabei die Hochsprache, wie zum Beispiel eine Mathematik-Bibliothek oder eine reine
Bezier-Bibliothek. Auch Unterprogramme werden unterstützt.
Neben der mächtigen Sprache werden auch eine Vielzahl von L-Systemen unterstützt.
RayTraced Evolution unterstützt parametrisierte und stochastische L-Systeme, T0L- und
B0L-Systeme sowie Timed L-Systeme für Animationen. Alle L-Systeme müssen jedoch deterministisch und propagierend sein.
Das Programm selbst ist ein Kommandozeilenprogramm und hat keine weitere graphische
Oberfläche.
63
3
Programme, die L-Systeme simulieren/interpretieren
Der einzige Nachteil ist, dass nur eine spärliche Dokumentation existiert und ansonsten keine
weitere Unterstützung.
Abbildung 3.3: 3D-Modell erzeugt von RayTraced Evolution
3.4 LinSys3D
Als Nächstes soll das von Andrea Esuli entwickelte Programm LinSys3D [[L3D]] vorgestellt
werden. Die derzeitige Version ist 1.2, sie stammt aus dem Jahre 2001.
LinSys3D ist eine Windows-Anwendung mit einer graphischen Oberfläche ohne Kommandozeilen-Unterstützung.
Die Applikation unterstützt eine Reihe von L-Systemen, so zum Beispiel stochastische, parametrisierte und kontextsensitive L-Systeme. Alle L-Systeme müssen deterministisch und propagierend sein. Ein Unterschied zu vielen anderen Programmen ist der Ablauf, also wie die
Pflanze synthetisiert wird.
Im ersten Schritt wird das L-Schema angegeben. Dazu wird als Erstes in eckigen Klammern
das Alphabet aufgelistet. Die Produktionen selbst werden in geschweiften Klammern aufgelistet, dabei gilt folgende Syntax:
{Symbol, linker Kontext, rechter Kontext, [Bedingung] :
<Wahrscheinlichkeit1> Produktion1, <Wahrscheinlichkeit2> Produktion2,
..., <WahrscheinlichkeitN> ProduktionN}
Nachdem das L-Schema definiert ist, wird ein Syntaxcheck ausgeführt. Sollte das L-Schema
einen Fehler aufweisen, muss dieser erst behoben werden, bevor die Verarbeitung fortgesetzt
werden kann.
64
3
Programme, die L-Systeme simulieren/interpretieren
Als Nächstes werden vordefinierte Drahtgittermodelle geladen, zum Beispiel für ein Blatt
oder einen ganzen Blütenkranz. Dabei werden die Modelle aus einer Text-RAW Datei gelesen. Das Modellieren der Komponenten geschieht entweder per Hand oder mit Hilfe von Programmen wie Crossroads.
Im dritten Schritt wird den Symbolen aus dem Alphabet eine Eigenschaft zugewiesen. So
kann ein Symbol als Signal definiert werden, damit hat es keine graphische Bedeutung und
wird nur für das Wachstum der Pflanze benutzt. Ein Symbol kann auch eine Raumtransformation darstellen, zum Beispiel rotieren oder skalieren. Außerdem lassen sich durch Symbole
zwei verschiedene Typen von Grafikobjekten darstellen, ein statisches und ein parametrisiertes. Zu jedem Grafikobjekt muss das Drahtgittermodell aus dem vorherigen Schritt angegeben
werden.
Im vorletzten Schritt wird das L-Schema zum L-System vervollständigt und das zu interpretierende Wort generiert. Es wird das Axiom angegeben und die Anzahl der Ableitungsschritte.
Zuletzt wird das Wort mit Hilfe von OpenGL graphisch dargestellt. Das 3D-Modell kann
entweder als BMP-Grafik oder als POV-Ray Datei gespeichert werden.
Abbildung 3.4: Eine Rosenblüte erstellt von LinSys3D
65
3
Programme, die L-Systeme simulieren/interpretieren
3.5 Zusammenfassung und Übersicht
Eine tabellarische Zusammenfassung der Programme ist in den Tabellen 3.1-a und -b zu finden.
Ein wichtiger Aspekt von Lparser ist, dass es eine große Verbreitung aufweisen kann. Dem
entsprechend existieren unzählige Tools für Lparser. Einige sollen kurz genannt werden.
PlanD
Ist eine kleine Entwicklungsumgebung für den Lparser. Alle Einzelkomponenten von
Lparser sind in einer graphischen Oberfläche vereint. Der Viewer zeigt ein 3D-Modell der
aktuellen Grammatik an. Wird etwas verändert, wird das 3D-Modell sofort angepasst.
[[Plan]]
Lparser Update
Da Lparser freie Quelldateien hat, bietet es sich an, auch diese zu erweitern. Ein Autor hat
Lparser um parametrisierte L-Systeme erweitert. [[LP5]]
Lsys32
Ist eine graphische Oberfläche, in der ein L-System definiert werden kann. Zusätzlich bietet das Programm an, viele Parameter von Lparser über die Oberfläche zu steuern. [[L32]]
Im Folgenden sollen einige weitere Programme zu L-Systemen kurz vorgestellt werden.
GROGRA:
GROGRA ist eine Software, deren Hauptziel nicht nur die Visualisierung von L-Systemen ist.
Das Programm wurde entwickelt, um das Wachstum von Gehölzen zu simulieren und diese
zu analysieren, zum Beispiel den Saftfluss im Baum oder die Fotosynthese. Die Software
wurde zur Waldökosystemforschung entwickelt.3 [[GRO]]
Fractint:
Ein schon älteres, aber bekanntes Programm ist Fractint. Mit der Software lässt sich fast jedes
erdenkliche Fraktal erzeugen. Einfache PDB0L-Systeme werden auch unterstützt. Die graphische Ausgabe ist normalerweise zweidimensional, kann aber auf die dritte Ebene erweitert
werden. Es existiert auch eine Exportfunktion für Renderer. Für den POV-Ray existiert nur
ein RAW-Format, welches erst mit einem weiteren Tool namens RAW2POV umgewandelt
werden muss. Dieses Tool wurde aber von den Entwicklern von POV-Ray eingestellt und ist
nicht mehr verfügbar. [[Fra]]
Lworld:
Lworld ist ein Programm zur Animation von L-Systemen mit OpenGL. Das Programm wurde
an der Universität Zürich von Herrn Hansrudi Noser entwickelt. [[Lwo]]
LS-SketchBook:
LS-SketchBook unterstützt derzeitig parametrisierte, stochastische und kontextsensitive LSysteme. Im Vergleich zu anderen Programmen unterstützt es auch nicht propagierende LSysteme. Die Ausgabe erfolgt mit OpenGL. Außerdem gibt LS-SketchBook den kompletten
Wachstumsprozess graphisch aus. Dieser kann als Animation mitgeschnitten werden. [[LSB]]
3
Hier soll Herrn Prof. Dr. Winfried Kurth (BTU Cottbus) gedankt werden, dass er eine Kopie von GROGRA für
diese Arbeit zur Verfügung gestellt hat.
66
3
Programme, die L-Systeme simulieren/interpretieren
L-studio:
L-studio ist die Windows Version des Virtual Laboratory. L-studio wurde, wie das Virtual
Laboratory, von Prusinkiewicz entwickelt. Das Programm ist sehr mächtig, aber teilweise
schwer zu bedienen. Es lassen sich parametrisiert, stochastische und kontextsensitive LSysteme nutzen und visualisieren. L-studio selbst ist nur ein Editor, innerhalb dessen das LSystem, die Parameter, Farben, Oberflächen, Konturen und viele weitere Optionen beschrieben werden können. Die Visualisierung wird von einem externen Programm namens Cpfg
vorgenommen. In Cpfg lassen sich die 3D-Modelle als Bild (BMP, TGA, ...), RayShade Datei, PostScript, String, Gls, View Volume, Inventor oder OBJ-Datei speichern. Das Programm
liegt der CD-ROM nicht mit bei, weil es nur als Demo-Version herunterzuladen ist, die in
einem Monat abläuft. Die Demo-Version lässt sich unter
http://www.cpsc.ucalgary.ca/Research/bmv/lstudio/ herunterladen. [[LSt]]
Abbildung 3.5: Eine Lilie erstellt mit L-studio
67
3
Programme, die L-Systeme simulieren/interpretieren
Name
Version / Autor / Homepage
Jahr
Lparser
4.0 /1995 Laurens Lapre
http://home.wanadoo.nl/laurens.lapre/
L-System 4.01 /1999 Timothy C. Perz
http://www.geocities.com/tperz/L4Home.htm
RayTraced 1.1 / 1997 Nguyen Duc Cuong
Evolution
http://www.stud.tu-ilmenau.de/~juhu/GX/RTEvol/
LinSys3D 1.2 / 2001 Andrea Esuli
http://web.tiscali.it/esuli/LinSys3d/
Sourcen
fügbar
Ja / in C
PDB0L-Systeme
Kommandozeile
/ GUI
Ja / nur Viewer
Nein
Parametrisierte PDB0L-Systeme
Nein / Ja
Nein
Parametrisierte, stochastische,
Ja / Nein
Timed, Table, PDB0L-Systeme
Parametrisierte, stochastische, kon- Nein / Ja
textsensitive, PDB0L-Systeme
Nein
ver- Unterstützt folgende L-Systeme
Tabelle 3.1-a: Übersicht über die Software zu L-Systemen
Viewer
Externer Viewer, Drahtgittermodell
Drahtgittermodell, 3D-Modell mit
/ohne Texturen, Textur/ Farbe wählbar
Nein
3D-Modell, Multi-/ Camera/Fullscreen, mehrere Lichtquellen, OpenGL
Formate
Mutation
Vorteile
DXF, RAW, POV, Anzahl der Mutations- Viele Tools, Sourcen,
WRL, BLB, RDF schritte
viele Formate, breite Unterstützung
DXF, BMP, JPG Verschiedene einMutation, Viewer, Ranstellbare Parameter
dom-Generator, GUI
POV / Treiber
Nein
Viele L-Systeme, Treiber,
abhängig
mächtige Sprache
BMP, POV
Nein
Viele L-Systeme, Viewer,
Komponentenansatz
Tabelle 3.1-b: Übersicht über die Software zu L-Systemen
68
Nachteile
Nur PDB0L-Systeme, viele Einzelprogramme, einfache Mutation
Wenige Formate und läuft nicht
stabil
Keine Mutation, kein Viewer,
keine gute Dokumentation
Keine Mutation, wenige Formate,
komplexe Handhabung
4
Entwurf und Implementierung der Fassade
4 Entwurf und Implementierung der Fassade
4.1 Der Begriff der Fassade
Mit dem Wort Fassade bringt man häufig ein Gebäude in Verbindung, sie bildet sozusagen
die Außenhaut eines Hauses. Dabei verdeckt die Fassade die gesamte Komplexität nach innen. So sind Wasserrohre, Elektroleitungen und vieles von außen nicht sichtbar. Durch die
Fassade wird einem Außenstehenden der Einstieg /die Einsicht in das Gebäude erschwert.
Über die Fassade wird jedoch eine Schnittstelle definiert, über die der Außenstehende sein
Anliegen an die Personen im Gebäude übermitteln kann.
Dieses wiederkehrende Muster in der Architektur, engl. Patterns, hat der Architekt Christopher Alexander in seinem Buch „A Pattern Language“ 1977 festgehalten [GHJV96]. Inspiriert
von Alexanders Buch und seinen Ideen setzt Erich Gamma diese Pattern in spezifische Entwurfsmuster für Programmierprobleme um. Eines dieser Entwurfsmuster ist die Fassade, sie
zählt zur Untergruppe der Strukturmuster. Zweck dieser Strukturmuster ist das Zusammenführen von vielen verschiedenen Objekten zu einer großen Struktur mit einer einheitlichen
Schnittstelle. Die Fassade selbst wird in [GHJV96] wie folgt definiert:
„Bietet eine einheitliche Schnittstelle zu einer Menge von Schnittstellen eines Subsystems. Die Fassadenklasse definiert eine abstrakte Schnittstelle, welche die Benutzung
des Subsystems vereinfacht.“
Normalerweise werden Entwurfsmuster im objektorientierten Bereich verwendet. Die Fassade
in der vorliegenden Diplomarbeit wird aus Performancegründen jedoch in C entwickelt. Die
Merkmale der Fassade lassen sich dabei auch ohne die Verwendung einer objektorientierten
Sprache realisieren.
4.2 Einführung zum Entwurf
Mit diesem Kapitel beginnt der Praxisteil der Diplomarbeit. Hier liegt der Schwerpunkt auf
dem Konzept, dem Entwurf und der Implementierung der Software.
Die Entwicklung der Software unterteilt sich in zwei separate Bereiche. Der erste Teil ist der
bereits aus dem Theorieteil bekannte Prozess der Verarbeitung der Grammatik bis zur Bilderzeugung. Dieser theoretische Ablauf ist in Abbildung 2.39 dargestellt. Er wird im Verlauf des
Kapitels diskutiert und als Fassade implementiert. Auch auf die Programme, die von der Fassade aus bedient werden, wird noch näher eingegangen. Außerdem wird die Auswahl der
Programme begründet und erläutert, welche Probleme mit den jeweiligen Programmen einhergehen. Ziel ist es, das entwickelte Programm in einen Evolutionsprozess einzubetten oder
auch in andere Abläufe zu integrieren, die eine Visualisierung von L-Systemen benötigen.
Dabei soll die Fassade eine beherrschbare Anzahl von Optionen beherbergen, ohne dass die
Übersichtlichkeit verloren geht.
Nachdem im ersten Teil der Softwareentwicklung eine Verarbeitungsschicht erstellt wurde,
findet im zweiten Teil die Entwicklung einer Oberflächen-Schicht statt. Das Ziel der Oberflächen-Schicht ist das praktische Arbeiten mit dem Prozess aus dem ersten Teil, nicht die Einbettung in einen automatischen Prozess sein. Die entwickelte graphische Oberfläche bietet die
Möglichkeit, L-Systeme komfortabel einzugeben, Optionen für den Prozess zu setzen und den
Prozess für die Bilderzeugung ablaufen zu lassen.
69
4
Entwurf und Implementierung der Fassade
Für die Fassade und für die graphische Oberfläche wurden zwei verschiedene Entwicklungsumgebungen und zwei verschiedene Programmiersprachen verwendet. Im Folgenden wird
zunächst die Entwicklung der Fassade erläutert. Auf die graphische Oberfläche wird dann in
Kapitel 5 eingegangen.
Zuvor soll jedoch erwähnt werden, dass die fertige Fassade Lprocess heißt und die graphische
Oberfläche „Visual L“.
4.3 Die Entwicklungsumgebung
Für die Entwicklung der Fassade wird auf das Visual Studio 6.0 von Microsoft zurückgegriffen, denn diese Entwicklungsumgebung bietet eine komfortable und gut strukturierte Umgebung zur Entwicklung von Konsolenapplikationen und einen leistungsfähigen Debugger. Als
Programmiersprache wird C gewählt, weil C auf anderen Betriebssystemen sehr verbreitet ist.
Außerdem bietet ein in C entwickeltes Programm eine hohe Geschwindigkeit, was gerade bei
einem Evolutionsprozess nicht unwesentlich ist, weil dieser insgesamt sehr zeitaufwendig ist.
Wobei bei der Entwicklung darauf geachtet wurde, so wenig wie möglich Windowsspezifische Funktionen und Bibliotheken zu verwenden, damit die Möglichkeit besteht, das
Programm mit wenig Aufwand auf einem anderen Betriebssystem zum Einsatz zu bringen.
4.4 Modulübersicht
In Kapitel 2.6 wurde schon kurz auf die einzelnen Module für die Bilderzeugung und den
Evolutionsprozess eingegangen. Nun werden der Bilderzeugungsteil und seine Module etwas
genauer betrachtet.
Wort-Generierung:
Die Wort-Generierung erhält ein L-System als Eingabe. Dabei wird geprüft, ob das L-System
vollständig ist und syntaktisch einwandfrei. Mit vollständig ist gemeint, dass die Rekursionstiefe, die Regeln und ein Axiom definiert sind. Mit syntaktisch einwandfrei ist gemeint,
dass die Produktionen den Regeln der Syntax des Wort-Generierungsprozesses entsprechen.
Der Prozess erkennt anhand der Grammatik um was für ein L-System es sich handelt und leitet das L-System entsprechend der Anzahl der Rekursionsschritte ab. Als Ergebnis entsteht
ein einzelnes Wort, welches die Strukturvorlage für das Bild liefert. Diese Struktur ist abhängig von der Interpretation der einzelnen Zeichen des Wortes.
70
4
Entwurf und Implementierung der Fassade
Modeller:
Die Aufgabe des Modellers ist es, aus einem generierten Wort eine 3D-Szene zu beschreiben.
Dazu bekommt der Modeller als Eingabe das Wort aus dem vorherigen Schritt. Um die Strukturinformation zu interpretieren, benötigt der Modeller eine Interpretationsvorschrift. Sie
schreibt vor, welches Symbol für welches Turtle-Kommando steht. In der Regel sind die
Standardsymbole für die Turtle-Kommandos in der Interpretationsvorschrift enthalten. Die
eigentliche Aufgabe des Modellers ist es, die Turtle-Kommandos, wie zum Beispiel F, in 3DPrimitive umzuwandeln und diese den Strukturinformation entsprechend anzuordnen. Als 3DPrimitiv wird üblicherweise der Zylinder verwendet. Einige Programme zu L-Systemen geben
auch die Möglichkeit vor, andere Primitive zu verwenden, wie zum Beispiel einen Kubus.
Durch die von Prusinkiewicz in [PL90] vorgenommene Erweiterung der Turtle-Interpretation
ist es darüber hinaus auch möglich, komplexe Objekte mit Hilfe von Polygone zu modellieren. Polygone werden von vielen Renderern als 3D-Primitiv unterstützt, daher lassen sich
auch Polygone, die im generierten Wort enthalten sind, problemlos in die 3D-Szene umzusetzen. Zusätzlich zu den Primitiven muss der Modeller auch eine Kamera einfügen und diese
auf die 3D-Szene ausrichten sowie mindestens eine Lichtquelle, damit das 3D-Modell überhaupt sichtbar wird. Als Ergebnis erhält man eine 3D-Szene mit einem 3D-Modell samt
Lichtquelle und Kamera.
Adapter:
Das 3D-Modell, das vom Modeller erstellt wird, ist nicht speziell auf einen Renderer zugeschnitten, bzw. liegt nicht in einem kompatiblen Format für einen speziellen Renderer vor.
Diese Aufgabe muss der Adapter lösen. Der Adapter transformiert die allgemeine Beschreibung der 3D-Szene vom Modeller in eine spezielle Beschreibung für einen speziellen Renderer und speichert dies in dem Format des Renderers ab.
Renderer:
Aus der Beschreibung der 3D-Szene vom Adapter erzeugt der Renderer eine Grafik und speichert sie in einem Grafikformat ab. Der Renderer verwendet zur Erstellung der Szene die
Strahlenverfolgungstechnik (Ray-Tracing). Weitere Information zum Renderer und zur Strahlenverfolgung folgen in Kapitel 4.5.
71
4
Entwurf und Implementierung der Fassade
Abbildung 4.1: Module des Bilderzeugungsprozesses
4.5 Vereinfachungen und die entsprechende Modulübersicht
Im Rahmen einer Diplomarbeit können nicht alle Module selbst entwickelt werden. Es wird
daher auf einige schon existierende Programme zurückgegriffen. In diesem Kapitel wird näher erläutert, um welche Programme es sich hierbei handelt und warum sie verwendet werden.
Um einen ersten Überblick zu bekommen, werden der theoretische und der praktische Ablauf
in Abbildung 4.2 einander gegenübergestellt. So wird deutlich, welches Programm welche
Aufgabe aus dem theoretischen Teil übernehmen soll.
72
4
Entwurf und Implementierung der Fassade
Abbildung 4.2: Der theoretische, der praktische Ablauf sowie die verwendeten Programme
Lparser und LV2POVID:
Wie schon im Kapitel 3.1 erläutert, ist Lparser ein Softwarepaket aus mehreren Komponenten. Für die Eingabe wird nur eine Text-Datei benötigt, in der ein L-System nach den syntaktischen Regeln vom Lparser enthalten ist. Des Weiteren wird aus dem Softwarepaket noch das
Programm LV2POVID benötigt, um das 3D-Modell von Lparser in dass von POV-Ray zu
überführen. Im nächsten Kapitel wird näher auf Lparser eingegangen. An dieser Stelle soll
zunächst erklärt werden, warum Lparser ausgewählt wurde.
Um einen Prozess zu erstellen, der automatisch ablaufen soll, ist es notwendig, dass eine
Schnittstelle existiert, wenn ein externes Programm eingebettet werden soll. Weiterhin ist es
von Vorteil, wenn das Programm keine graphische Oberfläche enthält, denn es soll keine Interaktion mit einem Nutzer existieren. Daher bietet es sich an ein Programm zu verwenden,
das über die Kommandozeile gestartet werden kann.
73
4
Entwurf und Implementierung der Fassade
Anhand der Erläuterungen in Kapitel 3 lassen sich zwei Programme erkennen, die über die
Konsole bedient werden können, eines davon ist Lparser und das andere RayTraced Evolution. RayTraced Evolution besitzt gegenüber Lparser einige Vorteile. So unterstützt es wesentlich mehr L-Systeme und kann durch das Treiber-Konzept theoretisch jeden beliebigen Renderer zur Bilderzeugung verwenden. Außerdem besitzt es eine mächtige, hochsprachenähnliche Sprache, mit der sehr komplexe Modelle erzeugt werden können. RayTraced Evolution
hat einen gravierenden Nachteil, der das tiefer gehende Arbeiten mit dem Programm fast unmöglich macht. Es besitzt nur eine sehr knappe und nicht vollständige Dokumentation. Der
komplette Aufbau einer Beschreibungs-Datei ist nicht erklärt. Das Gleiche gilt für viele Funktionen, die in der Abarbeitung eine wichtige Rolle spielen.
Der Vorteil von Lparser liegt in seiner weiten Verbreitung, denn dadurch bietet sich Möglichkeiten, schnell und umfassend Information zu dem Programm im Internet zu erhalten. Des
Weiteren ist Lparser ein Open-Source Programm, das heißt der Quellcode kann herunter geladen und beliebig verändert werden, ohne dabei eine Lizenzbestimmung zu verletzen. Dies gilt
aber nur für das Hauptprogramm Lparser selbst, andere Teile des Softwarepaketes sind nicht
Open-Source, beispielsweise die Programme LViewer und LV2POVID. Lparser hat dafür
einige andere Nachteile. So besitzt die Version 4 nur die Möglichkeit, PDB0L-Systeme zu
interpretieren. Lediglich die Kommandos der Turtle-Interpretation sind parametrisiert. Darüber hinaus sind einige Teile von Lparser nicht fehlerfrei. So werden zum Beispiel von
LViewer immer die gleichen Kamerapositionen für vollkommen unterschiedliche 3D-Modelle
erzeugt und LV2POVID übernimmt diese Daten nicht richtig in die POV-Ray Datei. Aber der
wichtigste Grund hinsichtlich der Entscheidung für Lparser ist, dass die Syntax der Beschreibungs-Datei einfach und intuitiv ist. Das macht das Verstehen von L-Systemen viel leichter
und damit auch deren Gebrauch. Außerdem kann der Evolutionsprozess so leichter die LSysteme verändern und selbst erstellte L-Systeme können sehr einfach in eine Datei gespeichert werden. Bei RayTraced Evolution müsste der Evolutionsprozess eine BeschreibungsDatei mit mehreren Unterfunktionen erzeugen und die L-Systeme teilweise in ein hochsprachenähnliches Konstrukt umformen.
POV-Ray:
Im Bereich der Ray-Tracing Software gibt es nur sehr wenige Renderer, die frei verfügbar
sind, und diese erzeugen selten so qualitative Szenen wie POV-Ray. Das liegt teilweise daran,
dass POV-Ray auf eine sehr lange Entwicklungsgeschichte zurückschauen kann. Im Vergleich zu anderen Renderern, wie zum Beispiel NFF, der von dem Autor stammt, der auch
RayTraced Evolution entwickelte, ist POV-Ray um viele Größenordnungen performanter. So
benötigt der NFF Ray Tracer für eine Beispielszene, die mit RayTraced Evolution erstellt
wurde, knapp zwei Minuten. POV-Ray benötigt für dieselbe Szene, die auch mit RayTraced
Evolution erstellt wurde, keine zehn Sekunden. Weitere Vorteile von POV-Ray sind die hervorragenden Dokumentationen sowie die große Verbreitung. So erhält man in Newsgroups
wie comp.graphics.rendering.raytracing innerhalb kürzester Zeit Antworten auf Fragen zu
POV-Ray. Alternativ kann man auch über den Server von POV-Ray auf eine spezielle
Newsgroup zugreifen und dort seine Fragen stellen. Zusammengefasst kann man sagen, dass
POV-Ray einer der schnellsten freien Renderer mit einer sehr hohen Qualität bei der Bilderzeugung ist. Daher wird der Renderer POV-Ray gewählt.
74
4
Entwurf und Implementierung der Fassade
4.6 Lparser
Die einzelnen Bestandteile von Lparser wurden schon in Kapitel 3.1 vorgestellt. An dieser
Stelle wird zusätzlich dazu auf die Probleme mit dem Lparser eingegangen.
Zuvor wird jedoch eine Weiterentwicklung von Lparser näher erläutert.
Lparser in der Version 4 besitzt einige Nachteile, beispielsweise die geringe Unterstützung
von verschiedenen L-Systemen oder die Abhängigkeit vom DOS4GW-Treiber. Weil Laurens
Lapre den Quellcode von Lparser freigegeben hat, konnten zahlreiche Entwickler den Lparser
erweitern. Eine dieser Personen ist Ken Kopp. Er hat im Jahre 2000 den Lparser auf die Version 5.1 gestellt. Seine Version von Lparser benötigt keinen DOS4GW-Treiber und unterstützt parametrisierte und kontextsensitive L-Systeme. Da diese Erweiterungen die wesentlichen Nachteile von Lparser aufheben, wird für den Bilderzeugungsprozess auf diese erweiterte Version zurückgegriffen.4
Lparser benötigt als Eingabe eine LS-Datei, in der das L-System definiert ist. Dabei interpretiert das Programm die erste auftretende Zeichenkette als die Rekursionstiefe, wobei Kommentare nicht ausgewertet werden. Somit muss die Rekursionstiefe nicht zwangsweise in der
ersten Zeile stehen. Danach wird der Basiswinkel für die Turtle-Interpretation angegeben sowie der Anfangswert für die Stärke, also die Breite eines Zylinder-Primitives. Die vierte auftretende Zeichenkette wird als Axiom interpretiert. Alle weiteren Zeichenketten werden als
Regeln des L-Systems verstanden.
Beispiel 4.1 für eine einfache LS-Datei:
Das Beispiel zeigt ein vollständiges L-System für den Lparser.
5
18
20
P
/*
/*
/*
/*
Rekursionstiefe
Basiswinkel
Anfangsstärke
Axiom
/* Regeln
P -> I+[P+R]--//[--L]I[++L]-[PR]++PR
I -> FS[//&&L][//^^L]FS
S -> SFS
L -> ['{+f-ff-f+|+f-ff-f}]
R -> [&&&C'/W////W////W////W////W]
C -> FF
W -> ['^F][{&&&&-f+f|-f+f}]
Kommentare in einer LS-Datei werden durch „/*“ kenntlich gemacht. Ansonsten sind die Bedeutungen der Zeichen identisch mit denen der Turtle-Interpretation. Die allgemeine Syntax
für eine Regel ist:
<linker Kontext> < <Symbol> > <rechter Kontext>
: <bool’scher Ausdruck> -> <rechte Seite der Regel>
Beispiel 4.2 für eine Regel in einer LS-Datei:
Das Beispiel zeigt eine Produktion, die alle Teile einer Produktion unterstützt.
a < b(x) > c : x > 5 -> a(4) c(x) FFF [+a(x)]
Ein Unterschied zwischen Version 4 und 5 besteht darin, dass sich die Syntax für die LSDatei leicht verändert hat. Diese Unterschiede werden in Tabelle 4.1 dargestellt.
4
Wenn im Folgenden von der Version 4 von Lparser gesprochen wird, so ist das Original von Laurens Lapre
gemeint und mit Version 5 die erweiterte Variante von Ken Kopp.
75
4
Entwurf und Implementierung der Fassade
Bedeutung
Kommentar
Regelzuweisung
Rotieren um die z-Achse, entgegen dem Uhrzeigersinn
Rotieren um die z-Achse, Uhrzeigersinn
Symbol für das Ende der LS-Datei
Version 4
#
=
<
>
@
Version 5
/*
->
\
/
keins
Tabelle 4.1: Unterschiede zwischen Version 4 und 5
Außerdem können define-Anweisungen verwendet werden, wie man sie in C kennt.
Trotz einer immensen Weiterentwicklung von Lparser, der Code hat sich mehr als verdoppelt,
sind weiterhin einige Schwächen vorhanden. Lparser prüft nicht, ob plausible Werte für Rekursionstiefe, Basiswinkel, Stärke und Axiom vorliegen. Außerdem können Zeichen in einer
Produktion auftauchen, für die es keine Produktion zum Ableiten gibt. Es existiert auch keine
Prüfung, ob die Anzahl der öffnenden und schließenden, eckigen und geschweiften Klammern die gleiche ist. Dies wäre jedoch im Hinblick auf die Evolution von Interesse, weil LSysteme generiert werden können, die nicht der strengen Definition der Grammatik entsprechen. So kann eine komplette Produktion entfernt werden, ohne weitere Produktionen abzuändern und es können willkürlich, eckige Klammern gesetzt werden. Außerdem werden auch
„nicht L-Systeme“ interpretiert, weil diese Prüfungen nicht stattfindet. Bezogen auf die Produktionsregeln sei noch darauf hingewiesen, dass anstelle eines Symbols auch „*“ als Wildcard stehen kann, wenn zum Beispiel bei einer kontextsensitiven Regel der linke Kontext
nicht verwendet wird. Gleiches gilt auch für bool’sche Ausdrücke. Wenn die Regel auf jeden
Fall angewandt werden soll, dann kann das Wildcardzeichen eingesetzt werden. Außerdem ist
man nicht gezwungen, die Produktion vollständig zu beschreiben. Wenn es keine kontextsensitive Regel ist, dann kann der kontextsensitive Teil wegfallen. Das Gleiche gilt für den Bedingungsteil von L-Systemen.
Beispiel 4.3 für Regeln in LS-Dateien:
Im Folgenden werden einige Produktionen angegeben, die in einer LS-Datei auftreten
könnten.
a
-> F + F + F + b(5)
b(x) : x > 4
-> c F f f + F a
* < c > F
-> [ - F - F a]
Ein Fehler in Lparser ist die Interpretation der Kommandozeilenargumente. Nicht alle Kommandozeilenargumente werden zuverlässig interpretiert. Das liegt daran, dass Lparser zur
Auswertung der Argumente auf eine Funktion namens Get_Comline_opt() zurückgreift.
Für diese Funktion wird vorher ein so genannter „Option String“ definiert, aus dem die gültigen Optionen abgeleitet werden. Um Lparser problemlos in den Bilderzeugungsprozess zu
integrieren, wird das Programm erweitert, unter anderem auch die Anzahl der Kommandozeilenargumente. Als die neuen Argumente an den Option String angehängt wurden, hat Lparser
beim Aufruf mit den Argumenten immer einen Fehler geworfen. Erst nachdem die Position
der neuen Argumente im Option String auf die Mitte verlegt wurde, wurden keine Fehler
mehr geworfen. Ungewöhnlich ist die im Programm verwendete Vektorschreibweise für die
Punkte zur Erzeugung von Primitiven. So wird beispielsweise für die Anfangs- und Endpunkt
eines Zylinders an erster Stelle die x-Koordinate angegeben, an zweiter die z-Koordinate und
an dritter Stelle die y-Koordinate. Dies wird selbst bei POV-Beschreibungen für die 3DPrimitiven fortgesetzt, obwohl diese normalerweise die Reihenfolge (x, y, z) benötigen.
76
4
Entwurf und Implementierung der Fassade
Normalerweise müsste im Anschluss nach dem Lparser das Programm LViewer ausgeführt
werden. Denn durch das Programm LViewer wird eine Datei namens INFO.TXT erstellt, in
der die Kameraeinstellungen gespeichert werden. Diese Datei benötigt wiederum LV2POVID
für die POV-Ray Datei. Der LViewer hat aber einige Nachteile. So benötigt er den
DOS4GW-Treiber und die Erstellung der INFO.TXT Datei geschieht nicht automatisch, sondern erst durch das Drücken der Taste F2. Außerdem sind die geschriebenen Daten in der
INFO.TXT nicht korrekt, denn LViewer fügt immer wieder die gleichen Daten ein, selbst bei
vollkommen unterschiedlichen Szenen. Daher wurde die Lparser Version 5 insoweit erweitert, dass bei Erstellung einer POV-Ray Datei Lparser selbst die INFO.TXT Datei erzeugt und
eine sinnvolle Kameraposition berechnet.
Auch das dritte Programm im Softwarepaket von Lparser funktioniert nicht wie erwartet.
Aufgabe des Programms LV2POVID ist es, die nicht POV-Ray gerechte Datei in eine POVRay gerechte Datei um zuwandeln. Außerdem liest das Programm die INFO.TXT Datei aus
und setzt anhand der Daten die Kamera und die Lichtquelle. Es zeigt sich aber, dass an Stelle
der Werte für die Kameraposition die Werte für die Kameraausrichtung eingesetzt werden
und dass die Kameraausrichtung immer die Werte (0, 0, 0) zugewiesen bekommt. Außerdem
wird mehrfach die Lichtquelle direkt im Körper des 3D-Modells platziert. Interessanterweise
liest das Programm LV2POVID die INFO.TXT Datei richtig aus und verdreht nicht die Koordinaten, wie das bei Lparser der Fall ist. Das liegt vielleicht auch daran, dass die Daten in
der INFO.TXT schon verdreht sind. Eine weitere Schwachstelle von LV2POVID ist, dass
Dreiecke so beschrieben werden, wie bei POV-Ray in Version 2.X. Die aktuelle POV-RayVersion 3.5 gibt zwar Warnungen aus, dass die Dreiecke nicht in der empfohlenen Syntax
vorliegen, erzeugt aber die Dreiecke noch richtig. Außerdem gibt LV2POVID Daten an, die
nicht notwendig sind, bzw. Daten, die zu einem falschen Ergebnis führen, wie zum Beispiel
die Direction- oder Angle-Anweisung für die Kamera.
4.7 Persistence of Vision-Ray
1986 beginnt David K. Buck auf dem Amiga die Entwicklung eines Ray-Tracing Programms
namens DKBTrace. 1989 wird der Name DKBTrace in POV-Ray (POV = Persistence Of Vision) geändert, weil mittlerweile sehr viele Menschen das Programm weiterentwickelten haben und Buck nicht als Projektleiter aufgefasst werden will. Damit ist POV-Ray [[Pov]] eines
der ältesten Ray-Tracing Programme und es ist auch eines der bekanntesten. POV-Ray ist ein
freier Renderer, dessen Quelldateien für jedermann zugänglich sind. Darüber hinaus gibt es
zahlreiche Tools für POV-Ray. Eines der bekanntesten ist MORay, eine graphische Oberfläche zur Modellierung von 3D-Modellen.
POV-Ray basiert auf der Strahlenverfolgung. Das ist ein Prozess zur Simulierung der Lichtausbreitung in einer Szene. Zur Vereinfachung wird bei der Strahlenverfolgung nicht jeder
Lichtstrahl von der Lichtquelle aus bis zum Auge verfolgt, sondern genau anders herum wird
jeder Strahl eines Pixels auf dem Bildschirm so lange verfolgt, bis er eine Lichtquelle erreicht,
bis die Energie des Strahls zu schwach ist oder bis der Strahl die Szene verlässt. Trifft der
Strahl auf ein Objekt, wird die Beleuchtung an diesem Punkt berechnet und es werden zwei
neue Strahlen erzeugt, einer der reflektiert wird und einer der gebrochen wird.
77
4
Entwurf und Implementierung der Fassade
Abbildung 4.3: Strahlenverfolgung in einer Szene [ESK97]
Die Entwicklung einer 3D-Szene in POV-Ray läuft anders ab als bei Ray-Tracing Programmen wie 3D-Studio oder Lightwave. So kommt POV-Ray zwar mit einer graphischen Oberfläche, diese ist aber nicht zum graphischen Modellieren der 3D-Szene geeignet, sondern zum
textbasierten Beschreiben der 3D-Szene. Somit ist die graphische Oberfläche ein Text-Editor,
der auch zum Schreiben von Programmen genutzt werden kann, weil der POV-Ray Editor
Syntax-Highlighting für verschiedene Programmiersprachen enthält.
Die Beschreibungssprache von POV-Ray ist an C/C++ angelehnt und ebenfalls case-sensitiv.
Als Kommentarzeichen kann // für einen Zeilenkommentar oder /* und */ als Kommentar
über mehrere Zeilen genutzt werden, sie sind also identisch mit den Kommentarmöglichkeiten
in C++.
Ebenfalls identisch mit C/C++ ist die Bedeutung der #include-Anweisung. Somit können Beschreibungsteile ausgegliedert werden oder als allgemeine Modelle für andere 3D-Szenen
genutzt werden. Die #declare-Anweisung wird zum Beispiel benutzt, um Konstanten zu definieren. Die Anweisung kann aber auch benutzt werden, um ein neues 3D-Objekt zu definieren. Elemente einer 3D-Szene werden stattdessen mit ihrem Namen aufgerufen und in geschweiften Klammern näher spezifiziert. Die Reihenfolge der 3D-Elemente, #includeAnweisungen und #declare-Anweisung spielt an sich keine Rolle. Wenn aber auf den Inhalt
einer Include-Datei zugegriffen wird, dann muss diese auch vorher deklariert sein. Gleiches
gilt für Konstanten oder neue 3D-Objekte, die mit der #declare-Anweisung definiert wurden,
auch diese müssen vor der Nutzung bekannt sein. Um eine bessere und nicht fehleranfällige
Struktur der Beschreibung zu erlangen, sollten am Anfang alle #include-Anweisungen stehen,
gefolgt von den #declare-Anweisungen. Weiterhin hat POV-Ray in seiner Beschreibungssprache auch Direktiven wie IF und WHILE.
Eine sinnvolle 3D-Szene besteht mindestens aus einer Kamera, einer Lichtquelle und einem
Objekt. Wie eine solche Szene aussehen könnte, wird an folgendem Beispiel deutlich.
78
4
Entwurf und Implementierung der Fassade
Beispiel 4.4 für eine 3D-Szene:
Es wird die Beschreibung für eine einfache 3D-Szene unter POV-Ray angegeben.
#include "colors.inc"
#include "glass_old.inc"
#include "metals.inc"
camera {
location <1.5, 10, -10>
look_at <1.5, 1, 2>
}
light_source {
<500 , 1000, -50>
color White
}
plane {
<0, 1, 0>,
-1
pigment {
checker color White
color Black
scale 4
}
}
sphere {
<0, 1.5, 2>,
3
texture {
T_Chrome_5A
pigment {
color Blue
}
}
}
sphere {
<3, 1.5, 2>, 3
texture {
T_Glass1
}
}
// Kameraposition
// Kameraausrichtung
// Position x, y, z
// Farbe
//
//
//
//
//
Ebene
Oberflächennormale
Position zur y-Achse
Muster Schachbrett
Schwarz/Weiß
// Skalierung
//
//
//
//
//
Mittelpunkt
Radius
Textur
Chrome
Blau
// Glastextur
POV-Ray kennt als 3D-Primitive die Sphäre (Sphere), das Rechteck (Box), den Zylinder (Cylinder), den Torus, den Kegel (Cone) und die Ebene (Plane). Mit Hilfe von CSG (Constructive Solid Geometry) lassen sich aus den 3D-Primitiven neue komplexe 3D-Objekte erstellen,
wie zum Beispiel eine Kette. Dazu werden Objekte in CSG-Operationen eingebettet, wobei
die CSG-Operationen die Mengen-Operationen abbilden. Als Operatoren existieren Vereinigung (Union), Schnittmenge (Intersection) und Differenzmenge (Difference). Zusätzlich gibt
es noch den Operator Merge zum Verbinden von Objekten, er kommt der Vereinigung gleich.
Außerdem kennt POV-Ray auch Polygonen und auf Spline basierenden Formen sowie verschiedene Arten von Lichtquellen, wie zum Beispiel Pointlight, Spotlight, Ambient Light und
andere.
Für weitere Informationen sei auf die Dokumentation von POV-Ray verwiesen, auf
http://www.povray.org/documentation/.
[ESK96] [ESK97]
79
4
Entwurf und Implementierung der Fassade
Abbildung 4.4: Die gerenderte Szene
4.8 Schnittstelle der Fassade
Mit der Fassade werden zwei Wege angeboten, diverse Einstellungen festzulegen. Der erste
Weg sind die Kommandozeilenargumente. In Tabelle 4.2 sind alle Kommandozeilenargumente und deren Auswirkungen aufgelistet.
Kommandozeilenargument
-?
-H[Zahl]
-W[Zahl]
-T
-A
-C
-c
-E
-e
-F
Auswirkung
Ruft einen Bildschirm auf mit Erklärungen zu den Kommandozeilenargumenten.
Gibt die Höhe der Grafik in Pixel an.
Gibt die Breite der Grafik in Pixel an.
Das Ausgabeformat ist eine Targa-Grafikdatei.
Aktiviert die Anti-Aliasing Funktion.
Konvertiert eine Datei in die Version 5.
Konvertiert eine Datei in die Version 5 und beendet danach den
Prozess.
Beendet POV-Ray nachdem der Prozess das Bild erzeugt hat.
Beendet POV-Ray ohne das Starten einer Bilderzeugung.
Dieses Argument gibt an, dass alle Dateien aus den Zwischenschritten erhalten bleiben sollen.
Tabelle 4.2: Kommandozeilenargumente der Fassade
80
4
Entwurf und Implementierung der Fassade
Die Aufrufskonventionen der Kommandozeile sind:
lprocess [Optionen] [zu konvertierende LS-Datei] [LS-Datei]
Die Optionen entsprechen den Kommandozeilenargumenten. Diese müssen nicht einzeln angegeben werden, sie können auch kombiniert werden, wie zum Beispiel:
lprocess -ACEW640H480 bekerpl bekerpl5
Das Aufrufen der Fassade würde bei diesen Argumenten die Anti-Aliasing Funktion aktivieren, die Datei bekerpl in die Version 5 konvertieren und in bekerpl5 abspeichern, POV-Ray
nach Beendigung des Prozesses ebenfalls beenden und die Größe der 3D-Grafik auf 640*480
Bildpunkte festlegen. Wird die Konvertierungsfunktion aktiviert, dann können auch zwei Dateinamen angegeben werden. In solch einem Fall speichert der Prozess die Konvertierung im
zweiten angegebenen Dateinamen ab. Wird nur ein Dateiname angegeben, dann wird der alte
Inhalt durch den neuen ausgetauscht.
Neben der Kommandozeile gibt es noch eine Konfigurationsdatei (LPROCESS.CFG), in der
einige Werte für diverse Einstellungen stehen müssen. Ohne eine vollständige Konfigurationsdatei startet die Fassade nicht den Bilderzeugungsprozess. In Tabelle 4.3 sind alle Einstellungen, deren Werte und ihre Bedeutung aufgelistet.
Einstellungen
Image
Height
Width
AA
Werte
„bmp“ oder „targa“
Positive ganze Zahl
Positive ganze Zahl
„on” oder „off”
POV
Pfad
QPOV
Pfad
Files
„on” oder „off”
CamX
Reelle Zahl
CamY
Reelle Zahl
CamZ
Reelle Zahl
LightX
Reelle Zahl
LightY
Reelle Zahl
LightZ
Reelle Zahl
//
keinen
Bedeutung
Speichert die Grafik als Bitmap (bmp) oder als Targa.
Gibt die Höhe der Grafik in Pixel an.
Gibt die Breite der Grafik in Pixel an.
Gibt an, ob die Anti-Aliasing Funktion aktiviert werden
soll.
Diese Einstellung muss den Pfad für POV-Ray beinhalten.
Diese Einstellung muss den Pfad für QuietPOV enthalten.
Durch Setzen der Einstellung, werden alle Dateien aus
den Zwischenschritten erhalten.
Diese Zahl wird benutzt, um mit der x-Koordinate der
Kameraposition multipliziert zu werden.
Diese Zahl wird benutzt, um mit der y-Koordinate der
Kameraposition multipliziert zu werden.
Diese Zahl wird benutzt, um mit der z-Koordinate der
Kameraposition multipliziert zu werden.
Mit dieser Zahl wird die x-Koordinate der Kamera multipliziert, zur relativen Positionierung der Lichtquelle.
Mit dieser Zahl wird die y-Koordinate der Kamera multipliziert, zur relativen Positionierung der Lichtquelle.
Mit dieser Zahl wird die z-Koordinate der Kamera multipliziert, zur relativen Positionierung der Lichtquelle.
Kommentarzeichen
Tabelle 4.3: Alle Werte der LPROCESS.CFG
81
4
Entwurf und Implementierung der Fassade
Wobei jede Einstellung, außer den Kommentaren, in der Form
Einstellung=Wert
erfolgen muss. Des Weiteren gilt, dass alle Werte von der Kommandozeile eine höhere Priorität haben als in der CFG-Datei. Wenn zum Beispiel beim Kommandozeilenaufruf die AntiAliasing Funktion angegeben wird und in der CFG-Datei steht ein „off“, dann wird das AntiAliasing trotzdem für die Bilderzeugung mit benutzt. Die Datei LPROCESS.CFG ist eine
einfache Textdatei.
4.9 Ablauf der Fassade
Bevor auf die Implementierung der Fassade eingegangen wird, soll der Ablauf der Verarbeitung in der Fassade geklärt werden. Dazu ist in Abbildung 4.5 das Ablaufdiagramm dargestellt, welches nachfolgend besprochen wird. Eine Raute symbolisiert dabei die Ausführung
einer Aktion und deren Auswertung. Ein Rechteck steht für das Ausführen einer Aktion, in
der keine Auswertung stattfindet.
82
4
Entwurf und Implementierung der Fassade
Abbildung 4.5: Ablaufdiagramm der Fassade
83
4
Entwurf und Implementierung der Fassade
Nach dem Start der Fassade werden die Kommandozeilenargumente verarbeitet, d. h. es wird
geprüft, ob die Kommandozeilenargumente und deren Kombinationen gültig und vollständig
sind. So wird beispielsweise die Kombination „-e -E“ nicht zugelassen, weil verlangt wird,
dass POV-Ray sofort und nach der Verarbeitung beendet wird. Außerdem wird geprüft, ob
Dateinamen vorliegen und ob nach den Dateinamen noch weitere Eingaben vorhanden sind.
Wurde bei der Prüfung ein Fehler festgestellt, wird das Programm mit einer Fehlermeldung
beendet.
Bevor der Prozess zur Bilderzeugung weiter abgearbeitet wird, wird zuvor geprüft, ob die
vollständige Abarbeitung für die definierte Zielsetzung notwendig ist. Wird beispielsweise als
Kommandozeilenargument angegeben, dass nur eine Datei konvertiert oder nur POV-Ray
beendet werden soll, dann wird die Aktion auf der Stelle ausgeführt und das Programm ohne
Fehlermeldung und ohne ein Bild zu erzeugen beendet.
Die zweite Schnittstelle zur Fassade bildet der Inhalt der CFG-Datei. Dieser Inhalt wird komplett ausgelesen und zwischengespeichert. Danach werden alle Variablen, die noch nicht
durch ein Kommandozeilenargument gesetzt wurden, durch den Inhalt der CFG-Datei gesetzt.
Stellt das Programm bei der Wertzuweisung an die Variablen fest, das kein Wert oder kein
gültiger Wert vorliegt, wird das Programm mit einer Fehlermeldung beendet.
Der letzte Vorbereitungsschritt besteht darin zu prüfen, ob für das Verzeichnis von QuietPOV
eine Umgebungsvariable gesetzt wurde. Sollte dies nicht der Fall sein, dann wird für die Verarbeitungszeit die Umgebungsvariable gesetzt.
Als ersten Verarbeitungsschritt konvertiert die Fassade eine LS-Datei von Version 4 in die
Version 5, falls dies angegeben wurde. Sollte während der Verarbeitung ein Fehler auftauchen, wird die Fassade mit einer Fehlermeldung beendet.
Bevor Lparser mit einer LS-Datei gestartet wird, wird die LS-Datei nach einem gültigen Inhalt geprüft, das heißt es wird überprüft, ob eine Rekursionstiefe, ein Startwinkel, eine Startdicke und ein Axiom angegeben wurden. Diese Prüfung entspricht mehr einer Plausibilitätsprüfung. Außerdem wird geprüft, ob runde Klammern im linken oder rechten Kontext bzw.
im Bedingungsteil vorliegen. Falls kein plausibeler Wert vorliegt oder runde Klammern vorliegen, beendet das Programm mit einer Fehlermeldung.
Lparser bekommt als Eingabe die LS-Datei und einige Kommandozeilenargumente. So soll
das Programm eine Beschreibungsdatei für POV-Ray erzeugen und diese als INC-Datei speichern. Außerdem werden Lparser über die Kommandozeile die Faktoren übergeben, mit denen die Kameraposition multipliziert wird. Die Kameraposition wird dabei anhand der Ausdehnung der 3D-Szene berechnet. Ist die 3D-Szene eine echte 3D-Szene, das heißt die Ausdehnungen des Modells bezieht sich auf alle drei Achsen, dann werden die Kamerakoordinaten berechnet, indem eine Ausdehnung der jeweiligen Achse mit dem übergebenen Kommandozeilenwert multipliziert wird. Ziel ist es, die Kamera relativ zur Modellausdehnung zu positionieren. In Abbildung 4.6 a) wird dies verdeutlicht. Sollte keine echte 3D-Szene vorliegen,
also eine Achsenausdehnung sowohl in negativer als auch positiver Richtung nahezu null
sein, dann wird der Kamera von den Koordinaten, die eine Ausdehnung besitzen, deren Mittelwert zugewiesen. Für die Koordinate, die eine Ausdehnung nahezu null besitzt, wird ein
neuer Wert berechnet. Dazu wird der Mittelwert der anderen beiden Koordinaten addiert und
mit dem zur Achse gehörigen Wert von der Kommandozeile multipliziert. Ziel ist es, die Kamera auf die Mitte des 2D-Modells auszurichten und mit der Koordinatenachse, die nahezu
null war, die Entfernung der Kamera zu regulieren. In Abbildung 4.6 b) wird eine x-Achse
gezeigt, die keine Ausdehnung aufweist.
84
4
Entwurf und Implementierung der Fassade
Abbildung 4.6: Beispielhafte Kameraposition für a) 3D-Szenen und b) 2D-Szenen
Nach Beendigung von Lparser wird anhand der Ausgabe von der Fassade geprüft, ob ein Fehler in einer Regel vorlag. Zusammen mit der Nummer der Regel wird dann eine Fehlermeldung ausgegeben.
Bevor LV2POVID gestartet wird muss geprüft werden, ob alle notwendigen Dateien vorhanden sind, das heißt ob die INFO.TXT, die LV2POVID.CFG und die OUTPUT.INC gefunden
wurden. Die INFO.TXT enthält die Daten für die Kameraposition, wobei es kein Fehler ist,
dass die Daten in der Datei doppelt vorhanden sind. LV2POVID erkennt die INFO.TXT nicht
an, wenn die Daten nicht doppelt vorhanden sind. In der LV2POVID.CFG steht der Pfad für
das Programm Fractint. Zwar wird das Programm Fractint nicht verwendet, trotzdem muss die
Datei mit irgendeinem Pfad vorhanden sein, ansonsten startet LV2POVID nicht. Auch die
von Lparser erzeugt Beschreibungs-Datei OUTPUT.INC muss vorhanden sein, denn wenn
eine der drei Dateien nicht vorliegt, wird das Programm mit einer Fehlermeldung beendet.
Nachdem LV2POVID die OUTPUT.INC in eine richtige POV-Ray Datei umgewandelt hat,
wird die von LV2POVID erzeugte POV-Ray Datei korrigiert. Die OUTPUT.INC heißt zu
diesem Zeitpunkt LPAR2POV.INC. Die zu korrigierende Datei heißt LPAR2POV.POV. Sie
beinhaltet die Daten für die Kamera und für die Lichtquelle. Da LV2POVID trotz der
INFO.TXT die Daten nicht richtig in die POV-Datei einträgt, muss dies korrigiert werden.
Daher wird in diesem Schritt die richtige Kameraposition nachgetragen und die Position der
Lichtquelle berechnet und eingetragen. Die Lichtquelle wird relativ zur Kameraposition platziert. Ausgehend von den Koordinaten der Kamera werden dazu die Koordinaten mit den definierten Werten aus der CFG-Datei multipliziert.
Bevor das Bild gerendert wird, prüft die Fassade, ob schon eine Instanz von POV-Ray existiert. Falls das nicht so ist, wird POV-Ray gestartet.
Nun werden alle für POV-Ray relevanten Daten in eine INI-Datei gespeichert, darunter auch
der Name der POV-Ray Datei und der späteren Bilddatei. Für die Ausgabedatei wird der Name der LS-Datei gewählt.
Im Anschluss wird das Programm QuietPOV mit der INI-Datei aufgerufen. QuietPOV ist ein
Kommandozeilenprogramm mit dem es möglich ist, über die Kommandozeile Befehle an
POV-Ray zu senden. Dazu nutzt das Programm die GUI-Extension-Schnittstelle von POVRay. Durch den Einsatz von QuietPOV wird das ständige Laden der graphischen Oberfläche
und das damit verbundene Auftauchen des Splash-Screens unterbunden. Der Splash-Screen
85
4
Entwurf und Implementierung der Fassade
darf aus lizenzrechtlichen Gründen nicht deaktiviert werden. Wenn POV-Ray nicht privat
benutzt wird, muss deutlich kenntlich sein, dass POV-Ray benutzt wird. Dies geschieht durch
den Splash-Screen.
Nachdem das Bild erzeugt ist wird geprüft, ob POV-Ray an dieser Stelle beendet werden soll.
Zuletzt wird anhand des Kommandozeilenargumentes „-F“, entschieden, ob die Dateien, die
zwischenzeitig erzeugt wurden, gelöscht werden sollen oder nicht.
4.10 Implementierung der Fassade
Bevor die eigentliche Fassade implementiert wird, muss Lparser auf die neuen Bedürfnisse,
auf das Berechnen der Kameraposition und auf das Erzeugen der INFO.TXT angepasst werden. Wie bereits erwähnt, basiert Lparser Version 5.1 auf der Version 4. Version 4 wurde
noch im reinen C geschrieben. Die Erweiterungen wurden von Ken Kopp mit C++ entwickelt.
Auch die Anpassungen an Version 5.1, die für die vorliegende Arbeit notwendigen wurden,
sind in Visual C++ 6.0 durchgeführt.
Um bei Lparser die maximale Ausdehnung der 3D-Szene zu erhalten, werden globale Variable definiert, die prüfen, ob die Koordinaten des Objektes größer sind, als die bisher gespeicherten, bevor ein Objekt in die POV-Ray Datei geschrieben wird. Sind die Koordinaten des
aktuellen Objektes größer, sowohl in negativer als auch in positiver Richtung, dann werden
diese in den Variablen gespeichert. Die Objekte werden in den Funktionen
Define_form() und Save_object() abgefangen und geprüft.
Zusätzlich wird die Anzahl der Kommandozeilenargumente erhöht bzw. um -x[num],
-y[num] und -z[num] erweitert, damit für die Kameraposition die Faktoren vorliegen, mit
denen sie multipliziert werden sollen. In der main()-Funktion werden alle Kommandozeilenargumente im Hinblick darauf geprüft, ob sie gesetzt wurden oder nicht. Dazu wird auf
eine Hilfsfunktion namens Get_comline_opt() zurückgegriffen. Diese Funktion benötigt vor ihrem Aufruf die Zuweisung eines „Option String“, in dem alle Symbole enthalten
sind, die als Argumente von der Kommandozeile übergeben werden können. Beim Aufruf
von Get_comline_opt() muss das Symbol angegeben werden, nach dem gesucht wird,
sowie eine bool’sche Variable die angibt, ob die Option vorhanden ist. Zusätzlich dazu wird
ein char-Array angegeben, in dem der Wert des Kommandozeilenargumentes enthalten ist,
falls für dieses Kommandozeilenargument ein Wert vorliegt.
Am Ende der main()-Funktion wird die Funktion createINFOTXT() aufgerufen, die
dann die INFO.TXT Datei erzeugt und die Kameraposition berechnet. Zur Berechnung der
Kameraposition wurden zwei Hilfsfunktionen entwickelt. Die eine heißt dAbs() und bildet
die Betragsfunktion für double-Werte ab. Die zweite Funktion heißt
percOfAxisValues(). Diese Funktion berechnet einen bestimmten Prozentwert von der
größten Ausdehnung einer bestimmten Achse, die beim Aufruf angegeben wird.
Zusammenfassung:
Modifiziert wurden die Funktionen main(), Define_form() und Save_object().
Die globalen double-Variablen maxX, minX, maxY, minY, maxZ, minZ, x, y, z wurden
hinzugefügt außerdem folgende Funktionen:
double dAbs(double number);
double percOfAxisValues(double perc, char type) ;
void
createINFOTXT(void);
86
4
Entwurf und Implementierung der Fassade
Um, wie im Quelltext von Lparser, das umständliche Suchen von Funktionen zu unterbinden,
wurden Funktionen gruppiert und in eigene C-Dateien ausgelagert. Somit ergibt sich eine
Modularisierung gemäß Tabelle 4.4.
Modulname
Lprocess.c
globals.h
cfg_file.c / cfg_file.h
com_line.c / com_line.h
lparser.c / lparser.h
misc.c / misc.h
pov.c / pov.h
Kurzbeschreibung
Enthält lediglich die main()-Funktion.
Enthält wichtige Präprozessor-Anweisungen, globale Variablen
und Strukturen, die für alle Module zur Verfügung stehen.
Alle Funktion, die zum Auslesen und Interpretieren der CFGDatei benötigt werden, sind in dem Modul enthalten.
Enthält Funktionen zur Verarbeitung und Interpretation der
Kommandozeilenargumente.
Das Modul enthält alle Funktionen, die mit dem Lparser-Paket
interagieren.
Die einzelnen Funktionen, die keinem speziellen Modul zugewiesen werden können, finden sich hier wieder.
Alle Funktionen, die mit POV-Ray oder QuietPOV kommunizieren, sind in diesem Modul enthalten.
Tabelle 4.4: Module der Fassade
Die Datei Lprocess.c enthält lediglich die main()-Funktion. Diese entspricht im Groben
dem Ablaufdiagramm aus Abbildung 4.6. Lediglich die Prüfung und das Starten einer POVRay Instanz wird von der Fassade vor dem Ausführen von Lparser getätigt. Dies ist nötig,
damit POV-Ray ausreichend Zeit bekommt, die GUI-Extension-Schnittstelle zur Verfügung
zustellen und Befehle über die Schnittstelle von QuietPOV entgegenzunehmen. Falls bei der
Verarbeitung ein Fehler auftritt, gibt die Fassade den Wert -1 an das Betriebssystem zurück.
Die globals.h enthält zwei wichtige Strukturen, die
InfoStruct und die CFGStruct. In der InfoStruct sind alle wichtigen Informationen
für den Verlauf der Bilderzeugung enthalten, die während der Verarbeitung benötigt werden.
Der Inhalt der Variablen ergibt sich aus den Werten der Kommandozeile und aus den Optionen der CFG-Datei. So sind zum Beispiel Variablen in der InfoStruct, die die Höhe und
Breite der Grafik in Pixel speichern oder Variablen, die den Pfad für POV-Ray und QuietPOV enthalten. Besonders hervorzuheben ist die Variable state. Diese 8-Bit Variable speichert in jedem Bit eine bestimmte Einstellung, beispielsweise ob als Ausgabeformat das Targa- oder das Bitmapformat verwendet werden soll. Daher kann die Variable mit Bitmaskierungen abgefragt und gesetzt werden. Die einzelnen Bedeutungen der Bits können aus der
Abbildung 4.7 entnommen werden. Die Struktur CFGStruct wird als Zwischenspeicher
zum Einlesen der CFG-Datei benutzt. Sie besteht aus zwei char-Arrays, die als left und
right bezeichnet werden. In left wird die linke Optionsseite und damit auch der Bezeichner einer Option gespeichert, right speichert entsprechend die rechte Optionsseite und damit den Wert eines Bezeichners. Der restliche Inhalt der globals.h, besteht aus diversen
Präprozessor-Anweisungen. So werden die Bitmasken für die einzelnen Werte von state
definiert, ebenso deren nicht erlaubte Kombinationen. Außerdem werden diverse Maximumwerte definiert, wie zum Beispiel die maximale Länge einer Zeile in einer LS-Datei oder in
der CFG-Datei. Eine wichtige define-Anweisung verbirgt sich hinter POV_APP. Der String
enthält den Namen von POV-Ray, der die Adresse für Windows-Nachrichten darstellt. Windows-Programme erhalten über Nachrichten Informationen vom Betriebssystem, beispielsweise dafür, dass eine Taste für das Programm gedrückt wurde. So enthält jedes WindowsProgramm eine Nachrichtenschleife, in der die jeweilige Nachricht ausgewertet wird. Tiefer87
4
Entwurf und Implementierung der Fassade
gehende Informationen zur Windows-Programmierung sind in [Ri97] [DS98] [To99] [LA98]
zu finden. Um eine POV-Ray Instanz zu finden oder zu beenden, benötigen die APIFunktionen von Windows den Applikationsnamen von POV-Ray. Dieser ist aber nicht identisch mit der Executable-Datei oder mit der Beschreibung der Datei. Um solch einen Applikationsnamen zu erhalten, kann man mit Tools die Nachrichtenverarbeitung von Windows beobachten. Eines dieser Tools heißt WinSight. Es stammt von Borland und ist in einigen Borland-Produkten, beispielsweise in Delphi enthalten. Der aktuelle Applikationsname von POVRay in der Version 3.5 lautet „Pov35MainWinClass“. Wenn eine neue Version erscheint,
kann sich dieser Name ändern.
Abbildung 4.7: Bitbelegung der Variable state
Das Modul cfg_file.c enthält drei Funktionen, deren Aufgabe es ist, die Kommandozeilenargumente zu verarbeiten und zu interpretieren. Dabei wird die meiste Arbeit in der Funktion
checkingCFGFile() durchgeführt. Diese Funktion ist auch die einzige Funktion, die
nicht der Faustregel folgt, wenn eine Funktion mehr als zwei Bildschirmgrößen groß ist, so
lagere Teile dieser Funktion aus. Auf eine Ausgliederung wurde in diesem Fall verzichtet,
weil dies aufgrund der starken Verzahnung des Quelltextes nicht sinnvoll erschien. Alle anderen Funktionen orientieren sich an dieser Regel, weil dadurch die einzelnen Teile besser gewartet und getestet werden können. Außerdem erhöht die Ausgliederung auch die Übersichtlichkeit des Quelltextes [JAH01]. Die Funktion checkingCFGFile() versucht zunächst
die Datei LPROCESS.CFG zu öffnen. Wenn der Versuch fehlschlägt, dann wird die
InfoStruct mit Standardwerten gefüllt und die Funktion createDefaultCFGFile()
aufgerufen, die eine CFG-Datei mit Standardwerten erzeugt. Danach wird in einer Schleife
die Datei ausgelesen und gefundene Optionen in einer Variablen von der Struktur
CFGStruct gespeichert. Im Anschluss werden alle Variablen aus der InfoStruct belegt,
88
4
Entwurf und Implementierung der Fassade
die noch keinen gültigen Wert aufweisen. Zum Suchen eines Wertes aus der ausgelesenen
Datei gibt es eine Hilfsfunktion namens getValueFromList(). Die Funktion bekommt
einen Zeiger auf das Array aus Variablen der Struktur CFGStruct übergeben sowie einen
String, der den zu suchenden Bezeichner enthält. Zurück geliefert wird der Wert des Bezeichners als String oder NULL, wenn der Bezeichner nicht gefunden wurde.
Das nächste Modul heißt com_line.c. Es ist eine Sammlung von sieben Funktionen für die
Kommandozeile, die drei triviale Funktionen enthält: die Funktion print_Welcome(), die
einen einfachen Starthinweis ausgibt, die print_Comline_Help(),die über die Benutzung der Fassade einen kurzen Hilfebildschirm ausgibt sowie die Funktion
print_Comline_Error(), die eine formatierte Fehlermeldung auf der Standardfehlerausgabe ausgibt. Die eigentliche Verarbeitung und Interpretation der Kommandozeilenargumente verrichtet die Funktion checkingComline(). In dieser Funktion wird als Erstes
die Argumentenliste von *argv[] durchlaufen und auf Optionen durchsucht. Dazu wird in
den Strings gesucht, ob das erste Zeichen ein Minus ist. Die folgenden Zeichen werden in
einer for-Schleife so lange durchlaufen, bis der String endet. Die einzelnen Zeichen werden
in einer switch-Anweisung ausgewertet. Trifft das Programm auf ein unbekanntes Zeichen,
wird das Programm mit einer Fehlermeldung beendet. Trifft das Programm stattdessen auf die
Zeichen „H“ oder „W“, wird eine Funktion aufgerufen, die versucht, eine Zahl aus dem String
zu extrahieren. „H“ und „W“ sind die einzigen Kommandozeilenargumente, denen ein Wert
folgen kann. Die Funktion zur Extraktion der Zahl heißt extractValueFromString().
Diese Funktion bekommt den String übergeben, die Position, von der ab die Zahl anfangen
soll sowie die Information, wohin die Zahl gespeichert werden soll. Zurück geliefert wird die
Anzahl der verarbeiteten Zeichen. Folgt beim Durchsuchen der Strings von *argv[] ein
String, der kein Minuszeichen am Anfang enthält, dann ist es ein Dateiname. Das Durchsuchen und Interpretieren der Option endet an dieser Stelle. Nun wird ausgewertet, wie viele
Dateinamen übergeben wurden. Wurden zu viele übergeben, endet das Programm mit einer
Fehlermeldung. Zusätzlich wird mit der Funktion checkFileExtension()geprüft, ob
der angegebene Dateiname die Endung „.LS“ enthält. Ist dies nicht der Fall, wird die Endung
hinzugefügt. Am Ende der Funktion checkingComline() wird eine Funktion namens
checkingState() aufgerufen. Diese Funktion prüft, ob die Angaben der Kommandozeile
ausreichend und gültig sind.
Die beiden Dateien lparser.c und lparser.h bilden das Modul zur Interaktion mit dem LparserSoftwarepaket. Unter anderem ist in ihnen auch eine Konvertierungsroutine enthalten, die LSDateien für die Version 4 in die Version 5 konvertiert. Diese Routine heißt
convertLP4toLP5(). Sie benötigt den Namen der zu konvertierenden LS-Datei sowie
den Dateinamen, in dem der konvertierte Inhalt gespeichert wird. Die Konvertierungsroutine
ersetzt lediglich Symbole und führt keine Prüfung des Inhaltes durch. Sollten die Ziel- und
Quelldatei den gleichen Namen haben, dann wird der konvertierte Inhalt in die Datei
„temp.ls“ gespeichert und im Anschluss daran in den Namen der Zieldatei umbenannt. Die
Funktion Lparser() ruft das Programm Lparser auf und leitet die Ausgabe in die Datei
LPARSER.LOG um. Bevor die Fassade Lparser aufruft, wird die Funktion
Lparser()aufgerufen. Diese wiederum ruft checkingLSFile() auf, die einen Plausibilitätstest der Werte vornimmt, weil Lparser selbst dazu nicht in der Lage ist. Hierfür wird die
LS-Datei geöffnet und nach der ersten Zeile gesucht, die nicht nur aus einem Kommentar oder einer Leerzeile besteht. Dabei wird geprüft, ob die ersten drei Werte gültige Zahlen darstellen und ob der vierte Werte ein Axiom und keine Regel ist. Zusätzlich wird mit der Funktion checkingRulesForBrackets() geprüft, ob der linke oder rechte Kontext sowie
der Bedingungsteil runde Klammern aufweisen. Nachdem die LS-Datei geprüft und Lparser
ausgeführt wurden, wird die LPARSER.LOG Datei ausgewertet, um festzustellen, ob eine
Regel als ungültig erklärt wurde. Danach endet die Aufgabe der Funktion Lparser().
89
4
Entwurf und Implementierung der Fassade
Eine weitere Funktion dieses Moduls ist Lv2povid(). Aufgabe dieser Funktion ist es, mit
dem Programm LV2POVID zu kommunizieren. Bevor dies geschieht wird jedoch getestet, ob
alle wichtigen Dateien vorhanden sind, in diesem Fall sind das die Dateien OUTPUT.INC,
INFO.TXT und LV2POVID.CFG. Beim Aufruf von LV2POVID wird die Ausgabe in die
Datei LV2POVID.LOG umgeleitet. Diese Datei wird nicht weiter ausgewertet. Eine weitere
Routine in dem Modul ist CorrectingPOVFile(). Diese Funktion erstellt eine neue
POV-Ray Datei mit den korrekten Daten für die Kameraposition, für die Kameraausrichtung
und für die Lichtquellenposition. Der Inhalt der neuen POV-Datei ist identisch mit dem Inhalt
von LV2POVID, korrigiert wurden lediglich die Daten . Um die Vektordaten aus der
INFO.TXT auszulesen, gibt es noch die Funktion extractVectorFromLine(). Diese
Funktion sucht nach einer öffnenden Klammer in einer gegebenen Zeile und versucht vom
Anfang der geöffneten Klammer bis zur nächsten schließenden Klammer drei Werte zu extrahieren.
Das nächste Modul enthält zwei einzelne, nicht zusammengehörende Funktionen. Die Funktion deleteFiles() löscht alle Dateien, die zwischenzeitig erzeugt wurden, wie zum Beispiel die LOG-Dateien von Lparser oder LV2POVID. Darüber hinaus wird in der gesamten
Fassade zum Löschen und Umbenennen von Dateien die Funktion system() verwendet.
Entsprechend sind die Befehle, die system() an das Betriebssystem übergibt, betriebssystemabhängig. Zum Nachschlagen der Befehle für den Kommandozeileninterpreter von Windows sei auf [Mi93] verwiesen. Die Funktion checkingEnvVariables() prüft, ob eine
Umgebungsvariable für QuietPOV angelegt wurde. Ist dies nicht der Fall, dann wird die Umgebungsvariable mit SetEnvironmentVariable() gesetzt. Dies ist eine der wenigen
API-Funktionen von Windows. Sie wird verwendet, weil Windows 98 keine Umgebungsvariable mit der system()-Funktion setzten kann. Das Überprüfen der Umgebungsvariablen
läuft wie folgt ab. Aus der Datei LPROCESS.CFG wird ein Pfad ausgelesen, mit dem angegeben wird, wo sich QuietPOV befindet. Nun werden nacheinander die Strings durchsucht,
die in *envp[] enthalten sind. Dazu wird geprüft, ob die ersten vier Zeichen das Wort
„Path“ ergeben. Wurde ein solcher String gefunden, wird dieser String Zeichen für Zeichen
mit dem String aus der CFG-Datei verglichen. Taucht ein Zeichen, auf das nicht übereinstimmt, dann werden die Zeichen bis zum nächsten Semikolon übersprungen. Das Semikolon
trennt die verschiedenen Werte der Umgebungsvariable „Path“.
Das letzte Modul beinhaltet die Funktionen für POV-Ray und QuietPOV. So startet
startPOV() eine Instanz von POV-Ray, sofern noch keine Instanz von POV-Ray läuft.
Dazu wird mit FindWindow() und dem Namen der POV-Ray Applikation nach einer bestehenden Instanz gesucht. Wurde der Wert Null zurückgeliefert, dann wird POV-Ray mit
WinExec()gestartet, dabei wird angegeben, dass das Fenster versteckt werden soll.
Wichtig ist in diesem Zusammenhang, dass WinExec() und FindWindow()APIFunktionen von Windows sind. Beendet wird POV-Ray mit endPOV(). Dazu wird erneut
mit FindWindow() nach einer laufenden Instanz gefragt. Wurde eine gefunden, wird die
Windows-Nachricht WM_CLOSE mit SendMessage()gesendet. Diese weist POV-Ray an,
sich zu beenden. Auch SendMessage() ist eine API-Funktion von Windows. Zum Aufrufen von QuietPOV wird die Funktion startQPOV() verwendet. Diese ruft QuietPOV auf
und leitet die Ausgabe in die Datei POV.LOG um. Bevor aber QuietPOV aufgerufen wird,
muss die Fassade für POV-Ray noch eine INI-Datei erzeugen. Diese Aufgabe erfüllt die
Funktion createINI(). Dabei werden alle POV-Ray relevanten Informationen aus der
InfoStruct in die POV.INI Datei geschrieben, beispielsweise das Ausgabeformat oder die
Größe der Grafik. Außerdem befindet sich in dem Modul noch eine Hilfsfunktion namens
getFilenameWithoutExt(). Diese gibt den übergebenen Dateinamen, nur ohne die
Endung, zurück. Sie wird verwendet, um für POV-Ray den Namen für die Ausgabedatei anzugeben.
90
5
Die graphische Oberfläche
5 Die graphische Oberfläche
5.1 Einführung zur graphischen Oberfläche
Auf den nächsten Seiten wird der Entwurf der graphischen Oberfläche dargestellt. Anders als
bei der Fassade wird hier der Quelltext hier nur angerissen, weil der Schwerpunkt der Diplomarbeit liegt nicht bei der graphischen Oberfläche liegt, sondern auf der Fassade. Ziel der
Applikation ist es, eine graphische Oberfläche für die Fassade herzustellen. Daher wird im
Rahmen der Entwicklung der graphischen Oberfläche der Schwerpunkt auf Verständlichkeit
und Einfachheit gelegt und nicht auf neue Funktionalität. Anzumerken ist jedoch, dass eine
notwendige Voraussetzung für die Nutzung der graphischen Oberfläche ein Vorwissen zu LSystemen und zur Turtle-Interpretation ist.
5.2 Die Entwicklungsumgebung für die graphische Oberfläche
Visual C++ 6.0 bietet mit der MFC eine objektorientierte Kapslung der Windows-API sowie
eine Entwicklungsumgebung mit einer guten Möglichkeit zur Entwicklung von graphischen
Oberflächen. Die Entwicklungszeit ist aber wesentlich länger als beim Rückgriff auf eine
RAD-Entwicklungsumgebung (Rapid Application Development), die speziell für die Bedürfnisse der Entwicklung von graphischen Oberflächen angepasst wurde. Daher wird Delphi 6.0
als RAD-Entwicklungsumgebung ausgewählt.
Delphi ist eine Entwicklungsumgebung, die graphische Komponenten wie Buttons in der
VCL (Visual Component Library) gekapselt hat und damit die Möglichkeit bietet, über
Mausklicks Buttons zu erstellen und zu platzieren, ohne dabei einen Quellcode zu schreiben.
Als Programmiersprache wird eine sehr stark weiterentwickelte Variante von Pascal verwendet, die sich Object Pascal nennt, und die eine sehr starke und leistungsfähige objektorientierte Sprache bildet. Ein weiterer Grund für den Einsatz von Delphi ist, dass es im objektorientierten Bereich sehr schnelle Programme erzeugen kann. Diese Programme sind in der Regel
sogar schneller als C++ Anwendungen [SH03]. Object Pascal stellt zwar eine spezielle Weiterentwicklung von Borland dar, trotzdem können in Delphi entwickelte Programme auch
unter anderen Betriebssystemen laufen. Denn Borland bietet ein Produkt namens Kylix an,
das Delphi-Anwendungen problemlos kompiliert, wenn keine API-Funktionen von Windows
verwendet werden. Damit die Oberfläche auf Linux schnell portiert werden kann, wurde bei
der Entwicklung der graphischen Oberfläche darauf geachtet, möglichst wenige APIFunktionen von Windows zu verwenden.
5.3 Externe Bibliotheken und Komponenten
Obwohl Delphi eine RAD-Entwicklungsumgebung ist und ein sehr großes Sortiment an
Komponenten und Bibliotheken enthält, gibt es dennoch Funktionen und Komponenten, die
Delphi nicht enthält. Bei der Entwicklung der graphischen Oberfläche wird deshalb auf einige
externe Komponenten und Bibliotheken zurückgegriffen. Dabei wurde darauf geachtet, dass
diese mindestens Freeware, wenn nicht sogar Open Source sind. Diese externen Bestandteile
werden hier kurz dargestellt. Dabei wird auch ihr Anteil bei der Entwicklung der Oberfläche
verdeutlicht.
91
5
Die graphische Oberfläche
Die Standard-Grafikbibliothek von Delphi versteht zwar das Grafikformat Windows-Bitmap,
aber nicht Targa. Da die Fassade auch Targa-Dateien erstellt, wird eine externe Bibliothek zur
Erweiterung der Standard-Grafikbibliothek von Delphi benötigt. Mit [[Gra]] ist es möglich,
das Standardobjekt TPicture so zu erweitern, dass es auch Targa-Grafiken lesen kann. Dadurch ist es möglich, mit jeder Komponente, die TPicture zum Laden von Grafiken benutzt, eine Targa-Datei anzuzeigen. Für die hier entwickelte Oberfläche wird die Komponente
TImage verwendet.
Um das Verständnis für die Variablen zur Positionierung der Lichtquelle und der Kamera zu
erleichtern, wird zur Visualisierung auf DirectX zurückgegriffen. Nun ist die DirectX-API als
COM-Objekt ausgelegt und die eigentliche Verwendung für C/C++ bestimmt. Die direkte
COM-Programmierung ist unter Delphi zwar möglich [Ko99], aber nicht unproblematische.
Daher wäre eine Kapslung der DirectX-API für Delphi sehr wünschenswert. Realisiert wurde
dies durch das JEDI Project ( http://delphi-jedi.org/ ), dessen Ziele die Entwicklung von Bibliotheken und Komponenten ist sowie die Kapslung von API-Funktionen und deren vollständige Dokumentation. Damit verbunden ist die Absicht, die Möglichkeiten der Entwicklung
mit Delphi zu erhöhen. Für die hier entwickelte graphische Oberfläche wird die Kapslung von
DirectX 8 verwendet, weil für diese Version die meisten Informationen für die Entwicklung
unter Delphi vorliegen. [[DX8]]
Das JEDI Project kapselt jedoch nicht nur die API von DirectX sondern auch die von Windows. Im Sammelsurium der visuellen Komponenten [[JVCL]] befindet sich auch eine Kapslung für die Windows-API CreateProcess(), die Komponente
JvCreateProcess, mit dieser wird Lprocess für die graphische Oberfläche gestartet.
Diese Komponente wurde verwendet, um den Rückgabewert von Lprocess auszuwerten und
die Konsolenausgabe umzuleiten.
5.4 Entwurf der graphischen Oberfläche
In diesem Kapitel soll auf die Gestaltung der graphischen Oberfläche eingegangen werden
und auf die damit verbundene Funktionalität. Dazu werden die relevanten Formulare (die Bezeichnung von Fenstern beim Entwurf) besprochen. Nicht besprochen werden Formulare, die
keine direkte Funktionalität zur Oberfläche beisteuern, wie zum Beispiel das Info-Formular,
die Formulare zur reinen Anzeige der Turtle-Kommandos, die Ausdrücke für den Bedingungsteil oder das Formular zur Anzeige der Konsolenausgabe. All diese Formulare zeigen
lediglich Texte an und bieten nur die Möglichkeit der reinen Betrachtung an. Ein weiteres
triviales Formular ist das Formular zur Anzeige des erzeugten Bildes. Auch dieses Formular
ist zur reinen Betrachtung da und wird daher nicht im Einzelnen erläutert.
5.4.1 Das Hauptformular - TMDIMain
„Visual L“ ist eine MDI-Anwendung. MDI steht für „Multiple Document Interface“. Damit
ist gemeint, dass die Anwendung ein Hauptfenster hat, in dem Kindfenster enthalten sein
können. Dabei stellen die Kindfenster die eigentliche Anwendung dar. Das Hauptfenster bildet den gemeinsamen Rahmen für alle Kindfenster. Durch das MDI-Konzept ist es möglich,
mehrere Instanzen einer Anwendung aufzurufen. Das Gegenstück zu MDI ist SDI, dies steht
für „Single Document Interface“. In einem SDI-Programm existiert nur ein Fenster für die
Anwendung. Der Mehraufwand einer MDI-Anwendung ist gerade bei Anwendungen, die
Editor-Funktionen realisieren sollen, gerechtfertig.
92
5
Die graphische Oberfläche
Denn sie ermöglicht es dem Benutzer, mehrere Dokumente zu öffnen, diese zu vergleichen
bzw. abzugleichen oder ein Dokument durch ein anderes zu ergänzen, ohne dabei zwei Instanzen einer Anwendung zu starten. In Abbildung 5.1 ist das Hauptformular der graphischen
Oberfläche zu sehen.
Visuell besteht das Hauptformular nur aus einer Menüleiste, einer Symbolleiste und einer
Statusleiste. Um das Einarbeiten in die Applikation zu erleichtern, wird Wert darauf gelegt,
übliche Menüpunkte, die man bei einer MDI-Anwendung erwartet, zu integrieren und deren
Reihenfolge und Bezeichnung beizubehalten. In die Menüleiste werden die Menüpunkte aufgenommen, die am meisten vom Anwender benutzt werden. Die Statusleiste gibt Informationen zu dem Element aus, über dem sich gerade der Mauszeiger befindet. Hinter der Gestaltung steht folgenden Zielsetzung: das Hauptformular darf nicht zu überladen wirken, nicht zu
Verwirrungen führen und dem Anwender einen schnellen Einstieg bieten.
Abbildung 5.1: Das Hauptfenster von „Visual L“
Wenn eine LS-Datei importiert wird, dann wird der Inhalt der Datei an Lprocess weitergegeben, um den Inhalt zu konvertieren. Daher wird auch vorher gefragt, ob der Inhalt in die gleiche Datei oder in einer anderen Datei gespeichert werden soll. Sollte Lprocess mit einem Fehler bei der Konvertierung terminieren, wird die Konsolenausgabe in einem separaten Fenster
ausgegeben, damit der Benutzer sich die Fehlermeldung anschauen kann. Nach dem Konvertieren wird der Inhalt der LS-Datei geprüft und alle gültigen Teile werden in die Komponenten eines Kindfensters übertragen. Sollte ein Teil nicht gültig sein, wird in der unteren
TListBox Komponente des Kindfensters eine Nachricht eingefügt, welcher Teil nicht übernommen wurde und warum. So erscheint zum Beispiel eine Nachricht bei einem ungültigen
oder fehlenden Wert für die Rekursionstiefe, den Basiswinkel, die Basisstärke oder für das
Axiom.
93
5
Die graphische Oberfläche
Fehlerhafte Produktionen sind solche, die zum Beispiel das Symbol für einen linken oder
rechten Kontext haben, aber keinen Inhalt aufweisen. Beim Öffnen einer bestehenden LSDatei in Version 5 wird die gleiche Prüfung durchgeführt. Beim Start der Anwendung wird
außerdem der Inhalt der CFG-Datei ausgelesen und in einer Struktur gespeichert, die für alle
weiteren Teile der Anwendung zugänglich ist. Sollte die CFG-Datei nicht bestehen, wird diese durch eine CFG-Datei mit Standardwerten ersetzt. Sollte ein Wert in einer CFG-Datei ungültig sein, so wird ebenfalls ein Standardwert verwendet. Beim Beenden der Anwendung
wird der Inhalt der Struktur wieder zurück in die CFG-Datei geschrieben. Außerdem wird mit
Hilfe von Lprocess eine eventuell gestartete Instanz von POV-Ray beendet.
5.4.2 Das Kindfenster - TMDIChildMain
Die eigentliche Anwendung spielt sich bei einer MDI-Anwendung im Kindfenster ab. Das
Kindfenster kann in zwei Teile aufgeteilt werden. In Abbildung 5.2 wird das Kindfenster dargestellt.
Abbildung 5.2: Das Kindfenster von „Visual L“
Links befindet sich der Teil, in dem das L-System editiert wird und in dem seine relevanten
Einstellungen vorgenommen werden. Um nicht die Übersicht zu verlieren, werden drei Reiter
verwendet. Auf dem ersten wird das L-System definiert und editiert. So existieren Editierfelder, in denen die Rekursionstiefe, der Basiswinkel, die Basisstärke und das Startaxiom eingegeben werden können. Außerdem wurde eine Eingabemaske für die Definition einer Produktion erstellt. Viele Applikationen zu L-Systemen besitzen einen einfachen Text-Editor, in dem
eine strenge Abfolge eingehalten werden muss, um ein gültiges L-System zu definieren
[[L3D]] [[LS4]]. Für den Benutzer ist nicht auf Anhieb ersichtlich, wie das umgesetzt werden
kann. Um dieser Schwäche entgegenzuwirken und um die Syntax und das Verständnis für die
Definition eines L-Systems zu erhöhen, werden Editierfelder und die Eingabemaske erstellt.
Wird eine Produktion hinzugefügt, wie beim Öffnen einer LS-Datei, so wird die Produktion
daraufhin überprüft, ob die Teile der Produktion auch richtig angegeben wurden.
94
5
Die graphische Oberfläche
Ist das nicht der Fall, gibt es beispielsweise bei einer Produktion einen Bedingungsteil aber
keine Parameter im Produktionskopf, dann wird diese nicht angenommen. Zusätzlich dazu
kann das L-System geprüft werden, indem auf den Button „Prüfen“ geklickt wird. Dabei wird
kontrolliert, ob die Anzahl der schließenden und öffnenden, eckigen und geschweiften
Klammern in einer Produktion gleich sind. Ebenfalls kontrolliert wird, ob für ein Symbol
mehrere oder keine Produktion definiert wurde und ob die im Produktionskopf definierten
Parameter verwendet wurden oder solche, die nicht definiert wurden. Auf dem zweiten Reiter
können einige Grafikparameter eingestellt werden, beispielsweise ob Anti-Aliasing verwendet
werden soll, welche Höhe und Breite die Grafik haben soll und ob das Grafikformat Targa
oder Windows-Bitmap sein soll. Auf dem letzten Reiter kann die relative Position der Kamera
zum Objekt bestimmt werden sowie die relative Position der Lichtquelle zur Kamera. Dazu
kann über sechs TTrackBar Komponenten die Einstellung des x, y und z-Wertes vorgenommen werden. Um sich die Auswirkungen zu verdeutlichen kann ein Fenster eingeblendet
werden, auf dem ein 3D-Kubus zu sehen ist. Entsprechend der Einstellung der TTrackBar
Komponenten werden die Kamera und das Licht umpositioniert.
Die rechte Seite des Kindfensters besteht lediglich aus sechs TButton Komponenten. Unter
anderem kann mit den Buttons „Bedingungsteil“ und „Turtle Kommandos“ ein Fenster eingeblendet werden, in dem die Turtle-Kommandos und die erlaubten Zeichen für gültige Ausdrücke im Bedingungsteil angezeigt werden. Der Button mit der Aufschrift „Schließen“ sollte
selbsterklärend sein, ebenso wie die Buttons „Speichern“ und „Speichern unter ...“. Wird auf
den Button „Bild erzeugen“ geklickt, dann wird als Erstes das L-System geprüft. Diese Prüfung entspricht der Prüfung die erfolgt, wenn auf den Button „Prüfen“ geklickt wird. All diese
Prüfungen sind nur Warnungen, das heißt sie liefern nur Hinweise auf unerwartete Ergebnisse, es kann trotzdem ein Bild erzeugt werden. Nach der Prüfung werden die aktuellen Einstellungen in der CFG-Datei gespeichert, damit Lprocess diese Parameter anwenden kann. Anschließend wird über die JvCreateProcess Komponente Lprocess gestartet. Tritt während der Verarbeitung ein Fehler auf, wird die Konsolenausgabe dem Benutzer in einem separaten Fenster zugänglich gemacht. Im Normalfall wird am Ende der Bilderzeugung ein Fenster geöffnet, in dem das Bild angezeigt wird. Während der Bilderzeugung ist die Anwendung
aktiv, das heißt der Anwender kann weiterarbeiten, während er auf das Bild wartet.
5.4.3 Einstellungs-Dialog - TCFGForm
Um den Inhalt der CFG-Datei ohne das Öffnen eines Kindfensters einzeln bearbeiten zu können, wird ein Dialog-Formular namens TCFGForm angelegt. Dieses Dialog-Formular ist in
Abbildung 5.3 zu sehen.
Seine Einstellungsmöglichkeiten sind fast identisch mit denen des Kindfensters. Das Formular enthält zwei Reiter, einen für allgemeine Einstellungen und einen anderen für die Kamera
und das Licht. Im Unterschied zum Kindfenster wurden aber zwei Editierfelder hinzugefügt,
um den Pfad für POV-Ray und QuietPOV anzugeben. Alternativ kann das Programm auch
nach dem Pfad von POV-Ray und QuietPOV suchen.
Das Dialog-Formular wird modal angezeigt, das heißt solange das Dialog offen ist, kann nicht
mit der Applikation weiter gearbeitet werden.
95
5
Die graphische Oberfläche
Abbildung 5.3: Das Dialog-Fenster zum Editieren der CFG-Datei
5.4.4 Die 3D-Ansicht - TDirect3DForm
Die 3D-Ansicht zur Visualisierung der relativen Position von Kamera und Lichtquelle wurde
mit DirectX 8 realisiert. In Abbildung 5.4 ist eine beispielhafte Darstellung dieser beiden Einstellungen zu sehen.
Die ausführliche Beschreibung der Entwicklung einer Anwendung mit DirectX kann an dieser
Stelle nicht gegeben werden, weil hier nur sehr allgemein auf die Programmierung eingegangen wird. Daher sei zur DirectX-Programmierung auf folgende Quelle verwiesen [Rou01]
[SDU99] [Du00] [EG01]. Speziell zur Entwicklung von DirectX-Anwendungen mit Delphi
sei auf [Ra04] verwiesen.
Mit der Einführung von Windows 95 wollte Microsoft die DOS-Ebene verlassen und die
Entwickler motivieren, Windows-Anwendungen zu entwickeln. Dazu zählt auch, dass künftige Spiele nicht mehr unter DOS sondern unter Windows laufen sollten. Das Problem war damals, dass Windows keine Unterstützung für aufwendige Grafikprogrammierung bot. Daher
hat Microsoft 1995 die WinG (Windows Graphics) Bibliothek eingeführt. Diese war der Vorläufer von DirectX (vgl. dazu [Ras95]). Aber WinG konnte sich nicht durchsetzen. Deshalb
wurde die Bibliothek vollkommen überarbeitet, stark erweitert und als DirectX veröffentlich.
DirectX teilt sich dabei in mehrere Teile auf, in DirectPlay, DirectDraw und DirectSound. Ein
weiterer Teil heißt Direct3D. Er ist für das Programmieren von 3D-Anwendungen gedacht.
Wie alle Komponenten von DirectX basiert auch Direct3D auf der COM-Technologie.
Im Gegensatz zur Modellierung mit einem Renderer kennt Direct3D als 3D-Primitive nur
Dreiecke, die aus drei Vertex-Punkten bestehen. Hintergrund dafür ist die Hardware der 3DGrafikkarten und der darauf befindlichen Vertex-Shader. Um einen Kubus unter Direct3D zu
visualisieren, muss der Kubus in Dreiecke zerlegt werden. Da für jede Seite zwei Dreiecke
benötigt werden, kommt man auf zwölf Dreiecke und auf 36 Vertex-Punkte, weil jedes Dreieck aus drei Vertex-Punkten besteht. Nun muss eine Datenstruktur angelegt werden, in der die
36 Vertex-Punkte gespeichert werden können.
96
5
Die graphische Oberfläche
Microsoft bietet dazu das FVF (Flexible Vertex Format) an. Über die Konstante
D3D8T_CUSTOMVERTEX kann eine flexible Datenstruktur für Vertex-Punkte festgelegt
werden. So benötigt man neben der x-, y- und z-Koordinate noch die Farbe und den Normalen-Vektor für jeden Vertex-Punkt.
Bevor die 3D-Szene von Direct3D gerendert werden kann, müssen einige Funktionen die
Szene initialisieren und vorbereiten. So werden in der Prozedur D3DInit die gesamten
Komponenten zur Kommunikation und Erzeugung der 3D-Szene initialisiert. In der Prozedur
D3DInitScene wird die 3D-Szene vorbereitet. An dieser Stelle werden Lichtquelle und
Kamera positioniert und bei der Lichtquelle werden diverse Einstellungen festgelegt, beispielsweise der Typ der Lichtquelle und deren Reichweite. Um die Szene richtig beleuchten
zu können, muss den Dreiecken noch ein Material zugewiesen werden. Über die Datenstruktur des Materials kann festgelegt werden, ob das Objekt das Licht zum Beispiel stärker oder
schwächer reflektieren soll. Im Anschluss kann in der Prozedur D3DRender die 3D-Szene
beschrieben werden. Dazu wird zwischen den Anweisungen BeginScene und EndScene
der Quelltext zur Beschreibung der 3D-Szene hinterlegt. Jegliche Transformation der 3DObjekte wird an dieser Stelle verrichtet, auch das komplette Laden der Vertex-Punkte. Damit
Direct3D beim Beenden der Anwendung ordentlich beendet werden kann, müssen zwei weitere Prozeduren definiert und ausgeführt werden. In der Prozedur D3DKillScene muss der
Speicher aller Vertex-Listen wieder freigegeben werden und in der Prozedur D3DShutdown
müssen alle Variablen gelöst werden, die mit den COM-Objekten von Direct3D verbunden
sind.
Abbildung 5.4: Die 3D-Ansicht mit Direct3D
97
6
Tests und Auswertung der entwickelten Software
6 Tests und Auswertung der entwickelten Software
Im folgenden Kapitel werden die beiden Applikationen Fassade und graphische Oberfläche
auf ihre Tauglichkeit getestet. Dazu werden beide Anwendungen getrennt betrachtet. Die
Tests beziehen sich größtenteils auf Software-Tests, das heißt es wird geprüft, ob die Programme zuverlässig laufen und wie sie auf widrige Umstände reagieren.
6.1 Test der Fassade
6.1.1 Testumgebung
Die Tests für die Fassade wird auf einem Intel „Pentium 4 Mobil“ mit 2,5 GHz, 512 MB
RAM und einer NVIDIA „GeForce4 4200 Go“ Grafikkarte durchgeführt. Als Betriebssystem
wird Microsoft Windows XP mit Service Pack 1 verwendet. Da der Computer ein Notebook
ist, wird das Notebook während der Testläufe stationär an eine Steckdose angeschlossen. Somit laufen die Komponenten des Notebooks nicht im Energiesparmodus, sondern im Desktopmodus und nutzen ihre volle Performance.
6.1.2 Testumfang
Die Fassade wird auf drei Aspekte geprüft, auf die Performance, die Belastung und auf die
Funktionalität.
Beim Performancetest wird neben der Abarbeitungszeit noch die Frage geklärt, ob die Fassade performanter ist, wenn die Anwendung als Release-Version kompiliert wurde. Außerdem
wird aufgezeigt, wie zeitintensiv welche Teile der Fassade sind.
Der Belastungstest läuft parallel zum Performancetest. Mit ihm soll die Prozessor-, Speicherund Festplattenbelastung aufgezeigt werden. Außerdem wird überprüft, ob die Anwendung
unter Belastung zuverlässig läuft.
Zum Abschluss wird noch getestet, wie sich die Anwendung verhält bei unerwarteten Werten
von der Kommandozeile, von der CFG-Datei oder von der LS-Datei.
6.1.3 Testdurchführung
Für den Performancetest wurde eine Batch-Datei angelegt. In dieser wird am Anfang und am
Ende die aktuelle Systemzeit ausgegeben. Dazwischen wird die gerade zu testende Anwendung mit verschiedenen Dateien aufgerufen. Für jede Anwendung wird der Test mehrmals
durchgeführt, um das arithmetische Mittel berechnen zu können. Der Performancetest wird
mit der gleichen Batch-Datei und dem gleichen Inhalt auf die Release- und Debug-Version
des Lparsers angewendet, um den Leistungsunterschied zu zeigen.
Zum Aufzeichnen der Belastung des Systems während des Performancetests wird der Windows XP eigenen Leistungs-Monitor verwendet. Dabei werden die Prozessorzeit, die durchschnittliche Warteschlangenlänge des physikalischen Datenträgers sowie die angeforderten
Seiten des Arbeitsspeichers pro Sekunde beobachtet.
98
6
Tests und Auswertung der entwickelten Software
Es sei darauf hingewiesen, dass bei einem Software-Monitor ein Teil der Performance an das
Monitor-Programm verloren geht und dass die Ergebnisse daher nicht der reinen Abarbeitungszeit entsprechen. Zusätzlich wird das Ergebnis des Performancetests und des Belastungstests von Prozessen und Programmen im Hintergrund beeinflusst. Diese weiteren Prozesse und Programme sind wiederum abhängig vom jeweiligen individuell installierten System.
Viele Fehler geschehen bei der Eingabe. Deshalb wird für den Funktionstest der Fassade die
Schnittstellen getestet. Dabei werden auch die Grenzwerte der Argumentenverarbeitung von
der Kommandozeile ausgetestet. Außerdem werden die Grenzwerte für das Auslesen und
Verarbeiten der CFG- und LS-Datei auf ihre Tauglichkeit hin geprüft, indem sie mit ungültigem Inhalt gefüllt werden. Die Fassade selbst wird über die Eingabeaufforderung aufgerufen
und nicht über ein externes Programm.
6.1.4 Auswertung
6.1.4.1 Performancetest
Die Auswertung erfolgt in der Reihenfolge der Abarbeitung der Fassade. Der erste Verarbeitungsschritt ist die Konvertierung der LS-Datei in die Version 5. Konvertiert werden fünfmal
117 Dateien. Das Ergebnis ist in Tabelle 6.1 zu sehen. Die Dateien sind in der Regel klein und
das Ersetzen einzelner Zeichen benötigt nicht viel Zeit.
1. Durchlauf
3 Sek.
2. Durchlauf
3 Sek.
3. Durchlauf
3 Sek.
4. Durchlauf
3 Sek.
5. Durchlauf
4 Sek.
Mittelwert
3:20 Sek.
Tabelle 6.1: Zeit zum Konvertieren von 117 Dateien
Der nächste Schritt ist die Verarbeitung der Dateien durch Lparser. Dazu werden zehnmal 101
Dateien zur Verarbeitung an den Lparser weitergegeben. Dabei wurde der Lparser fünfmal in
der kompilierten Debug-Version und fünfmal in der Release-Version verwendet. Interessant
ist, dass die Debug-Version gegenüber der Release-Version zwar dreimal größer ist, aber auch
fast dreimal schneller. Wegen der extremen Langsamkeit von Lparser werden 6 Dateien nicht
verwendet, weil sie eine zu lange Verarbeitungszeit (mehrere Stunden oder Tage) benötigen
würden.
1. Durchlauf
3:48 Min.
2. Durchlauf
3:49 Min.
3. Durchlauf
3:50 Min.
4. Durchlauf
3:50 Min.
5. Durchlauf
3:54 Min.
Mittelwert
3:50:20 Min.
Tabelle 6.2: Zeit für die Verarbeitung von 101 Dateien vom Lparser in der Debug-Version
1. Durchlauf
10:01 Min.
2. Durchlauf
9:58 Min.
3. Durchlauf
9:59 Min.
4. Durchlauf
10:00 Min.
5. Durchlauf
9:59 Min.
Mittelwert
9:59:40 Min.
Tabelle 6.3: Zeit für die Verarbeitung von 101 Dateien vom Lparser in der Release-Version
Nun werden die 3D-Szenen von Lparser mit dem Programm LV2POVID in POV-Ray Szenen
transformiert. Dazu werden 95 Dateien fünfmal von LV2POVID verarbeitet. Aus nicht ersichtlichen Gründen hat LV2POVID sechs Dateien aus dem vorherigen Schritt nicht verarbeitet können.
99
6
Tests und Auswertung der entwickelten Software
1. Durchlauf
2:23 Min.
2. Durchlauf
2:23 Min.
3. Durchlauf
2:22 Min.
4. Durchlauf
2:23 Min.
5. Durchlauf
2:23 Min.
Mittelwert
2:22:80 Min.
Tabelle 6.4: Zeit für das Transformieren von 95 Dateien
Der letzte Schritt ist die Erzeugung der 3D-Grafiken. Dazu wird POV-Ray vorher gestartet,
wobei die Befehle über QuietPOV gesendet werden. Der Test wird mit 95 Dateien zehnmal
durchgeführt, fünfmal ohne Anti-Aliasing und fünfmal mit Anti-Aliasing.
1. Durchlauf
8:02 Min.
2. Durchlauf
7:47 Min.
3. Durchlauf
7:59 Min.
4. Durchlauf
8:00 Min.
5. Durchlauf
8:00 Min.
Mittelwert
7:57:60 Min.
Tabelle 6.5: Zeit für das Erzeugen von 95 Grafiken ohne Anti-Aliasing
1. Durchlauf
12:33 Min.
2. Durchlauf
12:25 Min.
3. Durchlauf
12:36 Min.
4. Durchlauf
12:14 Min.
5. Durchlauf
12:23 Min.
Mittelwert
12:26:20 Min.
Tabelle 6.6: Zeit für das Erzeugen von 95 Grafiken mit Anti-Aliasing
Nun wird der komplette Verarbeitungszyklus mit Lprocess durchlaufen. Dabei werden 95
Dateien fünfmal verarbeitet. Alle Dateien waren schon konvertiert und es wurde kein AntiAliasing verwendet. Interessanterweise lässt sich die Release-Version von Lprocess zwar
kompilieren, aber unter Windows XP auch dann nicht ausführen, wenn es unter Windows XP
kompiliert wurde. Daher wurde nur die Debug-Version getestet. Vom Lparser wird die Debug-Version für diesen Test verwendet.
1. Durchlauf
14:46 Min.
2. Durchlauf
14:20 Min.
3. Durchlauf
14:26 Min.
4. Durchlauf
14:32 Min.
5. Durchlauf
14:11 Min.
Mittelwert
14:27:00 Min.
Tabelle 6.7: Zeit für die Verarbeitung von 95 Dateien mit Lprocess
Wenn nun die Verarbeitungszeit vom Lparser auf 95 Dateien normalisiert wird, wird folgende
Zeitaufteilung sichtbar.
100
6
Tests und Auswertung der entwickelten Software
Zeitaufteilung bei der Fassade
3%
25%
Lparser
LV2POVID
POV-Ray
Lprocess
55%
17%
Abbildung 6.1: Tortendiagramm der Zeitaufteilung
6.1.4.2 Belastungstest
In Tabelle 6.8 ist die Belastung der Komponenten Prozessor, Speicher und Datenträger für die
Anwendungen Lparser, LV2POVID, POV-Ray und Lprocess (der komplette Verarbeitungszyklus) angegeben. Dabei ist der Wertebereich normalisiert von 0 (Minimum) bis 100 (Maximum). Die Daten wurden parallel zum Performancetest aufgenommen und liegen daher
auch fünfmal vor. Alle Werte sind dementsprechend arithmetische Mittelwerte.
Programm
Lparser (Debug)
Lparser (Release)
LV2POVID
POV-Ray (ohne Anti-Aliasing)
POV-Ray (mit Anti-Aliasing)
Lprocess
Prozessor Speicher Datenträger
99,80
0,00
1,00
100,00
0,00
0,00
99,00
2,60
0,00
77,00
0,00
0,00
84,80
0,00
0,00
87,00
0,20
0,00
Tabelle 6.8: Ergebnis der Belastungen der einzelnen Komponenten
Während der Batch-Abarbeitung ist kein Programm aus Belastungsgründen abgestürzt, hat
nicht mehr reagiert oder ist mit zunehmenden Durchläufen langsamer geworden. Wie der Tabelle zu entnehmen ist, ist der Prozessor der größte Belastungsfaktor. Der Speicher wird lediglich am Anfang kurzfristig stärker beansprucht.
6.1.4.3 Funktionstest
Kommandozeile:
Das Programm wird nicht ausgeführt, wenn eine ungültige Kombination von Optionen vorliegt, wie zum Beispiel „-eE“.
101
6
Tests und Auswertung der entwickelten Software
Denn dies würde bedeuten, dass POV-Ray sofort und nach Beendigung der Verarbeitung beendet werden soll. Auch wenn Optionen doppelt erscheinen und eine ungültige Kombination
darstellen, wird das Programm nicht ausgeführt. Wird jedoch die Option für die Bildhöhe
zweimal angegeben, dann wird der zweite Wert verwendet. Ist beim zweiten Erscheinen der
Option kein Wert angegeben, dann wird das Programm mit einer Fehlermeldung beendet. Das
Programm terminiert ebenfalls ordentlich mit einer Fehlermeldung, wenn nach der Option für
die Bildhöhe kein Wert folgt, nicht definierte Zeichen verwendet werden oder Zahlen an nicht
erlaubter Stelle erscheinen.
Werden zwei Dateinamen angegeben, aber die Option zur Konvertierung nicht gesetzt, dann
wird immer der zweite Dateiname zur Verarbeitung verwendet. Entsprechend terminiert das
Programm mit einer Fehlermeldung, wenn die Datei nicht existiert. Im Übrigen dürfen die
Dateinamen keine Leerzeichen enthalten, weil dies vom Kommandozeileninterpreter als zwei
getrennte Argumente interpretiert wird.
Bei der Reihenfolge der Optionen und Dateinamen müssen die Optionen immer an erster Stelle angegeben werden. Sollten Optionen nach einem Dateinamen gefunden werden oder mehr
als zwei Dateinamen, dann wird das Programm mit einer Fehlermeldung beendet. Sollte das
Programm nur auf die Option „-e“ (POV-Ray sofort beenden) treffen, gefolgt von einem Dateinamen, dann wird POV-Ray beendet und der Dateiname ignoriert.
Die Länge der Zeichenkette für die Option kann beliebig lang sein, aber nicht die Zeichenkette für einen Dateinamen. In der Datei globals.h kann die maximale Länge für die Zeichenkette
eines Dateinamens bestimmt werden, wird diese Länge überschritten, stürzt das Programm ab.
Soll mit sehr langen Dateinamen gearbeitet werden, kann der Wert für die maximale Dateinamenlänge in der globals.h (Standardwert sind 50 Zeichen) geändert und die Fassade neu
kompiliert werden.
CFG-Datei:
Liegt keine Datei mit dem Namen LPROCESS.CFG vor, wird eine Datei mit Standardwert
erzeugt. Die Optionen innerhalb der CFG-Datei können in einer willkürlichen Reihenfolge
vorliegen. Sollte eine Option nicht komplett vorhanden sein oder für eine Option kein Wert
vorliegen, dann wird die Fassade mit einer Fehlermeldung beendet. Ebenfalls wird das Programm mit einer Fehlermeldung beendet, wenn ein nicht erwarteter Wert eingelesen wurde,
wie zum Beispiel ein „aaa“, statt ein „on“ bei der Option „Files“. Die Fassade terminiert mit
einer Fehlermeldung, wenn sich POV-Ray oder QuietPOV nicht in dem Verzeichnis befinden, das in der CFG-Datei hinterlegt wurde. Die Kommentare in der CFG-Datei müssen sich
in einer separaten Zeile befinden, ansonsten wird dem Wert der Option der Kommentar zur
Weiterverarbeitung hinzugefügt.
Sollte die maximale Länge für eine Zeile in der CFG-Datei überschritten werden, dann wird
das Programm mit einer Fehlermeldung beendet. Da sich dieser Überlauf erst in der nächsten
Zeile bemerkbar macht, wird in der Fehlermeldung die Folgezeile als Fehlerquelle angegeben.
Der Standardwert für die Länge einer Zeile beträgt 150 Zeichen. Dies ist in der Datei globals.h definiert. Sollten längere Zeilen benötigt werden, muss der Wert verändert und die Fassade neu kompiliert werden.
LS-Datei:
Fehlen die Parameter für Rekursionstiefe, Basiswinkel, Basisstärke und Startaxiom, dann
wird das Programm mit einer entsprechenden Fehlermeldung beendet. Sollte eine Datei eine
Zeile enthalten, die mehr als 1000 Zeichen enthält, stürzt Lparser ab. Die Fassade selbst terminiert mit der Fehlermeldung, dass keine „output.inc“ für LV2POVID vorliegt und beendet
sich ordnungsgemäß, obwohl Lparser abgestürzt ist. Um die Länge einer Zeile zu ändern,
muss im Quelltext von Lparser eine Präprozessor-Anweisung geändert und Lparser neu kompiliert werden.
102
6
Tests und Auswertung der entwickelten Software
6.2 Test der graphischen Oberfläche
6.2.1 Testumfang
Der Test der graphischen Oberfläche teilt sich in zwei Bereiche auf. Erstens soll die Funktionalität der Software getestet werden und zweitens eine Usability-Studie über die Anwendbarkeit der graphischen Oberfläche präsentiert werden.
Der Funktionstest soll im laufenden Betrieb zeigen, wie die Anwendung auf Fehleingaben
reagiert, bzw. wie sie auf Probleme mit den Inhalten von LS-Dateien und / oder mit dem Inhalt in der CFG-Datei umgeht.
Für die Usability-Studie werden freiwillige Personen gesucht. Diese werden mit dem theoretischen Wissen zu L-Systemen ausgestattet und im Anschluss daran mit der Oberfläche konfrontiert. Im Anschluss daran sollen sie einfach kleine Aufgaben lösen.
6.2.2 Testdurchführung
Beim Funktionstest werden die Inhalte von LS-Dateien und der Inhalt der CFG-Datei manipuliert, um Fehler zu provozieren und die Reaktion des Programms zu beobachten. Seitens der
Oberfläche werden bei den Eingabefeldern und Schiebereglern die Grenzwerte ausgetestet.
Bei der Usability-Studie werden die Personen mit den theoretischen Grundlagen der LSysteme vertraut gemacht. Dazu wird der Vorgang der Ableitung bei einem L-System erläutert. Während dessen werden notwendige Begriffe eingeführt, wie zum Beispiel die Begriffe
Rekursionstiefe und Produktionen. Zuletzt wird die Turtle-Interpretation erläutert und mit
wenigen einfachen Kommandos illustriert. Nach dem theoretischen Teil sollen die Personen
kleine Aufgaben lösen. So soll zum Beispiel ein bestehendes L-System geladen und das Bild
erzeugt werden oder ein neues Kindfenster geladen werden sowie die Felder für Rekursionstiefe, Basiswinkel, Basisstärke und Startaxiom exemplarisch ausgefüllt werden. Zusätzlich
dazu soll noch eine Produktion eingegeben werden und die Personen sollen sich den 3DKubus anschauen und mit den Schiebereglern die Werte für die Kamera- und Lichtquellenposition verändern. Dabei sollen sie kommentieren, ob das Ergebnis zu erwarten war.
6.2.3 Auswertung
6.2.3.1 Funktionstest
Wird beim Öffnen einer LS-Datei ein Fehler entdeckt, wird dies im Kindfenster in der Nachrichtenliste eingefügt. Alle anderen nicht fehlerhaften Teile werden in die entsprechenden
Komponenten des Kindfensters eingetragen. Ein Fehler ist beispielsweise, wenn die Rekursionstiefe, der Basiswinkel, die Basisstärke oder das Startaxiom fehlen. Ein weiterer Fehler ist,
wenn eine Zeile länger als 1000 Zeichen ist. Seitens der Produktionen wird ein Fehler ausgegeben, wenn der Produktionskopf oder -körper fehlt oder wenn zwar eine Klammer für den
linken oder rechten Kontext vorhanden aber ohne Inhalt ist. Weiterhin als Fehler deklariert
ist, wenn im Produktionskopf ein Klammer-Ausdruck steht, dieser aber fehlerhaft ist, zum
Beispiel wenn Sonderzeichen in der Parameterliste vorhanden sind oder wenn sich hinter der
rechten Klammer noch Zeichen befinden. Falls ein Bedingungsteil vorhanden ist, wird dieser
daraufhin geprüft, ob ungültige Zeichen vorliegen, beispielsweise Klammern.
103
6
Tests und Auswertung der entwickelten Software
Die gleiche Prüfung wird im Kindfenster jedes Mal durchlaufen, wenn eine neue Produktion
hinzugefügt wird.
Das Programm reagiert auf das Nichtvorhandensein der CFG-Datei, indem eine neue mit
Standardwerten erzeugt wird. Wurde in einer CFG-Datei ein Wert nicht gefunden, wird der
Option ein Standardwert zugewiesen.
Wird im Formular TCFGForm auf den Button „Suchen“ geklickt, dann wird der Pfad der
ersten Datei übernommen, die den Namen der Applikation von POV-Ray oder QuietPOV
enthält. Das Eingabefeld für die Breite und Höhe der Grafik akzeptiert nur Zahlen und den
Rücklauf. Die Eingabe ist auf vier Zeichen limitiert. Die Regler zur Positionierung der Kamera und der Lichtquelle können beliebig gestellt werden und führen zu keinem Fehler.
Im Kindfenster sind die Eingabefelder für Rekursionstiefe, Basiswinkel und Basisstärke darauf limitiert, nur Zahlen und den Rücklauf zu akzeptieren. Das Eingabefeld für die Rekursionstiefe ist auf zwei Zeichen begrenzt, beim Basiswinkel sind es drei und bei der Basisstärke
zehn Zeichen. Wird auf den Button „Prüfen“ geklickt, werden die Produktionen des LSystems auf mögliche Schwächen geprüft. Dazu zählt beispielsweise eine ungleiche Anzahl
von öffnenden und schließenden sowie eckigen und geschweiften Klammern. Außerdem wird
geprüft, ob die angegebenen Parameter im Produktionskopf verwendet werden oder Parameter, die nicht im Produktionskopf enthalten sind. Ebenfalls geprüft wird, ob für ein Symbol
keine oder mehrere Produktionen existieren. Die Prüfung geht aber nicht so weit, Bedingungsausdrücke im Hinblick auf ihre Gültigkeit zu prüfen oder so weit zu kontrollieren, ob zu
jedem Zeitpunkt eine Produktion angewendet werden kann. Deshalb wird auch eine Warnung
ausgegeben, wenn für ein Symbol zwei Produktionen angegeben werden, die einen unterschiedlichen Bedingungsteil aufweisen.
6.2.3.2 Usability-Studie
Zu Beginn der Studie werden zwei Studenten der Informatik mit der Thematik konfrontiert.
Aufgrund ihres Vorwissen konnte ihnen der Theorieteil in weniger als zwei Minuten vermittelt werden. Nach zehn Minuten waren die Studenten mit dem kompletten Programm vertraut.
Zusätzlich dazu werden noch fünf weitere Personen für die Usability-Studie herangezogen,
die über unterschiedliche Erfahrung und ein unterschiedliches Vorwissen verfügen. Das Ergebnis ist in Tabelle 6.9 zusammengefasst.
Alter Beruf
21
23
22
23
24
3DWissen
Kaufmann Keine
Tischler
Keine
Azubi
Profi
Student
keine
Abiturientin Keine
ComputerTheorie Bild
3DProduktionsWissen
erzeugen Einstellungen bearbeitung
Grundlagen
10 Min.
2 Min.
2 Min.
7 Min.
Grundlagen
5 Min.
4 Min.
2 Min.
5 Min.
Profi
5 Min.
1 Min.
1 Min.
3 Min.
Fortgeschritten 5 Min.
1 Min.
2 Min.
2 Min.
Grundlagen
6 Min.
1 Min.
2 Min.
5 Min.
Tabelle 6.9: Ergebnis der Usability-Studie
Es kann gesagt werden, dass die Einarbeitungszeit bis zu zehn Minuten betragen kann, wenn
das theoretische Vorwissen vorliegt. Dabei hat sich gezeigt, dass die Auswirkungen der
Schieberegler durch den 3D-Kubus sehr gut vermittelt werden können.
104
7
Zusammenfassung und Ausblick zu dieser Arbeit
7 Zusammenfassung und Ausblick zu dieser Arbeit
7.1 Zusammenfassung
Das zentrale Thema dieser Diplomarbeit sind die L-Systeme. Demzufolge werden sie im theoretischen Teil ausführlich behandelt. Dabei werden die wichtigsten L-Systeme und deren
Erweiterungen für die Computergrafik vertieft betrachtet. Des Weiteren werden die besprochenen L-Systeme grob klassifiziert. Die wichtigsten L-Systeme werden zur Klassifikation
auf einen Zeitstrahl gesetzt. Zusätzlich wird für die Computergrafik ein Entscheidungsautomat zu L-Systemen entwickelt. Eine weitere Klassifikation erfolgt über die Einteilung der LSysteme in zwei Klassen sowie über die Eingliederung in die Chomskyhierarchie. Auf diese
Weise entsteht ein Überblick über die Vielfalt und Mächtigkeit dieser Grammatik. Neben den
theoretischen Aspekten der L-Systeme werden hierbei auch die Turtle-Interpretation dargestellt, mit denen es möglich ist, ganze Bäume und andere komplexe Objekte zu visualisieren.
Nach einer kurzen Einführung in die Genetischen Algorithmen und in die Genetische Programmierung werden die Mutationsmöglichkeiten von L-Systemen vertieft betrachtet. Es
zeigt sich, dass gerade die Genetischen Algorithmen (Evolutionäre Algorithmen) für LSysteme von vielen Autoren bevorzugt verwendet werden und dass die Anzahl der Mutationsmöglichkeit sehr hoch ist.
Im Zusammenhang mit den Anwendungsmöglichkeiten wird auch vertieft auf die Evolutionsschleife und auf deren Anwendungsmöglichkeiten eingegangen. Aus theoretischer Sicht zeigt
der Teil der Bilderzeugung einen hohen Freiheitsgrad, deshalb lässt er sich in viele Teilbereiche aufteilen. So kann man beispielsweise nicht nur die Grammatik mutieren lassen, sondern
auch die Interpretationsvorschrift der Grammatik.
Da im Rahmen einer Diplomarbeit nicht alle Module implementiert werden können, wird auf
bestehende Softwarepakete zurückgegriffen. Dabei ist die Wahl auf das Softwarepaket Lparser Kombination mit POV-Ray gefallen.
Beim Entwurf der Fassade zeigt sich, dass das Softwarepaket Lparser viele Schwächen und
Fehler beinhaltet. Deshalb wird auf eine externe Weiterentwicklung von Lparsers zurückgegriffen, mit der sogar kontextsensitive und parametrisierte L-Systeme verarbeiten werden
können. Trotzdem muss das Programm LViewer ersetzt werden. Dazu wird Lparser insoweit
erweitert, dass er selbst eine möglichst gute Kameraposition berechnet. Außerdem muss die
von LV2POVID erzeugt POV-Ray Datei korrigiert werden, weil die Kameraposition und die
Position der Lichtquelle nicht richtig übernommen werden. Das Ergebnis ist eine stabile Fassade, die für den Evolutionsprozess eine gute Schnittstelle bietet.
Als Renderer wird POV-Ray mit einem Tool namens QuietPOV verwendet. Damit sind neben
der Fassade noch vier weitere Programme notwendig. Bei der Implementierung der Fassade
wird darauf Wert gelegt, dass der Quelltext möglichst wenige API-Funktionen von Windows
verwendet, um in einer kurzen Zeit auf einem anderen Betriebssystem portiert werden zu
können.
Um einen einfacheren Einstieg in die Modellierung mit L-Systemen zu bekommen und das
eigentliche Arbeiten mit L-Systemen zu vereinfachen, wird eine graphische Oberfläche entwickelt. Für das Arbeiten mit der Oberfläche sind Kenntnisse zu L-Systemen notwendig. Neben den Editierfunktionen wird auch eine Prüfroutine für L-Systeme entwickelt. Außerdem
wird mit Direct3D ein 3D-Kubus geschaffen. Mit der interaktiven Bedienung von Schiebereglern soll die Bedeutung der Faktoren für die relative Positionierung der Kamera und die
Lichtquelle veranschaulicht werden.
105
7
Zusammenfassung und Ausblick zu dieser Arbeit
7.2 Ausblick
Es folgen zum Schluss noch einige mögliche Erweiterungen der Software.
Seitens der aktuellen Konfiguration des Systems wäre es wünschenswert, das Programm
Lparser performanter zu programmieren, weil die verwendete Version 5.1 das Beispiel
„airhorse.ls“ nicht verarbeitet. Außerdem wäre eine gewisse Plausibilitätsprüfung von Vorteil,
um Warnungen auszugeben, wenn zum Beispiel eine Regel nie verwendet wird.
Weiterhin interessant wäre eine Modularisierung der Software und die damit einhergehenden
Freiheitsgrade.
So könnten verschiedenste L-Systeme verwendet werden, beispielsweise das selten vorkommende Table L-System. Man könnte sogar so weit gehen, mit Timed L-Systemen Animationen einzubinden. Einen zusätzlichen Freiheitsgrad erhält man durch den Modeller. Dieser
würde ermöglichen, die Interpretationsvorschriften mutieren zu lassen. Dadurch kann aus
einem Fahrbefehl schnell eine Richtungsänderung werden. Da der Modeller bevorzugt eine
allgemeine 3D-Beschreibung erzeugen soll, kann das 3D-Modell mit einem Adapter in eine
Beschreibung für verschiedenste Renderer transformiert werden. Dadurch wäre man nicht an
ein bestimmtes Ray-Tracing Programm gebunden.
Seitens der graphischen Oberfläche könnte versucht werden, die Eingabe intuitiver zu designen. Das Ergebnis könnte auf eine X-Frog ähnliche Oberfläche hinauslaufen. X-Frog wird
zum Modellieren von Pflanzen verwendet. Dabei werden die 3D-Modelle aus Komponenten
modelliert. Nun könnte man den Nutzer für eine intuitivere Oberfläche Teile der Pflanze einzeln modellieren lassen und diese als Komponenten in das L-System aufnehmen. Diese Komponenten könnten interaktiv modelliert werden und dann in eine oder mehrere Produktionen
transformiert werden. Der Nutzer könnte dann angeben, ob die Komponente mitwachsen soll
oder „schreibgeschützt“ ihre Struktur beibehalten soll. Somit könnte ein Nutzer beim Erzeugen einer Pflanze ein Blatt erzeugen und als Komponente abspeichern. Da in der Pflanze des
Nutzers keine anderen Blatttypen vorkommen, soll nur dieser eine Blatttyp verwendet werden
und überall an der Pflanze verteilt werden. Das Blatt soll sich aber nicht weiter verändern aus
der Sicht des Wortes, welches dahinter steht. Um jedoch nicht immer das gleiche Blatt zu
haben, kann er dann über Parameter, die er dem Blatt übergibt, leichte Veränderungen hervorbringen.
Eine andere Erweiterung für die graphische Oberfläche ist das Managen von Projekten, so
dass es zu jedem Projekt auch eine eigene CFG-Datei für die Fassade gibt. Alternativ könnte
man die Schnittstelle von Lprocess erweitern und die CFG-Datei über die Kommandozeile
angeben.
Weitreichendere Auswirkungen könnten durch Java und RMI-Methoden erreicht werden. So
könnte man ein Java-Interface zur Fassade bauen, um ein Web-Interface zu kreieren oder sogar einen Web-Service zur Erzeugung von Bildern aus L-Systemen anzubieten. Man könnte
auch die Fassade in Java implementieren und die externen Programme in ein separates JavaProgramm mit einer RMI-Methode kapseln. Auf diese Weise müssten die externen Programme nicht mehr auf einem Computer liegen. Nun könnte die Fassade zu einem Steuerprogramm für die parallele Abarbeitung von einkommenden Jobs ausgebaut werden. Die externen Programme melden sich bei der Fassade an und bekommen Jobs zugeteilt. Dabei könnten
sich Programme auch mehrfach bei der Fassade anmelden. So können zwei POV-Ray Programme auf verschiedenen Rechnern zur Verfügung stehen. Die Fassade würde in diesem
Fall den Programmen ohne Auslastung die vorhandenen Jobs zuteilen.
Würde man die Bilderkennung von Pflanzen mit L-Systemen weiterführen, könnte man sich
einen Roboter vorstellen, der Gartenaufgaben übernimmt. Er könnte dann beispielsweise anhand der erkannten Pflanze entsprechend düngen oder sie als Unkraut entfernen.
106
A
Abbildungsverzeichnis
Anhang:
A Abbildungsverzeichnis
Abbildung 1.1: Phyllotaxis der Sonnenblume [De03] ............................................................... 1
Abbildung 1.2: Stellungen der Knospen [De03]........................................................................ 2
Abbildung 2.1: Transitionsdiagramm für den EA...................................................................... 7
Abbildung 2.2: Übergang vom Anfangszustand zum ersten Folgezustand ............................... 8
Abbildung 2.3: Einführung der Zellteilung und der entsprechende Folgezustand .................... 9
Abbildung 2.4: Ableitungsbaum zum Beispiel 2.4 .................................................................. 11
Abbildung 2.5-a: Ableitungsbaum für Beispiel 2.7 ................................................................. 15
Abbildung 2.5-b: Ableitungsbaum für Beispiel 2.7 ................................................................. 15
Abbildung 2.6: Ableitungsbaum vom Beispiel 2.8 .................................................................. 18
Abbildung 2.7: Ableitungsbaum für das B0L-System ............................................................. 19
Abbildung 2.8: Zweidimensionaler fadenförmiger Organismus.............................................. 20
Abbildung 2.9: Struktur einer Produktion eines parametrisierten 0L-Systems ....................... 21
Abbildung 2.10: Ableitungsbaum für das Beispiel 2.11 .......................................................... 22
Abbildung 2.11: Signalverlauf in einem PIL-System .............................................................. 23
Abbildung 2.12: Die graphische Interpretation der ersten vier generierten Worte .................. 29
Abbildung 2.13: Die Entwicklung einer Pflanze mit einem L-System.................................... 30
Abbildung 2.14: Turtle-Interpretation im dreidimensionalen Raum ....................................... 31
Abbildung 2.15: Die 3D-Grafik zum Beispiel 2.16 ................................................................. 34
Abbildung 2.16: Beispiel einer Membran ................................................................................ 37
Abbildung 2.17: Ein vollständiges Eco-Grammar System [CPS95]........................................ 38
Abbildung 2.18: PAP eines GA [Sch03].................................................................................. 39
Abbildung 2.19: Crossover in GA............................................................................................ 40
Abbildung 2.20: Mutation in GA ............................................................................................. 41
Abbildung 2.21: Automat als Datenstruktur ............................................................................ 41
Abbildung 2.22: Lineare Struktur ............................................................................................ 42
Abbildung 2.23: Bäume als Datenstruktur............................................................................... 43
Abbildung 2.24: Crossover in GP – Lineare Befehlssequenz.................................................. 44
Abbildung 2.25: Crossover in GP - Baum ............................................................................... 44
Abbildung 2.26: Mutation beim Baum .................................................................................... 45
Abbildung 2.27: Eine Produktion als Chromosom .................................................................. 47
Abbildung 2.28: Beispiele für Point Mutation ......................................................................... 48
Abbildung 2.29: Inversion einer Produktion............................................................................ 49
Abbildung 2.30: Deletion bei einer Produktion ....................................................................... 49
Abbildung 2.31: Beispiel für Duplication an einer Produktion ............................................... 50
Abbildung 2.32: Mutation einer Produktion mit Translocation............................................... 50
Abbildung 2.33: Hinzufügen eines neuen Symbols ................................................................. 51
Abbildung 2.34: Der Mutator von L-System ........................................................................... 52
Abbildung 2.35: Die nicht mutierte Pflanze............................................................................. 53
Abbildung 2.36: Eine Mutation der Blume.............................................................................. 53
Abbildung 2.37: Eine zweite Mutation der Blume .................................................................. 54
Abbildung 2.38: Ein L-System als Baum dargestellt............................................................... 55
Abbildung 2.39: Mutationsmöglichkeiten unter GP [Ja03] ..................................................... 55
Abbildung 2.40: Grobe Struktur des Systems.......................................................................... 57
Abbildung 3.1: Baum, erzeugt von Lparser und gerendert mit POV-Ray............................... 62
Abbildung 3.2: Beispiel für eine 3D-Grafik von L-System ..................................................... 63
Abbildung 3.3: 3D-Modell erzeugt von RayTraced Evolution................................................ 64
107
A
Abbildungsverzeichnis
Abbildung 3.4: Eine Rosenblüte erstellt von LinSys3D .......................................................... 65
Abbildung 3.5: Eine Lilie erstellt mit L-studio ........................................................................ 67
Abbildung 4.1: Module des Bilderzeugungsprozesses ............................................................ 72
Abbildung 4.2: Der theoretische, der praktische Ablauf sowie die verwendeten Programme 73
Abbildung 4.3: Strahlenverfolgung in einer Szene [ESK97]................................................... 78
Abbildung 4.4: Die gerenderte Szene ...................................................................................... 80
Abbildung 4.5: Ablaufdiagramm der Fassade.......................................................................... 83
Abbildung 4.6: Beispielhafte Kameraposition für a) 3D-Szenen und b) 2D-Szenen .............. 85
Abbildung 4.7: Bitbelegung der Variable state ................................................................... 88
Abbildung 5.1: Das Hauptfenster von „Visual L“ ................................................................... 93
Abbildung 5.2: Das Kindfenster von „Visual L“ ..................................................................... 94
Abbildung 5.3: Das Dialog-Fenster zum Editieren der CFG-Datei ......................................... 96
Abbildung 5.4: Die 3D-Ansicht mit Direct3D ......................................................................... 97
Abbildung 6.1: Tortendiagramm der Zeitaufteilung .............................................................. 101
108
B
Tabellenverzeichnis
B Tabellenverzeichnis
Tabelle 2.1: Übergangsmatrix für den EA ................................................................................. 7
Tabelle 2.2: Übergangsmatrix für den EA ................................................................................. 8
Tabelle 2.3: Übersichtstabelle der besprochenen L-Systeme................................................... 25
Tabelle 3.1-a: Übersicht über die Software zu L-Systemen..................................................... 68
Tabelle 3.1-b: Übersicht über die Software zu L-Systemen .................................................... 68
Tabelle 4.1: Unterschiede zwischen Version 4 und 5 .............................................................. 76
Tabelle 4.2: Kommandozeilenargumente der Fassade............................................................. 80
Tabelle 4.3: Alle Werte der LPROCESS.CFG ........................................................................ 81
Tabelle 4.4: Module der Fassade.............................................................................................. 87
Tabelle 6.1: Zeit zum Konvertieren von 117 Dateien.............................................................. 99
Tabelle 6.2: Zeit für die Verarbeitung von 101 Dateien vom Lparser in der Debug-Version . 99
Tabelle 6.3: Zeit für die Verarbeitung von 101 Dateien vom Lparser in der Release-Version99
Tabelle 6.4: Zeit für das Transformieren von 95 Dateien ...................................................... 100
Tabelle 6.5: Zeit für das Erzeugen von 95 Grafiken ohne Anti-Aliasing .............................. 100
Tabelle 6.6: Zeit für das Erzeugen von 95 Grafiken mit Anti-Aliasing................................. 100
Tabelle 6.7: Zeit für die Verarbeitung von 95 Dateien mit Lprocess .................................... 100
Tabelle 6.8: Ergebnis der Belastungen der einzelnen Komponenten..................................... 101
Tabelle 6.9: Ergebnis der Usability-Studie ............................................................................ 104
109
C
Literaturverzeichnis
C Literaturverzeichnis
[Ab86]
Lois A. Abbott. Investigations into Drosophila Wing Development – Result
from a Lindenmayer Model. The Book of L, Hrsg. Grzegorz Rozenberg und Arto Salomaa, S. 1 - 11, Springer-Verlag, 1986.
[Ba98]
Helmut Balzert. Lehrbuch der Software-Technik: Software-Management,
Software-Qualitätssicherung, Unternehmensmodellierung. Spektrum Akademischer Verlag, 1998.
[Ba99]
Heide Balzert. Lehrbuch der Objektmodellierung: Analyse und Entwurf.
Spektrum Akademischer Verlag, 1999.
[Ba00]
Helmut Balzert. Lehrbuch der Software-Technik: Software-Entwicklung.
Spektrum Akademischer Verlag, 2000.
[Ban03]
Wolfgang Banzhaf. Einführung in das Genetische Programmieren. Vorlesungsskript, Universität Dortmung Fachbereich Informatik, 2003.
http://ls11-www.cs.uni-dortmund.de/people/banzhaf/GP/GP-Vorlesung.pdf
(Stand: 06/2004).
[Be01]
Martin Bellardi. Lindenmayer-Systeme. Referate zum Seminar: Zufall+Rauschen, 2001.
http://bellardi.de/pdf/L-Systeme.pdf (Stand: 06/2004).
[BNKF98]
Wolfgang Banzhaf, Peter Nordin, Robert E. Keller und Frank D. Francone.
Genetic Programming: An Introduction. dpunkt Verlag, 1998.
[Bo04]
Ingo Boersch. Evolution. Vorlesungsskript zu Wissensverarbeitung, Fachhochschule Brandenburg Fachbereich Informatik und Medien, 2004.
http://ots.fh-brandenburg.de/mod/ams/data/wva/unterlagen/evo.pdf
(Stand: 06/2004).
[BPFGK03] Frédéric Boudon, Przemyslaw Prusinkiewicz, Pavol Federl, Christophe
Godin und Radoslaw Kardowski. Interactive design of bonsai tree models.
Proceedings of Eurographics 2003, Hrsg. P. Brunet und D. Fellner, Band 22,
2003.
http://www.cpsc.ucalgary.ca/Research/bmv/papers/index.html
(Stand: 06/2004).
[Bü03]
Frank Büttgen. Prozeduraler Städtebau. Seminararbeit, RheinischWestfälische Technische Hochschule Aachen, 2003.
[CJ92]
T. W. Chien und Helmut Jürgensen. Parameterized L Systems for Modelling:
Potential and Limitations. Lindenmayer Systems, Hrsg. Grzegorz Rozenberg
und Arto Salomaa, S. 213 - 229, Springer-Verlag, 1992.
[CK86]
Karl Culik und Juhani. Karhumäki. A New Proof for the D0L Sequence
Equivalence Problem and Its Implications. The Book of L, Hrsg. Grzegorz Rozenberg und Arto Salomaa, S. 63 - 74, Springer-Verlag, 1986.
110
C
Literaturverzeichnis
[CPS95]
Erzsebet Csuhaj-Varju, Gheorghe Păun und Arto Salomaa. Conditional Tabled Eco-Grammar Systems versus (E)T0L-Systems. Journal of Universal
Computer Science, Band: 1, S. 252 - 268, Springer-Verlag, 1995.
http://www.jucs.org/jucs_1_5/conditional_tabled_eco_grammar/paper.pdf
(Stand: 06/2004).
[Cs02]
Judit Csima. Investigations on Simple Eco-Grammar Systems. Ph. D. Dissertation, Eötvös Loránd University, 2002.
http://sziami.cs.bme.hu/~csima/phd1.ps (Stand: 06/2004).
[DB03]
Roger Luke DuBois. Applications of Generative String-Substitution Systems in
Computer Music. Ph. D. Dissertation, University of Columbia, 2003.
http://www.music.columbia.edu/~luke/dissertation/dissertation.pdf
(Stand: 06/2004).
[De03]
Oliver Deussen. Computergenerierte Pflanzen. Springer-Verlag, 2003.
[DK00]
Walter Doberenz und Thomas Kowalski. Borland Delphi 5: Grundlagen und
Profiwissen. Carl Hanser Verlag, 2000.
[DS98]
Stephen R. Davis und Richard J. Simon. Win98 Programmierung für Dummies. MITP-Verlag GmbH, 1998.
[Du00]
Robert L. Dunlop. DirectX 7 Programmierung. Markt+Technik Verlag, 2000.
[Eb02]
Michael Ebner. Delphi 6 nachschlagen und verstehen. Addison-Wesley Verlag, 2002.
[EG01]
Wolfgang F. Engel und Amir Geva. Direct 3D Spieleprogrammierung. SYBEX-Verlag GmbH, 2001.
[El00]
Frank Eller. workshop Delphi 5. Addison-Wesley Verlag, 2000.
[El01]
Frank Eller. workshop Delphi 6. Addison-Wesley Verlag, 2001.
[ESK96]
José Encarnação, Wolfgang Straßer und Reinhard Klein. Graphische Datenverarbeitung 1. R. Oldenbourg Verlag, 1996.
[ESK97]
José Encarnação, Wolfgang Straßer und Reinhard Klein. Graphische Datenverarbeitung 2. R. Oldenbourg Verlag, 1997.
[FMF04]
Bettina Fiege, Ute Manthey und Gabriele Frank. Persönliche E-Mail Korrespondenz (auf CD-ROM der Diplomarbeit enthalten).
Auszüge auf:
http://www.uni-jena.de/data/unijena_/faculties/minet/casio/Lindenmayer2.html
(Stand: 06/2004).
[Fo00]
Charles Fox. Genetic Hierarchical Music Structures. Ph. D. Dissertation,
Clare College Cambridge, 2000.
http://www.robots.ox.ac.uk/~charles/mg/dissertation.doc (Stand: 06/2004).
111
C
Literaturverzeichnis
[Gä96]
Stefan Gärtner. Partitions-limierte Lindenmayer-Systeme. Shaker Verlag,
1996.
[GFMP01]
Tobias Gross, Nils Faltin, Roman Mülchen und Michael Plath. Computergrafik Interaktiv - Grafiti. Computer Graphics & Software Ergonomie Carl von
Ossietzky Universität Oldenburg, 2001.
http://olli.informatik.uni-oldenburg.de/Grafiti3/grafiti/flow1/page1.html
(Stand: 06/2004).
[GHJV96]
Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides. Entwurfsmuster. Addison Wesley Verlag, 1996.
[Gw03]
Brad Goodwin. The Wonderful World of Birds and Feathers. CS 563 Advance
Topics in Computer Graphics, Worcester Polytechnic Institute, 2003.
http://www.cs.wpi.edu/~emmanuel/courses/cs563/write_ups/bradg/feathers/fea
therpaper_files/feathers.ppt (Stand: 06/2004).
[Gö99]
Wilhelm Göhler. Formelsammlung: Höhere Mathematik. Verlag Harri
Deutsch, 1999.
[GR92]
Narendra S. Goel und Ivan Rozehnal. A High-Level Language for L-systems
and Its Applications. Lindenmayer Systems Hrsg. Grzegorz Rozenberg und Arto Salomaa, S. 231 - 251,Springer-Verlag, 1992.
[HLP01]
Gregory S. Hornby, Hod Lipson und Jordan B. Pollack. Evolution of Generative Design Systems for Modular Physical Robots. IEEE International Conference on Robotics and Automation, 2001.
http://www.demo.cs.brandeis.edu/papers/author.html#hornby (Stand: 06/2004).
[Ho03]
Gregory S. Hornby. Generative Representation for Evolutionary Design
Automation. Ph. D. Dissertation, Brandeis University Dept. of Computer Science, 2003.
http://www.demo.cs.brandeis.edu/papers/author.html#hornby (Stand: 06/2004).
[Hol01]
Markus Holenstein. Aesthetic Selection. Diplomarbeit, Universität Zürich,
2001.
[Hon01]
Juha Honkala. Three Variants of the DT0L Sequence Equivalence Problem.
Journal of Universal Computer Science, Band: 7, S. 886 – 892, SpringerVerlag, 2001.
http://www.jucs.org/jucs_7_10/three_variants_of_the/paper.pdf
(Stand: 06/2004).
[HP01a]
Gregory S. Hornby und Jordan B. Pollack. Evolving L-Systems To Generate
Virtual Creatures. Computers and Graphics, 25:6, S. 1041 - 1048, 2001.
http://www.demo.cs.brandeis.edu/papers/author.html#hornby (Stand: 06/2004).
[HP01b]
Gregory S. Hornby und Jordan B. Pollack. The Advantages of Generative
Grammatical Encodings for Physical Design. Congress on Evolutionary Computation, 2001.
http://www.demo.cs.brandeis.edu/papers/author.html#hornby (Stand: 06/2004).
112
C
Literaturverzeichnis
[HP01c]
Gregory S. Hornby und Jordan B. Pollack. Body-Brain Co-evolution Using Lsystems as a Generative Encoding. Genetic and Evolutionary Computation
Conference, 2001.
http://www.demo.cs.brandeis.edu/papers/author.html#hornby (Stand: 06/2004).
[HP02]
Gregory S. Hornby und Jordan B. Pollack. Creating High-Level Components
with a Generative Representation for Body-Brain Evolution. Artifical Life, 8:3,
2002.
http://www.demo.cs.brandeis.edu/papers/author.html#hornby (Stand: 06/2004).
[HR75]
Gabor T. Herman und Grzegorz Rozenberg. Developmental Systems and
Languages. North-Holland Publishing Company und American Elsevier Publishing Company, 1975.
[HU00]
John E. Hopcroft und Jeffrey D. Ullman. Einführung in die Automatentheorie,
Formale Sprachen und Komplexitätstheorie. Oldenbourg Wissenschaftsverlag
GmbH, 2000.
[Ja95]
Christian Jacob. Genetic L-System Programming: Breeding and Evolving Artificial Flowers with Mathematica. First International Mathematica Symposium,
Computational Mechanics Pub., 1995.
http://pages.cpsc.ucalgary.ca/~jacob/IMS-ArtFlowers.pdf (Stand: 05/2004).
[Ja97]
Christian Jacob. Principia Evolvica : Simulierte Evolution mit Mathematiker.
dpunkt Verlag, 1997.
[Ja03]
Christian Jacob. Artificial Intelligence through Evolution. Vorlesungsskript,
University of Calgary, 2003.
http://pages.cpsc.ucalgary.ca/~jacob/Courses/Fall2003/CPSC533/Slides/09GP.pdf (Stand: 06/2004).
[JAH01]
Ron Jeffries, Ann Anderson und Chet Hendrickson. Extrem Programming
installed. Addison-Wesley Verlag, 2001.
[JM86]
Helmut Jürgensen und David E. Matthews. Stochastic 0L Systems and Formal Power Series. The Book of L, Hrsg. Grzegorz Rozenberg und Arto Salomaa, S. 167 - 177, Springer-Verlag, 1986.
[Ke86]
Alica Kelemenová. Complexity of L-Systems. The Book of L, Hrsg. Grzegorz
Rozenberg und Arto Salomaa, S. 179 - 191, Springer-Verlag, 1986.
[Ko99]
Andreas Kosch. COM/DCOM für Delphi. Software & Support Verlag, 1999.
[KPM92]
Kamala Krithivasan, M. V. Nagendra Prasad und Meena Mahajan. „Forgetful“ L Systems. Lindenmayer Systems, Hrsg. Grzegorz Rozenberg und Arto Salomaa, S. 419 - 436, Springer-Verlag, 1992.
[KR90]
Brian W. Kernighan und Dennis M. Ritchie. Programmieren in C. Carl Hanser Verlag, 1990.
113
C
Literaturverzeichnis
[KR92]
Alica Kelemenová und Miriam Removčíková. A0L- and CFG- Size of Languages. Lindenmayer Systems, Hrsg. Grzegorz Rozenberg und Arto Salomaa,
S. 177 - 182, Springer-Verlag, 1992.
[KTV99a]
Gabriella Kókai, Zoltán Tóth und Róbert Ványi. Modelling Blood Vessels of
the Eye with Parametric L-Systems Using Evolutionary Algorithms. Proceedings Joint European Conference on Artificial Intelligence in Medicine and
Medical Decision Making, S. 433 - 443, Springer-Verlag, 1999.
http://www2.informatik.uni-erlangen.de/download/Papers/ (Stand: 06/2004).
[KTV99b]
Gabriella Kókai, Zoltán Tóth und Róbert Ványi. Parametric L-System Description of the Retina with Combined Evolutionary Operators. Proceedings
GECCO, Genetic and Evolutionary Computation Conference, Band: 2, S. 1588
- 1596, 1999.
http://www2.informatik.uni-erlangen.de/download/Papers/ (Stand: 06/2004).
[Ku99]
Winfried Kurth. Die Simulation der Baumarchitektur mit Wachstumsgrammatiken. Wissenschaftlicher Verlag Berlin, 1999.
[Kud86]
Manfred Kudlek. Languages Defined By Indian Parallel Systems. The Book of
L, Hrsg. Grzegorz Rozenberg und Arto Salomaa, S. 233 - 243, SpringerVerlag, 1986.
[LA98]
Richard C. Leinecker und Tom Archer. Die Windows 98 Programmier-Bibel.
ITP-Verlag GmbH, 1998.
[Li68a]
Aristid Lindenmayer. Mathematical Models for Cellular Interactions in
Develeopment. I. Filaments with One-sided Inputs. J. Theoret. Biol., Heft 18 /
1968, S. 280 - 299, 1968.
[Li68b]
Aristid Lindenmayer. Mathematical Models for Cellular Interactions in
Develeopment. II. Simple and Branching Filaments with Two-sided Inputs. J.
Theoret. Biol., Heft 18 / 1968, S. 300 - 315, 1968.
[Lo00]
Dirk Louis. Delphi 5 New Reference. Markt + Technik Verlag, 2000.
[LS92]
Klaus-Jörn Lange und Michael Schudy. The Complexity of the Emptiness
Problem for E0L Systems. Lindenmayer Systems, Hrsg. Grzegorz Rozenberg
und Arto Salomaa, S. 167 - 175, Springer-Verlag, 1992.
[LT86]
J. van Leeuwen und R. B. Tan. Computer Networks with Compact Routing
Tables. The Book of L, Hrsg. Grzegorz Rozenberg und Arto Salomaa, S. 259 273, Springer-Verlag, 1986.
[Mi93]
Microsoft Corporation. Microsoft MS-DOS 6.2 Benutzerhandbuch. Microsoft
Corporation, 1993.
[Mi99]
Zbigniew Michalewicz. Genetic Algorithms + Data Structures = Evolution
Programs. Springer-Verlag, 1999.
114
C
Literaturverzeichnis
[Mo96]
Kenrick Mock. Introduction to Artificial Life and Genetic Algorithms. Vorlesungsskript, University of Alaska, 1996.
http://www.math.uaa.alaska.edu/~afkjm/cs405/handouts/genetic.ppt
(Stand: 05/2004).
[NLA86]
A. Nakamura, Aristid Lindenmayer und K. Aizawa. Some Systems for Map
Generation. The Book of L Hrsg. Grzegorz Rozenberg und Arto Salomaa, S.
323 - 332, Springer-Verlag, 1986.
[OW02]
Thomas Ottmann und Peter Widmayer. Algorithmen und Datenstrukturen.
Spektrum Akademischer Verlag, 2002.
[Pa92]
Gheorghe Păun. Parallel Communicating Systems of L Systems. Lindenmayer
Systems Hrsg. Grzegorz Rozenberg und Arto Salomaa, S. 405 - 417, SpringerVerlag, 1992.
[Pa98]
Gheorghe Păun. Computing with Membranes. 1998.
http://psystems.disco.unimib.it/ (Stand: 06/2004).
[Pa00]
Gheorghe Păun. From Cells to Computers: Computing with Membranes (P
Systems). Präsentiert auf dem Workshop on Grammar Systems, 2000.
http://psystems.disco.unimib.it/ (Stand: 06/2004).
[PHHM95]
Przemyslaw Prusinkiewicz, Mark Hammel, Jim Hanan und Radomìr Měch.
The Artificial Life of Plants. Artificial life for graphics, animation, and virtual
reality, Band 7 von SIGGRAPH ’95, S. 1 – 38, ACM Press, 1995.
http://www.cpsc.ucalgary.ca/Research/bmv/papers/index.html
(Stand: 06/2004).
[PHHM96a] Przemyslaw Prusinkiewicz, Mark Hammel, Jim Hanan und Radomìr Měch.
Visual models of plant development. Handbook of formal languages, Hrsg.
Grzegorz Rozenberg und Arto Salomaa, Springer-Verlag, 1996.
http://www.cpsc.ucalgary.ca/Research/bmv/papers/index.html
(Stand: 06/2004).
[PHHM96b] Przemyslaw Prusinkiewicz, Mark Hammel, Jim Hanan und Radomìr Měch.
L-Systems: From The Theory to Visual Models of Plants. Proceedings of the
2nd CSIRO Symposium on Computational Challanges in Life Sciences, CSIRO
Publishing, 1996.
http://www.cpsc.ucalgary.ca/Research/bmv/papers/index.html
(Stand: 06/2004).
[PHM]
Przemyslaw Prusinkiewicz, Jim Hanan und Radomìr Měch. Extensions to the
graphical interpretation of L-systems based on turtle geometry. University of
Calgary Dept. of Computer Science.
http://www.cpsc.ucalgary.ca/Research/bmv/lstudio/graph.pdf (Stand: 06/2004).
[PJM94]
Przemyslaw Prusinkiewicz, Mark James und Radomìr Měch. Synthetic Topiary. Proceedings of SIGGRAPH 94, S. 351- 358, 1994.
http://www.cpsc.ucalgary.ca/Research/bmv/papers/index.html
(Stand: 06/2004).
115
C
Literaturverzeichnis
[PL90]
Przemyslaw Prusinkiewcz und Aristid Lindenmayer. The Algorithmic Beauty
of Plants. Springer-Verlag, 1990.
[PP99]
Ulla Kirch-Prinz und Peter Prinz. C für PCs. MITP-Verlag GmbH, 1999.
[Pr86]
Przemyslaw Prusinkiewicz. Graphical Applications of L-Systems. Proceedings
of SIGGRAPH 1986, S. 247 - 253, 1986.
[Pr93]
Przemyslaw Prusinkiewicz. Modeling and Visualization of Biological Structures. Proceedings of Graphics Interface 1993, S. 128 - 137, 1993.
http://www.cpsc.ucalgary.ca/Research/bmv/papers/index.html
(Stand: 06/2004).
[PR01]
Gheorghe Păun und Grzegorz Rozenberg. A Guide to Membrane Computing.
2001.
http://psystems.disco.unimib.it/ (Stand: 06/2004).
[Qu02]
Uwe Quasthoff. Genetische Programmierung Teil 1 und Teil 2. Vorlesungsskript, Universität Leipzig Institut für Informatik, 2002.
http://wortschatz.informatik.unileipzig.de/asv/lehre/ss02/QGenProgrGesamt080702.pdf (Stand: 06/2004).
[Ra04]
Jürgen Rathlev. Direct3D mit Delphi unter DirectX 8. Delphi-Source.de, 2004.
http://www.delphisource.de/downloads/dcount.php?cat=t&id=tutorials/direct3d/direct3d.zip
(Stand: 06/2004).
[Ras95]
Mathias Rasch. Grafikprogrammierung mit Windows 95. DATA BECKER
GmbH & Co. KG, 1995.
[Ri97]
Jeffrey Richter. Microsoft Windows Programmierung für Experten. Microsoft
Press Deutschland, 1997.
[Ro04]
Grzegorz Rozenberg. Grzegorz Rozenberg. Grzegorz Rozenberg, 2004.
http://www.wi.leidenuniv.nl/home/rozenber/ (Stand: 06/2004).
[Rou01]
Christian Rousselle. Spieleprogrammierung mit DirectX und Visual C++.
Markt+Technik Verlag, 2001.
[RS86]
Grzegorz Rozenberg und Arto Salomaa. The Book of L. Springer-Verlag,
1986.
[Ru86]
Keijo Ruohonen. Equivalence Problems for Regular Sets of Word Morphisms.
The Book of L, Hrsg. Grzegorz Rozenberg und Arto Salomaa, S. 393 - 401,
Springer-Verlag, 1986.
[Sc02]
Thorsten Schnier. Lindenmayer Systems Part II: Nature Inspired Design. University of Birmingham School of Computer Science, 2002.
http://www.cs.bham.ac.uk/~xin/courses/design/11-l-systems-1-4up.pdf
(Stand: 06/2004).
116
C
Literaturverzeichnis
[Sch03]
Tino Schonert. Einsatz evolutionärer Algorithmen zur Optimierung der Tourenplanung eines Wachschutzunternehmens. Diplomarbeit, Fachhochschule
Brandenburg Fachbereich Information und Medien, 2003.
http://ots.fh-brandenburg.de/mod/diplom/data/da_tino_schonert.pdf
(Stand: 06/2004).
[SD01]
Wolfgang Scholl und Rainer Drews. Handbuch Mathematik. Orbis Verlag,
2001.
[SDU99]
Victor Sirotin, Victor Debeloff und Yuri Urri. DirectX-Programmierung mit
Visual C++. Addison Wesley Longman Verlag, 1999.
[Se92]
Robert Sedgewick. Algorithmen. Addison Wesley Longman Verlag, 1992.
[SGT99]
Wolfgang Schäfer, Kurt Georgi und Gisela Trippler. Mathematik-Vorkurs. B.
G. Teubner, 1999.
[Si97]
ACM SIGGRAPH. 1997 ACM SIGGRAPH Awards: Computer Graphics
Achievement Award Przemyslaw Prusinkiewcz. ACM SIGGRAPH, 1997.
http://www.siggraph.org/awards/1997/AchievementAward.html
(Stand: 06/2004).
[SH03]
Arne Schäpers und Rudolf Huttary. Daniel Düsentrieb: C#, Java, C++ und
Delphi im Effizienztest, Teil 2. c’t, Heft: 21/2003, ab S. 222, Heise Zeitschriften
Verlag, 2003.
[To99]
Hrsg. Toolbox Redaktion. Visual C++ 6. C&L Computer und Literaturverlag,
1999.
[Wa01]
Johannes Waldmann. L-Systeme. Vorlesungsskript, Universität Leipzig Institut
für Informatik, 2001.
http://www.informatik.uni-leipzig.de/~joe/edu/ss01/l/l-pub.ps
(Stand: 06/2004).
[Wä99]
Dietmar Wätjen. Automatentheorie und Formale Sprachen: Vorlesungsskript.
Skript zur Vorlesung Automatentheorie und Formale Sprachen an der Technischen Universität Braunschweig, 1999.
[Wi95]
Gerhard Willms. Das C Grundlagen Buch. DATA BECKER GmbH & Co.
KG, 1995.
[Za01]
Mair Zamir. Arterial Branching within the Confines of Fractal L-System Formalism. J. Gen. Physiol., Band: 118, S. 267 - 275, The Rockefeller University
Press, 2001.
http://www.jpg.org/cgi/content/full/118/3/267 (Stand: 05/2004).
117
D
Zusammenfassung der wichtigsten Literaturquellen
D Zusammenfassung der wichtigsten Literaturquellen
Zur Theorie von L-Systemen:
[HR75] [PL90]
Zur graphischen Interpretation von L-Systemen:
[PL90]
Zu Genetischen Algorithmen und Genetischer Programmierung:
[Mi99] [Ja97] [BNKF98] [Bo04]
Zur Mutation von L-Systemen:
[Ja03] [Ja97] [Hol01]
Zur Programmentwicklung mit C:
[KR90] [PP99]
Zur Programmentwicklung mit Delphi:
[DK00] [Lo00] [El00] [El01]
Zur Windows-Programmierung:
[Ri97]
Zur DirectX-Programmierung:
[Ra04] [Du00]
118
E
Softwarequellen
E Softwarequellen
[[DX8]]
DirectX 8-Units für Delphi, Kapslung der DirectX-API für Delphi, Open
Source (die Kapslung, nicht DirectX selbst).
http://www.delphisource.de/downloads/dcount.php?cat=t&id=tutorials/direct3d/direct3d8.zip
(Stand: 06/2004).
[[Fra]]
Fractint Version 20.0 für MS-DOS, läuft unter Windows in einer DOS-Box,
erzeugt Bilder aus Fraktalen, Open Source.
http://spanky.triumf.ca/pub/fractals/programs/ibmpc/frain200.zip
(Stand: 06/2004).
[[Gra]]
GraphicEx image library Version 9.9 für Delphi ab Version 4.0, Erweiterung
der Grafikbibliothek von Delphi um weitere Dateiformate, Open Source.
http://scripts.soft-gems.net/download.php?ID=13 (Stand: 06/2004).
[[Gro]]
GROGRA Version 3.3 für Windows, Simulation von Wachstumsgrammatiken, Freeware.
http://www.grogra.de/ (Stand: 06/2004). Erwerb der Software nur über den direkten Kontakt mit Prof. Dr. Winfried Kurth.
[[Imo]]
Imogene Version 1.0 für Windows, erzeugt durch künstliche Evolution Grafiken, die von einem Nutzer bewertet werden, Shareware.
ftp://bells.cs.ucl.ac.uk/genetic/ftp.io.com/code/imogenes.zip (Stand: 06/2004).
[[JVCL]]
JEDI Visual Component Library Version 2.10 für Delphi ab Version 5.0,
Sammelsurium von verschiedensten Komponenten, Open Source.
http://prdownloads.sourceforge.net/jvcl/JCL%2BJVCL210FullInstall.zip?use_
mirror=switch (Stand: 06/2004). Wichtiges Fix für die Version 2.10
http://prdownloads.sourceforge.net/jvcl/JVCL210FIX030313.zip?use_mirror=s
witch (Stand: 06/2004).
[[L32]]
LSys32 für Windows, grafische Oberfläche für den Lparser Version 4.0, Freeware.
http://chris.lichti.org/Lab/LSys32/lsys.zip (Stand: 06/2004).
[[L3D]]
LinSys3d Version 1.2 für Windows, erzeugt 3D-Szenen aus L-Systemen,
Freeware.
http://web.tiscali.it/esuli/LinSys3d/LS3dSetup.exe (Stand: 06/2004).
[[Lap]]
L-Systems Application, Java Applet, erzeugt 2D-Grafiken aus L-Systemen.
http://www.cpsc.ucalgary.ca/Research/bmv/java/LSystems/LSys.html
(Stand: 06/2004).
[[LP4]]
Lparser Version 4.0 für MS-DOS, läuft unter Windows in einer DOS-Box,
erzeugt 3D-Szenen aus L-Systemen, Freeware.
http://home.wanadoo.nl/laurens.lapre/lparser4.zip (Stand: 06/2004).
119
E
Softwarequellen
[[LP5]]
Lparser Version 5.1 für Windows, erzeugt 3D-Szenen aus L-Systemen, Open
Source.
http://vortex.bd.psu.edu/~kopp/Lparser.zip (Stand: 06/2004).
[[LS4]]
L-System 4.0.1 für Windows, erzeugt 3D-Szenen aus L-Systemen, Freeware.
http://www.geocities.com/tperz/l4setup.zip (Stand: 06/2004).
[[LSB]]
LS-SketchBook Version 0.1 für Windows, erzeugt 3D-Szenen aus LSystemen, Freeware.
http://coco.ccu.uniovi.es/malva/sketchbook/lssketchbook/download/LS%20Ske
tchBook%2001b.zip (Stand: 06/2004).
[[LSt]]
L-studio Version 3.1 für Windows, erzeugt 3D-Szenen aus L-Systemen,
Kommerzielle Software.
http://www.cpsc.ucalgary.ca/Research/bmv/lstudio/lstudio-demo.zip
(Stand: 06/2004).
[[LWo]]
Lworld Version 7.0 für Windows, erzeugt Animationen aus L-Systemen,
Freeware.
http://www.ifi.unizh.ch/~noser/Lworld/LworldWinPCv7.zip (Stand: 06/2004).
[[Plan]]
PlanD für Windows, Entwicklungssystem für L-Systeme, Freeware.
http://www.imn.htwk-leipzig.de/~sdietze1/planD.zip (Stand: 06/2004).
[[Pov]]
POV-Ray Version 3.5 für Windows, Ray Tracer, Freeware.
http://www.povray.org/redirect/www.povray.org/ftp/pub/povray/Official/Wind
ows/povwin35.exe (Stand: 06/2004).
[[Qpov]]
QuietPOV Version 1.1 für Windows, GUI Extension für POV-Ray, Freeware.
http://www.throwable.com/dreampeach/download/QuietPOV/QuietPOVinstall.exe (Stand: 06/2004).
[[RTE]]
RayTraced Evolution Version 1.0b3 für Windows, erzeugt 3D-Szenen aus LSystemen, Freeware.
http://www.rz.tu-ilmenau.de/~juhu/GX/RTEvol/win/rte.zip (Stand: 06/2004).
120
F
Bedienungsanleitung für Visual L
F Bedienungsanleitung für Visual L
Voraussetzung für das Benutzen der Software ist eine Grafikkarte die, DirectX 8 unterstützt,
sowie eine installierte Version von POV-Ray 3.5 und QuietPOV.
Für den ersten schnellen Einstieg in die Software öffnen Sie eine bestehende LS-Datei, indem
Sie in der Symbolleiste auf „Öffnen“ klicken oder über das Menü „Datei“ auf „Öffnen“ gehen. Dort suchen Sie sich eine vorhandene LS-Datei aus und öffnen diese.
Nach dem Öffnen erscheint ein neues Fenster, siehe Abbildung 5.2. Auf der rechten Seite des
Fensters befinden sich mehrere Buttons. Einer besitzt die Aufschrift „Bild erzeugen“, auf diesen klicken Sie, um die 3D-Grafik für das L-System zu erhalten.
Die Bilderzeugung kann unter Umständen länger dauern. Wenn das Bild erzeugt wurde, öffnet sich ein Fenster, in dem die 3D-Grafik enthalten ist. Parallel dazu liegt die 3D-Grafik im
Verzeichnis von „Visual L“. Durch einen Klick auf das Bild schließt sich das Fenster wieder.
Wenn Ihnen das Bild zu klein ist, können Sie auf den Reiter „Einstellungen“ des Fensters
klicken, in dem das L-System geöffnet ist, und dort die Höhe und Breite der Grafik in Pixel
angeben. Außerdem können Sie angeben, ob das Targa-Format zur Speicherung der Grafik
verwendet werden soll und ob beim Erstellen der 3D-Grafik die Anti-Aliasing Funktion aktiviert werden soll. Das Anti-Aliasing entfernt unschöne Treppeneffekte in der 3D-Grafik,
durch das Aktivieren dieser Option kann die Bilderzeugung jedoch bis zu 40 % länger dauern.
Nicht immer entsprechen die voreingestellte Kamera- und Lichtposition den eigentlichen
Wünschen. Um eine Umpositionierung vorzunehmen, kann auf den Reiter „Kamera und
Licht“ geklickt werden. Auf dieser Seite befinden sich einige Schieberegler, mit denen sich
die Werte für die Kamera- und Lichtposition verändern lassen. Damit die Auswirkungen besser verständlich sind, kann ein 3D-Kubus zugeschaltet werden, indem auf „3D-Ansicht“ geklickt wird. Der 3D-Kubus ist nur exemplarisch und soll keine Vorschau der späteren Grafik
darstellen. Er ist lediglich eine Vorschau über die Auswirkungen der Schieberegler und der
Umpositionierung der Licht- und Kameraposition. Wenn das Fenster mit dem 3D-Kubus geöffnet wird, können an den Schiebereglern andere Werte eingestellt werden, der 3D-Kubus
aktualisiert sich von selbst mit den neuen Werten. Anzumerken ist, dass die Lichtposition
relativ zur Kameraposition ist, d. h. wenn die Kamera verschoben wird, verschiebt sich auch
automatisch das Licht.
All diese Einstellungen können auch über das Menü „Einstellungen“ und „CFG-Datei“ vorgenommen werden. Zusätzlich zu den schon bekannten Einstellungen kann der Pfad von
POV-Ray und QuietPOV eingestellt bzw. gesucht werden. Diese Einstellungen werden in
einer Textdatei namens „LPROCESS.CFG“ festgehalten. Sollte die Datei gelöscht sein oder
einen ungültigen Wert enthalten, dann meldet sich „Visual L“ und erzeugt eine neue CFGDatei oder benutzt Standardwerte.
Wenn Sie auf den Button „Bild erzeugen“ klicken, wird im Hintergrund eine andere Applikation namens „Lprocess“ gestartet, diese wiederum startet weitere Programme, um das Bild zu
erzeugen. „Lprocess“ selbst und die Programme „Lparser“ und „LV2POVID“ befinden sich
im Verzeichnis von „Visual L“. Alle drei Programme und die im Verzeichnis befindlichen
CFG-Dateien werden benötigt, um das Bild zu generieren.
121
F
Bedienungsanleitung für Visual L
Nun soll ein eigenes L-System definiert werden. Dazu klicken Sie in der Symbolleiste auf
„Neu“ oder auf den Menüpunkt „Datei“ und dann „Neu“. Dieses Mal erscheint ein leeres
Fenster, in dem noch nichts eingetragen ist.
Auf dem Reiter „L-System“ befinden sich zwei größere Bereiche. Der eine ist mit „Startwerte“ betitelt und der andere mit „Produktionen“. Im Bereich „Startwerte“ können Sie die Rekursionstiefe (wie oft das Wort abgeleitet werden soll), den Start-/Basiswinkel (für die TurtleInterpretation), die Start-/Basisstärke (wie dick/stark die 3D-Zylinder sein sollen) und das
Startaxiom/-wort angeben.
Die Felder können Sie mit folgenden Werten füllen: die Rekursionstiefe beträgt zwei, der
Basiswinkel beträgt 90, die Basisstärke ist 100 und das Startaxiom ist „F-F-F-F“.
Im unteren Bereich können die einzelnen Produktionen/Regeln für das L-System definiert
werden. Die Eingabemaske für eine Produktion besteht aus 5 Feldern. Das erste ist der linke
Kontext der Produktion, gefolgt vom Produktionskopf. Das dritte Feld ist der rechte Kontext
und das vierte die Bedingung, unter der die Produktion ausgeführt werden soll. Das letzte
Feld ist der Produktionskörper, der beim Ableiten für das Symbol im Produktionskopf ersetzt
wird. Sollten der linke oder rechte Kontext oder der Bedingungsteil nicht benötigt werden,
dann kann ein „*“ eingesetzt werden. Wurde die Produktion definiert, kann auf „Hinzufügen“
geklickt werden. Sollte die Produktion fehlerhaft sein, erscheint eine Meldung. Als kleine
Hilfe für die Erstellung der Produktionen können zwei Tabellen eingeblendet werden. Die
eine enthält alle Turtle-Kommandos (Button „Turtle Kommandos“) und die andere Tabelle
enthält alle Ausdrücke, die im Bedingungsteil verwendet werden dürfen (Button „Bedingungsteil“). Um eine vorhandene Produktion zu bearbeiten, kann entweder auf die Produktion
im Listenfeld doppelgeklickt werden oder die Produktion einmal angeklickt werden und dann
über den Button „Bearbeiten“ bearbeitet werden. Um eine Produktion zu löschen, muss diese
ausgewählt werden und auf den Button „Löschen“ geklickt werden. Der Button „Alle Löschen“ löscht alle Produktionen.
Nun soll die folgende Produktion eingegeben werden. Für den Produktionskopf schreiben Sie
„F“ und in den Produktionskörper (letztes Eingabefeld) schreiben Sie „F-F+F+FF-F-F+F“.
Danach klicken Sie auf „Hinzufügen“.
Das eingegebene L-System können Sie auf Schwächen prüfen, dazu klicken Sie einfach auf
„Prüfen“. Dies kann gerade bei größeren L-Systemen hilfreich sein. Eine Schwäche in einer
Produktion wäre, wenn die Anzahl der öffnenden und schließenden, eckigen oder geschweiften Klammern ungleich ist oder wenn für ein Symbol keine oder mehrere Produktionen existieren. Eine weitere Schwäche liegt vor, wenn die angegebenen Parameter im Produktionskopf nicht verwendet werden, bzw. wenn einige verwendet werden, die nicht im Produktionskopf stehen.
Speichern Sie nun das L-System, indem Sie auf „Speichern“ klicken. Dem L-System können
Sie den Namen „Koch“ geben. Jetzt können Sie auf „Bild erzeugen“ klicken. Sie sehen nun
die quadratische Kochinsel (ein Fraktal). Sie können nun die Rekursionstiefe auf 3 Stellen
und erneut das Bild erzeugen lassen.
122
G CD-ROM Inhalt
G CD-ROM Inhalt
Verzeichnisstruktur
Inhalt
/
Wurzel
Die Applikationen die entstanden sind
Kompilierte Fassade und Dokumente dazu
Kompilierte GUI und Dokumente dazu
Schriftliche Arbeit als PDF und DOC
Grafiken aus der Diplomarbeit
Freie Literaturen zur Diplomarbeit
Über Eco-Grammar
Über GA und GP
Über L-Systeme
Über P-Systeme
Enthält sonstige Literaturquellen
Quelltexte zu den Applikationen der DA
Quelltext von Lprocess und Lparser
Quelltext von der GUI
Programme zum Betrachten der Texte
Software zur Diplomarbeit
JVCL, DirectX Kapslung, GraphicEx
Diverse Software zu L-Systemen
POV-Ray 3.5 für Windows und QuietPOV
Enthält Imogene
Applikationen
Fassade
GUI
Diplomarbeit
Abbildungen
Literatur
Eco-Grammar
Evolution
L-Systeme
P-Systeme
Sonstiges
Quelltexte
Fassade
GUI
Resourcen
Software zur Diplomarbeit
Delphi Ergänzungen
L-Systeme
POV-Ray
Sonstiges
123
H
Quelltexte
H Quelltexte
Quelltexte von Lprocess:
Dateiname: Lprocess.c
/*********************************************************************************************
Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
09. 06. 04
[email protected]
Lprocess
1.0
Modulename:
Lprocess.c
Modulversion:
1.0
Modulbeschreibung:
In dieser Datei ist die main() Funktion enthalten für das Programm Lprocess.
*********************************************************************************************/
/*********************************************************************************************
INCLUDE-DATEIEN
*********************************************************************************************/
#include
#include
#include
#include
#include
#include
#include
<stdio.h>
"globals.h"
"com_line.h"
"cfg_file.h"
"lparser.h"
"pov.h"
"misc.h"
/*********************************************************************************************
PROTOTYPEN
*********************************************************************************************/
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
extern
void
char
char
char
char
char
char
void
void
char
char
char
print_Welcome(void);
checkingComline(int *argc, char *argv[]);
checkingCFGFile(void);
convertLP4toLP5(char *old_filename, char *new_filename);
Lparser(void);
Lv2povid(void);
CorrectingPOVFile(void);
startPOV(void);
endPOV(void);
startQPOV(void);
deleteFiles(void);
checkingEnvVariables(char *envp[]);
/*********************************************************************************************
Funktionsname: main
Rückgabewert
Typ:
Bedeutung:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
int
Gibt einen Fehlerstatus zurück (-1) oder
das kein Fehler aufgetreten ist (0).
int
argc
Enthält die Anzahl der Argumente von der Kommandozeile.
char **
argv
Zeigerarray auf die Strings der Kommandozeile
char **
envp
Zeigerarray auf die Strings der Umgebungsvariablen
Funktionsbeschreibung:
124
H
Quelltexte
Die main()-Funktion delegiert die einzelnen Aufgaben an entsprechende Funktionen weiter.
*********************************************************************************************/
int main(int argc, char *argv[], char *envp[])
{
char c = 0;
print_Welcome();
puts("Checking commandline ...");
if((c = checkingComline(&argc, argv)) != 1)
return c;
/* Wird das Programm nur zum beenden von POV-Ray oder zum konvertieren einer Datei
aufgerufen, dann endet hier die Abarbeitung schon.*/
if((info.state & KILL_POV_AND_CONV_ONLY) == KILL_POV_AND_CONV_ONLY)
{
puts("Converting Lparser 4 file into Lparser 5 file ...");
if(convertLP4toLP5(info.filename, info.conv_filename) < 0)
return -1;
endPOV();
printf("\nFinishing successfully!\n\n");
return 0;
}
else if((info.state & KILL_POV_ONLY) == KILL_POV_ONLY)
{
endPOV();
printf("\nFinishing successfully!\n\n");
return 0;
}
else if((info.state & CONVERT_ONLY) == CONVERT_ONLY)
{
puts("Converting Lparser 4 file into Lparser 5 file ...");
if(convertLP4toLP5(info.filename, info.conv_filename) == -1)
return -1;
printf("\nFinishing successfully!\n\n");
return 0;
}
puts("Checking CFG file ... ");
if(checkingCFGFile() < 0)
return -1;
puts("Checking Environmentvariables ...");
if(checkingEnvVariables(envp) < 0)
return -1;
if((info.state & CONVERT) == CONVERT)
{
puts("Converting Lparser 4 file into Lparser 5 file ...");
if(convertLP4toLP5(info.filename, info.conv_filename) < 0)
return -1;
}
startPOV();
puts("Starting Lparser ...");
if(Lparser() < 0)
return -1;
puts("Finishing Lparser");
puts("Starting LV2POVID ...");
if(Lv2povid() < 0)
return -1;
puts("Finishing LV2POVID");
puts("Correcting POV File ...");
if(CorrectingPOVFile() < 0)
return -1;
puts("Finishing Correction");
puts("Starting Render-Process ...");
if(startQPOV() < 0)
return -1;
125
H
Quelltexte
if((info.state & KILL_POV) == KILL_POV)
endPOV();
if((info.state & KEEP_FILES) == 0)
{
puts("Deleting nonrelevant files ...");
if(deleteFiles() < 0)
return -1;
}
printf("\nFinishing successfully!\n\n");
return 0;
}
Dateiname: globals.h
/*********************************************************************************************
Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
09. 06. 04
[email protected]
Lprocess
1.0
Modulename:
globals.h
Modulversion:
1.0
Modulbeschreibung:
In diesem Modul werden einige Präprozessor-Variablen und zwei Strukturen definiert,
die in allen Modulen benötigt werden.
*********************************************************************************************/
#ifndef
___globals_H___
#define
___globals_H___
/* Bitwerte für die state Variable
#define CONVERT
#define CONVERT_ONLY
#define KILL_POV
#define KILL_POV_ONLY
#define ANTI_ALIASING
#define TARGA_FILE
#define KEEP_FILES
1
/* Entspricht dem Schalter -C
/* Konvertieren einer Datei
2
/* Entspricht dem Schalter -c
/* Konvertieren einer Datei und Programm beenden
4
/* Entspricht dem Schalter -E
/* POV-Ray am Ende beenden
8
/* Entspricht dem Schalter -e
/* Nur POV-Ray beenden und Programm verlassen
16
/* Entspricht dem Schalter -A
/* Anti-Aliasing zur Bilderzeugung nutzen
32
/* Entspricht dem Schalter -T
/* Bild als Targa Datei speichern
64
/* Entspricht dem Schalter -F
/* Dateien aus den Zwischenschritten nicht löschen
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
#define KILL_POV_AND_CONV_ONLY
10
/* Entspricht dem Schalter -c und -e */
/* Konvertieren einer Datei und POV-Ray beenden
*/
/* Bitwerte für nichterlaubte Schalterkombinationen
#define ILLEGAL_OPTS_cC
3
/* Schalterkombination -c -C
#define ILLEGAL_OPTS_cE
6
/* Schalterkombination -c -E
#define ILLEGAL_OPTS_eC
9
/* Schalterkombination -e -C
#define ILLEGAL_OPTS_eE
12
/* Schalterkombination -e -E
*/
*/
*/
*/
*/
/* Alle Anweisungen werden für char Arrays benutzt.
*/
/* Daher muss zum eigentlich Wert noch +1 gerechnet werden für \0 .
*/
/* Maximale länge des Wertes von Width und Heigt von der Kommandozeile.*/
#define MAX_VALUE_WIDTH_HEIGHT
11
#define MAX_FILENAME_LENGTH
51
/* Maximale länge eine Dateinamens. */
/* Maximale länge einer Zeile in der CFG Datei.
*/
#define MAX_CFG_LINE_LENGTH
151
/* Maximale länge einer Zeile in einer LS Datei.
*/
#define MAX_LS_LINE_LENGTH
1001
/* Maximale Anzahl von Zeichen von der Kommandozeile.
*/
#define MAX_COMLINE
128
/* Maximale länge einer Zeile in einer POV Datei.
*/
#define MAX_POV_LINE_LENGTH
151
/* Maximale länge einer Option auf der linken Seite des =, in der CFG Datei.
*/
#define MAX_CFG_LEFTSIDE
11
126
H
Quelltexte
/* Maximale länge einer Option auf der rechten Seite des =, in der CFG Datei
#define MAX_CFG_RIGHTSIDE
141
/* Maximale Anzahl von Optionen die in einer CFG Datei stehen können.
#define MAX_OPTS_IN_CFG
13
#define POV_APP
"Pov35MainWinClass"
*/
*/
/* Name der POV-Ray Instanze. */
/* Die Struktur enthält alle wichtigen Information, von der Kommandozeile und aus der*/
/* CFG Datei, die alle Teile von Lprocess benötigen, daher gibt es für die Struktur */
/* eine globale Variable namens info.
*/
struct InfoStruct
{
char
filename[MAX_FILENAME_LENGTH],
/* Name einer LS Datei */
conv_filename[MAX_FILENAME_LENGTH], /* Name einer weiteren */
/* LS Datei, falls eine konvertierte Datei in einer anderen */
/* gespeichert werden soll.
*/
pathPOV[MAX_CFG_RIGHTSIDE],
/* Pfad für POV-RAY
*/
pathQPOV[MAX_CFG_RIGHTSIDE];
/* Pfad für QuietPOV */
unsigned char state;
/* Bitweise Speicherung von
*/
/* Optionen für Lprocess
*/
int
height,
/* Höhe des Bildes
*/
width;
/* Breite des Bildes
*/
double
x,
/* Faktor mit dem die X-Koordinate
*/
/* der Kamera multipliziert wird.
*/
y,
/* Faktor mit dem die Y-Koordinate
*/
/* der Kamera multipliziert wird.
*/
z,
/* Faktor mit dem die Z-Koordinate
*/
/* der Kamera multipliziert wird.
*/
light_x,
/* Faktor für die realtive Position */
/* der Lichtquelle zur Kamera
*/
light_y,
/* Faktor für die realtive Position */
/* der Lichtquelle zur Kamera
*/
light_z;
/* Faktor für die realtive Position */
/* der Lichtquelle zur Kamera
*/
}
info;
/* Diese Struktur wird zum auslesen und interpretieren der CFG-Datei benötigt.
struct CFGStruct
{
/* Speicherung der linken Seite einer Option. */
char
left[MAX_CFG_LEFTSIDE];
/* Speicherung der rechten Seite einer Option. */
char
right[MAX_CFG_RIGHTSIDE];
};
*/
#endif
Dateiname: com_line.h
/*********************************************************************************************
Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
09. 06. 04
[email protected]
Lprocess
1.0
Modulename:
com_line.h
Modulversion:
1.0
Modulbeschreibung:
Header-Datei zum Modul com_line.c.
*********************************************************************************************/
#ifndef
___com_line_H___
#define ___com_line_H___
extern void print_Welcome(void);
extern void print_Comline_Help(void);
#endif
127
H
Quelltexte
Dateiname: com_line.c
/*********************************************************************************************
Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
09. 06. 04
[email protected]
Lprocess
1.0
Modulename:
com_line.c
Modulversion:
1.0
Modulbeschreibung:
Wichtige Ausgaben für die Kommandozeile und Funktionen zum verarbeiten der Argumente
von der Kommandozeile, sind in dem Modul enthalt.
*********************************************************************************************/
/*********************************************************************************************
INCLUDE-DATEIEN
*********************************************************************************************/
#include
#include
#include
#include
#include
<stdio.h>
<string.h>
<ctype.h>
<stdlib.h>
"globals.h"
/*********************************************************************************************
Funktionsname: print_Welcome
Funktionsbeschreibung:
Eine einfache Startnachricht für die Konsole.
*********************************************************************************************/
void print_Welcome(void)
{
puts("-------------------------------------");
puts(" Lprocess Version 1.0 by Jan Derer");
puts(" Contact: [email protected]");
printf("-------------------------------------\n\n");
}
/*********************************************************************************************
Funktionsname: print_Comline_Help
Funktionsbeschreibung:
Zeigt auf der Konsole eine kurze Hilfe für Lprocess an.
*********************************************************************************************/
void print_Comline_Help(void)
{
printf("Usage: lprocess [options] [ls-file to convert] [ls-file]\n\n");
puts("Examples:");
puts("lprocess -H640 -W480 -A bekerpl.ls");
puts("Make a Bitmap-Image of the L-System bekerpl with 640*480 and Anti-Aliasing");
puts("lprocess -C bekerpl bekerpl5");
puts("Convert bekerpl and save the L-System to bekerpl5 and generate a Image");
puts("lprocess -e");
printf("Just closing the POV-Ray Instance\n\n");
printf("-?\t\tThis screen\n");
printf("-H[num]\t\tImage height in pixel\n");
printf("-W[num]\t\tImage width in pixel\n");
printf("-T\t\tUsing Imageformat Targa\n");
printf("-A\t\tUsing Anti-Aliasing\n");
printf("-C\t\tConverting ls-file to Version 5 and continue\n");
printf("-c\t\tConverting only\n");
printf("-E\t\tClose POV-Ray at the end of this process\n");
printf("-e\t\tClose POV-Ray now\n");
printf("-F\t\tKeep all intermediate Files\n\n");
128
H
Quelltexte
printf("WARNING: By converting a file without a destination file,\n");
printf("
the content of the old file will be overwritten!\n\n");
}
/*********************************************************************************************
Funktionsname: print_Comline_Error
Parameter
Typ:
char *
Name:
errorString
Bedeutung:
String der die Fehlermeldung enthält.
Funktionsbeschreibung:
Sollte bei der Verarbeitung der Argumente der Kommandozeile ein Fehler auftreten, dann
gibt die Funktion eine formatierte Fehlermeldung aus.
*********************************************************************************************/
void print_Comline_Error(char *errorString)
{
fprintf(stderr, "ERROR: %s\n", errorString);
fprintf(stderr, "Usage: lprocess [options] [ls-file to convert] [ls-file]\n");
fprintf(stderr, "Type -? to get help!\n\n");
}
/*********************************************************************************************
Funktionsname: checkingState
Rückgabewert
Typ:
char
Bedeutung:
Gibt einen Fehlerstatus zurück (-1) oder
das kein Fehler aufgetreten ist (0).
Funktionsbeschreibung:
Prüft ob die Argumente der Kommandozeile so gewählt wurden, dass ein reibungsloser Ablauf garantiert wird.
*********************************************************************************************/
char checkingState(void)
{
if(((info.state & ILLEGAL_OPTS_cC) == ILLEGAL_OPTS_cC) ||
((info.state & ILLEGAL_OPTS_eE) == ILLEGAL_OPTS_eE) ||
((info.state & ILLEGAL_OPTS_cE) == ILLEGAL_OPTS_cE) ||
((info.state & ILLEGAL_OPTS_eC) == ILLEGAL_OPTS_eC))
{
print_Comline_Error("Illegal combination of options!");
return -1;
}
else if(((info.state & KILL_POV_ONLY) == 0) && (strlen(info.filename) == 0))
{
print_Comline_Error("No filename found!");
return -1;
}
else if(((info.state & KILL_POV_AND_CONV_ONLY) == KILL_POV_AND_CONV_ONLY) &&
(strlen(info.filename) == 0))
{
print_Comline_Error("Require filename to convert!");
return -1;
}
return 0;
}
/*********************************************************************************************
Funktionsname: extractValueFromString
Rückgabewert
Typ:
int
Bedeutung:
Gibt die Anzahl der verarbeiteten Zeichen zurück.
Wurde keine Zahl gefunden, wird 0 zurück geliefert.
Parameter
Typ:
char *
Name:
string
Bedeutung:
String aus dem eine Zahl extrahiert werden soll.
129
H
Quelltexte
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
int
index
Index für den String, ab dem die Zahl anfangen soll.
int *
result
Zeiger auf eine Variable, in dem der Wert gespeichert werden
soll.
Funktionsbeschreibung:
Extrahiert aus einem String von der Position index ab, eine Zahl aus dem String, sofern
eine Zahl vorliegt. Das Ergebnis wird in result abgespeichert. Zurück geliefert wird
dieAnzahl der verarbeiteten Zeichen.
*********************************************************************************************/
int extractValueFromString(char *string, int index, int *result)
{
char temp[MAX_VALUE_WIDTH_HEIGHT], c = 0;
while(isdigit(string[index]))
temp[c++] = string[index++];
if(c == 0)
{
print_Comline_Error("No Value found!");
return 0;
}
else
{
temp[c] = '\0';
*result = atoi(temp);
}
return c;
}
/*********************************************************************************************
Funktionsname: checkFileExtension
Parameter
Typ:
char *
Name:
filename
Bedeutung:
String des Dateinamens
Typ:
char *
Name:
modfilename
Bedeutung:
Zeiger auf ein String, in dem der neue Dateiname gespeichert
wird.
Funktionsbeschreibung:
Diese Funktion überprüft, ob der angegebene Dateiname die Endung .ls enthält. Liegt der
Fall nicht vor, wird die Endung an den Dateiname rangehangen.
*********************************************************************************************/
void checkFileExtension(char *filename, char *modfilename)
{
int length = strlen(filename);
if((filename[length-3] != '.') || (toupper(filename[length-2]) != 'L') ||
(toupper(filename[length-1]) != 'S'))
sprintf(modfilename, "%s.ls", filename);
else
strcpy(modfilename, filename);
}
/*********************************************************************************************
Funktionsname: checkingComline
Rückgabewert
Typ:
char
Bedeutung:
Gibt einen Fehlerstatus zurück (-1) oder
das kein Fehler aufgetreten ist (0).
Parameter
Typ:
int *
Name:
argc
Bedeutung:
Enthält die Anzahl der Argumente von der Kommandozeile.
130
H
Quelltexte
Typ:
Name:
Bedeutung:
char **
argv
Zeigerarray auf die Strings der Kommandozeile
Funktionsbeschreibung:
Die Aufgabe dieser Funktion ist es, die Argumente der Kommandozeile verarbeiten und die
entsprechenden Variablen mit dem Wert zu setzen.
*********************************************************************************************/
char checkingComline(int *argc, char *argv[])
{
char
*string
= NULL, /*
/*
temp[MAX_FILENAME_LENGTH], c; /*
int
i, j,
/*
k
= 0,
/*
length
= 0;
/*
/*
Zwischenspeicherung der einzelnen
Inhalte des Arrays argv.
Zwischenspeicher
Zählvariablen
Zwischenspeicher
Speicherung der Länge eines Strings vom
Array argv[].
*/
*/
*/
*/
*/
*/
*/
/* Falls zuwenige Argumente angegeben wurde, wird die Hilfe aufgerufen.
if(*argc == 1)
{
print_Comline_Help();
printf("Need arguments\n\n");
return 0;
}
*/
/* Durchlaufen des Arrays argv[]
for(i = 1; i < *argc; i++)
{
string = argv[i];
/* Nächsten String zuweisen zur Verarbeitung.
*/
/* Falls der String eine Option enthält, dann wird der String hier weiterverarbeitet.
if(string[0] == '-')
{
*/
*/
/* Sollte der String nur das Zeichen enthalten, dann springe zur nächsten Iteration */
if((length = strlen(string)) == 1)
continue;
/* Durchlaufen des Strings und auswerten der einzelnen Zeichen.
for(j = 1; j < length; j++)
{
c = string[j];
switch(c)
{
case 'C':
info.state |= CONVERT;
break;
case 'c':
info.state |= CONVERT_ONLY;
break;
case 'E':
info.state |= KILL_POV;
break;
case 'e':
info.state |= KILL_POV_ONLY;
break;
case 'A':
info.state |= ANTI_ALIASING;
break;
case 'T':
info.state |= TARGA_FILE;
break;
case 'F':
info.state |= KEEP_FILES;
break;
*/
/* Falls eine Höhe oder Breite angegeben wird, dann wird aus dem String */
/* noch die Zahl extrahiert.
*/
case 'H':
k = extractValueFromString(string, j+1,
&info.height);
if(k == 0)
return -1;
else
j += k;
break;
case 'W':
k = extractValueFromString(string, j+1,
&info.width);
if(k == 0)
return -1;
else
j += k;
break;
131
H
Quelltexte
case '?':
print_Comline_Help();
return 0;
break;
default :print_Comline_Error("Found illegal option!");
return -1;
break;
}
}
}
/* Falls der String keine Optionen enthält, muss es ein Dateiname sein.
else
{
*/
/* Wenn mehr als zwei weitere Argumente noch da sind, wird das Programm abgebrochen */
if((*argc-i) > 2)
{
print_Comline_Error("To many filenames or options after
filename");
return -1;
}
/* Liegt nur ein Dateiname vor, dann wird es in info.filename gespeichert. */
else if((*argc-i) == 1)
strcpy(info.filename, argv[i]);
/* Andernfalls liegen zwei Dateinamen vor, die entsprechend gespeichert werden. */
else
{
strcpy(info.filename, argv[i]);
strcpy(info.conv_filename, argv[++i]);
if(info.conv_filename[0] == '-')
{
print_Comline_Error("To many filenames or options after
filename");
return -1;
}
checkFileExtension(info.conv_filename, temp);
strcpy(info.conv_filename, temp);
}
checkFileExtension(info.filename, temp);
strcpy(info.filename, temp);
}
}
/* Prüfe die Plausibilität der Eingabe.
if(checkingState() == -1)
return -1;
*/
return 1;
}
Dateiname: cfg_file.h
/*********************************************************************************************
Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
09. 06. 04
[email protected]
process
1.0
Modulename:
cfg_file.h
Modulversion:
1.0
Modulbeschreibung:
Header-Datei zum Modul cfg_file.c.
*********************************************************************************************/
#ifndef
___cfg_file_H___
#define ___cfg_file_H___
extern char checkingCFGFile(void);
#endif
132
H
Quelltexte
Dateiname: cfg_file.c
/*********************************************************************************************
Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
09. 06. 04
[email protected]
process
1.0
Modulename:
cfg_file.c
Modulversion:
1.0
Modulbeschreibung:
Das Modul enthält alle wichtigen Funktion die zum auslesen und interpretieren der
CFG-Datei benötigt werden.
*********************************************************************************************/
/*********************************************************************************************
NCLUDE-DATEIEN
*********************************************************************************************/
#include
#include
#include
#include
#include
<stdio.h>
<string.h>
<ctype.h>
<stdlib.h>
"globals.h"
/*********************************************************************************************
Funktionsname: createDefaultCFGFile
Funktionsbeschreibung:
Wurde keine CFG-Datei gefunden, wird eine Datei erzeugt mit Standardwerten und Kommentaren.
*********************************************************************************************/
void createDefaultCFGFile(void)
{
FILE *CFGFile = NULL;
CFGFile = fopen("LPROCESS.CFG", "wt");
fprintf(CFGFile, "// Imageformat: Image=bmp | targa\n");
fprintf(CFGFile, "Image=bmp\n");
fprintf(CFGFile, "// Imageheight in pixel\n");
fprintf(CFGFile, "Height=480\n");
fprintf(CFGFile, "// Imagewidth in pixel\n");
fprintf(CFGFile, "Width=640\n");
fprintf(CFGFile, "// Anti-Aliasing: AA=on | off\n");
fprintf(CFGFile, "AA=off\n");
fprintf(CFGFile, "// Path for POV-Ray\n");
fprintf(CFGFile, "POV=c:\\programme\\pov-ray for windows v3.5\\bin\\\n");
fprintf(CFGFile, "// Path for QuietPOV\n");
fprintf(CFGFile, "QPOV=c:\\programme\\pov-ray for windows v3.5\\guiext\\quietpov\\\n");
fprintf(CFGFile, "// Keep all files? Files=on | off\n");
fprintf(CFGFile, "Files=off\n");
fprintf(CFGFile, "// Value to multiplicate with X to get the best cameraposition\n");
fprintf(CFGFile, "CamX=2\n");
fprintf(CFGFile, "// Value to multiplicate with Y to get the best cameraposition\n");
fprintf(CFGFile, "CamY=2\n");
fprintf(CFGFile, "// Value to multiplicate with Z to get the best cameraposition\n");
fprintf(CFGFile, "CamZ=2\n");
fprintf(CFGFile, "// Value to multiplicate with X to get the position for the
lightsource\n");
fprintf(CFGFile, "LightX=1\n");
fprintf(CFGFile, "// Value to multiplicate with Y to get the position for the
lightsource\n");
fprintf(CFGFile, "LightY=1\n");
fprintf(CFGFile, "// Value to multiplicate with Z to get the position for the
lightsource\n");
fprintf(CFGFile, "LightZ=1\n");
fclose(CFGFile);
}
133
H
Quelltexte
/*********************************************************************************************
Funktionsname: getValueFromList
Rückgabewert
Typ:
char*
Bedeutung:
Gibt den String eines bestimmten Wertes aus der Liste zurück.
Liegt kein Wert mit der Bezeichnung vor, wird NULL zurück
gegeben.
Parameter
Typ:
struct CFGStruct []
Name:
list
Bedeutung:
Zeiger auf ein Array mit den Wertepaaren aus der CFG-Datei.
Typ:
char *
Name:
value
Bedeutung:
Die Bezeichnung des Wertes nachdem gesucht wird.
Funktionsbeschreibung:
Wenn die Wertepaare aus der CFG-Datei eingelesen wurden, kann mit der Funktion über den
Bezeichner, der Wert als String zurück gegeben werden. Datei wird sequenziell die
Datenstruktur abgearbeitet.
*********************************************************************************************/
char* getValueFromList(struct CFGStruct list[], char *value)
{
char i;
for(i = 0; i < MAX_OPTS_IN_CFG; i++)
if(stricmp(list[i].left, value) == 0)
return list[i].right;
return NULL;
}
/*********************************************************************************************
Funktionsname: checkingCFGFile
Rückgabewert
Typ:
char
Bedeutung:
Gibt einen Fehlerstatus zurück (-1) oder
das kein Fehler aufgetreten ist (0).
Funktionsbeschreibung:
Diese Funktion liest die CFG-Datei ein, füllt die Datenstruktur und interpretiert den
Inhalt.
*********************************************************************************************/
char checkingCFGFile(void)
{
FILE
*CFGFile
= NULL; /*
char
zeile[MAX_CFG_LINE_LENGTH],
/*
changingSide,
/*
/*
/*
counter
= 0,
/*
/*
countOK,
/*
/*
*temp
= NULL, /*
path;
/*
/*
/*
/*
int
length
= 0,
/*
/*
i, j,
/*
errorLine
= 0;
/*
/*
struct CFGStruct
opts[MAX_OPTS_IN_CFG]; /*
/*
Zeiger auf CFG-Datei
Speicher für eine Zeile
Zeigt an auf welcher Seite
man sich gerade befindet.
0 = linke, 1 = rechte Seite
Zähler für die Anzahl der
gelesene Optionen
Gibt an ob die Zeile eine
Option enthält oder nicht.
Zwischenspeicher für ein String
Wird gesetzt wenn die Option
ein Pfadnamen enthält in dem
Leerzeichen mitgespeichert
werden.
Speichert die Länge einer
gelesene Zeile.
Zählvariablen
Zähler um anzugeben, in welcher
Zeile ein Fehler vorliegt.
Datenstruktur zum speichern der
Optionen aus der CFG-Datei.
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
/* Existiert eine CFG-Datei? Wenn nicht, dann erstelle eine mit Standardwerten und füllte*/
/* die Variablen auch mit Standardwerten.
*/
if((CFGFile = fopen("LPROCESS.CFG", "rt")) == NULL)
{
printf("Can't find LPROCESS.CFG! Using default values!\n\n");
134
H
Quelltexte
createDefaultCFGFile();
info.x = 2.0;
info.y = 2.0;
info.z = 2.0;
if(info.height == 0)
info.height = 480;
if(info.width == 0)
info.width = 640;
strcpy(info.pathPOV, "\\");
strcpy(info.pathQPOV, "\\");
return 0;
}
/* Schleife zum auslesen der CFG-Datei.
*/
while((!feof(CFGFile)) || (counter == MAX_OPTS_IN_CFG))
{
errorLine++;
fgets(zeile, MAX_CFG_LINE_LENGTH, CFGFile);/* Holen einer Zeile aus der Datei*/
length = strlen(zeile);
/* Länge des String berechnen.
*/
changingSide = 0;
j = 0;
countOK = 0;
path = 0;
/* Über die länge des Strings iterieren.
for(i = 0; i < length; i++)
{
*/
/* Wenn die aktuelle und die nächste Position ein / als Zeichen aufweisen, */
/* dann ist es ein Kommentar. Breche das weitere iterieren der Zeile ab. */
if(((i+1) < length) && (zeile[i] == '/') && (zeile[i+1] == '/'))
{
if((changingSide != 0) || (j != 0))
countOK = 0;
break;
}
/* Ist das Zeichen ein Gleichheitszeichen, dann wechsel die Seite. */
else if(zeile[i] == '=')
{
changingSide = 1;
j = 0;
}
/* Speicher Leerzeichen, wenn es eine Pfadoption ist und schon auf die rechte*/
/* Seite gewechselt wurde.
*/
else if(changingSide && path && (zeile[i] == ' '))
{
opts[counter].right[j++] = zeile[i];
opts[counter].right[j] = '\0';
}
/* Fange die nächste Iteration an, wenn das Zeichen ein Leer-,Tabzeichen oder*/
/* ein Newline Zeichen ist.
*/
else if((zeile[i] == ' ') || (zeile[i] == '\t') || (zeile[i] == '\n'))
continue;
/* Kopiere die Zeichen in die Datenstruktur für die linke Seite.
/* Setze die Pfadvariable, falls der Wert POV oder QPOV ist.
else if((changingSide == 0) && (j < (MAX_CFG_LEFTSIDE-1)))
{
if((j == 0) && ((toupper(zeile[0]) == 'P') ||
(toupper(zeile[0]) == 'Q')))
path = 1;
countOK = 1;
opts[counter].left[j++] = zeile[i];
opts[counter].left[j] = '\0';
}
*/
*/
/* Kopiere die Zeichen in die Datenstruktur für die rechte Seite. */
else if(changingSide)
{
opts[counter].right[j++] = zeile[i];
opts[counter].right[j] = '\0';
}
135
H
Quelltexte
/* Falls nichts zu trifft, liegt ein Fehler vor.
*/
else
{
fprintf(stderr, "ERROR: Error in Line %d of LPROCESS.CFG\n\n",
errorLine);
return -1;
}
}
/* Zähle die Anzahl der Optionen hoch, falls eine Option gefunden wurde.
if(countOK)
counter++;
*/
}
fclose(CFGFile);
/* Es folgt die Auswertung der CFG-Datei.
*/
/* Falls per Kommandozeile nicht Anti-Aliasing gesetzt wurde, dann prüfe nach,
*/
/* ob in der CFG-Datei die Option gesetzt wurde.
*/
if((info.state & ANTI_ALIASING) == 0)
{
temp = getValueFromList(opts, "AA");
if(temp == NULL)
{
fprintf(stderr, "ERROR: There exists no value for AA in
LPROCESS.CFG!\n\n");
return -1;
}
else if(stricmp(temp, "on") == 0)
info.state |= ANTI_ALIASING;
else if(stricmp(temp, "off") == 0)
;
else
{
fprintf(stderr, "ERROR: Illegal Value for AA in LPROCESS.CFG!\n\n");
return -1;
}
}
/* Falls per Kommandozeile nicht Targa gesetzt wurde, dann prüfe nach, ob in der */
/* CFG-Datei die Option gesetzt wurde.
*/
if((info.state & TARGA_FILE) == 0)
{
temp = getValueFromList(opts, "Image");
if(temp == NULL)
{
fprintf(stderr, "ERROR: There exists no value for Image in
LPROCESS.CFG!\n\n");
return -1;
}
else if(stricmp(temp, "targa") == 0)
info.state |= TARGA_FILE;
else if(stricmp(temp, "bmp") == 0)
;
else
{
fprintf(stderr, "ERROR: Illegal Value for Image in LPROCESS.CFG!\n\n");
return -1;
}
}
/* Wurde über die Kommandozeile keine Höhe gesetzt, dann wird in der
*/
/* CFG-Datei nachgeschaut ob ein Wert vorliegt.
*/
if(info.height == 0)
{
temp = getValueFromList(opts, "Height");
if(temp == NULL)
{
fprintf(stderr, "ERROR: There exists no value for Height in
LPROCESS.CFG!\n\n");
return -1;
}
else if((info.height = atoi(temp)) < 1)
{
fprintf(stderr, "ERROR: Illegal Value for Height in LPROCESS.CFG!\n\n");
return -1;
}
}
136
H
Quelltexte
/* Wurde über die Kommandozeile keine Breite gesetzt, dann wird in der
*/
/* CFG-Datei nachgeschaut ob ein Wert vorliegt.
*/
if(info.width == 0)
{
temp = getValueFromList(opts, "Width");
if(temp == NULL)
{
fprintf(stderr, "ERROR: There exists no value for Width in
LPROCESS.CFG!\n\n");
return -1;
}
else if((info.width = atoi(temp)) < 1)
{
fprintf(stderr, "ERROR: Illegal Value for Width in LPROCESS.CFG!\n\n");
return -1;
}
}
/* Setze in der info Datenstruktur die Variable x, mit dem Wert aus der CFG-Datei. */
temp = getValueFromList(opts, "CamX");
if(temp == NULL)
{
fprintf(stderr, "ERROR: There exists no value for CamX in LPROCESS.CFG!\n\n");
return -1;
}
else if((info.x = atof(temp)) == 0.0)
{
fprintf(stderr, "ERROR: Illegal Value for CamX in LPROCESS.CFG!\n\n");
return -1;
}
/* Setze in der info Datenstruktur die Variable y, mit dem Wert aus der CFG-Datei. */
temp = getValueFromList(opts, "CamY");
if(temp == NULL)
{
fprintf(stderr, "ERROR: There exists no value for CamY in LPROCESS.CFG!\n\n");
return -1;
}
else if((info.y = atof(temp)) == 0.0)
{
fprintf(stderr, "ERROR: Illegal Value for CamY in LPROCESS.CFG!\n\n");
return -1;
}
/* Setze in der info Datenstruktur die Variable z, mit dem Wert aus der CFG-Datei. */
temp = getValueFromList(opts, "CamZ");
if(temp == NULL)
{
fprintf(stderr, "ERROR: There exists no value for CamZ in LPROCESS.CFG!\n\n");
return -1;
}
else if((info.z = atof(temp)) == 0.0)
{
fprintf(stderr, "ERROR: Illegal Value for CamZ in LPROCESS.CFG!\n\n");
return -1;
}
/* Setze in der info Datenstruktur die Variable light_x, mit dem Wert aus der CFG-Datei. */
temp = getValueFromList(opts, "LightX");
if(temp == NULL)
{
fprintf(stderr, "ERROR: There exists no value for LightX in
LPROCESS.CFG!\n\n");
return -1;
}
else if((info.light_x = atof(temp)) == 0.0)
{
fprintf(stderr, "ERROR: Illegal Value for LightX in LPROCESS.CFG!\n\n");
return -1;
}
/* Setze in der info Datenstruktur die Variable light_y, mit dem Wert aus der CFG-Datei. */
temp = getValueFromList(opts, "LightY");
if(temp == NULL)
{
fprintf(stderr, "ERROR: There exists no value for LightY in
LPROCESS.CFG!\n\n");
return -1;
137
H
Quelltexte
}
else if((info.light_y = atof(temp)) == 0.0)
{
fprintf(stderr, "ERROR: Illegal Value for LightY in LPROCESS.CFG!\n\n");
return -1;
}
/* Setze in der info Datenstruktur die Variable light_z, mit dem Wert aus der CFG-Datei. */
temp = getValueFromList(opts, "LightZ");
if(temp == NULL)
{
fprintf(stderr, "ERROR: There exists no value for LightZ in
LPROCESS.CFG!\n\n");
return -1;
}
else if((info.light_z = atof(temp)) == 0.0)
{
fprintf(stderr, "ERROR: Illegal Value for LightZ in LPROCESS.CFG!\n\n");
return -1;
}
/* Speicher den Pfad, aus der CFG-Datei, für POV-Ray in die Variable pathPOV.
temp = getValueFromList(opts, "POV");
if(temp == NULL)
{
fprintf(stderr, "ERROR: There exists no path for POV-Ray in
LPROCESS.CFG!\n\n");
return -1;
}
else
{
length = strlen(temp);
if(length == 1)
sprintf(zeile, "pvengine.exe");
else if(temp[length-1] == '\\')
sprintf(zeile, "%spvengine.exe", temp);
else
sprintf(zeile, "%s\\pvengine.exe", temp);
if((CFGFile = fopen(zeile, "rb")) == NULL)
{
fprintf(stderr, "ERROR: POV-Ray could not be found in this
directory!\n\n");
return -1;
}
fclose(CFGFile);
strcpy(info.pathPOV, temp);
}
*/
/* Speicher den Pfad, aus der CFG-Datei, für QuietPOV in die Variable pathQPOV.
temp = getValueFromList(opts, "QPOV");
if(temp == NULL)
{
fprintf(stderr, "ERROR: There exists no path for QuietPOV in
LPROCESS.CFG!\n\n");
return -1;
}
else
{
length = strlen(temp);
if(length == 1)
sprintf(zeile, "quietpov.exe");
else if(temp[length-1] == '\\')
sprintf(zeile, "%squietpov.exe", temp);
else
sprintf(zeile, "%s\\quietpov.exe", temp);
if((CFGFile = fopen(zeile, "rb")) == NULL)
{
fprintf(stderr, "ERROR: QuietPOV could not be found in this
directory!\n\n");
return -1;
}
fclose(CFGFile);
strcpy(info.pathQPOV, temp);
}
*/
138
H
Quelltexte
/* Falls per Kommandozeile nicht Files gesetzt wurde, dann prüfe nach, ob in der */
/* CFG-Datei die Option gesetzt wurde.
*/
if((info.state & KEEP_FILES) == 0)
{
temp = getValueFromList(opts, "Files");
if(temp == NULL)
{
fprintf(stderr, "ERROR: There exists no value for Files in
LPROCESS.CFG!\n\n");
return -1;
}
else if(stricmp(temp, "on") == 0)
info.state |= KEEP_FILES;
else if(stricmp(temp, "off") == 0)
;
else
{
fprintf(stderr, "ERROR: Illegal Value for Files in LPROCESS.CFG!\n\n");
return -1;
}
}
return 0;
}
Dateiname: misc.h
/*********************************************************************************************
Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
09. 06. 04
[email protected]
Lprocess
1.0
Modulename:
misc.h
Modulversion:
1.0
Modulbeschreibung:
Header-Datei zum Modul misc.c.
*********************************************************************************************/
#ifndef
___misc_H___
#define ___misc_H___
extern char deleteFiles(void);
extern char checkingEnvVariables(char *envp[]);
#endif
Dateiname: misc.c
/*********************************************************************************************
Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
09. 06. 04
[email protected]
Lprocess
1.0
Modulename:
misc.c
Modulversion:
1.0
Modulbeschreibung:
Das Modul enthält Funktion die nicht direkt einem anderen Modul zugewiesen werden konnten.
*********************************************************************************************/
/*********************************************************************************************
INCLUDE-DATEIEN
*********************************************************************************************/
#include <stdio.h>
#include <Windows.h>
139
H
Quelltexte
#include "globals.h"
/*********************************************************************************************
Funktionsname: deleteFiles
Rückgabewert
Typ:
int
Bedeutung:
Gibt einen Fehlerstatus zurück (-1) oder
das kein Fehler aufgetreten ist (0).
Funktionsbeschreibung:
Löscht alle Dateien die in den Zwischenschritten erzeugt wurden.
*********************************************************************************************/
char deleteFiles(void)
{
char buffer[MAX_COMLINE];
sprintf(buffer, "DEL *.log");
if((system(buffer)) > 1)
{
fprintf(stderr, "ERROR: Cannot delete Log-Files!\n");
return -1;
}
sprintf(buffer, "DEL *.in?");
if((system(buffer)) > 1)
{
fprintf(stderr, "ERROR: Cannot delete Files!\n");
return -1;
}
sprintf(buffer, "DEL info.txt");
if((system(buffer)) > 1)
{
fprintf(stderr, "ERROR: Cannot delete info.txt!\n");
return -1;
}
sprintf(buffer, "DEL lpar2pov.pov");
if((system(buffer)) > 1)
{
fprintf(stderr, "ERROR: Cannot delete info.txt!\n");
return -1;
}
return 0;
}
/*********************************************************************************************
Funktionsname: checkingEnvVariables
Rückgabewert
Typ:
int
Bedeutung:
Gibt einen Fehlerstatus zurück (-1) oder
das kein Fehler aufgetreten ist (0).
Parameter
Typ:
char **
Name:
envp
Bedeutung:
Zeigerarray auf die Strings der Umgebungsvariablen
Funktionsbeschreibung:
Prüft ob eine Umgebungsvariable für QuietPOV gesetzt wurde und setzt diese gegebenenfalls. Zur Prüfung wird der Pfad aus der CFG-Datei mit den Umgebungsvariablen verglichen.
*********************************************************************************************/
char checkingEnvVariables(char *envp[])
{
char
*string;
int
i, length,
next
found
unsigned int
j
= 0,
= 0;
= 0;
140
H
Quelltexte
/* Durchsuchen der Umgebungsvariablen nach einem Pfad.
while((*envp++) != NULL)
{
*/
string = *envp;
if((toupper(string[0]) == 'P') && (toupper(string[1]) == 'A') &&
(toupper(string[2]) == 'T') && (toupper(string[3]) == 'H') &&
(toupper(string[4]) == '='))
{
length = strlen(string);
for(i = 5; i < length; i++)
{
if(string[i] == info.pathQPOV[j])
{
/* Umgebungsvariable wurde gefunden.
if(j == strlen(info.pathQPOV)-1)
{
found = 1;
break;
}
j++;
*/
}
/* Überspringe die weitere Überprüfung bis zum nächsten Semikolon. */
else if(next)
{
if(string[i] == ';')
{
j = 0;
next = 0;
}
}
}
break;
}
}
/* Wenn keine Umgebungsvariable gefunden wurde, dann setze diese jetzt.
if(!found)
{
puts("Setting path for QuietPOV ...");
if(!SetEnvironmentVariable("PATH", info.pathQPOV))
{
fprintf(stderr, "ERROR: Cannot setting path for QuietPOV!\n");
return -1;
}
}
*/
return 0;
}
Dateiname: lparser.h
/*********************************************************************************************
Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
09. 06. 04
[email protected]
Lprocess
1.0
Modulename:
lparser.h
Modulversion:
1.0
Modulbeschreibung:
Header-Datei zum Modul lparser.c.
*********************************************************************************************/
#ifndef
___lparser_H___
#define ___lparser_H___
extern char convertLP4toLP5(char *old_filename, char *new_filename);
extern char Lparser(void);
extern char Lv2povid(void);
141
H
Quelltexte
extern char CorrectingPOVFile(void);
#endif
Dateiname: lparser.c
/*********************************************************************************************
Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
09. 06. 04
[email protected]
Lprocess
1.0
Modulename:
lparser.c
Modulversion:
1.0
Modulbeschreibung:
Das Modul stellt Funktionen bereit zur Konvertierung von LS-Dateien der Version 4 in
die Version 5, sowie zum Ausführen der Einzelprogramme des Lparser Paketes und zur Behebung von Fehlern die, die Lparser Programme machen.
*********************************************************************************************/
/*********************************************************************************************
INCLUDE-DATEIEN
*********************************************************************************************/
#include
#include
#include
#include
<stdio.h>
<string.h>
<stdlib.h>
"globals.h"
/*********************************************************************************************
Funktionsname: convertLP4toLP5
Rückgabewert
Typ:
char
Bedeutung:
Gibt einen Fehlerstatus zurück (-1) oder
das kein Fehler aufgetreten ist (0).
Parameter
Typ:
char *
Name:
old_filename
Bedeutung:
Dateiname der Datei die konvertiert wird.
Typ:
char *
Name:
new_filename
Bedeutung:
Dateiname der Datei die das Ergebnis enthält wird.
Funktionsbeschreibung:
Diese Funktion konvertiert eine LS-Datei aus Version 4, in die Version 5.
*********************************************************************************************/
char convertLP4toLP5(char *old_filename, char *new_filename)
{
FILE
*old_file
= NULL,
/* Zeiger auf die zu konvertierende Datei.
*new_file
= NULL;
/* Zeiger auf die Zieldatei.
char
same_file
= 0,
/* Wird gesetzt wenn Ziel- und Quelldatei
/* die selben sind.
EndOfFileSymbol= 0,
/* Wird gesetzt wenn das Ende Symbol einer
/* Version 4 Datei erreicht wurde.
buffer[MAX_COMLINE],
/* Zwischenspeicher
old_line[MAX_LS_LINE_LENGTH], /* Speichert die gelesene Zeile.
new_line[MAX_LS_LINE_LENGTH], /* Speichert die konvertierte Zeile.
comment;
/* Wird gesetzt wenn ein Kommentar anfängt.
int
i, j,
/* Zählvariablen
length
= 0;
/* Speichert die Länge einer Zeile.
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
/* Öffenen der zu konvertierende Datei.
*/
if((old_file = fopen(old_filename,"rt")) == NULL)
{
fprintf(stderr, "ERROR: File %s cannot be open to convert!\n", old_filename);
return -1;
}
142
H
Quelltexte
/* Falls in die selbe Datei konvertiert werden soll, dann wird der Inhalt in temp.ls*/
/* zwischengespeichert und später in die Zieldatei umbenannt.
*/
if(new_filename[0] == 0)
{
same_file = 1;
new_file = fopen("temp.ls","wt");
}
else
new_file = fopen(new_filename, "wt");
if(new_file == NULL)
{
fclose(old_file);
fprintf(stderr, "ERROR: The new file cannot be open!\n");
return -1;
}
/* In der Schleife werde die Zeile durchlaufen und entsprechend konvertiert.
while((!feof(old_file)) || (EndOfFileSymbol == 0))
{
fgets(old_line, MAX_LS_LINE_LENGTH, old_file);
length = strlen(old_line);
comment = 0;
for(i = 0, j = 0; i < length; i++, j++)
{
switch(old_line[i])
{
case '=':
if(comment) break;
/* Aus = wird ->
new_line[j] = '-';
new_line[++j] = '>';
break;
*/
*/
case '<':
if(comment) break;
/* Aus < wird \
new_line[j] = '\\';
break;
*/
case '>':
if(comment) break;
/* Aus > wird /
new_line[j] = '/';
break;
*/
case '#':
if(comment) break;
/* Aus # wird /*
new_line[j] = '/';
new_line[++j] = '*';
break;
*/
case '@':
if(i == 0)
/* Endezeichen erreicht
{
EndOfFileSymbol = 1;
i = length;
continue;
}
*/
default :
new_line[j] = old_line[i];
}
}
if(EndOfFileSymbol == 0)
{
new_line[j] = '\0';
fputs(new_line, new_file);
}
}
fclose(old_file);
fclose(new_file);
/* Falls Ziel- und Quelldatei die selben sind, dann wird die alte Datei
/* gelöscht und temp.ls in die Datei umbenannt.
if(same_file)
{
sprintf(buffer, "DEL %s", old_filename);
if(system(buffer) > 1)
{
fprintf(stderr, "ERROR: %s cannot be deleted!\n", old_filename);
return -1;
}
sprintf(buffer, "REN TEMP.LS %s", old_filename);
143
*/
*/
H
Quelltexte
if(system(buffer) > 1)
{
fprintf(stderr, "ERROR: Temp.ls cannot be renamed!\n");
return -1;
}
}
return 0;
}
/*********************************************************************************************
Funktionsname: checkingRulesForBrackets
Rückgabewert
Typ:
int
Bedeutung:
Gibt die Anzahl der gefundenen Fehler zurück.
Parameter
Typ:
FILE *
Name:
LSFile
Bedeutung:
Zeiger auf die zu durchsuchende Datei.
Funktionsbeschreibung:
Diese Funktion prüft ob sich runde Klammern in nicht erlaubten Regelteilen befinden.
*********************************************************************************************/
int checkingRulesForBrackets(FILE *LSFile)
{
char
line[MAX_LS_LINE_LENGTH];
/* Speicherplatz zum einlesen einer Zeile. */
int
i, state, length,
/* state speichert den letzten gefundenen Regelteil,*/
/* dabei steht 1 für linker Kontext, 2 für rechter */
/* Kontext und 3 für den Bedingungsteil.
*/
errorRule
= 0,
/* Zähler zur Angabe in welcher Regel ein Fehler ist*/
bracket
= 0,
/* Speichert die Anzahl der gefundenen Klammern.
*/
errorCounter
= 0;
/* Zählt die Anzahl der gefundenen Fehler.
*/
/* Durchlaufe die Schleife bis zum Ende der Datei.
while(!feof(LSFile))
{
fgets(line, MAX_LS_LINE_LENGTH, LSFile);
length = strlen(line);
errorRule++;
state = 0;
/* Durchlaufe die Regel zeichenweise.
for(i = 0; i < length; i++)
{
*/
*/
/* Wurde eine Klammer gefunden?
if(line[i] == '(' || line[i] == ')')
bracket++;
*/
/* Linker Kontext gefunden?
else if(state == 0 && line[i] == '<')
{
*/
/* Wenn Klammern vorhanden, dann gebe eine Fehlermeldung aus! */
if(bracket > 0)
{
fprintf(stderr, "ERROR: There are brackets in the left
context of rule %d!\n", errorRule);
errorCounter++;
}
state++;
bracket = 0;
}
/* Rechter Kontext gefunden?
else if(state < 2 && line[i] == '>')
{
state = 2;
bracket = 0;
}
*/
/* Bedingungteil gefunden?
else if(line[i] == ':')
{
*/
144
H
Quelltexte
/* Wenn Klammern vorhanden (im r. Kontext), dann gebe eine Fehlermeldung aus! */
if(state == 2 && bracket > 0)
{
fprintf(stderr, "ERROR: There are brackets in the right
context of rule %d!\n", errorRule);
errorCounter++;
}
state = 3;
bracket = 0;
}
/* Hauptteil der Regel gefunden?
else if(line[i] == '-' && line[i+1] == '>')
{
*/
/* Klammern im rechten Kontext gefunden?
*/
if(state == 2 && bracket > 0)
{
fprintf(stderr, "ERROR: There are brackets in the right
context of rule %d!\n", errorRule);
errorCounter++;
}
/* Klammern im Bedinungsteil gefunden?
*/
else if(state == 3 && bracket > 0)
{
fprintf(stderr, "ERROR: There are brackets in the
condition of rule %d!\n", errorRule);
errorCounter++;
}
bracket = 0;
continue;
}
}
}
/* Die Anzahl der gefundenen Fehler wird zurück gegeben.
return errorCounter;
*/
}
/*********************************************************************************************
Funktionsname: checkingLSFile
Rückgabewert
Typ:
int
Bedeutung:
Gibt einen Fehlerstatus zurück (-1) oder
das kein Fehler aufgetreten ist (0).
Funktionsbeschreibung:
Prüft grob die LS-Datei ob alle wichtigen Werte angegeben wurden, Rekursionstiefe, Axiom, ...
*********************************************************************************************/
char checkingLSFile(void)
{
FILE
*LSFile
= NULL;
char
line[MAX_LS_LINE_LENGTH],
finishChecking = 0;
int
length, i;
/* Zeiger auf die LS-Datei
*/
/* Speicherung einer Zeile aus der LS-Datei */
/* Enthält den Status der Prüfung.
*/
if(!(LSFile = fopen(info.filename, "rt")))
{
fprintf(stderr, "ERROR: The file %s doesn't exists!\n", info.filename);
return -1;
}
/* Die Schleife prüft ob für folgende Parameter entsprechene Werte vorliegene:
/* Rekursionstiefe, Winkel, Anfangsdicke und Axiom
while((!feof(LSFile)) || (finishChecking < 4))
{
fgets(line, MAX_LS_LINE_LENGTH, LSFile);
/* Überspringe Kommentarzeilen
if(((line[0] == '/') && (line[1] == '*')) || (line[0] == '\n') ||
(line[0] == '#'))
145
*/
*/
*/
H
Quelltexte
continue;
/* Prüfe ob die ersten drei Werte Zahlen und keine Zeichen sind.
else if(finishChecking < 3)
{
length = strlen(line);
*/
for(i = 0; i < length; i++)
{
if((line[i] == ' ') || (line[i] == '\t') || (line[i] == '\n'))
continue;
else if((line[i] > 47) && (line[i] < 58))
break;
else
{
if(finishChecking == 0)
fprintf(stderr, "ERROR: In %s is not a recursion
depth define!\n", info.filename);
else if(finishChecking == 1)
fprintf(stderr, "ERROR: In %s is not a basic angel
define!\n", info.filename);
else
fprintf(stderr, "ERROR: In %s is not a starting
thickness define!\n", info.filename);
fclose(LSFile);
return -1;
}
}
finishChecking++;
}
/* Prüfe ob der vierte Wert ein richtiges Axiom ist und keine Regel.
*/
else if(finishChecking == 3)
{
length = strlen(line);
for(i = 0; i < length; i++)
{
if(((i+1) < length) && (line[i] == '-') && (line[i+1] == '>'))
{
fprintf(stderr, "In %s is not a axiom define!\n",
info.filename);
fclose(LSFile);
return -1;
}
}
finishChecking++;
}
}
/* Prüfen ob Klammern in den Regeln sind.
if(checkingRulesForBrackets(LSFile) > 0)
{
fclose(LSFile);
return -1;
}
*/
fclose(LSFile);
return 0;
}
/*********************************************************************************************
Funktionsname: Lparser
Rückgabewert
Typ:
int
Bedeutung:
Gibt einen Fehlerstatus zurück (-1) oder
das kein Fehler aufgetreten ist (0).
Funktionsbeschreibung:
Die Aufgabe der Funktion ist es Lparser aufzurufen
*********************************************************************************************/
char Lparser(void)
{
146
H
Quelltexte
FILE
*LOGFile
char
buffer[MAX_COMLINE],
line[MAX_LS_LINE_LENGTH];
linecounter
= 0;
int
= NULL;
/*
/*
/*
/*
/*
Zeiger auf die LOG-Datei zu Speicherung */
der Meldungen von Lparser.
*/
Zwischenspeicher
*/
Zwischenspeicher für eine Zeile der LOG-Datei*/
Zähler für die gelesenen Zeilen.
*/
strcpy(info.filename, (strlen(info.conv_filename) > 0 ? info.conv_filename :
info.filename));
/* Prüfe den Inhalt der LS-Datei.
if(checkingLSFile() < 0)
return -1;
*/
/* Es wird das vorhandensein der Lparser Datei geprüft.
if(!(LOGFile = fopen("lparser.exe", "rb")))
{
fprintf(stderr, "ERROR: Lparser cannot be found!\n");
return -1;
}
fclose(LOGFile);
*/
/* Ausführen von Lparser mit den entsprechenden Parametern.
*/
sprintf(buffer, "Lparser -vc -x%.2f -y%.2f -z%.2f %s > lparser.log", info.x, info.y,
info.z, info.filename);
system(buffer);
/* Lese die LOG-Datei ob ein Fehler aufgetreten ist und melde dies.
if(!(LOGFile = fopen("lparser.log", "rt")))
{
fprintf(stderr, "ERROR: Lparser.log doesn't exists!\n");
return -1;
}
*/
while(!feof(LOGFile))
{
linecounter++;
fgets(line, MAX_LS_LINE_LENGTH, LOGFile);
if((line[0] == 'E') && (line[1] == 'r') && (line[2] == 'r'))
{
fprintf(stderr, "ERROR: There is a error in rule %d !\n",
linecounter-11);
return -1;
}
}
fclose(LOGFile);
return 0;
}
/*********************************************************************************************
Funktionsname: Lv2povid
Rückgabewert
Typ:
int
Bedeutung:
Gibt einen Fehlerstatus zurück (-1) oder
das kein Fehler aufgetreten ist (0).
Funktionsbeschreibung:
Lv2povid ruft das gleichnamige Programm auf, um eine POV-Datei zu erzeugen.
*********************************************************************************************/
char Lv2povid(void)
{
FILE
*file
char
buffer[MAX_COMLINE];
= NULL;
/* Prüfe ob alle wichtigen Dateien vorhanden sind.
if(!(file = fopen("output.inc", "rt")))
{
fprintf(stderr, "ERROR: There is no output.inc file for LV2POVID!\n");
return -1;
}
fclose(file);
147
*/
H
Quelltexte
if(!(file = fopen("info.txt", "rt")))
{
fprintf(stderr, "ERROR: There is no info.txt file for LV2POVID!\n");
return -1;
}
fclose(file);
if(!(file = fopen("lv2povid.cfg", "rt")))
{
fprintf(stderr, "ERROR: There is no lv2povid.cfg file for LV2POVID!\n");
return -1;
}
fclose(file);
/* Rufe LV2POVID auf.
sprintf(buffer, "LV2POVID 2 > lv2povid.log");
if(system(buffer) > 1)
{
fprintf(stderr, "ERROR: Cannot execute LV2POVID!\n");
return -1;
}
*/
return 0;
}
/*********************************************************************************************
Funktionsname: extractVectorFromLine
Parameter
Typ:
char *
Name:
string
Bedeutung:
Die Zeile aus der die Daten extrahiert werden sollen.
Typ:
double *
Name:
vector
Bedeutung:
Zeiger auf ein double-Array in dem die Vektordaten gespeichert
werde.
Funktionsbeschreibung:
Extrahiert aus einer Zeile die Informationen für eine Vektor.
*********************************************************************************************/
void extractVectorFromLine(char *string, double *vector)
{
char
puffer[25],
VectorPosition
= 0,
i, j
= 0,
startRecording
= 0;
int
len
= strlen(string);
for(i = 0; i < len; i++)
{
if(string[i] == '(')
startRecording = 1;
else if(string[i] == ' ' && startRecording == 1)
{
puffer[j+1] = '\0';
vector[VectorPosition++] = atof(puffer);
j = 0;
}
else if(string[i] == ')' && startRecording == 1)
{
puffer[j+1] = '\0';
vector[VectorPosition] = atof(puffer);
break;
}
else if(startRecording == 1)
puffer[j++] = string[i];
}
}
/*********************************************************************************************
Funktionsname: CorrectingPOVFile
Rückgabewert
Typ:
char
148
H
Quelltexte
Bedeutung:
Gibt einen Fehlerstatus zurück (-1) oder
das kein Fehler aufgetreten ist (0).
Funktionsbeschreibung:
Die von LV2POVID erzeugt POV-Datei enthält fehlerhafte Daten in bezug auf Kameraposition und Kameraausrichtung, sowie der Lichtquelleposition. Daher wird aus der INFO.TXT
die Daten herausgelesen und eine neue POV-Datei erzeugt.
*********************************************************************************************/
char CorrectingPOVFile(void)
{
FILE
*pov_file
FILE
*info_file
char
zeile[MAX_POV_LINE_LENGTH];
short i;
double CamVector[3],
CenterVector[3];
= NULL;
= NULL;
if(!(pov_file = fopen("Lpar2pov.pov", "wt")))
{
fprintf(stderr, "ERROR: Cannot open Lpar2pov.pov!\n");
return -1;
}
if(!(info_file = fopen("INFO.TXT", "rt")))
{
fprintf(stderr, "ERROR: Cannot open info.txt!\n");
return -1;
}
for(i = 0; i < 11; i++)
fgets(zeile, MAX_POV_LINE_LENGTH, info_file);
extractVectorFromLine(zeile, CamVector);
fgets(zeile, MAX_POV_LINE_LENGTH, info_file);
extractVectorFromLine(zeile, CenterVector);
fprintf(pov_file,
fprintf(pov_file,
fprintf(pov_file,
fprintf(pov_file,
fprintf(pov_file,
"# include \"colors.inc\"\n");
"# include \"textures.inc\"\n");
"# include \"shapes.inc\"\n\n");
"camera{\n");
" location < %.4f , %.4f , %.4f >\n", CamVector[0], CamVector[2],
CamVector[1]);
fprintf(pov_file, " look_at < %.4f , %.4f , %.4f >\n", CenterVector[0],
CenterVector[2], CenterVector[1]);
fprintf(pov_file, "}\n\n");
fprintf(pov_file, "#declare col_0 = colour red 1.0 green 1.0 blue 1.0;\n");
fprintf(pov_file, "#declare col_1 = colour red 0.8 green 0.498039 blue 0.196078;\n");
fprintf(pov_file, "#declare col_2 = colour red 1.0;\n");
fprintf(pov_file, "#declare col_3 = colour red 1.0 green 1.0;\n");
fprintf(pov_file, "#declare col_4 = colour green 1.0;\n");
fprintf(pov_file, "#declare col_5 = colour blue 1.0 green 1.0;\n");
fprintf(pov_file, "#declare col_6 = colour blue 1.0;\n");
fprintf(pov_file, "#declare col_7 = colour red 1.0 blue 1.0;\n");
fprintf(pov_file, "#declare col_8 = colour red 0.439216 green 0.858824 blue
0.576471;\n");
fprintf(pov_file, "#declare col_9 = colour red 1.0 green 0.498039 blue 0.0;\n");
fprintf(pov_file, "#declare col_10 = colour red 0.258824 green 0.258824 blue
0.435294;\n");
fprintf(pov_file, "#declare col_11 = colour red 0.6 green 0.196078 blue 0.8;\n");
fprintf(pov_file, "#declare col_12 = colour red 0.439216 green 0.576471 blue
0.858824;\n");
fprintf(pov_file, "#declare col_13 = colour red 0.556863 green 0.137255 blue
0.137255;\n");
fprintf(pov_file, "#declare col_14 = colour red 0.858824 green 0.858824 blue
0.439216;\n");
fprintf(pov_file, "#declare col_15 = colour red 0.623529 green 0.623529 blue
0.372549;\n");
fprintf(pov_file, "# include \"Lpar2pov.inc\"\n\n");
fprintf(pov_file, "object { Lsystem }\n\n");
fprintf(pov_file, "light_source { < %.4f , %.4f , %.4f > color White }\n",
CamVector[0]*info.light_x, CamVector[2]*info.light_z,
CamVector[1]*info.light_y);
//fprintf(pov_file, "light_source { < %.4f , %.4f , %.4f > color Gray60 }\n", );
fclose(pov_file);
fclose(info_file);
return 0;
}
149
H
Quelltexte
Dateiname: pov.h
/*********************************************************************************************
Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
09. 06. 04
[email protected]
Lprocess
1.0
Modulename:
pov.h
Modulversion:
1.0
Modulbeschreibung:
Header-Datei zum Modul pov.c.
*********************************************************************************************/
#ifndef
___pov_H___
#define ___pov_H___
extern void startPOV(void);
extern void endPOV(void);
extern char startQPOV(void);
#endif
Dateiname: pov.c
/*********************************************************************************************
Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
09. 06. 04
[email protected]
Lprocess
1.0
Modulename:
pov.c
Modulversion:
1.0
Modulbeschreibung:
Alle Funktionen die für den Prozess der Bilderzeugung verwendet werden, sind hier enthalten.
*********************************************************************************************/
/*********************************************************************************************
INCLUDE-DATEIEN
*********************************************************************************************/
#include <stdio.h>
#include <Windows.h>
#include "globals.h"
/*********************************************************************************************
Funktionsname: startPOV
Rückgabewert
Typ:
int
Bedeutung:
Gibt einen Fehlerstatus zurück (-1) oder
das kein Fehler aufgetreten ist (0).
Funktionsbeschreibung:
Prüft ob eine Instanz von POV-Ray schon läuft und startet gegebenenfalls eine Instanz.
*********************************************************************************************/
void startPOV(void)
{
char
buffer[MAX_COMLINE];
int
len
= strlen(info.pathPOV);
if(len == 1)
sprintf(buffer, "pvengine.exe");
else if(info.pathPOV[len-1] == '\\')
sprintf(buffer, "%spvengine.exe", info.pathPOV);
150
H
Quelltexte
else
sprintf(buffer, "%s\\pvengine.exe", info.pathPOV);
if(!FindWindow(POV_APP,0))
{
puts("Starting POV-Ray ...");
WinExec(buffer,SW_HIDE);
}
}
/*********************************************************************************************
Funktionsname: endPOV
Funktionsbeschreibung:
Es wird geprüft ob eine vorhandenen Instanz von POV-Ray läuft und beendet diese entsprechend.
*********************************************************************************************/
void endPOV(void)
{
HWND hPrg;
if((hPrg = FindWindow(POV_APP,0)) != NULL)
{
puts("Closing POV-Ray instance ...");
SendMessage(hPrg,WM_CLOSE,0,0);
}
}
/*********************************************************************************************
Funktionsname: getFilenameWithoutExt
Parameter
Typ:
char *
Name:
filename
Bedeutung:
String des Dateinamens
Typ:
char *
Name:
modfilename
Bedeutung:
Zeiger auf ein String, in dem der neue Dateiname gespeichert
wird.
Funktionsbeschreibung:
Liefert einen String vom Dateinamen, nur ohne die Endung.
*********************************************************************************************/
void getFilenameWithoutExt(char *filename, char *modfilename)
{
char
buffer[MAX_FILENAME_LENGTH];
int
i, len
= strlen(filename)-3;
for(i = 0; i < len; i++)
buffer[i] = filename[i];
buffer[i] = '\0';
sprintf(modfilename, "%s", buffer);
}
/*********************************************************************************************
Funktionsname: createINI
Rückgabewert
Typ:
int
Bedeutung:
Gibt einen Fehlerstatus zurück (-1) oder
das kein Fehler aufgetreten ist (0).
Funktionsbeschreibung:
Erzeugt eine INI-Datei für POV-Ray mit entsprechend gesetzten Optionen.
*********************************************************************************************/
char createINI(void)
{
151
H
Quelltexte
FILE
char
*INIFile
imageFilename[MAX_FILENAME_LENGTH];
= NULL;
if(!(INIFile = fopen("pov.ini", "wt")))
{
fprintf(stderr, "ERROR: POV.INI cannot create for QuietPOV!\n");
return -1;
}
fprintf(INIFile, "Width=%d\n", info.width);
fprintf(INIFile, "Height=%d\n", info.height);
fprintf(INIFile, "Display=off\n");
if((info.state & TARGA_FILE) == TARGA_FILE)
fprintf(INIFile, "Output_File_Type=T\n");
else
fprintf(INIFile, "Output_File_Type=S\n");
if((info.state & ANTI_ALIASING) == ANTI_ALIASING)
fprintf(INIFile, "Antialias=on\n");
else
fprintf(INIFile, "Antialias=off\n");
fprintf(INIFile, "Input_File_Name=Lpar2pov.pov\n");
getFilenameWithoutExt(info.filename, imageFilename);
fprintf(INIFile, "Output_File_Name=%s\n", imageFilename);
fclose(INIFile);
return 0;
}
/*********************************************************************************************
Funktionsname: startQPOV
Rückgabewert
Typ:
int
Bedeutung:
Gibt einen Fehlerstatus zurück (-1) oder
das kein Fehler aufgetreten ist (0).
Funktionsbeschreibung:
Startet QuietPOV, welches wiederum über die GUIExtesion von POV-Ray ein Kommando sendet, zur Erzeugung eines Bildes mit den Optionen, die in der INI-Datei enthalten sind.
*********************************************************************************************/
char startQPOV(void)
{
FILE
*QPOVFile
char
buffer[MAX_COMLINE];
int
len
= NULL;
= strlen(info.pathQPOV);
if(len == 1)
sprintf(buffer, "quietpov.exe");
else if(info.pathQPOV[len-1] == '\\')
sprintf(buffer, "%squietpov.exe", info.pathQPOV);
else
sprintf(buffer, "%s\\quietpov.exe", info.pathQPOV);
if((QPOVFile = fopen(buffer, "rb")) == NULL)
{
fprintf(stderr, "ERROR: QuietPOV cannot be found!\n");
return -1;
}
else
fclose(QPOVFile);
puts("Creating INI-File for QuietPOV ...");
if(createINI() == -1)
return -1;
puts("Rendering image ...");
sprintf(buffer, "quietpov -start pov.ini > pov.log");
if((system(buffer)) > 1)
{
fprintf(stderr, "ERROR: Cannot find QuietPOV!\n");
return -1;
}
152
H
Quelltexte
puts("Finishing Rendering ...");
return 0;
}
Quelltext von Lparser (lparser.c):
Die modifizierten Stellen sind fett hervorgehoben.
/* ------------------------------------------------------------------------LPARSER, L-System Parser/Mutator
------------------------------------------------------------------------Laurens Lapre
[email protected]
http://www.xs4all.nl/~ljlapre/
Modified by Ken Kopp 7/30/2000
[email protected]
Modified by Jan Derer 06/09/2004
[email protected]
------------------------------------------------------------------------*/
// --------------------------------- Includes ---------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
<stdio.h>
<iostream.h>
<float.h>
<stdlib.h>
<string.h>
<stdarg.h>
<math.h>
<malloc.h>
<ctype.h>
<time.h>
//////////////////////////////////////////////////////////////////////////////////////////////
////
/*
Adding global variables and prototyps of functions
*/
/*
by Jan Derer
*/
double maxX = 0.0,
maxY = 0.0,
maxZ = 0.0,
minX = 0.0,
minY = 0.0,
minZ = 0.0,
x, y, z;
void createINFOTXT(void);
//
//
//
//
//
//
//
Saving
Saving
Saving
Saving
Saving
Saving
Values
the maximum
the maximum
the maximum
the maximum
the maximum
the maximum
to multiply
positiv
positiv
positiv
negativ
negativ
negativ
value
value
value
value
value
value
of
of
of
of
of
of
a
a
a
a
a
a
x-coordinate
y-coordinate
z-coordinate
x-coordinate
y-coordinate
z-coordinate
// Writing INFO.TXT for LV2POVID with the best cameraposition
//////////////////////////////////////////////////////////////////////////////////////////////
////
// -------------------------------- Constants added by Ken --------------------------
#define PRED_L
30
#define SUCC_L
#define COND_L
#define VAR_L
500
30
10
#define
#define
#define
#define
1000
2*1024*1024
1000
10
MAX_RULES
MAX_OBJ_LEN
MAX_LIN_LEN
MAX_VARS
#define MAX_EXP_LEN
#define MAX_NUM_LEN
100
10
//
//
//
//
//
Max length
contexts
Max length
Max length
Max length
//
//
//
//
Max
Max
Max
Max
of a rule's predecessor, and previous and next
of a rule's successor string
of a rule's condition
of a variable in a parametric rule
number
length
length
number
of
of
of
of
rules in the file
object (production) string
a line in the file
variables in a parametric rule
// Max length of an expression in a parametric rule
// Max length of a real number
153
H
Quelltexte
#define MAX_CONST
#define MAX_SUCC
#define REC_DEPTH
10
10
// Max number of constants
// Max number of successors
3
// Depth of recursion
// ----------------------------- Class definitions added by Ken -------------------------
class Rule_Type {
protected:
char Pred[PRED_L];
char *Succ[MAX_SUCC];
double Probs[MAX_SUCC];
char *Prev_Ctx, *Next_Ctx;
char *Cond;
int N_Param;
int N_Succ;
//
//
//
//
//
//
//
The main block and replacement string
The array of possible successors
The probability distribution array
Pointers to previous and next contexts
Pointer to the condition
The total number of parameters in the rule
The number of possible successors
int Block_Len, Prev_Len;
public:
Rule_Type(char *Prd, char *Prv, char *Nxt, char *Cnd, char *Suc, double Prb,int N_Par);
~Rule_Type();
void Add_Succ(char *Suc, double Prb);
bool Same_Rule(char *Prd, char *Prv, char *Nxt, char *Cnd);
bool Rule_Applies(long i);
bool Condition_Met(long i);
int Do_Replace(long i);
int Prd_Len() { return strlen(Pred); }
int Get_Pred(char *Str);
int Get_Pred2(char *Str);
bool Insert(char *Str, int N_Args, bool App);
void Replace(char *Str1, char *Str2);
void Swap_Dirs();
void Swap_Sizes();
void Rand_Expr();
};
// ------------------------------- Global variables added by Ken --------------------------
int N_Rules = 0;
char Object_Str[MAX_OBJ_LEN];
char New_Str[MAX_OBJ_LEN];
long Obj_Len;
Rule_Type *Rules[MAX_RULES];
int N_Const = 0;
char Const_Names[MAX_CONST][VAR_L];
char Const_Vals[MAX_CONST][MAX_NUM_LEN];
char Ign_Chars[MAX_NUM_LEN];
/* Basic types --------------------------------------------------------------------------- */
/* Simple types */
#define u8
#define u16
#define u32
#define s8
#define s16
#define s32
#define r32
#define r64
unsigned char
unsigned short int
unsigned long int
signed char
signed short int
signed long int
float
double
/* My own boolean type */
#define boolean
s16
#ifndef TRUE
#define TRUE
(s16) 1
#endif
154
H
Quelltexte
#ifndef FALSE
#define FALSE
#endif
(s16) 0
/* Constants ---------------------------------------------------------------------------- */
/* Max char size of filename and large string */
#define max_file
512
/* Max vectors per polygon */
#define vectors_p_poly
15
/* Max polygons per object */
#define max_p_object
400
/* Max size of the [] and {} stacks during drawing */
#define max_stack
1024L
/* Version id for the VOL file format */
#define VersionID
11
/* Vector indices */
#define _x
#define _y
#define _z
/* Some
#define
#define
#define
#define
#define
#define
#define
#define
0
1
2
often used consts */
zero
(r32) 0.0
one
(r32) 1.0
half_pi
(r32) 1.570796
pi
(r32) 3.141592
two_pi
(r32) 6.283185
LF
((char) 10)
CR
((char) 13)
min_bar
"---------------------------------------------------------"
/* Bounding space for floats */
#define float_min
(r32) -1e30
#define float_max
(r32) 1e30
/* Array and vector types ------------------------------------------------- */
/* A large string */
typedef char
string_file[max_file];
/* Vector arrays */
typedef r32
typedef vector
typedef vector
typedef r32
vector[3];
vectors_4[4];
vectors_max[vectors_p_poly];
float_array[max_p_object + 1];
/* Polygon arrays */
typedef s16
typedef polygon_type
polygon_type[4];
polygon_array[max_p_object + 1];
/* Intrinsics ------------------------------------------------------------- */
#define
#define
#define
#define
Abs_s16(A)
Abs_s32(A)
Abs_r32(A)
Abs_r64(A)
#define MIN(A,B)
#define MAX(A,B)
#define
#define
#define
#define
((s16)
((s32)
((r32)
((r64)
abs((A)))
abs((A)))
fabs((A)))
fabs((A)))
(((A) < (B)) ? (A) : (B))
(((A) > (B)) ? (A) : (B))
Vector_copy_max_r32(n,A,B)
Vector_copy_r32(A,B)
Mem_copy(A,B,C)
Mem_clear(A,B,C)
memcpy(((void
memcpy(((void
memcpy(((void
memset(((void
*)
*)
*)
*)
(B)),
(B)),
(B)),
(A)),
((void *) (A)), n*12L)
((void *) (A)),
12L)
((void *) (A)), (C))
(C), (B))
/* Vector utils and inlines ----------------------------------------------- */
155
H
Quelltexte
#define
#define
#define
#define
#define
#define
Clip_low(a,b)
Clip_high(a,b)
Clamp(a,b,c)
Wrap(a,b,c)
Lerp(a,b,c)
Swap(a,b)
a = ((a) < (b)) ? (b) : (a)
a = ((a) > (b)) ? (b) : (a)
a = ((a) < (b)) ? (b) : ((a) > (c)) ? (c) : (a)
a = ((a) < (b)) ? (a) + (c) : ((a) > (c)) ? (a) - (c) : (a)
((b) + (((c) - (b)) * (a)))
{(a) ^= (b); (b) ^= (a); (a) ^= (b);}
#define Vector_length(A)
((r32) sqrt(
(r32)(A[_x] * A[_x])\
+ (r32)(A[_y] * A[_y])\
+ (r32)(A[_z] * A[_z]) ))
#define Vector_equal(A,B)
((A[_x] == B[_x]) && (A[_y] == B[_y]) && (A[_z] == B[_z]))
#define Scalar_product(A,B) (A[_x] * B[_x] + A[_y] * B[_y] + A[_z] * B[_z])
#define
A[_x]
A[_y]
A[_z]
}
Vector_make(A,a,b,c) {\
= a;\
= b;\
= c;\
#define Vector_break(A,a,b,c) {\
a = A[_x];\
b = A[_y];\
c = A[_z];\
}
#define
A[_x]
A[_y]
A[_z]
}
Vector_lerp(a,A,B) {\
= Lerp(a, A[_x], B[_x]);\
= Lerp(a, A[_y], B[_y]);\
= Lerp(a, A[_z], B[_z]);\
#define Vector_normalize(A)\
{ r32 Dist = (r32) 1.0 / Vector_length(A);\
\
A[_x] *= Dist;\
A[_y] *= Dist;\
A[_z] *= Dist;\
}
#define
B[_x]
B[_y]
B[_z]
}
Vector_copy(A,B) {\
= A[_x];\
= A[_y];\
= A[_z];\
#define
C[_x]
C[_y]
C[_z]
}
Vector_product(A,B,C) {\
= A[_y] * B[_z] - A[_z] * B[_y];\
= A[_z] * B[_x] - A[_x] * B[_z];\
= A[_x] * B[_y] - A[_y] * B[_x];\
#define
C[_x]
C[_y]
C[_z]
}
Vector_min(A,B,C) {\
= A[_x] - B[_x];\
= A[_y] - B[_y];\
= A[_z] - B[_z];\
#define
C[_x]
C[_y]
C[_z]
}
Vector_plus(A,B,C) {\
= A[_x] + B[_x];\
= A[_y] + B[_y];\
= A[_z] + B[_z];\
#define
A[_x]
A[_y]
A[_z]
}
Vector_dec(A,B) {\
-= B[_x];\
-= B[_y];\
-= B[_z];\
#define
A[_x]
A[_y]
A[_z]
}
Vector_neg(A) {\
= (-A[_x]);\
= (-A[_y]);\
= (-A[_z]);\
156
H
Quelltexte
#define
A[_x]
A[_y]
A[_z]
}
Vector_inc(A,B) {\
+= B[_x];\
+= B[_y];\
+= B[_z];\
#define
C[_x]
C[_y]
C[_z]
}
Vector_plus_fac(A,B,t,C) {\
= A[_x] + (t) * B[_x];\
= A[_y] + (t) * B[_y];\
= A[_z] + (t) * B[_z];\
#define
D[_x]
D[_y]
D[_z]
}
Vector_plus_fac2(A,B,b,C,c,D)
= A[_x] + (b) * B[_x] + (c) *
= A[_y] + (b) * B[_y] + (c) *
= A[_z] + (b) * B[_z] + (c) *
#define
C[_x]
C[_y]
C[_z]
}
Vector_combine(A,a,B,b,C) {\
= (a) * A[_x] + (b) * B[_x];\
= (a) * A[_y] + (b) * B[_y];\
= (a) * A[_z] + (b) * B[_z];\
#define
A[_x]
A[_y]
A[_z]
}
Vector_add(A,d) {\
+= d;\
+= d;\
+= d;\
#define
A[_x]
A[_y]
A[_z]
}
Vector_sub(A,d) {\
-= d;\
-= d;\
-= d;\
#define
A[_x]
A[_y]
A[_z]
}
Vector_div(A,d) {\
/= d;\
/= d;\
/= d;\
#define
A[_x]
A[_y]
A[_z]
}
Vector_mul(A,d) {\
*= d;\
*= d;\
*= d;\
{\
C[_x];\
C[_y];\
C[_z];\
/* Vector procs ----------------------------------------------------------- */
static vector
M1, M2, M3;
r32
Do_angle(r32 x1, r32 y1, r32 x2, r32 y2)
{
r32
/* The current movetransform matrix */
/*
*
*
*
Calculate the angle between
x-axis and x1,y1 -> x2,y2. It
can handle all kinds of weird
exceptions */
temp, x, y;
x = x2 - x1;
y = y2 - y1;
if (x == zero) {
if (y < zero)
temp = -half_pi;
else
temp = half_pi;
} else {
temp = atan(y / x);
if (x < zero) {
if (y < zero)
temp = -pi + temp;
else
temp = pi + temp;
157
H
Quelltexte
}
}
if (Abs_r32(temp) < (r32) 0.0001)
temp = zero;
if (temp < zero)
temp += two_pi;
else if (temp > two_pi)
temp -= two_pi;
return temp;
}
void
Move_transform(vector v)
{
/* Transform the vector according
* to the current movetransform
* matrix */
vector
t;
Vector_copy_r32(v, t);
v[_x] = Scalar_product(M1, t);
v[_y] = Scalar_product(M2, t);
v[_z] = Scalar_product(M3, t);
}
void
Set_move_transform(r32 a, vector no)
{
/* Set a movetransformation matrix
* based on an angle rotation of
* 'a' around the vector 'no' */
n11, n22, n33, nxy, nxz, nyz, sina, cosa;
r32
cosa = (r32) cos(a);
sina = (r32) sin(a);
n11 = no[_x] * no[_x];
n22 = no[_y] * no[_y];
n33 = no[_z] * no[_z];
nxy = no[_x] * no[_y];
nxz = no[_x] * no[_z];
nyz = no[_y] * no[_z];
M1[_x] = n11 + (one - n11) * cosa;
M1[_y] = nxy * (one - cosa) - no[_z] * sina;
M1[_z] = nxz * (one - cosa) + no[_y] * sina;
M2[_x] = nxy * (one - cosa) + no[_z] * sina;
M2[_y] = n22 + (one - n22) * cosa;
M2[_z] = nyz * (one - cosa) - no[_x] * sina;
M3[_x] = nxz * (one - cosa) - no[_y] * sina;
M3[_y] = nyz * (one - cosa) + no[_x] * sina;
M3[_z] = n33 + (one - n33) * cosa;
}
/* File and conio procs --------------------------------------------------- */
static boolean
void
Set_lowhigh(boolean b)
{
native_mode = TRUE;
/* TRUE native Intel mode Low-High,
* FALSE High-Low */
native_mode = b;
}
void
User_error(char *s,...)
{
/* Displays and error messages and
158
H
Quelltexte
* exits the program */
string_file
va_list
buf;
args;
va_start(args, s);
vsprintf(buf, s, args);
va_end(args);
fprintf(stdout, "\n\nError: %s\n\n", buf);
fflush(stdout);
exit(EXIT_FAILURE);
}
void
Message(char *s,...)
{
string_file
va_list
/* Sends a message to the output
* stream */
buf;
args;
va_start(args, s);
vsprintf(buf, s, args);
va_end(args);
fprintf(stdout, "%s", buf);
fflush(stdout);
fflush(stdout);
}
void
Fget_bin_r32(FILE * f, r32 *val)
{
/* Get a r32 value, check for order */
s32
temp, ta, tb, tc, td;
r32 *tempr = ((r32 *) ((void *) &temp));
ta
tb
tc
td
=
=
=
=
(s32)
(s32)
(s32)
(s32)
getc(f);
getc(f);
getc(f);
getc(f);
if (native_mode) {
temp = td << 8;
temp += tc;
temp = temp << 8;
temp += tb;
temp = temp << 8;
temp += ta;
} else {
temp = ta << 8;
temp += tb;
temp = temp << 8;
temp += tc;
temp = temp << 8;
temp += td;
}
*val = *tempr;
}
void
Fget_bin_s16(FILE * f, s16 *val)
{
s32 ta, tb;
/* Get a s16 value, check for order */
ta = (s32) getc(f);
tb = (s32) getc(f);
if (native_mode) {
*val = (s16) tb << 8;
*val += (s16) ta;
} else {
*val = (s16) ta << 8;
*val += (s16) tb;
}
}
159
H
Quelltexte
void
Fget_bin_s32(FILE * f, s32 *val)
{
s32 ta, tb, tc, td;
ta
tb
tc
td
=
=
=
=
(s32)
(s32)
(s32)
(s32)
/* Get a s32 value, check for order */
getc(f);
getc(f);
getc(f);
getc(f);
if (native_mode) {
*val = td << 8;
*val += tc;
*val = *val << 8;
*val += tb;
*val = *val << 8;
*val += ta;
} else {
*val = ta << 8;
*val += tb;
*val = *val << 8;
*val += tc;
*val = *val << 8;
*val += td;
}
}
void
Fget_bin_u16(FILE * f, u16 *val)
{
s32 ta, tb;
/* Get a u16 value, check for order */
ta = (s32) getc(f);
tb = (s32) getc(f);
if (native_mode) {
*val = (u16) tb << 8;
*val += (u16) ta;
} else {
*val = (u16) ta << 8;
*val += (u16) tb;
}
}
void
Fget_bin_s8(FILE * f, s8 *val)
{
*val = (s8) getc(f);
}
void
Fget_bin_u8(FILE * f, u8 *val)
{
*val = (u8) getc(f);
}
void
Fget_bin_u32(FILE * f, u32 *val)
{
s32 ta, tb, tc, td;
ta
tb
tc
td
=
=
=
=
(s32)
(s32)
(s32)
(s32)
/* Get a u32 value, check for order */
getc(f);
getc(f);
getc(f);
getc(f);
if (native_mode) {
*val = td << 8;
*val += tc;
*val = *val << 8;
*val += tb;
160
H
Quelltexte
*val
*val
} else {
*val
*val
*val
*val
*val
*val
}
= *val << 8;
+= ta;
= ta << 8;
+= tb;
= *val << 8;
+= tc;
= *val << 8;
+= td;
}
void
Fput_bin_u8(FILE * f, u8 r)
{
if (fwrite(&r, sizeof(u8), 1, f) != 1)
User_error("Can't continue writing outputfile");
}
void
Fput_bin_s8(FILE * f, s8 r)
{
if (fwrite(&r, sizeof(s8), 1, f) != 1)
User_error("Can't continue writing outputfile");
}
void
Fput_bin_r32(FILE * f, r32 r)
{
if (fwrite(&r, sizeof(r32), 1, f) != 1)
User_error("Can't continue writing outputfile");
}
void
Fput_bin_s16(FILE * f, s16 r)
{
if (fwrite(&r, sizeof(s16), 1, f) != 1)
User_error("Can't continue writing outputfile");
}
void
Fput_bin_u16(FILE * f, u16 r)
{
if (fwrite(&r, sizeof(u16), 1, f) != 1)
User_error("Can't continue writing outputfile");
}
void
Fput_bin_s32(FILE * f, s32 r)
{
if (fwrite(&r, sizeof(s32), 1, f) != 1)
User_error("Can't continue writing outputfile");
}
void
Fput_bin_u32(FILE * f, u32 r)
{
if (fwrite(&r, sizeof(u32), 1, f) != 1)
User_error("Can't continue writing outputfile");
}
/* File buffer procs ------------------------------------------------------ */
/* Sinces there is a lot of fileio we use large buffers */
#define f_buffer_size 30L * 1024L
static char
*f_buffer[3] = {NULL, NULL, NULL};
void
161
H
Quelltexte
Buffer_IO(FILE * f, s16 i)
{
/* Attach filebuffer i to open file */
setvbuf(f, f_buffer[i], _IOFBF, f_buffer_size);
}
void
Init_file_buf(s16 i)
{
/* Init filebuffer i */
if (f_buffer[i] != NULL)
return;
f_buffer[i] = (char *) malloc(f_buffer_size);
if (f_buffer[i] == NULL)
User_error("Not enough memory to allocate file buffer");
}
/* Feedback percentage counter -------------------------------------------- */
/* These vars are used to calculate the percentages counters for feedback */
static r32
bar_fac2 = zero;
static s16
old_bar2 = 0;
static u32
bar_max2 = 0;
void
Process_start2(u32 max)
{
/* Start bar 2 with the maximum
* value it's going to get */
bar_fac2 = 100.0 / (r32) max;
bar_max2 = max;
}
void
Process_update2(u32 now)
{
/* Update the percentage counter
* when needed */
s16 bar = (s16) (bar_fac2 * (r32) now);
if (bar != old_bar2) {
old_bar2 = bar;
Message("\r%3d%%\r", bar);
}
}
void
Process_end2(void)
{
Message("\r
\r");
}
/* Close bar */
/* Comline procs ---------------------------------------------------------- */
#define OPTCHAR '-'
static
static
static
static
static
static
char
char
int
char
s16
char
empty[] = "";
*optarg = empty;
optind = 1, opterr = 1;
opts[150] = "";
/* option string */
s_argc = 0;
/* pointers to comline */
**s_argv = NULL;
static int
getopt(int argc, char *argv[], const char *optstring)
{
/* Taken from a source lib
* somewhere */
static char
*in_pointer = empty;
char
*find;
if (!*in_pointer) {
if ((!argv[optind]) || (optind >= argc) || (argv[optind][0] != OPTCHAR))
return -1;
162
H
Quelltexte
in_pointer = argv[optind];
in_pointer++;
optind++;
if (*in_pointer == OPTCHAR) {
return -1;
}
};
if (*in_pointer == '\0') {
return 0;
};
find = strchr(optstring, *in_pointer);
if (find == NULL) {
if (opterr)
User_error("Option -%c not known", *in_pointer);
in_pointer = empty;
return '?';
};
if (*(find + 1) == ':') {
if (!*(in_pointer + 1)) {
if (optind >= argc) {
if (opterr)
User_error("No argument for option -%c", *in_pointer);
optarg = empty;
} else {
if (argv[optind][0] == OPTCHAR) {
if (opterr)
User_error("No argument for option -%c but found %s instead",
*in_pointer, argv[optind]);
}
optarg = argv[optind++];
}
} else {
optarg = ++in_pointer;
}
in_pointer = empty;
} else {
optarg = empty;
in_pointer++;
}
return *find;
}
void
Get_comline_opt(char *c, boolean * found, char *result)
{
/* Check if comline option 'c' has
* been used and return parameter
* if any */
int
f, argc;
char
**argv;
argc = s_argc;
argv = s_argv;
optind = 1;
while ((f = getopt(argc, argv, opts)) != -1) {
if (f == *c) {
strcpy(result, optarg);
*found = TRUE;
return;
}
};
*found = FALSE;
strcpy(result, "");
}
void
Get_comline_filename(char *c)
{
int
/* Get the filename argument from
* the comline */
argc;
163
H
Quelltexte
char
**argv;
argc = s_argc;
argv = s_argv;
optind = 1;
while (getopt(argc, argv, opts) != -1);
if (optind == argc)
User_error("Ran out of arguments before finding file name");
strcpy(c, argv[optind]);
}
void
Get_comline_progname(char *c)
{
/* Get the program name from the
* comline */
strcpy(c, s_argv[0]);
}
/* Main lparser vars ------------------------------------------------------ */
/* Settings stack used for solving [] references */
typedef struct s_rec {
vector
pos;
/* position in 3space of turtle
* origin */
vector
fow;
/* forward direction */
vector
vector
vector
lef;
upp;
last;
vector
last_v[9];
r32
r32
r32
r32
r32
s16
s16
} s_rec;
dis;
ang;
thick;
dis2;
tr;
col;
last_col;
/*
/*
/*
*
/*
*
/*
/*
/*
/*
/*
/*
/*
left direction */
up direction */
last position used for
connecting cylinders */
last vertices of object used for
connecting cylinders */
value of F distance */
value of basic angle */
value of thickness */
value of Z distance */
trope value */
current color */
color of last object */
/* Polygon stack used for solving {} references */
typedef struct p_rec {
s16
count;
/* number of vertices */
vector
*ver;
/* vertex store */
} p_rec;
/* Flags */
static boolean
static boolean
static boolean
static boolean
static boolean
static boolean
static boolean
static boolean
static boolean
static boolean
static boolean
static boolean
static boolean
static boolean
static boolean
/* Init vars */
static r32
static r32
static u32
static u32
static s16
static r32
trope_set = FALSE; /*
rand_set = FALSE;
user_form = FALSE;
closed_form = FALSE;
pov_form = FALSE;
pov_form2 = FALSE;
pov_form3 = FALSE;
blb_form = FALSE;
inc_out = FALSE;
dxf1 = FALSE;
dxf2 = FALSE;
dxf3 = FALSE;
vrml = FALSE;
growing = FALSE;
/*
last_recur = FALSE; /*
*
see at comline scannign */
real is used for recursion level */
processing the last recursion
step */
zmin = 1e30, thick, min_thick = zero, rand_amount = zero;
trope_amount = zero;
polcount = 0;
poly_limit = 500000L, max_string;
num = 0, col = 2, lev, last_col = 0;
dis, ang, dis2, tr = 0.2;
164
H
Quelltexte
static
static
static
static
vector
vector
r32
vector
sky = {0.0, 0.0, 1.0}, trope;
last = {1.0, 1.0, 1.0}, last_v[9];
recursion, fraction;
axis_x, axis_y, axis_z;
/* Stacks [] and {} */
static s_rec
*stack, org, save;
static s16
scount = 0;
static p_rec
*pstack;
static s16
pscount = 0;
/* Current active transform matrix for drawing */
static vector
C1, C2, C3;
/* Var for rnd */
static r32
/* Ouput files */
static FILE
static FILE
/* Object stores */
static polygon_array
rm = (r32) 1.0 / (r32) RAND_MAX;
*volume_file = NULL; /* the basic open geometry file */
*vf[8];
/* the 8 files when writing
* multiple povray blob files */
poly_store;
/*
*
static vector
ver[max_p_object]; /*
*
/* Storage of a loaded shape for the -X option
static char
x_name[max_file];
/*
static vector
form_c[max_p_object];
static polygon_array
form_s;
/*
static s16
form_ver, form_pol; /*
the store where polygons
accumulate before saved to disc */
the store where vertices
accumulate */
*/
filename of VOL file */
/* vertices */
polygons */
vertices and polygon counts */
/* Check for weird polygons ----------------------------------------------- */
/*
* Sometimes polygons and/or vertices end up containing floating point
* exception like NAN etc. These routines find these problems. They can create
* havoc on input parsers which expect the geometry to be flawless. Typical
* normalization routines blow up on NAN in vectors.
*/
/* IEEE coded floating point exceptions */
static u32
fp_exp1 = 0x7f800000L;
static u32
fp_exp2 = 0xff800000L;
static u32
fp_exp3 = 0xffc00000L;
static u32
fp_exp4 = 0x7fc00000L;
static
boolean
Bad_vertex(r32 x, r32 y, r32 z)
{
union {
r32
u32
}
/* Does this vertex contain a
* floation point exception ? */
f;
i;
u;
u.f = x;
if ((u.i == fp_exp1) ||
(u.i == fp_exp2) ||
(u.i == fp_exp3) ||
(u.i == fp_exp4)) {
return TRUE;
};
u.f = y;
if ((u.i == fp_exp1) ||
(u.i == fp_exp2) ||
(u.i == fp_exp3) ||
(u.i == fp_exp4)) {
return TRUE;
};
u.f = z;
165
H
Quelltexte
if ((u.i == fp_exp1) ||
(u.i == fp_exp2) ||
(u.i == fp_exp3) ||
(u.i == fp_exp4)) {
return TRUE;
};
return FALSE;
}
static
Invalid_polygon(s16 p)
{
boolean
vector
s16
r32
/* Can a normal be created on this
* polygon ? */
X, Y, Z, N;
i;
D, x, y, z;
for (i = 0; i < 4; i++) {
x = ver[poly_store[p][i]][_x];
y = ver[poly_store[p][i]][_y];
z = ver[poly_store[p][i]][_z];
if (Bad_vertex(x, y, z))
return TRUE;
}
for (i =
X[i]
Y[i]
Z[i]
}
0; i < 3; i++) {
= ver[poly_store[p][i]][_x];
= ver[poly_store[p][i]][_y];
= ver[poly_store[p][i]][_z];
N[_x] = Y[_x] * (Z[_y] - Z[_z]) + Y[_y] * (Z[_z] - Z[_x]) + Y[_z] * (Z[_x] - Z[_y]);
N[_y] = ((-X[_x])) * (Z[_y] - Z[_z]) - X[_y] * (Z[_z] - Z[_x]) - X[_z] * (Z[_x] - Z[_y]);
N[_z] = X[_x] * (Y[_y] - Y[_z]) + X[_y] * (Y[_z] - Y[_x]) + X[_z] * (Y[_x] - Y[_y]);
D = N[_x] * N[_x] + N[_y] * N[_y] + N[_z] * N[_z];
if (D <= (r32) 0.0001)
return TRUE;
else
return FALSE;
}
/* Output data file procs ------------------------------------------------- */
static void
Open_datafile(void)
{
char
s16
/* Open and setup the different
* output files depending on the
* flags */
S[max_file] = "";
i;
if (pov_form || pov_form2) {
strcpy(S, inc_out ? "output.inc" : "output.pov");
Message("Pov file
: %s\n", S);
volume_file = fopen(S, "wt");
if (!volume_file)
User_error("Cannot open file [%s]", S);
Buffer_IO(volume_file, 0);
return;
} else if (pov_form3) {
for (i = 0; i < 8; i++) {
sprintf(S, inc_out ? "output%d.inc" : "output%d.pov", i);
Message("Pov file
: %s\n", S);
vf[i] = fopen(S, "wt");
if (!vf[i])
User_error("Cannot open file [%s]", S);
fprintf(vf[i], "component 1.0 1.0 <0, 0, 0>\n");
}
return;
166
H
Quelltexte
} else if (blb_form) {
strcpy(S, "output.blb");
Message("Blob file
: %s\n", S);
volume_file = fopen(S, "wt");
if (!volume_file)
User_error("Cannot open file [%s]", S);
Buffer_IO(volume_file, 0);
fprintf(volume_file, "[blob]\nThreshold = 0.5\n");
return;
} else if (dxf1) {
strcpy(S, "output.dxf");
Message("Dxf file
: %s\n", S);
volume_file = fopen(S, "wt");
if (!volume_file)
User_error("Cannot open file [%s]", S);
Buffer_IO(volume_file, 0);
fprintf(volume_file, "999\nL-System Parser/Mutator\n");
fprintf(volume_file, "999\nPolyline Polyface Meshes\n");
if (user_form) {
/*
*
*
*
in this case build a block
section, include the loaded shape
and use only block inserts in the
entities section of the dxf file */
fprintf(volume_file, "0\nSECTION\n2\nTABLES\n0\nTABLE\n2\nAPPID\n70\n4\n
0\nAPPID\n2\nLPARSER\n70\n0\n0\nENDTAB\n0\nENDSEC\n");
fprintf(volume_file, "0\nSECTION\n2\nBLOCKS\n0\nBLOCK\n8\n0\n2\nBLOCK\n70\n0\n");
fprintf(volume_file, "10\n0.0\n20\n0.0\n30\n0.0\n3\nBLOCK\n");
fprintf(volume_file, "0\nPOLYLINE\n66\n1\n8\n0\n62\n0\n70\n64\n");
fprintf(volume_file, "1001\nLPARSER\n1071\n18500\n1070\n
11003\n1000\n<byblock>\n1070\n10999\n");
for (i = 1; i <= form_ver; i++) {
fprintf(volume_file, "0\nVERTEX\n8\n0\n62\n0\n");
fprintf(volume_file, "10\n%g\n", form_c[i][_x]);
fprintf(volume_file, "20\n%g\n", form_c[i][_y]);
fprintf(volume_file, "30\n%g\n", form_c[i][_z]);
fprintf(volume_file, "70\n192\n");
}
for (i = 1; i <= form_pol; i++) {
fprintf(volume_file, "0\nVERTEX\n8\n0\n62\n0\n");
fprintf(volume_file, "10\n0\n20\n0\n30\n0\n70\n128\n");
fprintf(volume_file, "71\n%d\n", form_s[i][0]);
fprintf(volume_file, "72\n%d\n", form_s[i][1]);
fprintf(volume_file, "73\n%d\n", form_s[i][2]);
fprintf(volume_file, "74\n%d\n", form_s[i][3]);
};
fprintf(volume_file, "0\nSEQEND\n8\n0\n0\nENDBLK\n8\n0\n0\nENDSEC\n");
};
fprintf(volume_file, "0\nSECTION\n2\nENTITIES\n");
} else if (dxf2) {
strcpy(S, "output.dxf");
Message("Dxf file
: %s\n", S);
volume_file = fopen(S, "wt");
if (!volume_file)
User_error("Cannot open file [%s]", S);
Buffer_IO(volume_file, 0);
fprintf(volume_file, "999\nL-System Parser/Mutator\n");
fprintf(volume_file, "999\n3d Faces List\n");
fprintf(volume_file, "0\nSECTION\n2\nENTITIES\n");
} else if (dxf3) {
strcpy(S, "output.raw");
Message("Raw file
: %s\n", S);
volume_file = fopen(S, "wt");
if (!volume_file)
User_error("Cannot open file [%s]", S);
Buffer_IO(volume_file, 0);
167
H
Quelltexte
} else if (vrml) {
strcpy(S, "output.wrl");
Message("VRML file
: %s\n", S);
volume_file = fopen(S, "wt");
if (!volume_file)
User_error("Cannot open file [%s]", S);
Buffer_IO(volume_file, 0);
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
"#VRML V1.0 ascii\n");
/* vrml header */
"\nSeparator {\n");
"\tShapeHints {\n");
"\t\tvertexOrdering UNKNOWN_ORDERING\n");
"\t\tshapeType UNKNOWN_SHAPE_TYPE\n");
"\t\tfaceType CONVEX\n");
"\t\tcreaseAngle 0.5\n");
"\t}\n");
"\tDirectionalLight {\n");
"\t\tdirection -0.3 -0.6 -0.9\n");
"\t}\n");
} else {
/* default output in Lviewer VOL
* format */
strcpy(S, "output.vol");
Message("Datafile
: %s\n", S);
volume_file = fopen(S, "wb");
if (!volume_file)
User_error("Cannot open file [%s]", S);
Buffer_IO(volume_file, 0);
Fput_bin_u8(volume_file, (u8) VersionID);
/* VOL header */
Fput_bin_r32(volume_file, (r32) 45.0);
Fput_bin_r32(volume_file, (r32) 45.0);
Fput_bin_r32(volume_file, (r32) 90.0);
Fput_bin_r32(volume_file, (r32) 45.0);
Fput_bin_r32(volume_file, (r32) 0.0);
Fput_bin_s16(volume_file, 0);
Fput_bin_s16(volume_file, 0);
Fput_bin_s16(volume_file, 100);
Fput_bin_s16(volume_file, 3000);
Fput_bin_u16(volume_file, (u16) 0);/* stream format */
}
}
static void
Close_datafile(void)
{
s16
/* Close the different open output
* files */
i;
if (pov_form3) {
Message("Objects
: %ld\n", polcount);
for (i = 0; i < 8; i++)
fclose(vf[i]);
return;
};
if (dxf1 || dxf2)
fprintf(volume_file, "0\nENDSEC\n0\nEOF\n");
if (vrml)
fprintf(volume_file, "}\n");
Message("Objects
fclose(volume_file);
: %ld\n", polcount);
}
static void
Save_object(s16 vertices, s16 polygons, s16 color)
{
/* Save an object from store to
* disc */
s32
t, i, max;
if (pov_form2 || pov_form3 || blb_form)/* in these case no 'real' geometry
* is saved */
return;
168
H
Quelltexte
polcount += polygons;
if (pov_form) {
for (t = 1; t <= polygons; t++) {
if (Invalid_polygon(t))
continue;
if (poly_store[t][2] == poly_store[t][3]) { /* 3 vertex triangle */
fprintf(volume_file, "object{triangle{");
fprintf(volume_file, "<%g, %g, %g>", ver[poly_store[t][0]][_x],
ver[poly_store[t][0]][_z], ver[poly_store[t][0]][_y]);
fprintf(volume_file, "<%g, %g, %g>", ver[poly_store[t][1]][_x],
ver[poly_store[t][1]][_z], ver[poly_store[t][1]][_y]);
fprintf(volume_file, "<%g, %g, %g>}", ver[poly_store[t][2]][_x],
ver[poly_store[t][2]][_z], ver[poly_store[t][2]][_y]);
fprintf(volume_file, "finish{t_leaf} pigment{color col_%d}}\n",
color % 16);
if(maxX < ver[poly_store[t][0]][_x])
maxX = ver[poly_store[t][0]][_x];
if(maxY < ver[poly_store[t][0]][_y])
maxY = ver[poly_store[t][0]][_y];
if(maxZ < ver[poly_store[t][0]][_z])
maxZ = ver[poly_store[t][0]][_z];
if(minX > ver[poly_store[t][0]][_x])
minX = ver[poly_store[t][0]][_x];
if(minY > ver[poly_store[t][2]][_y])
minY = ver[poly_store[t][2]][_y];
if(minZ > ver[poly_store[t][0]][_z])
minZ = ver[poly_store[t][0]][_z];
if(maxX < ver[poly_store[t][1]][_x])
maxX = ver[poly_store[t][1]][_x];
if(maxY < ver[poly_store[t][1]][_y])
maxY = ver[poly_store[t][1]][_y];
if(maxZ < ver[poly_store[t][1]][_z])
maxZ = ver[poly_store[t][1]][_z];
if(minX > ver[poly_store[t][1]][_x])
minX = ver[poly_store[t][1]][_x];
if(minY > ver[poly_store[t][2]][_y])
minY = ver[poly_store[t][2]][_y];
if(minZ > ver[poly_store[t][1]][_z])
minZ = ver[poly_store[t][1]][_z];
if(maxX < ver[poly_store[t][2]][_x])
maxX = ver[poly_store[t][2]][_x];
if(maxY < ver[poly_store[t][2]][_y])
maxY = ver[poly_store[t][2]][_y];
if(maxZ < ver[poly_store[t][2]][_z])
maxZ = ver[poly_store[t][2]][_z];
if(minX > ver[poly_store[t][2]][_x])
minX = ver[poly_store[t][2]][_x];
if(minY > ver[poly_store[t][2]][_y])
minY = ver[poly_store[t][2]][_y];
if(minZ > ver[poly_store[t][2]][_z])
minZ = ver[poly_store[t][2]][_z];
} else {
/* 4 vertex polygon == 2x triangle */
fprintf(volume_file, "object{triangle{");
fprintf(volume_file, "<%g, %g, %g>", ver[poly_store[t][0]][_x],
ver[poly_store[t][0]][_z], ver[poly_store[t][0]][_y]);
fprintf(volume_file, "<%g, %g, %g>", ver[poly_store[t][1]][_x],
ver[poly_store[t][1]][_z], ver[poly_store[t][1]][_y]);
fprintf(volume_file, "<%g, %g, %g>}", ver[poly_store[t][2]][_x],
ver[poly_store[t][2]][_z], ver[poly_store[t][2]][_y]);
fprintf(volume_file, "finish{t_leaf} pigment{color col_%d}}\n",
color % 16);
fprintf(volume_file, "object{triangle{");
fprintf(volume_file, "<%g, %g, %g>", ver[poly_store[t][2]][_x],
ver[poly_store[t][2]][_z], ver[poly_store[t][2]][_y]);
fprintf(volume_file, "<%g, %g, %g>", ver[poly_store[t][3]][_x],
ver[poly_store[t][3]][_z], ver[poly_store[t][3]][_y]);
fprintf(volume_file, "<%g, %g, %g>}", ver[poly_store[t][0]][_x],
ver[poly_store[t][0]][_z], ver[poly_store[t][0]][_y]);
169
H
Quelltexte
fprintf(volume_file, "finish{t_leaf} pigment{color col_%d}}\n", color % 16);
if(maxX > ver[poly_store[t][0]][_x])
maxX = ver[poly_store[t][0]][_x];
if(maxY > ver[poly_store[t][0]][_y])
maxY = ver[poly_store[t][0]][_y];
if(maxZ > ver[poly_store[t][0]][_z])
maxZ = ver[poly_store[t][0]][_z];
if(minX > ver[poly_store[t][0]][_x])
minX = ver[poly_store[t][0]][_x];
if(minY > ver[poly_store[t][2]][_y])
minY = ver[poly_store[t][2]][_y];
if(minZ > ver[poly_store[t][0]][_z])
minZ = ver[poly_store[t][0]][_z];
if(maxX > ver[poly_store[t][1]][_x])
maxX = ver[poly_store[t][1]][_x];
if(maxY > ver[poly_store[t][1]][_y])
maxY = ver[poly_store[t][1]][_y];
if(maxZ > ver[poly_store[t][1]][_z])
maxZ = ver[poly_store[t][1]][_z];
if(minX > ver[poly_store[t][1]][_x])
minX = ver[poly_store[t][1]][_x];
if(minY > ver[poly_store[t][2]][_y])
minY = ver[poly_store[t][2]][_y];
if(minZ > ver[poly_store[t][1]][_z])
minZ = ver[poly_store[t][1]][_z];
if(maxX > ver[poly_store[t][2]][_x])
maxX = ver[poly_store[t][2]][_x];
if(maxY > ver[poly_store[t][2]][_y])
maxY = ver[poly_store[t][2]][_y];
if(maxZ > ver[poly_store[t][2]][_z])
maxZ = ver[poly_store[t][2]][_z];
if(minX > ver[poly_store[t][2]][_x])
minX = ver[poly_store[t][2]][_x];
if(minY > ver[poly_store[t][2]][_y])
minY = ver[poly_store[t][2]][_y];
if(minZ > ver[poly_store[t][2]][_z])
minZ = ver[poly_store[t][2]][_z];
if(maxX > ver[poly_store[t][3]][_x])
maxX = ver[poly_store[t][3]][_x];
if(maxY > ver[poly_store[t][3]][_y])
maxY = ver[poly_store[t][3]][_y];
if(maxZ > ver[poly_store[t][3]][_z])
maxZ = ver[poly_store[t][3]][_z];
if(minX > ver[poly_store[t][3]][_x])
minX = ver[poly_store[t][3]][_x];
if(minY > ver[poly_store[t][3]][_y])
minY = ver[poly_store[t][3]][_y];
if(minZ > ver[poly_store[t][3]][_z])
minZ = ver[poly_store[t][3]][_z];
}
}
} else if (dxf1) {
/* dxf 3d mesh object */
fprintf(volume_file, "0\nPOLYLINE\n66\n1\n8\n%d\n70\n64\n", color);
for (i = 1; i <= vertices; i++) {
fprintf(volume_file, "0\nVERTEX\n8\n%d\n", color);
fprintf(volume_file, "10\n%g\n", ver[i][_x]);
fprintf(volume_file, "20\n%g\n", ver[i][_y]);
fprintf(volume_file, "30\n%g\n", ver[i][_z]);
fprintf(volume_file, "70\n192\n");
}
for (i = 1; i <= polygons; i++) {
if (Invalid_polygon(i))
continue;
fprintf(volume_file, "0\nVERTEX\n8\n%d\n", color);
fprintf(volume_file, "10\n0\n20\n0\n30\n0\n70\n128\n");
fprintf(volume_file, "71\n%d\n", poly_store[i][0]);
fprintf(volume_file, "72\n%d\n", poly_store[i][1]);
fprintf(volume_file, "73\n%d\n", poly_store[i][2]);
fprintf(volume_file, "74\n%d\n", poly_store[i][3]);
170
H
Quelltexte
}
fprintf(volume_file, "0\nSEQEND\n8\n%d\n", color);
} else if (dxf2) {
for (i = 1; i <= polygons; i++) {
/* dxf 3dface object */
if (Invalid_polygon(i))
continue;
if (poly_store[i][2] == poly_store[i][3]) { /* 3 vertex face */
fprintf(volume_file, "0\n3DFACE\n8\n%d\n", color);
fprintf(volume_file, "10\n%g\n", ver[poly_store[i][0]][_x]);
fprintf(volume_file, "20\n%g\n", ver[poly_store[i][0]][_y]);
fprintf(volume_file, "30\n%g\n", ver[poly_store[i][0]][_z]);
fprintf(volume_file, "11\n%g\n", ver[poly_store[i][1]][_x]);
fprintf(volume_file, "21\n%g\n", ver[poly_store[i][1]][_y]);
fprintf(volume_file, "31\n%g\n", ver[poly_store[i][1]][_z]);
fprintf(volume_file, "12\n%g\n", ver[poly_store[i][2]][_x]);
fprintf(volume_file, "22\n%g\n", ver[poly_store[i][2]][_y]);
fprintf(volume_file, "32\n%g\n", ver[poly_store[i][2]][_z]);
fprintf(volume_file, "13\n%g\n", ver[poly_store[i][2]][_x]);
fprintf(volume_file, "23\n%g\n", ver[poly_store[i][2]][_y]);
fprintf(volume_file, "33\n%g\n", ver[poly_store[i][2]][_z]);
} else {
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
}
/* 4 vertex face */
"0\n3DFACE\n8\n%d\n", color);
"10\n%g\n", ver[poly_store[i][0]][_x]);
"20\n%g\n", ver[poly_store[i][0]][_y]);
"30\n%g\n", ver[poly_store[i][0]][_z]);
"11\n%g\n", ver[poly_store[i][1]][_x]);
"21\n%g\n", ver[poly_store[i][1]][_y]);
"31\n%g\n", ver[poly_store[i][1]][_z]);
"12\n%g\n", ver[poly_store[i][2]][_x]);
"22\n%g\n", ver[poly_store[i][2]][_y]);
"32\n%g\n", ver[poly_store[i][2]][_z]);
"13\n%g\n", ver[poly_store[i][3]][_x]);
"23\n%g\n", ver[poly_store[i][3]][_y]);
"33\n%g\n", ver[poly_store[i][3]][_z]);
}
} else if (vrml) {
fprintf(volume_file, "\tSeparator {\n");
fprintf(volume_file, "\t\tMaterial {\n");
fprintf(volume_file, "\t\t\tdiffuseColor ");
switch (color) {
case 1:
fprintf(volume_file,
break;
case 2:
fprintf(volume_file,
break;
case 3:
fprintf(volume_file,
break;
case 4:
fprintf(volume_file,
break;
case 5:
fprintf(volume_file,
break;
case 6:
fprintf(volume_file,
break;
case 7:
fprintf(volume_file,
break;
case 8:
fprintf(volume_file,
break;
case 9:
fprintf(volume_file,
break;
case 10:
fprintf(volume_file,
/* translate colors from lparser to
* RGB values */
"%f %f %f", 0.3, 0.3, 0.3);
"%f %f %f", 0.8, 0.4, 0.4);
"%f %f %f", 0.8, 0.8, 0.4);
"%f %f %f", 0.4, 0.8, 0.4);
"%f %f %f", 0.4, 0.8, 0.8);
"%f %f %f", 0.4, 0.4, 0.8);
"%f %f %f", 0.8, 0.4, 0.8);
"%f %f %f", 0.2, 0.5, 0.2);
"%f %f %f", 0.2, 0.5, 0.5);
"%f %f %f", 0.2, 0.2, 0.5);
171
H
Quelltexte
break;
case 11:
fprintf(volume_file,
break;
case 12:
fprintf(volume_file,
break;
case 13:
fprintf(volume_file,
break;
case 14:
fprintf(volume_file,
break;
case 15:
fprintf(volume_file,
break;
default:
fprintf(volume_file,
break;
"%f %f %f", 0.5, 0.2, 0.5);
"%f %f %f", 0.6, 0.2, 0.2);
"%f %f %f", 0.5, 0.5, 0.5);
"%f %f %f", 0.7, 0.7, 0.7);
"%f %f %f", 0.9, 0.9, 0.9);
"%f %f %f", 0.5, 0.5, 0.5);
};
fprintf(volume_file, "\n\t\t}\n");
/* Write vertices */
fprintf(volume_file, "\t\tCoordinate3 {\n");
fprintf(volume_file, "\t\t\tpoint [\n");
for (t = 1; t < vertices; t++)
fprintf(volume_file, "\t\t\t\t%f %f %f,\n", ver[t][_x], ver[t][_z], ver[t][_y]);
for (t = vertices; t <= vertices; t++)
fprintf(volume_file, "\t\t\t\t%f %f %f\n", ver[t][_x], ver[t][_z], ver[t][_y]);
fprintf(volume_file, "\t\t\t]\n");
fprintf(volume_file, "\t\t}\n");
fprintf(volume_file, "\t\tMaterialBinding {\n\t\t\tvalue OVERALL\n\t\t}\n");
/* Write polygons */
fprintf(volume_file, "\t\tIndexedFaceSet {\n");
fprintf(volume_file, "\t\t\tcoordIndex [\n");
for (t = 1; t < polygons; t++) {
fprintf(volume_file, "\t\t\t\t");
if (poly_store[t][2] == poly_store[t][3])
max = 2;
else
max = 3;
for (i = 0; i <= max; i++)
fprintf(volume_file, "%d, ", poly_store[t][i] - 1);
fprintf(volume_file, "-1,\n");
}
for (t = polygons; t <= polygons; t++) {
fprintf(volume_file, "\t\t\t\t");
if (poly_store[t][2] == poly_store[t][3])
max = 2;
else
max = 3;
for (i = 0; i <= max; i++)
fprintf(volume_file, "%d, ", poly_store[t][i] - 1);
fprintf(volume_file, "-1\n");
}
fprintf(volume_file, "\t\t\t]\n");
fprintf(volume_file, "\t\t}\n");
fprintf(volume_file, "\t}\n");
} else if (dxf3) {
for (i = 1; i <= polygons; i++) {
/* simple raw format */
if (Invalid_polygon(i))
continue;
if (poly_store[i][2] == poly_store[i][3]) { /* 3 vertex triangle */
fprintf(volume_file, "%g ", ver[poly_store[i][0]][_x]);
fprintf(volume_file, "%g ", ver[poly_store[i][0]][_y]);
fprintf(volume_file, "%g ", ver[poly_store[i][0]][_z]);
fprintf(volume_file, "%g ", ver[poly_store[i][1]][_x]);
fprintf(volume_file, "%g ", ver[poly_store[i][1]][_y]);
fprintf(volume_file, "%g ", ver[poly_store[i][1]][_z]);
fprintf(volume_file, "%g ", ver[poly_store[i][2]][_x]);
fprintf(volume_file, "%g ", ver[poly_store[i][2]][_y]);
172
H
Quelltexte
fprintf(volume_file, "%g\n", ver[poly_store[i][2]][_z]);
} else {
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
fprintf(volume_file,
}
/* 4 vertex polygon = 2x triangle */
"%g ", ver[poly_store[i][0]][_x]);
"%g ", ver[poly_store[i][0]][_y]);
"%g ", ver[poly_store[i][0]][_z]);
"%g ", ver[poly_store[i][1]][_x]);
"%g ", ver[poly_store[i][1]][_y]);
"%g ", ver[poly_store[i][1]][_z]);
"%g ", ver[poly_store[i][2]][_x]);
"%g ", ver[poly_store[i][2]][_y]);
"%g\n", ver[poly_store[i][2]][_z]);
"%g ", ver[poly_store[i][0]][_x]);
"%g ", ver[poly_store[i][0]][_y]);
"%g ", ver[poly_store[i][0]][_z]);
"%g ", ver[poly_store[i][2]][_x]);
"%g ", ver[poly_store[i][2]][_y]);
"%g ", ver[poly_store[i][2]][_z]);
"%g ", ver[poly_store[i][3]][_x]);
"%g ", ver[poly_store[i][3]][_y]);
"%g\n", ver[poly_store[i][3]][_z]);
}
} else {
Fput_bin_s16(volume_file,
Fput_bin_s16(volume_file,
Fput_bin_s16(volume_file,
Fput_bin_s16(volume_file,
/* standard VOL output */
20);
vertices);
polygons);
color);
for (t = 1; t <= vertices; t++) {
Fput_bin_r32(volume_file, ver[t][_x]);
Fput_bin_r32(volume_file, ver[t][_y]);
Fput_bin_r32(volume_file, ver[t][_z]);
}
for (t = 1; t <= polygons; t++) {
for (i = 0; i <= 3; i++)
Fput_bin_s16(volume_file, poly_store[t][i]);
}
}
}
/* Add object ------------------------------------------------------------- */
static void
Inverse(vector t, vector v)
{
/* Inverse vector transform of a
* matrix built in C123 */
v[_x] = C1[_x] * t[_x] + C2[_x] * t[_y] + C3[_x] * t[_z];
v[_y] = C1[_y] * t[_x] + C2[_y] * t[_y] + C3[_y] * t[_z];
v[_z] = C1[_z] * t[_x] + C2[_z] * t[_y] + C3[_z] * t[_z];
}
static void
Set_ECS(vector n)
{
vector
r32
/* Build an ECS transform in the
* axis_xyz vars, used for dxf1
* output */
Wy, Wz;
fac = (r32) 0.015625;
Wy[_x] = (r32) 0.0;
Wy[_y] = (r32) 1.0;
Wy[_z] = (r32) 0.0;
Wz[_x] = (r32) 0.0;
Wz[_y] = (r32) 0.0;
Wz[_z] = (r32) 1.0;
Vector_copy_r32(n, axis_z);
Vector_normalize(axis_z);
if ((Abs_r32(n[_x]) < fac) && (Abs_r32(n[_y]) < fac)) {
Vector_product(Wy, axis_z, axis_x);
173
H
Quelltexte
} else {
Vector_product(Wz, axis_z, axis_x);
}
Vector_normalize(axis_x);
Vector_product(axis_z, axis_x, axis_y);
Vector_normalize(axis_y);
}
static void
Inverse_ECS(vector p1, vector p2)
{
/* Used for dxf1 output
p2[_x] = axis_x[_x] * p1[_x] + axis_x[_y] * p1[_y] + axis_x[_z]
p2[_y] = axis_y[_x] * p1[_x] + axis_y[_y] * p1[_y] + axis_y[_z]
p2[_z] = axis_z[_x] * p1[_x] + axis_z[_y] * p1[_y] + axis_z[_z]
}
static void
Read_form(void)
{
s32
s16
r32
r32
s16
*/
* p1[_z];
* p1[_z];
* p1[_z];
/* Read in and store an external
* object */
i, j;
ver, pol, k;
LR, LE, LT;
Dum32;
Dum16;
if (pov_form || pov_form2 || pov_form3 || blb_form)
return;
strcat(x_name, ".vol");
volume_file = fopen(x_name, "rb");
if (!volume_file)
User_error("Cannot open file [%s]", x_name);
Buffer_IO(volume_file, 0);
Dum16 = getc(volume_file);
Set_lowhigh((Dum16 == 11));
/* get storage mode */
Fget_bin_r32(volume_file, &Dum32);
/* header */
Fget_bin_r32(volume_file, &Dum32);
Fget_bin_r32(volume_file, &LR);
Fget_bin_r32(volume_file, &LE);
Fget_bin_r32(volume_file, &LT);
fread(&Dum16, sizeof(s16), 1, volume_file);
fread(&Dum16, sizeof(s16), 1, volume_file);
fread(&Dum16, sizeof(s16), 1, volume_file);
fread(&Dum16, sizeof(s16), 1, volume_file);
fread(&Dum16, sizeof(u16), 1, volume_file);
form_ver = 0;
form_pol = 0;
k = 0;
for (;;) {
if (feof(volume_file))
break;
Fget_bin_s16(volume_file,
Fget_bin_s16(volume_file,
Fget_bin_s16(volume_file,
Fget_bin_s16(volume_file,
&Dum16);
&ver);
/* vertex count */
&pol);
/* polygon count */
&Dum16);
for (i = 1; i <= ver; i++) {
/* vertices */
form_ver++;
Fget_bin_r32(volume_file, &form_c[form_ver][_x]);
Fget_bin_r32(volume_file, &form_c[form_ver][_y]);
Fget_bin_r32(volume_file, &form_c[form_ver][_z]);
}
for (i = 1; i <= pol; i++) {
/* polygons */
form_pol++;
for (j = 0; j <= 3; j++)
Fget_bin_s16(volume_file, &form_s[form_pol][j]);
for (j = 0; j <= 3; j++)
174
H
Quelltexte
form_s[form_pol][j] += k;
}
k = form_ver;
}
fclose(volume_file);
}
static void
Define_form(vector p1, vector p2, vector up, s16 c)
{
/* Insert external object. The
* basis is to create the resulting
* normalized direction vectors
* from the turtle vectors and use
* this a matrix for inverse
* transforming the object from its
* location round the origin to its
* location at the turtle origin */
vector
dis, d1, d2, d3, in, ext, Q, P;
s16
i;
r32
s, d, r1, r2;
char
layer[10] = "";
/* p1 = location, p2 = forward, up = up */
zmin = MIN(zmin, p1[_z]);
zmin = MIN(zmin, p2[_z]);
/* setup */
Vector_min(p2, p1, dis);
d = Vector_length(dis);
if (d == (r32) 0.0)
return;
s = d * thick;
s = (s < (r32) min_thick) ? min_thick : s;
/* d1 */
Vector_copy_r32(dis, d1);
Vector_normalize(d1);
if (dxf1) {
/* setup ECS and insert the block
* reference */
Set_ECS(d1);
Inverse_ECS(p1, d2);
Vector_copy(d1, ext);
Vector_copy(d2, in);
sprintf(layer, "%d", c);
fprintf(volume_file, "0\nINSERT\n8\n%s\n2\nBLOCK\n", layer);
fprintf(volume_file, "10\n%g\n20\n%g\n30\n%g\n", in[_x], in[_y], in[_z]);
fprintf(volume_file, "41\n%g\n42\n%g\n43\n%g\n", s, s, d);
fprintf(volume_file, "210\n%g\n220\n%g\n230\n%g\n", ext[_x], ext[_y], ext[_z]);
polcount++;
return;
};
/* d2 */
Vector_copy_r32(up, d2);
Vector_normalize(d2);
/* d3 */
Vector_product(d1, d2, d3);
Vector_normalize(d3);
/* setup transform */
Vector_copy_r32(d3, C1);
Vector_copy_r32(d2, C2);
Vector_copy_r32(d1, C3);
/* new x-axis */
/* new y-axis */
/* new z-axis */
if (pov_form) {
/* insert a reference to l_base */
d *= 0.7;
s *= 0.7;
r1 = 57.0 * Do_angle(0.0, 0.0, d1[_z], sqrt(d1[_x] * d1[_x] + d1[_y] * d1[_y]));
r2 = 57.0 * Do_angle(0.0, 0.0, d1[_x], d1[_y]);
fprintf(volume_file, "object{l_base ");
fprintf(volume_file, "finish{t_base} pigment{color col_%d}", c % 16);
fprintf(volume_file, "scale<%g, %g, %g>", s, d, s);
fprintf(volume_file, "rotate<%g, %g, %g>", 0.0, 0.0, -r1);
175
H
Quelltexte
fprintf(volume_file, "rotate<%g, %g, %g>", 0.0, -r2, 0.0);
fprintf(volume_file, "translate<%g, %g, %g>}\n", p1[_x], p1[_z], p1[_y]);
polcount++;
if(maxX < p1[_x])
maxX = p1[_x];
if(maxY < p1[_y])
maxY = p1[_y];
if(maxZ < p1[_z])
maxZ = p1[_z];
if(minX > p1[_x])
minX = p1[_x];
if(minY > p1[_y])
minY = p1[_y];
if(minZ > p1[_z])
minZ = p1[_z];
return;
} else if (pov_form2) {
/* write out a blob origin */
fprintf(volume_file, "component 1.0 %g <%g, %g, %g>\n", d, p1[_x], p1[_z], p1[_y]);
polcount++;
return;
} else if (pov_form3) {
/* write out a blob origin to the
* file based on the color */
fprintf(vf[c % 8], "component 1.0 %g <%g, %g, %g>\n", d, p1[_x], p1[_z], p1[_y]);
polcount++;
return;
} else if (blb_form) {
/* write out a blob in BLB format */
fprintf(volume_file, "Sphere = %g %g %g 1.0 %g\n", p1[_x], p1[_z], p1[_y], d);
polcount++;
return;
};
/* Else use the good old Lviewer VOL format */
for (i = 1; i <= form_ver; i++) {
/* vertices */
Q[_x] = form_c[i][_x] * s;
Q[_y] = form_c[i][_y] * s;
Q[_z] = form_c[i][_z] * d;
Inverse(Q, P);
Vector_plus(P, p1, ver[i]);
}
for (i = 1; i <= form_pol; i++) {
poly_store[i][0] = form_s[i][0];
poly_store[i][1] = form_s[i][1];
poly_store[i][2] = form_s[i][2];
poly_store[i][3] = form_s[i][3];
}
/* polygons */
Save_object(form_ver, form_pol, c);
/* save the stored object */
}
static void
Define_block(vector p1, vector p2, vector up, s16 c)
{
/* Insert basic block. Here we
* build a cube shape directly on
* the input vectors. */
vector
dis, d1, d2, d3;
s16
i;
r32
s, d;
if (pov_form || pov_form2 || pov_form3 || blb_form)
return;
zmin = MIN(zmin, p1[_z]);
zmin = MIN(zmin, p2[_z]);
/* setup */
Vector_min(p2, p1, dis);
d = Vector_length(dis);
if (d == (r32) 0.0)
return;
s = d * thick;
176
H
Quelltexte
s = (s < (r32) min_thick) ? min_thick : s;
s *= 0.5;
/* d1 */
Vector_copy_r32(dis, d1);
Vector_normalize(d1);
/* d2 */
Vector_copy_r32(up, d2);
Vector_normalize(d2);
/* d3 */
Vector_product(d1, d2, d3);
Vector_normalize(d3);
/* base 1, 3 */
Vector_plus(d2, d3, d1);
Vector_normalize(d1);
Vector_plus_fac(p1, d1, s, ver[1]);
Vector_plus_fac(p1, d1, -s, ver[3]);
/* base 2, 4 */
Vector_min(d2, d3, d1);
Vector_normalize(d1);
Vector_plus_fac(p1, d1, s, ver[2]);
Vector_plus_fac(p1, d1, -s, ver[4]);
/* end */
for (i = 1; i <= 4; i++)
Vector_plus(ver[i], dis, ver[i + 4]);
/* polygons */
poly_store[1][0]
poly_store[1][1]
poly_store[1][2]
poly_store[1][3]
=
=
=
=
1;
5;
6;
2;
poly_store[2][0]
poly_store[2][1]
poly_store[2][2]
poly_store[2][3]
=
=
=
=
2;
6;
7;
3;
poly_store[3][0]
poly_store[3][1]
poly_store[3][2]
poly_store[3][3]
=
=
=
=
3;
7;
8;
4;
poly_store[4][0]
poly_store[4][1]
poly_store[4][2]
poly_store[4][3]
=
=
=
=
4;
8;
5;
1;
poly_store[5][0]
poly_store[5][1]
poly_store[5][2]
poly_store[5][3]
=
=
=
=
1;
2;
3;
4;
poly_store[6][0]
poly_store[6][1]
poly_store[6][2]
poly_store[6][3]
=
=
=
=
8;
7;
6;
5;
Save_object(8, 6, c);
}
static void
Define_closed(vector p1, vector p2, vector up, s16 c)
{
/* Insert connected cylinder shape.
* The lastxxx vars are used to
* store the previous top of the
* cylinder for conntecing a next
* one. Since the vars are stacked
* for [] we can connect correctly
* according to current nesting
* level. */
vector
dis, d1, d2, d3, t1, t2;
s16
i, ii, col;
177
H
Quelltexte
r32
s, d, dd = float_max;
if (pov_form || pov_form2 || pov_form3 || blb_form)
return;
zmin = MIN(zmin, p1[_z]);
zmin = MIN(zmin, p2[_z]);
/* setup */
Vector_min(p2, p1, dis);
d = Vector_length(dis);
if (d == (r32) 0.0)
return;
s = d * thick;
s = (s < (r32) min_thick) ? min_thick : s;
s *= 0.5;
/* d1 */
Vector_copy_r32(dis, d1);
Vector_normalize(d1);
/* d2 */
Vector_copy_r32(up, d2);
Vector_normalize(d2);
/* d3 */
Vector_product(d1, d2, d3);
Vector_normalize(d3);
Vector_plus(d2, d3, t1);
Vector_normalize(t1);
Vector_min(d2, d3, t2);
Vector_normalize(t2);
Vector_plus_fac(p1,
Vector_plus_fac(p1,
Vector_plus_fac(p1,
Vector_plus_fac(p1,
t1,
t1,
t2,
t2,
s *= (r32) 0.7071;
Vector_plus_fac2(p1,
Vector_plus_fac2(p1,
Vector_plus_fac2(p1,
Vector_plus_fac2(p1,
t1,
t1,
t1,
t1,
s, ver[1]);
-s, ver[5]);
s, ver[3]);
-s, ver[7]);
s, t2, s, ver[2]);
-s, t2, s, ver[4]);
-s, t2, -s, ver[6]);
s, t2, -s, ver[8]);
/* end */
for (i = 1; i <= 8; i++)
Vector_plus(ver[i], dis, ver[i + 8]);
if (last_col == c) {
Vector_min(p1, last, dis);
d = Vector_length(dis);
if (d < (r32) 1.0) {
for (i = 1; i <= 8; i++) {
Vector_min(ver[1], last_v[i], dis);
d = Vector_length(dis);
if (d < dd) {
dd = d;
ii = i;
}
}
for (i = 1; i <= 8; i++) {
Vector_copy_r32(last_v[ii], ver[i]);
ii = (ii + 1) % 9;
if (ii == 0)
ii = 1;
}
}
};
/* polygons */
poly_store[1][0]
poly_store[1][1]
poly_store[1][2]
poly_store[1][3]
=
=
=
=
1;
9;
10;
2;
poly_store[2][0] = 2;
178
H
Quelltexte
poly_store[2][1] = 10;
poly_store[2][2] = 11;
poly_store[2][3] = 3;
poly_store[3][0]
poly_store[3][1]
poly_store[3][2]
poly_store[3][3]
=
=
=
=
3;
11;
12;
4;
poly_store[4][0]
poly_store[4][1]
poly_store[4][2]
poly_store[4][3]
=
=
=
=
4;
12;
13;
5;
poly_store[5][0]
poly_store[5][1]
poly_store[5][2]
poly_store[5][3]
=
=
=
=
5;
13;
14;
6;
poly_store[6][0]
poly_store[6][1]
poly_store[6][2]
poly_store[6][3]
=
=
=
=
6;
14;
15;
7;
poly_store[7][0]
poly_store[7][1]
poly_store[7][2]
poly_store[7][3]
=
=
=
=
7;
15;
16;
8;
poly_store[8][0]
poly_store[8][1]
poly_store[8][2]
poly_store[8][3]
=
=
=
=
8;
16;
9;
1;
Save_object(16, 8, c);
last_col = c;
Vector_copy_r32(p2, last);
for (i = 1; i <= 8; i++)
Vector_copy_r32(ver[i + 8], last_v[i]);
}
static void
Ground_plane(void)
{
r32
/* Add a simple large groundplane */
l = (r32) 1e5;
ver[1][_x] = -l;
ver[1][_y] = l;
ver[1][_z] = zmin;
ver[2][_x] = l;
ver[2][_y] = l;
ver[2][_z] = zmin;
ver[3][_x] = l;
ver[3][_y] = -l;
ver[3][_z] = zmin;
ver[4][_x] = -l;
ver[4][_y] = -l;
ver[4][_z] = zmin;
poly_store[1][0]
poly_store[1][1]
poly_store[1][2]
poly_store[1][3]
=
=
=
=
4;
3;
2;
1;
Save_object(4, 1, 1);
}
/* L-system routines ------------------------------------------------------ */
static r32
Rnd(void)
179
H
Quelltexte
{
/* Get a random number */
return (r32) rand() * rm;
}
#define Util_t(In,C1,C2,C3,Out) {\
Out[_x] = Scalar_product(C1,In);\
Out[_y] = Scalar_product(C2,In);\
Out[_z] = Scalar_product(C3,In);\
}
static void
Set_rot(r32 a, vector n)
{
r32
/* Set up a rotation matrix */
n11, n22, n33, nxy, nxz, nyz, sina, cosa;
cosa = cos(a);
sina = sin(a);
n11 = n[_x] * n[_x];
n22 = n[_y] * n[_y];
n33 = n[_z] * n[_z];
nxy = n[_x] * n[_y];
nxz = n[_x] * n[_z];
nyz = n[_y] * n[_z];
C1[_x] = n11 + (one - n11) * cosa;
C1[_y] = nxy * (one - cosa) - n[_z] * sina;
C1[_z] = nxz * (one - cosa) + n[_y] * sina;
C2[_x] = nxy * (one - cosa) + n[_z] * sina;
C2[_y] = n22 + (one - n22) * cosa;
C2[_z] = nyz * (one - cosa) - n[_x] * sina;
C3[_x] = nxz * (one - cosa) - n[_y] * sina;
C3[_y] = nyz * (one - cosa) + n[_x] * sina;
C3[_z] = n33 + (one - n33) * cosa;
}
static r32
Get_value(u32 *j)
{
s16
r32
char
/* Read a (xx) value from a
* production string at location j
* and make it into a real */
i = 0;
r = 0.0;
val[40] = "";
(*j)++;
(*j)++;
for (;;) {
if (Object_Str[*j] == ')')
break;
val[i] = Object_Str[*j];
i++;
(*j)++;
}
val[i] = '\0';
sscanf(val, "%f", &r);
if (last_recur)
r *= fraction;
return r;
}
// ----------------------------- End of function headers added by Ken -----------------------
static void
L_draw(void)
180
H
Quelltexte
{
/* Process a production string and
* generate form */
pos, end, v, fow, upp, lef;
i, max = strlen(Object_Str);
r, a, thick_l, ang_l, dis_l, dis2_l, trope_l;
vcount, pcount, j;
temp[max_file], next;
found, poly_on = FALSE;
star_num;
vector
u32
r32
s16
char
boolean
u32
/* Echo production string */
Get_comline_opt("l", &found, temp);
if (found) {
Message("Production
: ");
for (i = 0; Object_Str[i] != '\0'; i++) {
Message("%c", Object_Str[i]);
}
Message("\n");
};
/* Get user form if needed */
if (user_form)
Read_form();
/* Setup vectors */
pos[_x] = 0.0;
pos[_y] = 0.0;
pos[_z] = 0.0;
fow[_x] = 0.0;
fow[_y] = 0.0;
fow[_z] = 1.0;
lef[_x] = 0.0;
lef[_y] = 1.0;
lef[_z] = 0.0;
upp[_x] = 1.0;
upp[_y] = 0.0;
upp[_z] = 0.0;
Vector_normalize(trope);
/* Do it */
Open_datafile();
/* Start values */
org.col = col;
org.dis = dis;
org.dis2 = dis2;
org.ang = ang;
org.thick = thick;
org.tr = tr;
/* Feedback */
Process_start2(strlen(Object_Str));
for (i = 0; Object_Str[i] != '\0'; i++) {
Process_update2(i);
if (polcount > poly_limit)
break;
/* overflow */
next = Object_Str[i + 1];
/* the next char in the string */
switch (Object_Str[i]) {
default:
break;
/* the current char in the string */
case '@':
last_recur = !last_recur;
if (last_recur) {
thick_l = thick;
ang_l = ang;
dis_l = dis;
dis2_l = dis2;
trope_l = trope_amount;
dis *= fraction;
dis2 *= fraction;
thick *= fraction;
ang *= fraction;
/* Marks last recursion level during
* growing phase */
/* Store all vars and do fraction */
181
H
Quelltexte
trope_amount *= fraction;
} else {
thick = thick_l;
ang = ang_l;
dis = dis_l;
dis2 = dis2_l;
trope_amount = trope_l;
}
break;
/* Restore */
case '+':
save.ang = ang;
if (next == '(') {
ang = ((r32) 0.017453) * Get_value(&i);
if (last_recur)
ang *= fraction;
}
Set_rot(-ang, upp);
Util_t(fow, C1, C2, C3, v);
Vector_copy_r32(v, fow);
Util_t(lef, C1, C2, C3, v);
Vector_copy_r32(v, lef);
Vector_normalize(fow);
Vector_normalize(lef);
ang = save.ang;
break;
case '-':
save.ang = ang;
if (next == '(') {
ang = ((r32) 0.017453) * Get_value(&i);
if (last_recur)
ang *= fraction;
}
Set_rot(ang, upp);
Util_t(fow, C1, C2, C3, v);
Vector_copy_r32(v, fow);
Util_t(lef, C1, C2, C3, v);
Vector_copy_r32(v, lef);
Vector_normalize(fow);
Vector_normalize(lef);
ang = save.ang;
break;
case '~':
if (next == '(')
r = ((r32) 0.017453) * Get_value(&i);
else if (rand_set)
r = ((r32) 0.017453) * rand_amount;
else
r = (r32) 6.0;
a = (Rnd() * r * (r32) 2.0) - r;
Set_rot(a, upp);
Util_t(fow, C1, C2, C3, v);
Vector_copy_r32(v, fow);
Util_t(lef, C1, C2, C3, v);
Vector_copy_r32(v, lef);
Vector_normalize(fow);
Vector_normalize(lef);
a = (Rnd() * r * (r32) 2.0) - r;
Set_rot(a, lef);
Util_t(fow, C1, C2, C3, v);
Vector_copy_r32(v, fow);
Util_t(upp, C1, C2, C3, v);
Vector_copy_r32(v, upp);
Vector_normalize(fow);
Vector_normalize(upp);
a = (Rnd() * r * (r32) 2.0) - r;
Set_rot(a, fow);
Util_t(lef, C1, C2, C3, v);
Vector_copy_r32(v, lef);
Util_t(upp, C1, C2, C3, v);
Vector_copy_r32(v, upp);
Vector_normalize(lef);
Vector_normalize(upp);
break;
case 't':
182
H
Quelltexte
if ((fow[_x] == (r32) 0.0) && (fow[_y] == (r32) 0.0))
break;
save.tr = tr;
if (trope_set)
tr = trope_amount;
if (next == '(') {
tr = Get_value(&i);
if (last_recur)
tr *= fraction;
}
Vector_copy_r32(fow, trope);
trope[_x] = -trope[_x];
trope[_y] = -trope[_y];
trope[_z] = (r32) 0.0;
Vector_normalize(trope);
r = tr * Scalar_product(fow, trope);
Set_rot(-r, lef);
Util_t(fow, C1, C2, C3, v);
Vector_copy_r32(v, fow);
Util_t(upp, C1, C2, C3, v);
Vector_copy_r32(v, upp);
Vector_normalize(fow);
Vector_normalize(upp);
tr = save.tr;
break;
case '$':
Vector_min(fow, sky, v);
if (Vector_length(v) == (r32) 0.0)
break;
Vector_product(fow, sky, lef);
Vector_product(fow, lef, upp);
if (upp[_z] < (r32) 0.0) {
upp[_x] = -upp[_x];
upp[_y] = -upp[_y];
upp[_z] = -upp[_z];
lef[_x] = -lef[_x];
lef[_y] = -lef[_y];
lef[_z] = -lef[_z];
}
break;
case '&':
save.ang = ang;
if (next == '(') {
ang = ((r32) 0.017453) * Get_value(&i);
if (last_recur)
ang *= fraction;
}
Set_rot(ang, lef);
Util_t(fow, C1, C2, C3, v);
Vector_copy_r32(v, fow);
Util_t(upp, C1, C2, C3, v);
Vector_copy_r32(v, upp);
Vector_normalize(fow);
Vector_normalize(upp);
ang = save.ang;
break;
case '^':
save.ang = ang;
if (next == '(') {
ang = ((r32) 0.017453) * Get_value(&i);
if (last_recur)
ang *= fraction;
}
Set_rot(-ang, lef);
Util_t(fow, C1, C2, C3, v);
Vector_copy_r32(v, fow);
Util_t(upp, C1, C2, C3, v);
Vector_copy_r32(v, upp);
Vector_normalize(fow);
Vector_normalize(upp);
ang = save.ang;
break;
case '\\':
save.ang = ang;
183
H
Quelltexte
if (next == '(') {
ang = ((r32) 0.017453) * Get_value(&i);
if (last_recur)
ang *= fraction;
}
Set_rot(-ang, fow);
Util_t(lef, C1, C2, C3, v);
Vector_copy_r32(v, lef);
Util_t(upp, C1, C2, C3, v);
Vector_copy_r32(v, upp);
Vector_normalize(lef);
Vector_normalize(upp);
ang = save.ang;
break;
case '/':
save.ang = ang;
if (next == '(') {
ang = ((r32) 0.017453) * Get_value(&i);
if (last_recur)
ang *= fraction;
}
Set_rot(ang, fow);
Util_t(lef, C1, C2, C3, v);
Vector_copy_r32(v, lef);
Util_t(upp, C1, C2, C3, v);
Vector_copy_r32(v, upp);
Vector_normalize(lef);
Vector_normalize(upp);
ang = save.ang;
break;
case '%':
Set_rot(3.141592654, fow);
Util_t(lef, C1, C2, C3, v);
Vector_copy_r32(v, lef);
Util_t(upp, C1, C2, C3, v);
Vector_copy_r32(v, upp);
Vector_normalize(lef);
Vector_normalize(upp);
break;
case '|':
Set_rot(3.141592654, upp);
Util_t(fow, C1, C2, C3, v);
Vector_copy_r32(v, fow);
Util_t(lef, C1, C2, C3, v);
Vector_copy_r32(v, lef);
Vector_normalize(fow);
Vector_normalize(lef);
break;
case '!':
if (next == '(') {
if (last_recur)
thick *= one + fraction * (Get_value(&i) - one);
else
thick *= Get_value(&i);
} else {
if (last_recur)
thick *= one + fraction * ((r32) 0.7 - one);
else
thick *= (r32) 0.7;
}
break;
case '?':
if (next == '(') {
if (last_recur)
thick *= one + fraction * (Get_value(&i) - one);
else
thick *= Get_value(&i);
} else {
if (last_recur)
thick /= one + fraction * ((r32) 0.7 - one);
else
thick /= (r32) 0.7;
}
184
H
Quelltexte
break;
case ':':
if (next == '(') {
if (last_recur)
ang *= one + fraction * (Get_value(&i) - one);
else
ang *= Get_value(&i);
} else {
if (last_recur)
ang *= one + fraction * ((r32) 0.9 - one);
else
ang *= (r32) 0.9;
}
break;
case ';':
if (next == '(') {
if (last_recur)
ang *= one + fraction * (Get_value(&i) - one);
else
ang *= Get_value(&i);
} else {
if (last_recur)
ang /= one + fraction * ((r32) 0.9 - one);
else
ang /= (r32) 0.9;
}
break;
case '\'':
if (next == '(') {
r = Get_value(&i);
if (last_recur) {
dis *= one + fraction * (r - one);
dis2 *= one + fraction * (r - one);
} else {
dis *= r;
dis2 *= r;
}
} else {
if (last_recur) {
dis *= one + fraction * ((r32) 0.9 - one);
dis2 *= one + fraction * ((r32) 0.9 - one);
} else {
dis *= (r32) 0.9;
dis2 *= (r32) 0.9;
}
}
break;
case '\"':
if (next == '(') {
r = Get_value(&i);
if (last_recur) {
dis *= one + fraction * (r - one);
dis2 *= one + fraction * (r - one);
} else {
dis *= r;
dis2 *= r;
}
} else {
if (last_recur) {
dis /= one + fraction * ((r32) 0.9 - one);
dis2 /= one + fraction * ((r32) 0.9 - one);
} else {
dis /= (r32) 0.9;
dis2 /= (r32) 0.9;
}
}
break;
case 'Z':
save.dis2 = dis2;
if (next == '(') {
dis2 = Get_value(&i);
if (last_recur)
dis2 *= fraction;
185
H
Quelltexte
}
Vector_plus_fac(pos, fow, dis2, end);
if (user_form)
Define_form(pos, end, upp, col);
else if (closed_form)
Define_closed(pos, end, upp, col);
else
Define_block(pos, end, upp, col);
Vector_copy_r32(end, pos);
dis2 = save.dis2;
break;
case 'F':
save.dis = dis;
if (next == '(') {
dis = Get_value(&i);
if (last_recur)
dis *= fraction;
}
Vector_plus_fac(pos, fow, dis, end);
if (user_form)
Define_form(pos, end, upp, col);
else if (closed_form)
Define_closed(pos, end, upp, col);
else
Define_block(pos, end, upp, col);
Vector_copy_r32(end, pos);
dis = save.dis;
break;
case '[':
if (scount > max_stack)
User_error("Ran out of stack");
Vector_copy_r32(pos, stack[scount].pos);
Vector_copy_r32(fow, stack[scount].fow);
Vector_copy_r32(lef, stack[scount].lef);
Vector_copy_r32(upp, stack[scount].upp);
stack[scount].col = col;
stack[scount].dis = dis;
stack[scount].dis2 = dis2;
stack[scount].ang = ang;
stack[scount].thick = thick;
stack[scount].tr = tr;
if (closed_form) {
Vector_copy_r32(last, stack[scount].last);
stack[scount].last_col = last_col;
for (j = 1; j <= 8; j++)
Vector_copy_r32(last_v[j], stack[scount].last_v[j]);
}
scount++;
break;
case ']':
scount--;
Vector_copy_r32(stack[scount].pos, pos);
Vector_copy_r32(stack[scount].fow, fow);
Vector_copy_r32(stack[scount].lef, lef);
Vector_copy_r32(stack[scount].upp, upp);
col = stack[scount].col;
dis = stack[scount].dis;
dis2 = stack[scount].dis2;
ang = stack[scount].ang;
thick = stack[scount].thick;
tr = stack[scount].tr;
if (closed_form) {
Vector_copy_r32(stack[scount].last, last);
last_col = stack[scount].last_col;
for (j = 1; j <= 8; j++)
Vector_copy_r32(stack[scount].last_v[j], last_v[j]);
}
break;
case '{':
if (poly_on) {
pstack[pscount].count = vcount;
pstack[pscount].ver = (vector *) malloc(vcount * 12L);
if (pstack[pscount].ver == NULL)
User_error("Ran out of memory");
186
H
Quelltexte
Vector_copy_max_r32(vcount, ver, pstack[pscount].ver);
pscount++;
if (pscount > max_stack)
User_error("Ran out of stack");
}
poly_on = TRUE;
vcount = (s16) 1;
pcount = (s16) 1;
Vector_copy_r32(pos, ver[vcount++]);
break;
case 'f':
save.dis = dis;
if (next == '(') {
dis = Get_value(&i);
if (last_recur)
dis *= fraction;
}
Vector_plus_fac(pos, fow, dis, pos);
if (poly_on)
Vector_copy_r32(pos, ver[vcount++]);
dis = save.dis;
break;
case '.':
if (poly_on)
Vector_copy_r32(pos, ver[vcount++]);
break;
case 'g':
save.dis = dis;
if (next == '(') {
dis = Get_value(&i);
if (last_recur)
dis *= fraction;
}
Vector_plus_fac(pos, fow, dis, pos);
dis = save.dis;
break;
case 'z':
save.dis2 = dis2;
if (next == '(') {
dis2 = Get_value(&i);
if (last_recur)
dis2 *= fraction;
}
Vector_plus_fac(pos, fow, dis2, pos);
if (poly_on)
Vector_copy_r32(pos, ver[vcount++]);
dis2 = save.dis2;
break;
case '}':
if (vcount > (s16) 3) {
for (j = 1; j < vcount - 2; j++) {
poly_store[pcount][0] = 1;
poly_store[pcount][1] = j + 1;
poly_store[pcount][2] = j + 2;
poly_store[pcount++][3] = j + 2;
}
Save_object(vcount - 1, pcount - 1, col);
}
poly_on = FALSE;
if (pscount > 0) {
pscount--;
Vector_copy_max_r32(pstack[pscount].count, pstack[pscount].ver, ver);
vcount = pstack[pscount].count;
poly_on = TRUE;
}
break;
case 'c':
if (next == '(') {
col = (s16) Get_value(&i);
} else {
col++;
}
187
H
Quelltexte
break;
}
};
Process_end2();
}
/* Lparser main ----------------------------------------------------------- */
static void
Help(void)
{
char
s[max_file];
Get_comline_progname(s);
Message("%s [options] ls-filename\n\n", s);
Message("%s
Message("%s
Message("%s
Message("%s
Message("%s
Message("%s
Message("%s
Message("%s
Message("%s
Message("%s
\tnone
\t-v
\t-b
\t-B
\t-c
\t-d
\t-3
\t-R
\t-O
\t-V
(default) output Lviewer VOL file\n", "vol");
output POV object file\n", "pov");
output POV blob file\n", "pov");
output multiple POV blob files\n", "pov");
output inc files instead of pov files\n", "pov");
output polyface meshes DXF file\n", "dxf");
output 3dfaces DXF file\n", "dxf");
output triangles in RAW file\n", "raw");
output Blob Sculptor BLB file\n", "blb");
output VRML world file\n", "wrl");
Message("%s \t-X [name] use name.vol as base element\n", "base");
Message("%s \t-i
link base elements together\n", "base");
Message("%s
Message("%s
Message("%s
Message("%s
\t-S
\t-t
\t-r
\t-a
[num]
[num]
[num]
[num]
Message("%s
Message("%s
Message("%s
Message("%s
Message("%s
\t-u [num]
\t-l
\t-g
\t-L [num]
\t-P [num]
Message("%s \t-p [num]
set string size to num Kbytes\n", "set");
set minimum thickness\n", "set");
overrule recursion depth\n", "set");
overrule angle\n", "set");
mutate [num] times\n", "ls");
show final L-string\n", "ls");
add ground plane\n", "ls");
set amount for ~ command\n", "ls");
set amount for t command\n", "ls");
limit polygons to [num]\n", "limit");
Message("%s \t-x [num]
Message("%s \t-y [num]
Message("%s \t-z [num]
multiply x with [num] for campos\n", "new");
multiply y with [num] for campos\n", "new");
multiply z with [num] for campos\n", "new");
}
// ----------------------------- Function headers added by Ken ------------------------------
void L_Init();
void L_Finish();
void Replace_Once();
void L_System(int N);
void L_Mutate(int N);
void Print_Object();
float Expr_Eval(char *Expr_Str, int First, int Last);
bool BExp_Eval(char *Expr_Str, int First, int Last);
// --------------------------------- Main function (modified by Ken) -------------------------
void main(int argc, char *argv[]) {
char temp[max_file];
boolean found;
188
H
Quelltexte
// Store the pointers to the comline
s_argc = argc;
s_argv = argv;
// Display header and help file if needed
Message("%s\n", min_bar);
Message("%s v%s (%s)\n", "L-System Parser/Mutator", "5.1", __DATE__);
Message("%s\n", min_bar);
if (argc < 2) {
Help();
User_error("Need arguments\n");
};
// Set the option string
strcpy(opts, "VP:OhL:S:iecBbRd3vX:p:t:x:y:z:u:r:a:gl");
// Init files
Init_file_buf(0);
Init_file_buf(1);
Init_file_buf(2);
// Check for all the comline options
Get_comline_opt("t", &found, temp);
if (found)
sscanf(temp, "%f", &min_thick);
// set minimum thickness
Get_comline_opt("x", &found, temp);
if (found)
x = atof(temp);
else
x = 2;
// set x
Get_comline_opt("y", &found, temp);
if (found)
y = atof(temp);
else
y = 2;
// set y
Get_comline_opt("z", &found, temp);
if (found)
z = atof(temp);
else
z = 2;
// set z
Get_comline_opt("p", &found, temp);
if (found)
sscanf(temp, "%ld", &poly_limit);
// limit total generated polygons
Get_comline_opt("L", &rand_set, temp); // set random amount
if (rand_set) {
sscanf(temp, "%f", &rand_amount);
srand(0);
/* when using changing amounts of
* randomness for animations we want
* the seed to be the same so we set
* it here */
} else {
srand(time(NULL));
};
Get_comline_opt("P", &trope_set, temp); // set amount of trope
if (trope_set) {
sscanf(temp, "%f", &trope_amount);
srand(0);
// same for trope animations
};
Get_comline_opt("X", &user_form, x_name);
Get_comline_opt("i", &closed_form, temp);
Get_comline_opt("d",
Get_comline_opt("3",
Get_comline_opt("R",
Get_comline_opt("V",
Get_comline_opt("B",
&dxf1, temp);
&dxf2, temp);
&dxf3, temp);
&vrml, temp);
&pov_form3, temp);
/*
/*
/*
/*
/*
/* use a vol file as user shape */
/* create closed connected
* cylinders */
create a dxf file with inserts */
create a dxf with only polygons */
create a RAW triangle file */
create a WRL VRML v1.0 file */
create multiple povray blob files
189
H
Quelltexte
* for multi colord blobs */
Get_comline_opt("O",
Get_comline_opt("b",
Get_comline_opt("v",
Get_comline_opt("c",
&blb_form, temp);
&pov_form2, temp);
&pov_form, temp);
&inc_out, temp);
/*
/*
/*
/*
create
create
create
create
a
a
a
a
blb blob file */
povray blob file */
povray file */
povray inc file */
/*
* In these cases an user defined form is used. In the other cases a simple
* block or connected cylinder shape will be used.
*/
if (pov_form || pov_form2 || pov_form3 || blb_form)
user_form = TRUE;
// Read ls file and setup rules
L_Init();
L_Mutate(0);
// Create L-system production string
L_System((int)recursion);
// Parse production string and create geometry
L_draw();
// Add groundplane
Get_comline_opt("g", &found, temp);
if (found)
Ground_plane();
Close_datafile();
// De-allocate the memory allocated for the rules
L_Finish();
Message("\n");
//////////////////////////////////////////////////////////////////////////////////////////////
////
// Modified Part of Jan Derer
// Computation of the best Cameraposition
// and saving it, into the info.txt file for LV2POVID
createINFOTXT();
//////////////////////////////////////////////////////////////////////////////////////////////
////
}
// --------------------------- Function implementations added by Ken ------------------------
// --------------------------- Main L-system functions --------------------------------------
void L_System(int N) {
// This function executes the L-system.
int i;
Process_start2(lev - 1);
// Execute N productions in the current L-system
for (i = 0; i < N; i++) {
Process_update2(i);
Replace_Once();
}
Message("Size of string : %ld chars\n", strlen(Object_Str));
190
H
Quelltexte
Process_end2();
}
void Replace_Once() {
// This function executes one production in Object_Str, given the rules.
long i = 0, New_Len = 0;
int j, Rule;
bool Rule_Found;
New_Str[0] = '\0';
// Clear the new object string
while (i < Obj_Len) {
Rule_Found = false;
// Find the rule with the LONGEST predecessor that applies.
for (j = 0; j < N_Rules; j++) {
if (Rules[j]->Rule_Applies(i)) {
if (!Rule_Found) {
Rule = j;
Rule_Found = true;
}
else {
if (Rules[j]->Prd_Len() >= Rules[Rule]->Prd_Len())
Rule = j;
}
}
}
// If a rule was found, apply it; otherwise, simply add the current
// character to the new string.
if (Rule_Found) {
i += Rules[Rule]->Do_Replace(i);
New_Len = strlen(New_Str);
}
else {
New_Str[New_Len++] = Object_Str[i++];
New_Str[New_Len] = '\0';
}
}
strcpy(Object_Str, New_Str);
Obj_Len = strlen(Object_Str);
}
void Print_Object() {
cout << endl << Object_Str << endl;
}
void Replace_Str(char *Search_Str, char *Str1, char *Str2) {
// --------------- Function: Replace one substring with another -----------------// This function replaces all occurances of Str1 in Search_Str Str2.
char *Tmp_Ptr = strstr(Search_Str, Str1);
char Rem_Str[SUCC_L];
int i;
// Find the first occurance of Str1
while (Tmp_Ptr != NULL) {
// Replace this occurance of Str1 with Str2
i = Tmp_Ptr - Search_Str;
strcpy(Rem_Str, &Search_Str[i] + strlen(Str1));
Search_Str[i] = '\0';
strcat(Search_Str, Str2);
strcat(Search_Str, Rem_Str);
// Find the next occurance of Str1
Tmp_Ptr = strstr(Tmp_Ptr, Str1);
}
}
// ---------------------------- Functions used to initialize rules --------------------------
191
H
Quelltexte
void Repl_Var_Addr(char *Search_Str, char *Var, int Addr) {
/* --------------- Function: Replace Variables with Address ---------------------This function replaces all occurances of Var in Search_Str with (%Addr). It is
used by the constructor of Rule_Type to initialize the rule's successor. */
char Addr_Str[MAX_NUM_LEN + 3];
char Num_Str[MAX_NUM_LEN];
// First, create a new string of the form "(%Addr)"
strcpy(Addr_Str, "(%");
strcat(Addr_Str, itoa(Addr, Num_Str, 10));
strcat(Addr_Str, ")");
//Replace all occurrances of the variable with its address
Replace_Str(Search_Str, Var, Addr_Str);
}
void Repl_Vars_WCards(char *Dest, char *Src, char Var_List[MAX_VARS][VAR_L], int *N_Vars) {
/* ------------------ Function: Replace Variables with Wildcards ---------------------This function replaces all variables in Src with '#' and stores the resulting string
in Dest. (Note: Src is unchanged.) It also stores the variable names in Var_List,
beginning with the value of N_Vars. (This value is incremented each time a variable
is found and placed in Var_List). This function is used to initialize a rule's
predecessor, so that is is easy to see if matches the object string. */
char *Temp_Str;
int Var_Len;
Dest[0] = '\0';
while (strlen(Src) > 0) {
Temp_Str = strchr(Src, '(');
if (Temp_Str == NULL) {
// No more variables found
strcat(Dest, Src);
Src += strlen(Src);
}
else {
strncat(Dest, Src, strlen(Src) - strlen(Temp_Str) + 1);
// Find variables, separated by commas, until a ')' is found.
do {
Temp_Str++;
Var_Len = strcspn(Temp_Str, ",)");
if (Var_Len != 0) {
strcat(Dest, "#");
// Either a ',' or a ')'
strncat(Dest, &Temp_Str[Var_Len], 1);
// Copy this variable name to Var_List and increment N_Vars.
strncpy(Var_List[*N_Vars], Temp_Str, Var_Len);
Var_List[(*N_Vars)++][Var_Len] = '\0';
Temp_Str += Var_Len;
}
} while (Temp_Str[0] != ')');
Src = Temp_Str + 1;
}
}
}
Rule_Type::Rule_Type(char *Prd, char *Prv, char *Nxt, char *Cnd, char *Suc, double Prb, int
N_Par) {
/* This function initializes a rule, given all the necessary information about the rule. */
Prev_Ctx = NULL;
Next_Ctx = NULL;
Cond = NULL;
192
H
Quelltexte
// Initialize the predecessor
strcpy(Pred, Prd);
// Initialize the first successor (there may be more later)
Succ[0] = new char[SUCC_L];
strcpy(Succ[0], Suc);
// Initialize the probability of applying this successor
Probs[0] = Prb;
N_Param = N_Par;
// Initialize the previous context
if (strlen(Prv) > 0) {
Prev_Ctx = new char[PRED_L];
strcpy(Prev_Ctx, Prv);
}
// Initialize the next context
if (strlen(Nxt) > 0) {
Next_Ctx = new char[PRED_L];
strcpy(Next_Ctx, Nxt);
}
// Initialize the condition
if (strlen(Cnd) > 0) {
Cond = new char[COND_L];
strcpy(Cond, Cnd);
}
N_Succ = 1;
// Initially, there is only one successor.
}
Rule_Type::~Rule_Type() {
/* This function is the rule's destructor.
It de-allocates the memory allocated earlier. */
for (int i = 0; i < N_Succ; i++)
delete Succ[i];
if (Prev_Ctx != NULL) delete Prev_Ctx;
if (Next_Ctx != NULL) delete Next_Ctx;
if (Cond != NULL) delete Cond;
}
bool Rule_Type::Same_Rule(char *Prd, char *Prv, char *Nxt, char *Cnd) {
/* This function
most recently
read. If the
are identical
is used in stochastic L-systems to determine if the rule that was read in
is really just another possible successor for a rule that was previously
predecessor, previous context, next context, and condition of the new rule
to this rule, they are parts of the same rule. */
// First check the predecessor.
if (strcmp(Prd, Pred) != 0) return false;
// Check the previous context. Both contexts, and the condition, may be NULL, so we must
// check for null pointers to avoid errors.
if (Prev_Ctx != NULL) {
if (Prv[0] == '\0') return false;
else if (strcmp(Prv, Prev_Ctx) != 0) return false;
}
else if (Prv[0] != '\0') return false;
// Check the next context.
if (Next_Ctx != NULL) {
if (Nxt[0] == '\0') return false;
else if (strcmp(Nxt, Next_Ctx) != 0) return false;
}
else if (Nxt[0] != '\0') return false;
193
H
Quelltexte
// Check the condition.
if (Cond != NULL) {
if (Cnd[0] == '\0') return false;
else if (strcmp(Cnd, Cond) != 0) return false;
}
else if (Cnd[0] != '\0') return false;
// All of the tests pass; these two rules are the same.
return true;
}
void Rule_Type::Add_Succ(char *Suc, double Prb) {
/* This function is used to add a new possible successor to a rule that has already been
partially read. When the function Same_Rule() determines that the rule most recently
read is really just a new successor for a rule that has already been read, the new
rule can be inserted with just the successor and probability of using it. */
// First, create a new successor.
Succ[N_Succ] = new char[SUCC_L];
strcpy(Succ[N_Succ], Suc);
// Set up the probability for this successor.
Prb += Probs[N_Succ - 1];
Probs[N_Succ++] = Prb;
}
// ------------------------- Functions used to apply rules ---------------------------------
bool Obj_Matches(long i, char *Test_Str, int Dir, int *Block_Len) {
/* ------------------- Function: Does Object_Str match Test_Str? --------------------------This function checks to see if Test_Str matches Object_Str, starting at position i.
Dir is either 1 or -1, to look forward or backward in Object_Str. Block_Len is computed;
it's the length of the block in Object_Str that matches Test_Str. The character '#' in
Test_Str is considered any real number (a wildcard). This function returns TRUE if
Test_Str matches Object_Str at position i; or FALSE otherwise. */
long
long
char
char
Obj_Pos = i;
Tst_Pos = (Dir == 1) ? 0 : strlen(Test_Str) - 1;
*Obj_Ptr = &Object_Str[Obj_Pos];
*Tst_Ptr = &Test_Str[Tst_Pos];
int Tst_Len = strlen(Test_Str);
*Block_Len = 0;
while ((Obj_Pos >= 0) && (Obj_Pos < Obj_Len)) {
if (*Tst_Ptr != '#') {
if (*Tst_Ptr == *Obj_Ptr) {
Tst_Pos += Dir;
Tst_Ptr += Dir;
Obj_Pos += Dir;
Obj_Ptr += Dir;
*Block_Len = *Block_Len + 1;
if ((Tst_Pos < 0) || (Tst_Pos > (Tst_Len - 1)))
return true;
}
else return false;
}
else {
// We found a '#' in Test_Str. If we are looking at a real number in Object_Str,
// we must move through Object_Str until we reach the end of that number. Remember,
// the digits 0..9 or characters '+', '-', 'e', and '.' might be part of a number.
if (isdigit(*Obj_Ptr) || (strchr("+-e.", *Obj_Ptr) != NULL)) {
Tst_Pos += Dir;
Tst_Ptr += Dir;
while (isdigit(*Obj_Ptr) || (strchr("+-e.", *Obj_Ptr) != NULL)){
Obj_Pos += Dir;
194
H
Quelltexte
Obj_Ptr += Dir;
*Block_Len = *Block_Len + 1;
}
if ((Tst_Pos < 0) || (Tst_Pos > (Tst_Len - 1)))
return true;
}
else return false;
}
}
return false;
}
void Find_N_Args(char *Search_Str, float *Arg_List, int N_Args) {
/* ------------------- Function: Find N Arguments -------------------------------------This function finds N_Args numbers in Search_Str, and stores them in Arg_List. The
numbers must be enclosed in parentheses, and separated by commas. For example, if
Search_Str == A(1,2), then the arguments returned will be 1 and 2. */
char Num_Str[MAX_NUM_LEN];
int i = 0, j;
while (i < N_Args) {
Search_Str = strchr(Search_Str, '(');
while (*Search_Str != ')') {
Search_Str++;
j = 0;
while (isdigit(*(Search_Str + j)) || (strchr("+-e.", *(Search_Str + j))
!= NULL)) j++;
strncpy(Num_Str, Search_Str, j);
Num_Str[j] = '\0';
Arg_List[i++] = atof(Num_Str);
Search_Str += j;
}
}
}
void Repl_Addr_Args(char *Search_Str, char *Result_Str, float *Arg_List) {
/* ---------------------- Function: Replace Addresses with Arguments --------------------This function replaces all occurances of (%x) in Search_Str with the value of
Arg_List[x]. It stores the result in Result_Str. (Note: Search_Str is unchanged.)
For example, if Search_Str is A((%0),(%1)) and Arg_List is [1,2], then Result_Str
will be A(1,2). */
char *Rpl_Ptr = Search_Str;
char *Tmp_Ptr = Search_Str;
char Num_Str[MAX_NUM_LEN];
Result_Str[0] = '\0';
int i;
while (strlen(Rpl_Ptr) > 0) {
Rpl_Ptr = strchr(Tmp_Ptr, '%');
if (Rpl_Ptr == NULL) {
strcat(Result_Str, Tmp_Ptr);
break;
}
else if (*(Rpl_Ptr - 1) == '(') {
strncat(Result_Str, Tmp_Ptr, strlen(Tmp_Ptr) - strlen(Rpl_Ptr));
Rpl_Ptr++;
i = 0;
while (isdigit(*(Rpl_Ptr + i))) i++;
strncpy(Num_Str, Rpl_Ptr, i);
Num_Str[i] = '\0';
sprintf(Num_Str, "%g\0", Arg_List[atoi(Num_Str)]);
strcat(Result_Str, Num_Str);
Rpl_Ptr += i;
Tmp_Ptr = Rpl_Ptr;
}
else Rpl_Ptr++;
}
}
bool Rule_Type::Rule_Applies(long i) {
195
H
Quelltexte
/* ------------------------- Function: Does Rule Apply? -----------------------------This function checks to see if a rule applies at position i in Search_Str. Block_Len
is calculated; it's the length of characters in Object_Str that must be replaced if this
rule is applied. */
int N_Brack, Tmp;
long j;
Block_Len = 0; Prev_Len = 0;
// First, check if the predecessor matches
if (!Obj_Matches(i, Pred, 1, &Block_Len)) return false;
// Check for previous context
if (Prev_Ctx != NULL) {
// Find the previous context. Don't forget to ignore branches and ignored characters.
j = i - 1;
while ((j >= 0) && ((strchr(Ign_Chars, Object_Str[j]) != NULL) ||
(Object_Str[j] == '[') || (Object_Str[j] == ']'))) {
if (Object_Str[j] == ']') {
N_Brack = 0;
do {
if (Object_Str[j] == ']') N_Brack++;
if (Object_Str[j] == '[') N_Brack--;
j--;
} while (N_Brack > 0);
}
else j--;
}
if (j < 0) return false;
if (!Obj_Matches(j, Prev_Ctx, -1, &Prev_Len)) return false;
}
// Check for next context
if (Next_Ctx != NULL) {
// Again, find the next context, making sure to ignore branches.
j = i + 1;
while ((j < Obj_Len) && (Object_Str[j] != ']') &&
((strchr(Ign_Chars, Object_Str[j]) != NULL) ||
(Object_Str[j] == '['))) {
if (Object_Str[j] == '[') {
N_Brack = 0;
do {
if (Object_Str[j] == '[') N_Brack++;
if (Object_Str[j] == ']') N_Brack--;
j++;
} while (N_Brack > 0);
}
else j++;
}
if ((j >= Obj_Len) || (Object_Str[j] == ']')) return false;
if (!Obj_Matches(j, Next_Ctx, 1, &Tmp)) return false;
}
// Check if the condition is met
return Condition_Met(i);
}
bool Rule_Type::Condition_Met(long i) {
/* ----------------------------- Function: Is Condition Met? ----------------------------This function checks to see if a rule's condition is met at position i in Object_Str. */
char Cond_Str[COND_L];
float Arg_List[MAX_VARS];
if (Cond == NULL) return true;
else {
196
H
Quelltexte
Find_N_Args(&Object_Str[i - Prev_Len], Arg_List, N_Param);
Repl_Addr_Args(Cond, Cond_Str, Arg_List);
return BExp_Eval(Cond_Str, 0, strlen(Cond_Str) - 1);
}
}
int Rule_Type::Do_Replace(long i) {
/* This function executes a rule replacement at position i in Object_Str. */
char Succ_Str[SUCC_L];
char Rem_Str[SUCC_L];
char Num_Str[MAX_NUM_LEN];
float Arg_List[MAX_VARS];
char *Suc_Ptr;
char *Tmp_Ptr;
int N_Paren;
double Rnd = (double)(rand() % 100) / 100;
int j = 0;
// First, randomly choose the successor we will use.
while (Probs[j] < Rnd)
j++;
// Next, store any necessary numbers in the argument list.
Find_N_Args(&Object_Str[i - Prev_Len], Arg_List, N_Param);
// Next, create a successor string with the addresses replaced with numbers.
Repl_Addr_Args(Succ[j], Succ_Str, Arg_List);
// Next, evaluate any expressions within the successor string.
Suc_Ptr = Succ_Str;
Tmp_Ptr = Succ_Str;
while (strlen(Suc_Ptr) > 0) {
Suc_Ptr = strchr(Tmp_Ptr, '(');
if (Suc_Ptr == NULL) break;
while (*Suc_Ptr != ')') {
Tmp_Ptr = (++Suc_Ptr);
N_Paren = 0;
while (((*Suc_Ptr != ')') && (*Suc_Ptr != ',')) || (N_Paren != 0)) {
if (*Suc_Ptr == '(') N_Paren++;
if (*Suc_Ptr == ')') N_Paren--;
Suc_Ptr++;
}
sprintf(Num_Str, "%g\0", Expr_Eval(Tmp_Ptr, 0, Suc_Ptr - Tmp_Ptr - 1));
strcpy(Rem_Str, Suc_Ptr);
*Tmp_Ptr = '\0';
strcat(Succ_Str, Num_Str);
strcat(Succ_Str, Rem_Str);
Tmp_Ptr += strlen(Num_Str);
Suc_Ptr = Tmp_Ptr;
}
Suc_Ptr++;
Tmp_Ptr = Suc_Ptr;
}
// Finally, add the replacement string to the end of the new string.
strcat(New_Str, Succ_Str);
// Return the length of the section of the object string that was replaced.
return Block_Len;
}
// ----------------------- Functions used in evaluating expressions ---------------------
float Fact_Eval(char *Expr_Str, int First, int Last) {
197
H
Quelltexte
// This function evaluates a factor.
char Num_Str[MAX_NUM_LEN];
char *Tmp_Ptr;
if ((Expr_Str[First] == '(') && (Expr_Str[Last] == ')'))
return Expr_Eval(Expr_Str, First+1, Last-1);
else {
Tmp_Ptr = &Expr_Str[First];
strncpy(Num_Str, Tmp_Ptr, Last - First + 1);
Num_Str[Last - First + 1] = '\0';
return (float)atof(Num_Str);
}
}
float Term_Eval(char *Expr_Str, int First, int Last) {
// This function evaluates a term.
int N_Paren = 0;
int i = Last;
while ((((Expr_Str[i] != '*') && (Expr_Str[i] != '/')) || (N_Paren != 0)) &&
(i > First)) {
if (Expr_Str[i] == ')') N_Paren++;
if (Expr_Str[i] == '(') N_Paren--;
i--;
}
if (Expr_Str[i] == '*')
return Term_Eval(Expr_Str,First,i-1) * Fact_Eval(Expr_Str,i+1,Last);
if (Expr_Str[i] == '/')
return Term_Eval(Expr_Str,First,i-1) / Fact_Eval(Expr_Str,i+1,Last);
if (i == First)
return Fact_Eval(Expr_Str,First,Last);
return 0;
}
float Expr_Eval(char *Expr_Str, int First, int Last) {
// This function evaluates an arithmetic expression.
int N_Paren = 0;
int i = Last;
while (i > First) {
if (N_Paren == 0) {
if ((Expr_Str[i] == '+') && (Expr_Str[i-1] != 'e'))
return Expr_Eval(Expr_Str,First,i-1) + Term_Eval(Expr_Str,i+1,Last);
else if ((Expr_Str[i] == '-') && (Expr_Str[i-1] != 'e'))
return Expr_Eval(Expr_Str,First,i-1) - Term_Eval(Expr_Str,i+1,Last);
}
if (Expr_Str[i] == ')') N_Paren++;
if (Expr_Str[i] == '(') N_Paren--;
i--;
}
return Term_Eval(Expr_Str,First,Last);
}
bool BExp_Eval(char *Expr_Str, int First, int Last) {
// This function evaluates a boolean expression.
int N_Paren = 0;
int i = Last;
if (Expr_Str[First] == '!')
return (!BExp_Eval(Expr_Str, First+1, Last));
else if ((Expr_Str[First] == '(') && (Expr_Str[Last] == ')'))
return (BExp_Eval(Expr_Str, First+1, Last-1));
else {
while (i > First) {
if (N_Paren == 0) {
if (Expr_Str[i] == '&')
return (BExp_Eval(Expr_Str, First, i-1) &&
BExp_Eval(Expr_Str, i+1, Last));
else if (Expr_Str[i] == '|')
return (BExp_Eval(Expr_Str, First, i-1) ||
198
H
Quelltexte
BExp_Eval(Expr_Str, i+1, Last));
else if (Expr_Str[i] == '=')
return (Expr_Eval(Expr_Str, First, i-1) ==
Expr_Eval(Expr_Str, i+1, Last));
else if (Expr_Str[i] == '>') {
if (Expr_Str[i+1] == '=')
return (Expr_Eval(Expr_Str, First, i-1)
Expr_Eval(Expr_Str, i+2, Last));
else
return (Expr_Eval(Expr_Str, First, i-1)
Expr_Eval(Expr_Str, i+1, Last));
}
else if (Expr_Str[i] == '<') {
if (Expr_Str[i+1] == '=')
return (Expr_Eval(Expr_Str, First, i-1)
Expr_Eval(Expr_Str, i+2, Last));
else
return (Expr_Eval(Expr_Str, First, i-1)
Expr_Eval(Expr_Str, i+1, Last));
}
>=
>
<=
<
}
if (Expr_Str[i] == ')') N_Paren++;
if (Expr_Str[i] == '(') N_Paren--;
i--;
}
}
return (Expr_Eval(Expr_Str, First, Last) != 0);
}
// ------------------------ Functions used to initialize the L-system ----------------------
bool Get_Line(char *Str, FILE *In_File) {
/* This function reads a line correctly from the input file. It eliminates any unneeded
whitespace and ignores the comments (anything beginning with "/*". It reads until it
finds a valid line; if no valid lines are found, it returns FALSE. */
char Tmp_Str[MAX_LIN_LEN];
int i, j, Len;
do {
i = 0; j = 0;
fgets(Tmp_Str, MAX_LIN_LEN, In_File);
Len = strlen(Tmp_Str);
if (Len > MAX_LIN_LEN) return false;
while (i < Len) {
// Check for the beginning of a comment
if (i < (Len - 1)) {
if ((Tmp_Str[i] == '/') && (Tmp_Str[i+1] == '*'))
break;
}
// If it's a valid character, add it to the string being read
if (strchr(" \r\n\t", Tmp_Str[i]) == NULL) {
Str[j] = Tmp_Str[i];
j++;
}
else if ((Tmp_Str[i] == ' ') && (j > 0)) {
if ((Str[0] == '#') && (Str[j-1] != ' ')) {
Str[j] = ' ';
j++;
}
}
i++;
}
//Check for trailing space, then terminate the string.
if ((j > 0) && (Str[j-1] == ' ')) j--;
199
H
Quelltexte
Str[j] = '\0';
} while ((j == 0) && !feof(In_File));
return (j > 0);
}
void L_Init(void) {
/* This function reads a .LS file and sets up the initial axiom and rules. It parses the
file and does some error checking to make sure the rules are valid.
Note: This function was originally written by Larens Lapre, modified by Ken Kopp. */
FILE *In_File;
char name[max_file], Temp_Str[MAX_LIN_LEN];
boolean found, Read_Ok;
int i, Len, N_Vars;
double Prob;
char *Pred_Ptr, *Succ_Ptr, *Cond_Ptr;
char *Prev_Ptr, *Next_Ptr;
char Pred[PRED_L], Succ[SUCC_L], Cond[COND_L];
char Prev[PRED_L], Next[PRED_L];
char *CName_Str, *CVal_Str;
char Num_Str[MAX_NUM_LEN];
char Var_List[MAX_VARS][VAR_L];
// Seed the random number generator
srand(time(NULL));
// Initialize memory
stack = (s_rec *) malloc(sizeof(s_rec) * max_stack);
pstack = (p_rec *) malloc(sizeof(p_rec) * max_stack);
if ((stack == NULL) || (pstack == NULL))
User_error("Not enough memory to startup");
// Get file name
Get_comline_filename(name);
if (strstr(name, ".ls") == NULL)
strcat(name, ".ls");
In_File = fopen(name, "rt");
if (!In_File)
User_error("Cannot find file [%s]", name);
Message("L-system file
: %s\n", name);
// Preprocessor statements, such as #define and #ignore:
strcpy(Ign_Chars, "FfZz+-");
Get_Line(Temp_Str, In_File);
while (Temp_Str[0] == '#') {
if (strstr(Temp_Str, "#define") != NULL) {
// First, find the name of the constant.
CName_Str = strchr(Temp_Str, ' ');
CName_Str++;
// Next, find the value of the constant. If it is preceded by a '-', put
// parentheses around it so it won't fool the expression evaluator.
CVal_Str = strchr(CName_Str, ' ');
(++CVal_Str)[-1] = '\0';
if (CVal_Str[0] == '-') {
Len = strlen(CVal_Str);
for (i = Len; i > 0; i--)
200
H
Quelltexte
CVal_Str[i] = CVal_Str[i-1];
CVal_Str[0] = '(';
CVal_Str[Len+1] = ')';
CVal_Str[Len+2] = '\0';
}
// Insert the constant into the lists of constants.
strcpy(Const_Names[N_Const], CName_Str);
strcpy(Const_Vals[N_Const], CVal_Str);
N_Const++;
Message("Constant
: %s = %s\n", CName_Str, CVal_Str);
}
else if (strstr(Temp_Str, "#ignore:") != NULL) {
// Find the list of characters that are to be ignored.
CVal_Str = strchr(Temp_Str, ' ');
CVal_Str++;
strcpy(Ign_Chars, CVal_Str);
Message("Ignored
: %s\n", Ign_Chars);
}
Get_Line(Temp_Str, In_File);
}
// Recursion level
sscanf(Temp_Str, "%f", &recursion);
Get_comline_opt("r", &found, Temp_Str);
// Overrule?
if (found)
sscanf(Temp_Str, "%f", &recursion);
Message("Recursion depth: %g\n", recursion);
lev = (s16) recursion;
fraction = recursion - (r32) lev;
if (fraction > zero) {
lev++;
growing = TRUE;
}
else {
growing = FALSE;
}
// Check for fraction
// Basic angle
Get_Line(Temp_Str, In_File);
sscanf(Temp_Str, "%f", &ang);
Get_comline_opt("a", &found, Temp_Str);
if (found)
sscanf(Temp_Str, "%f", &ang);
Message("Basic angle
: %g\n", ang);
ang = (ang / 180.0) * 3.141592654;
// Overrule?
// Thickness
Get_Line(Temp_Str, In_File);
sscanf(Temp_Str, "%f", &thick);
Message("Thickness
: %g\n", thick);
thick /= 100.0;
// Initial Axiom
Get_Line(Temp_Str, In_File);
for (i = 0; i < N_Const; i++)
Replace_Str(Temp_Str, Const_Names[i], Const_Vals[i]);
strcpy(Object_Str, Temp_Str);
Obj_Len = strlen(Object_Str);
Message("Axiom
: %s\n", Object_Str);
// Get rules
while (!feof(In_File)) {
// First, read the next rule.
201
H
Quelltexte
Read_Ok = Get_Line(Temp_Str, In_File);
if (!Read_Ok) break;
Message("Rule
: %s\n", Temp_Str);
// Execute the preprocessor (#define) statements
for (i = 0; i < N_Const; i++)
Replace_Str(Temp_Str, Const_Names[i], Const_Vals[i]);
N_Vars = 0;
Prob = 1.0;
// Find the successor part (the section after the last '->').
Succ_Ptr = strstr(Temp_Str, "->");
if (Succ_Ptr == NULL) User_error("Error in rule.");
while (strstr(Succ_Ptr + 1, "->") != NULL)
Succ_Ptr = strstr(Succ_Ptr + 1, "->");
Succ_Ptr += 2;
Succ_Ptr[-2] = '\0';
// Check for stochastic L-system
if (Succ_Ptr[0] == '(') {
i = 0;
while (Succ_Ptr[i+1] != ')')
Num_Str[i++] = Succ_Ptr[i+1];
Num_Str[i] = '\0';
Prob = atof(Num_Str);
Succ_Ptr += (i + 2);
}
if (strlen(Succ_Ptr) == 0) User_error("Error in rule.");
// Find the condition part (the section after the ':').
Cond[0] = '\0';
Cond_Ptr = strchr(Temp_Str, ':');
if (Cond_Ptr != NULL) {
(++Cond_Ptr)[-1] = '\0';
if (strcmp(Cond_Ptr, "*") == 0) Cond_Ptr = NULL;
}
// Find any 'context sensitivity' blocks before and after the block section.
Next[0] = '\0';
Next_Ptr = strchr(Temp_Str, '>');
if (Next_Ptr != NULL) {
(++Next_Ptr)[-1] = '\0';
if (strcmp(Next_Ptr, "*") == 0) Next_Ptr = NULL;
}
Prev[0] = '\0';
Prev_Ptr = strchr(Temp_Str, '<');
if (Prev_Ptr != NULL) {
Pred_Ptr = Prev_Ptr + 1;
Prev_Ptr = Temp_Str;
Pred_Ptr[-1] = '\0';
if (strcmp(Prev_Ptr, "*") == 0) Prev_Ptr = NULL;
}
else Pred_Ptr = Temp_Str;
// Prepare each part of the rule for insertion into the rule list.
if (Prev_Ptr != NULL) Repl_Vars_WCards(Prev, Prev_Ptr, Var_List, &N_Vars);
Repl_Vars_WCards(Pred, Pred_Ptr, Var_List, &N_Vars);
if (Next_Ptr != NULL) Repl_Vars_WCards(Next, Next_Ptr, Var_List, &N_Vars);
if (Cond_Ptr != NULL) {
strcpy(Cond, Cond_Ptr);
for (i = 0; i < N_Vars; i++)
Repl_Var_Addr(Cond, Var_List[i], i);
202
H
Quelltexte
}
strcpy(Succ, Succ_Ptr);
for (i = 0; i < N_Vars; i++)
Repl_Var_Addr(Succ, Var_List[i], i);
// Insert the rule into the rule list, first checking to see if the predecessor
is already there (in which case,
// only the new successor needs to be added).
found = false;
for (i = 0; i < N_Rules; i++)
if (Rules[i]->Same_Rule(Pred, Prev, Next, Cond)) {
Rules[i]->Add_Succ(Succ, Prob);
found = true;
break;
}
if (!found)
Rules[N_Rules++] = new Rule_Type(Pred, Prev, Next, Cond, Succ, Prob, N_Vars);
}
fclose(In_File);
// Set start values for F and Z distances
dis = 100.0;
dis2 = dis * 0.5;
}
// -------------------------- Functions used to mutate the rules -------------------------
void L_Mutate(int N) {
/* This function executes mutations on the rules that have been initialized. The
mutations are randomly chosen, based on certain probabilities. It accepts one integer,
N (the number of mutations to execute). */
int i, j, k, n1, n2;
double R;
char Str1[PRED_L], Str2[PRED_L];
for (int l = 0; l < N; l++) {
R
i
j
k
=
=
=
=
(double)(rand() % 100) / 100;
rand() % N_Rules;
rand() % N_Rules;
0;
if (R <= 0.2) {
n1 = Rules[i]->Get_Pred(Str1);
// Choose a random number between 0 and 1
// Choose two random rules
// Insert (20% probability)
// Try up to 10 times to insert the predecessor of rule 1 into another rule.
while (!Rules[j]->Insert(Str1, n1, false) && (k < 10)) {
j = rand() % N_Rules;
k++;
}
}
else if (R <= 0.4) {
n1 = Rules[i]->Get_Pred2(Str1);
n2 = Rules[j]->Get_Pred2(Str2);
// Replace (20% probability)
// Try up to 10 times to replace every occurrance of rule 1's predecessor in
// rule 1's successor with another string.
while ((n1 != n2) && (k < 10)) {
j = rand() % N_Rules;
n2 = Rules[j]->Get_Pred2(Str2);
k++;
}
if (k < 10) Rules[i]->Replace(Str1, Str2);
}
else if (R <= 0.6) {
n1 = Rules[i]->Get_Pred(Str1);
// Append (20% probability)
203
H
Quelltexte
// Try up to 10 times to append rule 1's predecessor to anoter rule.
while (!Rules[j]->Insert(Str1, n1, true) && (k < 10)) {
j = rand() % N_Rules;
k++;
}
}
else if (R <= 0.8) {
Rules[i]->Swap_Dirs();
}
else if (R <= 0.9) {
Rules[i]->Swap_Sizes();
}
else if (R <= 1.0) {
Rules[i]->Rand_Expr();
}
// Swap directions (20% probability)
// Swap sizes (10% probability)
// Randomize expressions (10% probability)
}
}
int Rule_Type::Get_Pred(char *Str) {
/* This function returns the rule's entire predecessor, including the parameters. */
int i, N_Vars = 0;
char Tmp_Str[MAX_NUM_LEN];
int Len = strlen(Pred);
Str[0] = '\0';
for (i = 0; i < Len; i++) {
if (Pred[i] != '#') {
Tmp_Str[0] = Pred[i];
Tmp_Str[1] = '\0';
strcat(Str, Tmp_Str);
}
else {
strcat(Str, "(%");
strcat(Str, itoa(N_Vars++, Tmp_Str, 10));
strcat(Str, ")");
}
}
return N_Param;
}
int Rule_Type::Get_Pred2(char *Str) {
/* This function returns only part of the rule's predecessor--without any parameters.
For example, if the predecessor is A(x,y), then only A is returned. This is used for
replacement; if we are replacing B(y,z) with A(x,y), we really want to replace it
with A(y,z). */
int i = 0, N_Vars = 0;
char *Tmp_Str;
Tmp_Str = strchr(Pred, '(');
if (Tmp_Str == NULL)
strcpy(Str, Pred);
else {
strncpy(Str, Pred, &Tmp_Str[0] - &Pred[0]);
while (Tmp_Str[i+1] != ')') {
if (Tmp_Str[i+1] == '#')
N_Vars++;
i++;
}
}
return N_Vars;
}
bool Rule_Type::Insert(char *Str, int N_Args, bool App) {
/* This function inserts Str into a random position in the rule's successor. The position
must not be inside an expression, so this is checked for. It accepts the string to
insert, the number of arguments in the string being inserted, and the boolean flag
App (append if true). If the number of arguments in the string being inserted is
greater than the number of parameters in the rule in which it is being inserted, the
204
H
Quelltexte
string will not be inserted and FALSE (indicating failure) will be returned. */
int i = rand() % N_Succ;
int j = rand() % strlen(Succ[i]);
int N_Paren = 0;
char Rem_Str[SUCC_L];
if (N_Param < N_Args) return false;
if (App)
strcat(Succ[i], Str);
else {
for (int k = 0; k < j; k++) {
if (Succ[i][k] == '(') N_Paren++;
if (Succ[i][k] == ')') N_Paren--;
}
while (N_Paren > 0) {
if (Succ[i][j] == '(') N_Paren++;
if (Succ[i][j] == ')') N_Paren--;
j++;
}
strcpy(Rem_Str, &Succ[i][j]);
Succ[i][j] = '\0';
strcat(Succ[i], Str);
strcat(Succ[i], Rem_Str);
}
return true;
}
void Rule_Type::Replace(char *Str1, char *Str2) {
/* This function replaces all occurrances of Str1 in a rule's successor with Str2. */
for (int i = 0; i < N_Succ; i++)
Replace_Str(Succ[i], Str1, Str2);
}
void Rule_Type::Swap_Dirs() {
/* This function swaps the directions of drawing commands in a rule's successor. It
makes the following exchanges:
+ <-> & <-> ^
/ <-> \
| <-> %
: <-> ;
' <-> " */
int Len;
for (int i = 0; i < N_Succ; i++) {
Len = strlen(Succ[i]);
for (int j = 0; j < Len; j++) {
if (Succ[i][j] == '+') Succ[i][j] = '-';
else if (Succ[i][j] == '-') Succ[i][j] = '+';
else if (Succ[i][j] == '&') Succ[i][j] = '^';
else if (Succ[i][j] == '^') Succ[i][j] = '&';
else if (Succ[i][j] == '/') Succ[i][j] = '\\';
else if (Succ[i][j] == '\\') Succ[i][j] = '/';
else if (Succ[i][j] == '|') Succ[i][j] = '%';
else if (Succ[i][j] == '%') {
if (j == 0) Succ[i][j] = '|';
else if (Succ[i][j-1] != '(') Succ[i][j] = '|';
}
else if (Succ[i][j] == ':') Succ[i][j] = ';';
else if (Succ[i][j] == ';') Succ[i][j] = ':';
else if (Succ[i][j] == '\'') Succ[i][j] = '\"';
else if (Succ[i][j] == '\"') Succ[i][j] = '\'';
}
}
}
void Rule_Type::Swap_Sizes() {
/* This function swaps the sizes of drawing commands in a rule's successor. It
makes the following exchanges:
205
H
Quelltexte
F <-> Z
f <-> z
? <-> ! */
int Len;
for (int i = 0; i < N_Succ; i++) {
Len = strlen(Succ[i]);
for (int j = 0; j < Len; j++) {
if (Succ[i][j] == 'F') Succ[i][j] = 'Z';
else if (Succ[i][j] == 'Z') Succ[i][j] =
else if (Succ[i][j] == 'f') Succ[i][j] =
else if (Succ[i][j] == 'z') Succ[i][j] =
else if (Succ[i][j] == '?') Succ[i][j] =
else if (Succ[i][j] == '!') Succ[i][j] =
}
}
'F';
'z';
'f';
'!';
'?';
}
void Rule_Type::Rand_Expr() {
/* This function introduces randomness into all the expressions in a rule's successor.
It multiplies the first term in each expression by a random constant between -2 and 2. */
int j, N_Paren;
char Rem_Str[SUCC_L];
for (int i = 0; i < N_Succ; i++) {
j = 0;
while (Succ[i][j] != '\0') {
if ((Succ[i][j] == '(') || (Succ[i][j] == ',')) {
strcpy(Rem_Str, &Succ[i][j+1]);
Succ[i][j+1] = '\0';
sprintf(&Succ[i][j+1], "(%.2lf)*", (double)(rand() % 100) / 25.0 - 2.0);
strcat(Succ[i], Rem_Str);
N_Paren = 0;
j++;
while (((Succ[i][j] != ',') && (Succ[i][j] != ')')) || (N_Paren != 0)) {
if (Succ[i][j] == '(') N_Paren++;
if (Succ[i][j] == ')') N_Paren--;
j++;
}
}
else j++;
}
}
}
void L_Finish() {
for (int i = 0; i < N_Rules; i++)
delete Rules[i];
}
//////////////////////////////////////////////////////////////////////////////////////////////
////
/*
Adding functions to computate the best Cameraposition
*/
/*
by Jan Derer
*/
// Absolut-function for variables from type double
double dAbs(double number)
{
if(number < 0)
return (-1*number);
return number;
}
// Computate the percantage "perc" from the Axis x, y, or z
double percOfAxisValues(double perc, char type)
{
if(perc > 100.0 || perc < 0.0)
return -1;
switch(type)
{
case 'x':
case 'y':
return (__max(maxX, dAbs(minX)) * (perc/100.00));
break;
return (__max(maxY, dAbs(minY)) * (perc/100.00));
206
H
Quelltexte
case 'z':
default :
break;
return (__max(maxZ, dAbs(minZ)) * (perc/100.00));
break;
return -1;
}
}
// Computate the best cameraposition and create INFO.TXT
void createINFOTXT(void)
{
char
Center0 = 0;
double CamX = 0.0,
CamY = 0.0,
CamZ = 0.0,
CenterX = (maxX+minX)/2,
CenterY = (maxY+minY)/2,
CenterZ = (maxZ+minZ)/2,
maxXYZ = 0.0,
maxValue = 0.0;
printf("\nCalculating the best Cameraposition.\n");
if(minX >= -1 || (dAbs(minX) <= percOfAxisValues(1.0, 'x')))
Center0++;
if(minY >= -1 || (dAbs(minY) <= percOfAxisValues(1.0, 'y')))
Center0++;
if(maxZ <= 1 || (maxZ <= percOfAxisValues(1, 'z')))
Center0++;
switch(Center0)
{
// If the scene is a real 3D-Scene
case 0 :
CamX = minX*x;
CamY = minY*y;
CamZ = maxZ*z;
break;
// The scene is 2D and one axis have the expansion near 0
case 1 :
if(((dAbs(CenterX) <= 1) || (dAbs(CenterX) <=
percOfAxisValues(1.0, 'x'))) &&
((dAbs(CenterY) <= 1) || (dAbs(CenterY) <=
percOfAxisValues(1.0, 'y'))) &&
((dAbs(CenterZ) <= 1) || (dAbs(CenterZ) <=
percOfAxisValues(1.0, 'z'))))
{
maxValue = __max(__max(maxX, maxY), maxZ);
if(maxX <= 1)
{
CamX = maxValue * x;
CamY = CenterY;
CamZ = CenterZ;
}
else if(maxY <= 1)
{
CamX = CenterX;
CamY = maxValue * y;
CamZ = CenterZ;
}
else
{
CamX = CenterX;
CamY = CenterY;
CamZ = maxValue * z;
}
break;
}
if((dAbs(CenterX) <= 1) || (dAbs(CenterX) <=
percOfAxisValues(1.0, 'x')))
{
CamX = (dAbs(CenterY) + dAbs(CenterZ)) * x;
CamY = CenterY;
CamZ = CenterZ;
}
else if((dAbs(CenterY) <= 1) || (dAbs(CenterY) <=
percOfAxisValues(1.0, 'y')))
{
CamX = CenterX;
207
H
Quelltexte
CamY = (dAbs(CenterX) + dAbs(CenterZ)) * y;
CamZ = CenterZ;
}
else
{
CamX = CenterX;
CamY = CamY;
CamZ = (dAbs(CenterX) + dAbs(CenterY)) * z;
}
break;
// The scene is 2D and one axis have the expansion near 0 and
// the expansion of another axis is equal
case 2 :
if(dAbs(CenterX) > percOfAxisValues(1.0, 'x'))
maxXYZ = CenterX;
if((dAbs(CenterY) > percOfAxisValues(1.0, 'y')) &&
(maxXYZ < CenterY))
maxXYZ = CenterY;
if((dAbs(CenterZ) > percOfAxisValues(1.0, 'z')) && (maxXYZ <
CenterZ))
maxXYZ = CenterZ;
if((CenterX != maxXYZ) && (__max(maxX,dAbs(minX)) <=
percOfAxisValues(1.0, 'x')))
{
CamX = maxXYZ * x;
CamY = CenterY;
CamZ = CenterZ;
}
else if((CenterY != maxXYZ) && (__max(maxY,dAbs(minY)) <=
percOfAxisValues(1.0, 'y')))
{
CamX = CenterX;
CamY = maxXYZ * y;
CamZ = CenterZ;
}
else
{
CamX = CenterX;
CamY = CenterY;
CamZ = maxXYZ * z;
}
break;
// The expansion on every axis is equal and scene is 2D
default:
maxValue = __max(__max(maxX, maxY), maxZ);
if(maxX <= 1)
{
CamX = maxValue * x;
CamY = CenterY;
CamZ = CenterZ;
}
else if(maxY <= 1)
{
CamX = CenterX;
CamY = maxValue * y;
CamZ = CenterZ;
}
else
{
CamX = CenterX;
CamY = CenterY;
CamZ = maxValue * z;
}
break;
}
// Creating INFO.TXT
printf("Writing result into INFO.TXT.\n");
volume_file = fopen("INFO.TXT", "wt");
if (volume_file) {
fprintf(volume_file, "Model
: (output.vol)\n");
fprintf(volume_file, "--------------------------------------------------------\n");
fprintf(volume_file, "Minima xyz : (%.3f %.3f %.3f)\n", minX, minY, minZ);
fprintf(volume_file, "Maxima xyz : (%.3f %.3f %.3f)\n", maxX, maxY, maxZ);
208
H
Quelltexte
fprintf(volume_file, "Center
: (%.4f %.4f %.4f)\n\n", CenterX, CenterY,
CenterZ);
fprintf(volume_file, "Camera\n");
fprintf(volume_file, "--------------------------------------------------------\n");
fprintf(volume_file, "Perspective : 11.1364\n");
fprintf(volume_file, "Scale
: 486.32\n");
fprintf(volume_file, "Location
: (%.2f %.2f %.2f)\n", CamX, CamY, CamZ);
fprintf(volume_file, "Looking at : (%.4f %.4f %.4f)\n", CenterX, CenterY,
CenterZ);
fprintf(volume_file, "Up vector
: (0.0000000 0.0000000 0.0000000)\n\n");
fprintf(volume_file, "--------------------------------------------------------\n");
fprintf(volume_file, "Model
: (output.vol)\n");
fprintf(volume_file, "--------------------------------------------------------\n");
fprintf(volume_file, "Minima xyz : (%.3f %.3f %.3f)\n", minX, minY, minZ);
fprintf(volume_file, "Maxima xyz : (%.3f %.3f %.3f)\n", maxX, maxY, maxZ);
fprintf(volume_file, "Center
: (%.4f %.4f %.4f)\n\n", CenterX, CenterY,
CenterZ);
fprintf(volume_file, "Camera\n");
fprintf(volume_file, "--------------------------------------------------------\n");
fprintf(volume_file, "Perspective : 11.1364\n");
fprintf(volume_file, "Scale
: 486.32\n");
fprintf(volume_file, "Location
: (%.2f %.2f %.2f)\n", CamX, CamY, CamZ);
fprintf(volume_file, "Looking at : (%.4f %.4f %.4f)\n", CenterX, CenterY,
CenterZ);
fprintf(volume_file, "Up vector
: (0.0000000 0.0000000 0.0000000)\n\n");
fprintf(volume_file, "--------------------------------------------------------\n\n");
}
else
User_error("Cannot open file INFO.TXT\n");
fclose(volume_file);
printf("Finish.\n\n");
}
//////////////////////////////////////////////////////////////////////////////////////////////
////
/* ------------------------------------------------------------------------End of file.
------------------------------------------------------------------------*/
Quelltexte von Visual L:
Dateiname: VisualL.dpr
program VisualL;
{%ToDo 'VisualL.todo'}
uses
Forms,
MDIMainSource in 'MDIMainSource.pas' {MDIMain},
MDIChildSource in 'MDIChildSource.pas' {MDIChildMain},
AboutSource in 'AboutSource.pas' {AboutForm},
CFGSource in 'CFGSource.pas' {CFGForm},
ShowPicSource in 'ShowPicSource.pas' {ShowPicForm},
Direct3DSource in 'Direct3DSource.pas' {Direct3DForm},
TurtleComSource in 'TurtleComSource.pas' {TurtleComForm},
CondFormSource in 'CondFormSource.pas' {CondForm},
ConsoleFormSource in 'ConsoleFormSource.pas' {ConsoleFom};
{$R *.res}
begin
Application.Initialize;
Application.Title := 'Visual L Version 1.0';
Application.CreateForm(TMDIMain, MDIMain);
Application.CreateForm(TConsoleFom, ConsoleFom);
209
H
Quelltexte
Application.Run;
end.
Dateiname: AboutSource.pas
{ ---------------------------------------------------------------------------Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
05. 06. 04
[email protected]
VisualL
1.0
Klassenname:
TAboutForm
Version:
1.0
Kurzbeschreibung:
Diese Klasse repräsentiert das Info-Fenster.
---------------------------------------------------------------------------- }
unit AboutSource;
{ ---------------------------------------------------------------------------BESCHREIBUNG DER SCHNITTSTELLE DER UNIT
---------------------------------------------------------------------------- }
interface
{ ---------------------------------------------------------------------------Liste alle öffentlich eingebundenen Units
---------------------------------------------------------------------------- }
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, jpeg, ExtCtrls;
{ ---------------------------------------------------------------------------Deklaration eigener Datentypen
---------------------------------------------------------------------------- }
type
{ ---------------------------------------------------------------------------Klassenbeschreibung für TAboutForm
---------------------------------------------------------------------------- }
TAboutForm = class(TForm)
// Auflistung aller eingebundenen Komponenten der Klasse
Image1: TImage;
Panel1: TPanel;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
BitBtn1: TBitBtn;
Label4: TLabel;
// Auflistung aller Methoden für die Ereignisverarbeitung
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure BitBtn1Click(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
{ ---------------------------------------------------------------------------Öffentliche globale Variablen
---------------------------------------------------------------------------- }
var
AboutForm: TAboutForm;
// Die Instanz von TAboutForm ist öffentlich für alle anderen
// Units erreichbar
{ ---------------------------------------------------------------------------IMPLEMENTATIONSTEIL DER UNIT
---------------------------------------------------------------------------- }
210
H
Quelltexte
implementation
{ ---------------------------------------------------------------------------Compiler-Schalter
---------------------------------------------------------------------------- }
{$R *.dfm}
{ ---------------------------------------------------------------------------Liste aller private eingebundenen Units
---------------------------------------------------------------------------- }
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
FormClose
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
var TCloseAction
Action
Gibt an, wie das Fenster geschlossen werden ssoll
Methodenbeschreibung:
Methode wird beim Beenden des Formulars aufgerufen.
---------------------------------------------------------------------------- }
procedure TAboutForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
end;
// Speicher freigeben
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
BitBtn1Click
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Schließt das Formular, wenn auf "Schließen" gelickt wird.
---------------------------------------------------------------------------- }
procedure TAboutForm.BitBtn1Click(Sender: TObject);
begin
Close;
end;
// Schließe das Formular
end.
Dateiname: CFGSource.pas
{ ---------------------------------------------------------------------------Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
05. 06. 04
[email protected]
VisualL
1.0
Klassenname:
TCFGForm
Version:
1.0
Kurzbeschreibung:
Diese Klasse repräsentiert das CFG-Formular.
---------------------------------------------------------------------------- }
unit CFGSource;
211
H
Quelltexte
{ ---------------------------------------------------------------------------BESCHREIBUNG DER SCHNITTSTELLE DER UNIT
---------------------------------------------------------------------------- }
interface
{ ---------------------------------------------------------------------------Liste alle öffentlich eingebundenen Units
---------------------------------------------------------------------------- }
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, Buttons, ExtCtrls;
{ ---------------------------------------------------------------------------Deklaration eigener Datentypen
---------------------------------------------------------------------------- }
type
{ ---------------------------------------------------------------------------Klassenbeschreibung für TCFGForm
---------------------------------------------------------------------------- }
TCFGForm = class(TForm)
// Auflistung aller eingebundenen Komponenten der Klasse
Panel2: TPanel;
PageControl1: TPageControl;
OptionsSheet: TTabSheet;
LightCamSheet: TTabSheet;
GroupBox3: TGroupBox;
Label12: TLabel;
Label13: TLabel;
Label14: TLabel;
Label15: TLabel;
AACheck: TCheckBox;
TargaCheck: TCheckBox;
WidthEdit: TEdit;
HeightEdit: TEdit;
PathPOVEdit: TEdit;
PathQPOVEdit: TEdit;
SearchPOVBtn: TButton;
SearchQPOVBtn: TButton;
Panel1: TPanel;
Panel3: TPanel;
GroupBox1: TGroupBox;
CamXTrack: TTrackBar;
CamYTrack: TTrackBar;
CamZTrack: TTrackBar;
GroupBox2: TGroupBox;
LightXTrack: TTrackBar;
LightYTrack: TTrackBar;
LightZTrack: TTrackBar;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
OKBtn: TBitBtn;
CancelBtn: TBitBtn;
CamXLabel: TLabel;
CamYLabel: TLabel;
CamZLabel: TLabel;
LightXLabel: TLabel;
LightYLabel: TLabel;
LightZLabel: TLabel;
Label7: TLabel;
Label8: TLabel;
Direct3DBtn: TButton;
Splitter1: TSplitter;
// Auflistung aller Methoden für die Ereignisverarbeitung
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure CancelBtnClick(Sender: TObject);
procedure OKBtnClick(Sender: TObject);
procedure FormShow(Sender: TObject);
212
H
Quelltexte
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
CamXTrackChange(Sender: TObject);
CamYTrackChange(Sender: TObject);
CamZTrackChange(Sender: TObject);
LightXTrackChange(Sender: TObject);
LightYTrackChange(Sender: TObject);
LightZTrackChange(Sender: TObject);
SearchPOVBtnClick(Sender: TObject);
WidthEditKeyPress(Sender: TObject; var Key: Char);
SearchQPOVBtnClick(Sender: TObject);
Direct3DBtnClick(Sender: TObject);
// Auflistung aller privaten Methoden als Prototypen
private
function convertRealForTracker(value : real48) : integer;
public
{ Public-Deklarationen }
end;
{ ---------------------------------------------------------------------------Öffentliche globale Variablen
---------------------------------------------------------------------------- }
var
CFGForm: TCFGForm;
// Die Instanz von TCFGForm ist öffentlich für alle anderen
// Units erreichbar
{ ---------------------------------------------------------------------------IMPLEMENTATIONSTEIL DER UNIT
---------------------------------------------------------------------------- }
implementation
{ ---------------------------------------------------------------------------Compiler-Schalter
---------------------------------------------------------------------------- }
{$R *.dfm}
{ ---------------------------------------------------------------------------Liste aller private eingebundenen Units
---------------------------------------------------------------------------- }
uses MDIMainSource, imagehlp, Direct3DSource;
{ ---------------------------------------------------------------------------Globale Variablen die nur in dieser Unit erreichbar sind
---------------------------------------------------------------------------- }
var
CamX : real48;
CamY : real48;
CamZ : real48;
// Enthält den Wert für die x-Achse für die Kamera
// Enthält den Wert für die x-Achse für die Kamera
// Enthält den Wert für die x-Achse für die Kamera
{ ---------------------------------------------------------------------------Methodenname:
Rückgabewert
Typ:
Bedeutung:
Parameter
Typ:
Name:
Bedeutung:
convertRealForTracker
integer
Gibt eine Ganzzahl für die Tracker-Komponente zurück
real48
value
Fließkommazahl aus der CFG-Datei
Methodenbeschreibung:
Rechnet eine Fließkommazahl aus der CFG-Datei um, in eine ganze Zahl für die
Tracker-Komponente. Dabei ist die Auflösung 0.5 und geht von +10 bis -10.
---------------------------------------------------------------------------- }
function TCFGForm.convertRealForTracker(value: real48) : integer;
begin
// Wenn die Zahl größer 10 ist, dann weise das Maximum zu und beende
if value > 10 then
begin
result := 20;
Exit;
213
H
Quelltexte
end
// Wenn die Zahl kleiner -10 ist, dann weise das Minimum zu und beende
else if value < -10 then
begin
result := -20;
Exit;
end
// Wenn die Zahl gleich 0 ist, dann weise die 0 zu und beende
else if value = 0 then
begin
result := 0;
Exit;
end;
// Multipliziere die Fließkommazahl mit zwei und runde den Wert auf, bzw. ab.
result := round(value * 2);
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
FormClose
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
var TCloseAction
Action
Gibt an, wie das Fenster geschlossen werden ssoll
Methodenbeschreibung:
Methode wird beim Beenden des Formulars aufgerufen.
---------------------------------------------------------------------------- }
procedure TCFGForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if D3DActive then
D3DForm.Close;
Action := caFree;
end;
// Falls ein Direct3D-Fenster offen ist, dann schließe es
// Speicher freigeben
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
CancelBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Diese Methode wird aufgerufen, wenn auf "Abbrechen" geklickt wird. Das
Formular wird geschlossen
---------------------------------------------------------------------------- }
procedure TCFGForm.CancelBtnClick(Sender: TObject);
begin
Info.CamX := CamX;
// Übernehme die Werte der Kamera
Info.CamY := CamY;
Info.CamZ := CamZ;
Close;
// Schließe das Fenster
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
OKBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
214
H
Quelltexte
Methodenbeschreibung:
Diese Methode wird aufgerufen, wenn auf "Ok" geklickt wird. Das
Formular wird geschlossen und alle Werte übernommen.
---------------------------------------------------------------------------- }
procedure TCFGForm.OKBtnClick(Sender: TObject);
begin
// Übergebe alle Werte an die Info-Struktur
Info.AA := AACheck.Checked;
Info.Bitmap := not TargaCheck.Checked;
Info.Width := StrToInt(WidthEdit.Text);
Info.Height := StrToInt(HeightEdit.Text);
Info.PathPOV := PathPOVEdit.Text;
Info.PathQPOV := PathQPOVEdit.Text;
Info.CamX := StrToFloat(CamXLabel.Caption);
Info.CamY := StrToFloat(CamYLabel.Caption);
Info.CamZ := StrToFloat(CamZLabel.Caption);
Info.LightX := StrToFloat(LightXLabel.Caption);
Info.LightY := StrToFloat(LightYLabel.Caption);
Info.LightZ := StrToFloat(LightZLabel.Caption);
Close;
end;
// Schließe das Formular
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
FormShow
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird ausgeführt, wenn das Fenster sichtbar wird.
---------------------------------------------------------------------------- }
procedure TCFGForm.FormShow(Sender: TObject);
begin
// Alle Werte aus der Info-Struktur an die Komponenten übergeben
AACheck.Checked := Info.AA;
TargaCheck.Checked := not Info.Bitmap;
WidthEdit.Text := IntToStr(Info.Width);
HeightEdit.Text := IntToStr(Info.Height);
PathPOVEdit.Text := Info.PathPOV;
PathQPOVEdit.Text := Info.PathQPOV;
// Kamera und Licht initialisieren
CamX := Info.CamX;
CamY := Info.CamY;
CamZ := Info.CamZ;
CamXTrack.Position := convertRealForTracker(Info.CamX);
CamXLabel.Caption := FloatToStr(CamXTrack.Position * 0.5);
CamYTrack.Position := convertRealForTracker(Info.CamY);
CamYLabel.Caption := FloatToStr(CamYTrack.Position * 0.5);
CamZTrack.Position := convertRealForTracker(Info.CamZ);
CamZLabel.Caption := FloatToStr(CamZTrack.Position * 0.5);
LightXTrack.Position := convertRealForTracker(Info.LightX);
LightXLabel.Caption := FloatToStr(LightXTrack.Position * 0.5);
LightYTrack.Position := convertRealForTracker(Info.LightY);
LightYLabel.Caption := FloatToStr(LightYTrack.Position * 0.5);
LightZTrack.Position := convertRealForTracker(Info.LightZ);
LightZLabel.Caption := FloatToStr(LightZTrack.Position * 0.5);
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
CamXTrackChange
TObject
Sender
215
H
Quelltexte
Bedeutung:
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird aufgerufen, wenn an der TrackBar-Komponente der Wert verändert
wird. Diese Methode gleicht den Wert mit der Info-Struktur ab.
---------------------------------------------------------------------------- }
procedure TCFGForm.CamXTrackChange(Sender: TObject);
begin
// Wenn 0 eingestellt wird, dann gebe dieses direkt an
if CamXTrack.Position = 0 then
begin
CamXLabel.Caption := '0';
Info.CamX := 0;
end
// Ansonsten berechne den neuen Wert
else
begin
CamXLabel.Caption := FloatToStrF((CamXTrack.Position * 0.5), ffFixed, 1, 1);
Info.CamX := CamXTrack.Position * 0.5;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
CamYTrackChange
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird aufgerufen, wenn an der TrackBar-Komponente der Wert verändert
wird. Diese Methode gleicht den Wert mit der Info-Struktur ab.
---------------------------------------------------------------------------- }
procedure TCFGForm.CamYTrackChange(Sender: TObject);
begin
// Wenn 0 eingestellt wird, dann gebe dieses direkt an
if CamYTrack.Position = 0 then
begin
CamYLabel.Caption := '0';
Info.CamY := 0;
end
// Ansonsten berechne den neuen Wert
else
begin
CamYLabel.Caption := FloatToStrF((CamYTrack.Position * 0.5), ffFixed, 1, 1);
Info.CamY := CamYTrack.Position * 0.5;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
CamZTrackChange
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird aufgerufen, wenn an der TrackBar-Komponente der Wert verändert
wird. Diese Methode gleicht den Wert mit der Info-Struktur ab.
---------------------------------------------------------------------------- }
procedure TCFGForm.CamZTrackChange(Sender: TObject);
216
H
Quelltexte
begin
// Wenn 0 eingestellt wird, dann gebe dieses direkt an
if CamZTrack.Position = 0 then
begin
CamZLabel.Caption := '0';
Info.CamZ := 0;
end
// Ansonsten berechne den neuen Wert
else
begin
CamZLabel.Caption := FloatToStrF((CamZTrack.Position * 0.5), ffFixed, 1, 1);
Info.CamZ := CamZTrack.Position * 0.5;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
LightXTrackChange
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird aufgerufen, wenn an der TrackBar-Komponente der Wert verändert
wird. Diese Methode gleicht den Wert mit der Info-Struktur ab.
---------------------------------------------------------------------------- }
procedure TCFGForm.LightXTrackChange(Sender: TObject);
begin
// Wenn 0 eingestellt wird, dann gebe dieses direkt an
if LightXTrack.Position = 0 then
begin
LightXLabel.Caption := '0';
Info.LightX := 0;
end
// Ansonsten berechne den neuen Wert
else
begin
LightXLabel.Caption := FloatToStrF((LightXTrack.Position * 0.5), ffFixed, 1, 1);
Info.LightX := LightXTrack.Position * 0.5;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
LightYTrackChange
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird aufgerufen, wenn an der TrackBar-Komponente der Wert verändert
wird. Diese Methode gleicht den Wert mit der Info-Struktur ab.
---------------------------------------------------------------------------- }
procedure TCFGForm.LightYTrackChange(Sender: TObject);
begin
// Wenn 0 eingestellt wird, dann gebe dieses direkt an
if LightYTrack.Position = 0 then
begin
LightYLabel.Caption := '0';
Info.LightY := 0;
end
// Ansonsten berechne den neuen Wert
217
H
Quelltexte
else
begin
LightYLabel.Caption := FloatToStrF((LightYTrack.Position * 0.5), ffFixed, 1, 1);
Info.LightY := LightYTrack.Position * 0.5;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
LightZTrackChange
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird aufgerufen, wenn an der TrackBar-Komponente der Wert verändert
wird. Diese Methode gleicht den Wert mit der Info-Struktur ab.
---------------------------------------------------------------------------- }
procedure TCFGForm.LightZTrackChange(Sender: TObject);
begin
// Wenn 0 eingestellt wird, dann gebe dieses direkt an
if LightZTrack.Position = 0 then
begin
LightZLabel.Caption := '0';
Info.LightZ := 0;
end
// Ansonsten berechne den neuen Wert
else
begin
LightZLabel.Caption := FloatToStrF((LightZTrack.Position * 0.5), ffFixed, 1, 1);
Info.LightZ := LightZTrack.Position * 0.5;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
SearchPOVBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Diese Methode sucht nach den Pfad von POV-Ray, wenn auf den entsprechenden
Button geklickt wurde.
---------------------------------------------------------------------------- }
procedure TCFGForm.SearchPOVBtnClick(Sender: TObject);
var temp : array[0..255] of char;
// Speichert den Pfad von POV-Ray
begin
SearchTreeForFile('C:\', 'pvengine.exe', temp); // Suche den kompletten Baum ab
PathPOVEdit.Text := ExtractFileDir(temp);
// Extrahiere den keinen Pfad
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
RecursEditKeyPress
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
var char
Key
Zeiger auf das Zeichen, das eingegeben wurde
Methodenbeschreibung:
218
H
Quelltexte
Bei jeder Zeicheneingabe für Editierfelder die nur Zahlen akzeptieren,
wird diese Methode aufgerufen.
---------------------------------------------------------------------------- }
procedure TCFGForm.WidthEditKeyPress(Sender: TObject; var Key: Char);
begin
// Lasse nur Zahlen und den Rücklauf als Eingabe zu
if (Key in ['0'..'9']) or (Key = #8) then
Key := Key
else
Key := #0;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
SearchQPOVBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Diese Methode sucht nach den Pfad von QuietPOV, wenn auf den entsprechenden
Button geklickt wurde.
---------------------------------------------------------------------------- }
procedure TCFGForm.SearchQPOVBtnClick(Sender: TObject);
var temp : array[0..255] of char;
// Speichert den Pfad von POV-Ray
begin
SearchTreeForFile('C:\', 'QuietPOV.exe', temp); // Suche den kompletten Baum ab
PathQPOVEdit.Text := ExtractFileDir(temp);
// Extrahiere den keinen Pfad
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Direct3DBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Die Methode wird ausgeführt, wenn auf den Button "3D-Ansicht" geklickt wird.
---------------------------------------------------------------------------- }
procedure TCFGForm.Direct3DBtnClick(Sender: TObject);
begin
// Erzeuge ein Instanz des Direct3D-Fensters, wenn noch keine existiert
if D3DActive = false then
begin
D3DForm := TDirect3DForm.Create(Application);
D3DActive := true;
end;
end;
end.
Dateiname: CondFormSource.pas
{ ---------------------------------------------------------------------------Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
05. 06. 04
[email protected]
VisualL
1.0
219
H
Quelltexte
Klassenname:
TCondForm
Version:
1.0
Kurzbeschreibung:
Diese Klasse repräsentiert das Fenster, das zum Anzeigen der Ausdrücke für
den Bedingungsteil benutzt wird.
---------------------------------------------------------------------------- }
unit CondFormSource;
{ ---------------------------------------------------------------------------BESCHREIBUNG DER SCHNITTSTELLE DER UNIT
---------------------------------------------------------------------------- }
interface
{ ---------------------------------------------------------------------------Liste alle öffentlich eingebundenen Units
---------------------------------------------------------------------------- }
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons;
{ ---------------------------------------------------------------------------Deklaration eigener Datentypen
---------------------------------------------------------------------------- }
type
{ ---------------------------------------------------------------------------Klassenbeschreibung für TCondForm
---------------------------------------------------------------------------- }
TCondForm = class(TForm)
// Auflistung aller eingebundenen Komponenten der Klasse
GroupBox1: TGroupBox;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
Label7: TLabel;
Label8: TLabel;
Label9: TLabel;
Label10: TLabel;
Label11: TLabel;
BitBtn1: TBitBtn;
// Auflistung aller Methoden für die Ereignisverarbeitung
procedure BitBtn1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
{ ---------------------------------------------------------------------------Öffentliche globale Variablen
---------------------------------------------------------------------------- }
var
CondForm: TCondForm;
// Die Instanz von TCondForm ist öffentlich für alle anderen
// Units erreichbar
{ ---------------------------------------------------------------------------IMPLEMENTATIONSTEIL DER UNIT
---------------------------------------------------------------------------- }
implementation
{ ---------------------------------------------------------------------------Compiler-Schalter
---------------------------------------------------------------------------- }
220
H
Quelltexte
{$R *.dfm}
{ ---------------------------------------------------------------------------Liste aller private eingebundenen Units
---------------------------------------------------------------------------- }
uses MDIMainSource;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
BitBtn1Click
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Schließt das Formular, wenn auf "Schließen" gelickt wird.
---------------------------------------------------------------------------- }
procedure TCondForm.BitBtn1Click(Sender: TObject);
begin
Close;
end;
// Schließe das Fenster
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
FormClose
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
var TCloseAction
Action
Gibt an, wie das Fenster geschlossen werden ssoll
Methodenbeschreibung:
Methode wird beim Beenden des Formulars aufgerufen.
---------------------------------------------------------------------------- }
procedure TCondForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
CondActive := false;
Action := caFree;
end;
// Signalisiert, dass keine Instanz mehr gibt
// Speicher freigeben
end.
Dateiname: ConsoleFormSource.pas
{ ---------------------------------------------------------------------------Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
05. 06. 04
[email protected]
VisualL
1.0
Klassenname:
TConsoleFom
Version:
1.0
Kurzbeschreibung:
Diese Klasse repräsentiert das Fenster, das zum Anzeigen der Konsolenausgabe
benutzt wird.
---------------------------------------------------------------------------- }
unit ConsoleFormSource;
{ ---------------------------------------------------------------------------BESCHREIBUNG DER SCHNITTSTELLE DER UNIT
221
H
Quelltexte
---------------------------------------------------------------------------- }
interface
{ ---------------------------------------------------------------------------Liste alle öffentlich eingebundenen Units
---------------------------------------------------------------------------- }
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, ExtCtrls;
{ ---------------------------------------------------------------------------Deklaration eigener Datentypen
---------------------------------------------------------------------------- }
type
{ ---------------------------------------------------------------------------Klassenbeschreibung für TConsoleFom
---------------------------------------------------------------------------- }
TConsoleFom = class(TForm)
// Auflistung aller eingebundenen Komponenten der Klasse
Panel1: TPanel;
Panel2: TPanel;
CloseBtn: TBitBtn;
ListBox1: TListBox;
// Auflistung aller Methoden für die Ereignisverarbeitung
procedure CloseBtnClick(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
{ ---------------------------------------------------------------------------Öffentliche globale Variablen
---------------------------------------------------------------------------- }
var
ConsoleFom: TConsoleFom;
// Die Instanz von TConsoleFom ist öffentlich für alle anderen
// Units erreichbar
{ ---------------------------------------------------------------------------IMPLEMENTATIONSTEIL DER UNIT
---------------------------------------------------------------------------- }
implementation
{ ---------------------------------------------------------------------------Compiler-Schalter
---------------------------------------------------------------------------- }
{$R *.dfm}
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
CloseBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Schließt das Formular, wenn auf "Schließen" gelickt wird.
---------------------------------------------------------------------------- }
procedure TConsoleFom.CloseBtnClick(Sender: TObject);
begin
Close;
end;
// Formular schließen
222
H
Quelltexte
end.
Dateiname: Direct3DSource.pas
{ ---------------------------------------------------------------------------Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
05. 06. 04
[email protected]
VisualL
1.0
Klassenname:
TDirect3DForm
Version:
1.0
Kurzbeschreibung:
Diese Klasse repräsentiert das Fenster zur Direct3D-Anzeige.
---------------------------------------------------------------------------- }
unit Direct3DSource;
{ ---------------------------------------------------------------------------BESCHREIBUNG DER SCHNITTSTELLE DER UNIT
---------------------------------------------------------------------------- }
interface
{ ---------------------------------------------------------------------------Liste alle öffentlich eingebundenen Units
---------------------------------------------------------------------------- }
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Direct3D8, d3dx8;
{ ---------------------------------------------------------------------------Deklaration eigener Datentypen
---------------------------------------------------------------------------- }
type
// Datenstruktur zum Speichern eines Vertex-Punktes
TMyVertex = record
x,y,z
: single;
// Position des Vertex
nx, ny, nz : single;
// Normalen des Vertex
color
: dword;
// Farbe des Vertex
end;
// Deklaration der Vertex-Liste für 36 Vertex-Punkte
TMyVertices = array [0..35] of TMyVertex;
{ ---------------------------------------------------------------------------Klassenbeschreibung für TDirect3DForm
---------------------------------------------------------------------------- }
TDirect3DForm = class(TForm)
// Auflistung aller Methoden für die Ereignisverarbeitung
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
// Auflistung aller privaten Methoden als Prototypen und privaten Attribute
private
// Private Attribute
lpd3d
: IDIRECT3D8;
lpd3ddevice : IDirect3DDevice8;
MyVB
: IDirect3DVertexBuffer8;
light
: D3DLight8;
material
: D3DMATERIAL8;
//
//
//
//
//
Handle für die Direct3D Kommunikation
Handle für ein Direct3D Device
Attribut für den Vertex-Buffer
Attribut fürs Licht
Attribut fürs Material
// Private Methoden
procedure FatalError(hr : HResult; FehlerMsg : string);
223
H
Quelltexte
procedure D3DInit;
procedure D3DShutdown;
procedure D3DInitScene;
procedure D3DKillScene;
procedure D3DRender;
procedure MyIdleHandler (Sender: TObject; var Done: Boolean);
public
{ Public-Deklarationen }
end;
{ ---------------------------------------------------------------------------Deklaration von Konstanten und initalisieren von Variablen
---------------------------------------------------------------------------- }
const
// Definieren wie die FVF auszusehen hat
D3D8T_CUSTOMVERTEX = ( D3DFVF_XYZ or D3DFVF_NORMAL or D3DFVF_DIFFUSE );
// Alle 36 Vertex-Punkte für einen Kubus
MyVertices : TMyVertices = (
// Würfel
(x :-1.0; y :-1.0; z :-1.0; nx: 0.0; ny:
(x :-1.0; y : 1.0; z :-1.0; nx: 0.0; ny:
(x : 1.0; y : 1.0; z :-1.0; nx: 0.0; ny:
(x : 1.0; y : 1.0; z :-1.0; nx: 0.0; ny:
(x : 1.0; y :-1.0; z :-1.0; nx: 0.0; ny:
(x :-1.0; y :-1.0; z :-1.0; nx: 0.0; ny:
0.0;
0.0;
0.0;
0.0;
0.0;
0.0;
nz:
nz:
nz:
nz:
nz:
nz:
-1.0;
-1.0;
-1.0;
-1.0;
-1.0;
-1.0;
color
color
color
color
color
color
:
:
:
:
:
:
// Farbe rot
$FFF00000 ),
$FFF00000 ),
$FFF00000 ),
$FFF00000 ),
$FFF00000 ),
$FFF00000 ),
// Vorn
(x
(x
(x
(x
(x
(x
: 1.0;
: 1.0;
:-1.0;
:-1.0;
:-1.0;
: 1.0;
y
y
y
y
y
y
:-1.0;
: 1.0;
: 1.0;
: 1.0;
:-1.0;
:-1.0;
z
z
z
z
z
z
:
:
:
:
:
:
1.0;
1.0;
1.0;
1.0;
1.0;
1.0;
nx:
nx:
nx:
nx:
nx:
nx:
0.0;
0.0;
0.0;
0.0;
0.0;
0.0;
ny:
ny:
ny:
ny:
ny:
ny:
0.0;
0.0;
0.0;
0.0;
0.0;
0.0;
nz:
nz:
nz:
nz:
nz:
nz:
1.0;
1.0;
1.0;
1.0;
1.0;
1.0;
color
color
color
color
color
color
:
:
:
:
:
:
$FFF00000
$FFF00000
$FFF00000
$FFF00000
$FFF00000
$FFF00000
),
),
),
),
),
),
// Hinten
(x
(x
(x
(x
(x
(x
:-1.0;
:-1.0;
: 1.0;
: 1.0;
: 1.0;
:-1.0;
y
y
y
y
y
y
:
:
:
:
:
:
1.0;
1.0;
1.0;
1.0;
1.0;
1.0;
z
z
z
z
z
z
:-1.0;
: 1.0;
: 1.0;
: 1.0;
:-1.0;
:-1.0;
nx:
nx:
nx:
nx:
nx:
nx:
0.0;
0.0;
0.0;
0.0;
0.0;
0.0;
ny:
ny:
ny:
ny:
ny:
ny:
1.0;
1.0;
1.0;
1.0;
1.0;
1.0;
nz:
nz:
nz:
nz:
nz:
nz:
0.0;
0.0;
0.0;
0.0;
0.0;
0.0;
color
color
color
color
color
color
:
:
:
:
:
:
$FFF00000
$FFF00000
$FFF00000
$FFF00000
$FFF00000
$FFF00000
),
),
),
),
),
),
// Oben
(x
(x
(x
(x
(x
(x
: 1.0;
: 1.0;
:-1.0;
:-1.0;
:-1.0;
: 1.0;
y
y
y
y
y
y
:-1.0;
:-1.0;
:-1.0;
:-1.0;
:-1.0;
:-1.0;
z
z
z
z
z
z
:-1.0;
: 1.0;
: 1.0;
: 1.0;
:-1.0;
:-1.0;
nx:
nx:
nx:
nx:
nx:
nx:
0.0;
0.0;
0.0;
0.0;
0.0;
0.0;
ny:
ny:
ny:
ny:
ny:
ny:
-1.0;
-1.0;
-1.0;
-1.0;
-1.0;
-1.0;
nz:
nz:
nz:
nz:
nz:
nz:
0.0;
0.0;
0.0;
0.0;
0.0;
0.0;
color
color
color
color
color
color
:
:
:
:
:
:
$FFF00000
$FFF00000
$FFF00000
$FFF00000
$FFF00000
$FFF00000
),
),
),
),
),
),
// Unten
(x
(x
(x
(x
(x
(x
:-1.0;
:-1.0;
:-1.0;
:-1.0;
:-1.0;
:-1.0;
y
y
y
y
y
y
:-1.0;
: 1.0;
: 1.0;
: 1.0;
:-1.0;
:-1.0;
z
z
z
z
z
z
: 1.0;
: 1.0;
:-1.0;
:-1.0;
:-1.0;
: 1.0;
nx:
nx:
nx:
nx:
nx:
nx:
-1.0;
-1.0;
-1.0;
-1.0;
-1.0;
-1.0;
0.0;
0.0;
0.0;
0.0;
0.0;
0.0;
nz:
nz:
nz:
nz:
nz:
nz:
0.0;
0.0;
0.0;
0.0;
0.0;
0.0;
color
color
color
color
color
color
:
:
:
:
:
:
$FFF00000
$FFF00000
$FFF00000
$FFF00000
$FFF00000
$FFF00000
),
),
),
),
),
),
// Links
(x
(x
(x
(x
(x
(x
:
:
:
:
:
:
y
y
y
y
y
y
:-1.0;
: 1.0;
: 1.0;
: 1.0;
:-1.0;
:-1.0;
z
z
z
z
z
z
:-1.0;
:-1.0;
: 1.0;
: 1.0;
: 1.0;
:-1.0;
nx:
nx:
nx:
nx:
nx:
nx:
1.0;
1.0;
1.0;
1.0;
1.0;
1.0;
1.0;
1.0;
1.0;
1.0;
1.0;
1.0;
ny:
ny:
ny:
ny:
ny:
ny:
ny:
ny:
ny:
ny:
ny:
ny:
0.0;
0.0;
0.0;
0.0;
0.0;
0.0;
nz:
nz:
nz:
nz:
nz:
nz:
0.0;
0.0;
0.0;
0.0;
0.0;
0.0;
color
color
color
color
color
color
:
:
:
:
:
:
$FFF00000
$FFF00000
$FFF00000
$FFF00000
$FFF00000
$FFF00000
), // Rechts
),
),
),
),
));
{ ---------------------------------------------------------------------------Öffentliche globale Variablen
---------------------------------------------------------------------------- }
var
Direct3DForm: TDirect3DForm;
// Die Instanz von TDirect3DForm ist öffentlich für alle
// anderen Units erreichbar
{ ---------------------------------------------------------------------------IMPLEMENTATIONSTEIL DER UNIT
---------------------------------------------------------------------------- }
implementation
224
H
Quelltexte
{ ---------------------------------------------------------------------------Compiler-Schalter
---------------------------------------------------------------------------- }
{$R *.dfm}
{ ---------------------------------------------------------------------------Liste aller private eingebundenen Units
---------------------------------------------------------------------------- }
uses MDIMainSource;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
FormClose
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
var TCloseAction
Action
Gibt an, wie das Fenster geschlossen werden ssoll
Methodenbeschreibung:
Methode wird beim Beenden des Formulars aufgerufen.
---------------------------------------------------------------------------- }
procedure TDirect3DForm.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
D3DKillScene;
D3DShutdown;
D3DActive := false;
Action := caFree;
end;
//
//
//
//
Abschalten der Szene
Beenden von Direct3D
Signalisieren, dass es keine Instanz von dem Fenster gibt
Speicher freigeben
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
FormCreate
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird beim erzeugen des Formulars aufgerufen und Initialisiert
Variablen. Zusätzlich registriert er den IdleHandler
---------------------------------------------------------------------------- }
procedure TDirect3DForm.FormCreate(Sender: TObject);
begin
lpd3d := nil;
lpd3ddevice := nil;
MyVB := nil;
Application.OnIdle := MyIdleHandler;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
FatalError
HResult
hr
Wert des Handle-Result der Direct3D Szene
string
FehlerMsg
String der Fehlermeldung
Methodenbeschreibung:
Wird aufgerufen um eine Fehlermeldung an den Benutzer auszugeben.
225
H
Quelltexte
---------------------------------------------------------------------------- }
procedure TDirect3DForm.FatalError(hr : HResult; FehlerMsg : string);
var s : string;
begin
// Hole die Fehlermeldung
if hr <> 0 then
s := D3DXErrorString(hr) + #13 + FehlerMsg
else
s := FehlerMsg;
D3DKillScene;
D3DShutdown;
MessageDlg(s, mtError, [mbOK], 0);
Close;
end;
//
//
//
//
Szene abschalten
Direct3D beenden
Fehlermeldung ausgeben
Formular beenden
{ ---------------------------------------------------------------------------Methodenname:
D3DInit
Methodenbeschreibung:
Diese Methode initialisiert Direct3D und die damit verbundene Kommunikation
mit den COM-Objekten.
---------------------------------------------------------------------------- }
procedure TDirect3DForm.D3DInit;
var hr
: HRESULT;
d3dpp : TD3DPRESENTPARAMETERS;
d3ddm : TD3DDISPLAYMODE;
// Speichert die Ergebnisse der Verarbeitung
// Struktur zum festlegen von Einstellungen
// Struktur zur Anzeige
begin
// Erzeuge eine Verbindung zu Direct3D
lpd3d := Direct3DCreate8(D3D_SDK_VERSION);
// Falls Erzeugung fehlschläge, gib eine Fehlermeldung aus
if(lpd3d = nil) then
FatalError(0,'Fehler beim Erstellen von Direct3D!');
// Datenstrukturen mit null überschreiben, verhindert Seiteneffekte
ZeroMemory(@d3dpp, sizeof(d3dpp));
ZeroMemory(@light, sizeof(light));
ZeroMemory(@material, sizeof(material));
// Definiere Einstellungen für Direct3D
with d3dpp do
begin
SwapEffect := D3DSWAPEFFECT_DISCARD;
hDeviceWindow := Handle;
BackBufferCount := 1;
// Löschen alter Frames
// Handle vom Formular übergeben
// Ein Backpuffer
EnableAutoDepthStencil := TRUE;
AutoDepthStencilFormat := D3DFMT_D16;
// Z-Buffer Aktivierung
Windowed := TRUE;
// Im Fenstermodus anzeigen
hr:=lpd3d.GetAdapterDisplayMode(D3DADAPTER_DEFAULT, d3ddm);
// Falls Erzeugung fehlschläge, gib eine Fehlermeldung aus
if failed(hr) then
FatalError(hr,'Fehler beim Ermitteln des Dislaymodes');
BackBufferFormat := d3ddm.Format;
end;
// Erzeuge Direct3D Device
hr:=lpd3d.CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
Handle,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
226
// Handle vom Formular
// übergeben
// Vertexverarbeitung
H
Quelltexte
// von der Software
// Hardwarebeschleunigung
// deaktiviert
d3dpp,
lpd3ddevice);
// Falls Erzeugung fehlschläge, gib eine Fehlermeldung aus
if FAILED(hr) then
FatalError(hr,'Fehler beim Erzeugen des 3D-Device');
end;
{ ---------------------------------------------------------------------------Methodenname:
D3DShutdown
Methodenbeschreibung:
Wird zum beenden von Direct3D aufgerufen.
---------------------------------------------------------------------------- }
procedure TDirect3DForm.D3DShutdown;
begin
if assigned(lpd3ddevice) then lpd3ddevice:=nil;
if assigned(lpd3d) then lpd3d:=nil;
end;
// Verbindung lösen
// Verbindung lösen
{ ---------------------------------------------------------------------------Methodenname:
D3DInitScene
Methodenbeschreibung:
Aufgabe dieser Methode ist das initialisieren der 3D-Szene und deren Objekte.
---------------------------------------------------------------------------- }
procedure TDirect3DForm.D3DInitScene;
var hr
vbVertices
ViewMatrix
matProj
:
:
:
:
HRESULT;
pByte;
TD3DXMATRIX;
TD3DXMATRIX;
// Speichert die Ergebnisse der Verarbeitung
// Anzeigematrix
// Matrixprojektion
begin
// Initialisiere nur, wenn ein Direct3D Device erzeugt wurde
if assigned(lpd3ddevice) then
with lpd3ddevice do
begin
// Setze die Eigenschaften des Materials
material.Diffuse.r := 1.0;
material.Diffuse.g := 1.0;
material.Diffuse.b := 1.0;
material.Diffuse.a := 1.0;
material.Ambient.r := 1.0;
material.Ambient.g := 1.0;
material.Ambient.b := 1.0;
material.Ambient.a := 1.0;
material.Specular.r := 1.0;
material.Specular.g := 1.0;
material.Specular.b := 1.0;
material.Specular.a := 1.0;
material.Emissive.r := 0.0;
material.Emissive.g := 0.0;
material.Emissive.b := 0.0;
material.Emissive.a := 0.0;
material.Power := 5.0;
SetMaterial(material);
// Setze das Material
// Lichtquelle definieren
light._Type := D3DLIGHT_POINT;
light.Diffuse.r := 1.0;
light.Diffuse.g := 1.0;
light.Diffuse.b := 1.0;
// Lichtquellentyp
// Farbe der Lichtes
227
H
Quelltexte
light.Range := 1000.0;
light.Attenuation0 := 1.0;
// Reichweite
// Abnahme des Lichtes
// Position der Lichtquelle
light.Position := D3DXVECTOR3(((-1.0 * info.CamX) * info.LightX),
((1.0 * info.CamY) * info.LightY),
((-1.0 * info.CamZ) * info.LightZ));
SetLight(0, light);
LightEnable(0, TRUE);
SetRenderState(D3DRS_LIGHTING, 1);
// Setze die Lichtquelle auf Index 0
// Aktiviere das Licht auf Index 0
// Aktiviere global das Licht
// Erzeuge Vertex-Buffer für alle Vertexe
hr := CreateVertexBuffer (sizeof(TMyVertices),
D3DUSAGE_WRITEONLY,
D3D8T_CUSTOMVERTEX,
D3DPOOL_MANAGED,
MyVB);
// Größe der Vertex-Liste
// Nur Schreibzugriffe
// Eigene Vertexe
// Zeiger zum Buffer
// Falls Erzeugung fehlschläge, gib eine Fehlermeldung aus
if FAILED(hr) then
FatalError(0,'Fehler beim Erstellen des Vertex Buffers');
with MyVB do
begin
hr:=Lock(0,
0,
vbVertices,
0);
//
//
//
//
Offset des Anfangs
Größe des locks( 0 = alles )
Wenn erfolgreich, dann hier ablegen
sonstige Flags
// Falls Erzeugung fehlschläge, gib eine Fehlermeldung aus
if FAILED(hr) then
FatalError(0,'Fehler beim Locken des Vertex-Buffers');
// Kopiere Vertex-Buffer
Move(MyVertices, vbVertices^, SizeOf(TMyVertices));
Unlock;
end;
// Einstellungen für die Rückseiten der Dreiecke
SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
SetRenderState(D3DRS_ZENABLE, 0);
// Z-Buffer aktivieren
// Definieren der Ausrichtung der Kamera
D3DXMatrixLookAtLH(ViewMatrix,D3DXVECTOR3((-1.0*info.CamX),
(1.0*info.CamY),
(-1.0*info.CamZ)),
D3DXVECTOR3(0.0,0.0,0.0),
D3DXVECTOR3(0.0,1.0,0.0));
// Transformation für Kamera anwenden
SetTransform(D3DTS_VIEW,ViewMatrix);
D3DXMatrixPerspectiveFovLH(matProj,
D3DX_PI/4,
640/480,
1.0,
100.0);
SetTransform(D3DTS_PROJECTION,matProj
end;
//
//
//
//
//
);
Resultierende Matrix
Sichtwinkel
Seitenverhältnis
Mindeste Nähe
Maximal sichtbare Entfernung
end;
{ ---------------------------------------------------------------------------Methodenname:
D3DKillScene
Methodenbeschreibung:
Wird zum abschalten der Direct3D Szene ausgeführt.
---------------------------------------------------------------------------- }
procedure TDirect3DForm.D3DKillScene;
begin
MyVB:=nil;
end;
// Speicher freigeben
228
H
Quelltexte
{ ---------------------------------------------------------------------------Methodenname:
D3DRender
Methodenbeschreibung:
Innerhalb dieser Methode wird die Szene und alle daraufhin bezogene
Verarbeitung beschrieben.
---------------------------------------------------------------------------- }
procedure TDirect3DForm.D3DRender;
var matWorld
rot_matrix
trans_matrix
ViewMatrix
:
:
:
:
TD3DXMATRIX;
TD3DXMATRIX;
TD3DXMATRIX;
TD3DXMATRIX;
// Rotationsmatrix
// Translationsmatrix
// Matrix für die Kamera
begin
// Nur Ausführen, wenn ein Direct3D Device existiert
if assigned(lpd3ddevice) then
with lpd3ddevice do
begin
// Löschaktion
Clear(0,
nil,
D3DCLEAR_TARGET,
D3DCOLOR_XRGB(0,0,0),
1,
0 );
// Wie viele Rechtecke löschen
// Ganzer Bildschirm
// Hintergrund schwarz
// Lösche Z-Buffer
// Szenenbeschreibung
if SUCCEEDED(BeginScene) then
begin
SetVertexShader(D3D8T_CUSTOMVERTEX);
SetStreamSource(0, MyVB, sizeof(TMyVertex));
// Definition der Matrizen
D3DXMatrixRotationY(rot_matrix, 0.0);
D3DXMatrixTranslation(trans_matrix, 0.0, 0.0, 0.0);
D3DXMatrixMultiply(matWorld, rot_matrix, trans_matrix);
// Anwendung der Matrix
SetTransform(D3DTS_WORLD, matWorld);
DrawPrimitive(D3DPT_TRIANGLELIST, 0, 12); // Zeiche Kubus
// Definiere die Matrix für die Kameraausrichtung
D3DXMatrixLookAtLH(ViewMatrix, D3DXVECTOR3((-1.0*info.CamX),
(1.0*info.CamY),
(-1.0*info.CamZ)),
D3DXVECTOR3(0.0,0.0,0.0),
D3DXVECTOR3(0.0,1.0,0.0));
// Wende die Matrix für die Kameraausrichtung an
SetTransform(D3DTS_VIEW,ViewMatrix);
// Definiere die Position des Lichtes
light.Position := D3DXVECTOR3(((-1.0 * info.CamX) * info.LightX),
((1.0 * info.CamY) * info.LightY),
((-1.0 * info.CamZ) * info.LightZ));
// Aktivieren des Lichtes
SetLight(0, light);
LightEnable(0, TRUE);
EndScene;
end;
Present(nil, nil, 0, nil);
end;
// Zeige Resultate auf dem Bildschirm
end;
{ ----------------------------------------------------------------------------
229
H
Quelltexte
Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
MyIdleHandler
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
var boolean
Done
Gibt an, ob die Verarbeitung fertig ist
Methodenbeschreibung:
Dieser Handle wird aufgerufen, wenn keine anderen Daten verarbeitet werden
müssen.
---------------------------------------------------------------------------- }
procedure TDirect3DForm.MyIdleHandler (Sender: TObject; var Done: Boolean);
begin
D3DRender;
Done:=false;
end;
// Szene rendern
// Weiterhin ausführen
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
FormShow
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird ausgeführt, wenn das Fenster sichtbar wird.
---------------------------------------------------------------------------- }
procedure TDirect3DForm.FormShow(Sender: TObject);
begin
D3DInit;
// Initialisieren von D3D
D3DInitScene; // Initialisieren der Szene
D3DRender;
// Szene darstellen
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
FormKeyDown
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
var word
Key
Enthält den Code für das Zeichen
TShiftState
Shift
Gibt an, ob die Shift-Taste gedrückt wurde
Methodenbeschreibung:
Wird für das Formular aufgerufen, wenn eine Taste gedrückt wurde.
---------------------------------------------------------------------------- }
procedure TDirect3DForm.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key=VK_ESCAPE then close;
end;
// Wurde ESCAPE gedrückt, dann Schließe das Fenster
end.
230
H
Quelltexte
Dateiname: MDIChildSource.pas
{ ---------------------------------------------------------------------------Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
05. 06. 04
[email protected]
VisualL
1.0
Klassenname:
TMDIChildMain
Version:
1.0
Kurzbeschreibung:
Diese Klasse repräsentiert das MDI-Kindfenster.
---------------------------------------------------------------------------- }
unit MDIChildSource;
{ ---------------------------------------------------------------------------BESCHREIBUNG DER SCHNITTSTELLE DER UNIT
---------------------------------------------------------------------------- }
interface
{ ---------------------------------------------------------------------------Liste alle öffentlich eingebundenen Units
---------------------------------------------------------------------------- }
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, StdCtrls, Buttons, ExtCtrls;
{ ---------------------------------------------------------------------------Deklaration eigener Datentypen
---------------------------------------------------------------------------- }
type
{ ---------------------------------------------------------------------------Klassenbeschreibung für TMDIChildMain
---------------------------------------------------------------------------- }
TMDIChildMain = class(TForm)
// Auflistung aller eingebundenen Komponenten der Klasse
PageControl1: TPageControl;
GrammarSheet: TTabSheet;
OptionsSheet: TTabSheet;
Panel2: TPanel;
RunBtn: TButton;
Panel3: TPanel;
GroupBox1: TGroupBox;
RecursEdit: TEdit;
BasisAngleEdit: TEdit;
BasisThickEdit: TEdit;
AxiomEdit: TEdit;
Splitter1: TSplitter;
Panel4: TPanel;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
GroupBox3: TGroupBox;
AACheck: TCheckBox;
TargaCheck: TCheckBox;
WidthEdit: TEdit;
Label12: TLabel;
HeightEdit: TEdit;
Label13: TLabel;
LightCamSheet: TTabSheet;
GroupBox4: TGroupBox;
Label14: TLabel;
Label15: TLabel;
Label16: TLabel;
GroupBox5: TGroupBox;
Label17: TLabel;
231
H
Quelltexte
Label18: TLabel;
Label19: TLabel;
Image1: TImage;
SaveBtn: TButton;
SaveDialog: TSaveDialog;
SaveAsBtn: TButton;
CamXTrack: TTrackBar;
CamYTrack: TTrackBar;
CamZTrack: TTrackBar;
CamZLabel: TLabel;
CamYLabel: TLabel;
CamXLabel: TLabel;
LightXTrack: TTrackBar;
LightYTrack: TTrackBar;
LightZTrack: TTrackBar;
LightZLabel: TLabel;
LightYLabel: TLabel;
LightXLabel: TLabel;
GroupBox2: TGroupBox;
Splitter2: TSplitter;
Panel1: TPanel;
Splitter3: TSplitter;
Panel5: TPanel;
CheckBtn: TButton;
Panel9: TPanel;
MessageList: TListBox;
Panel6: TPanel;
Splitter5: TSplitter;
Panel7: TPanel;
EditBtn: TButton;
DelBtn: TButton;
Button1: TButton;
Panel8: TPanel;
Panel10: TPanel;
Label5: TLabel;
Label6: TLabel;
Label7: TLabel;
Label8: TLabel;
Label9: TLabel;
Label10: TLabel;
Label11: TLabel;
RightContextEdit: TEdit;
LeftContextEdit: TEdit;
ProHeadEdit: TEdit;
ConditionEdit: TEdit;
ProBodyEdit: TEdit;
AddBtn: TButton;
Panel11: TPanel;
ProductionList: TListBox;
DelAllMsgBtn: TButton;
Direct3DBtn: TButton;
Panel12: TPanel;
Panel13: TPanel;
Panel14: TPanel;
Panel15: TPanel;
Splitter4: TSplitter;
Add2Btn: TButton;
UpdateBtn: TButton;
CloseBtn: TButton;
CondBtn: TButton;
Label20: TLabel;
Button2: TButton;
// Auflistung aller Methoden für die Ereignisverarbeitung
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
procedure DelBtnClick(Sender: TObject);
procedure AddBtnClick(Sender: TObject);
procedure EditBtnClick(Sender: TObject);
procedure RecursEditChange(Sender: TObject);
procedure RecursEditKeyPress(Sender: TObject; var Key: Char);
procedure CheckBtnClick(Sender: TObject);
procedure SaveBtnClick(Sender: TObject);
procedure SaveAsBtnClick(Sender: TObject);
procedure RunBtnClick(Sender: TObject);
procedure DelAllMsgBtnClick(Sender: TObject);
procedure Direct3DBtnClick(Sender: TObject);
procedure CamXTrackChange(Sender: TObject);
232
H
Quelltexte
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
CamYTrackChange(Sender: TObject);
CamZTrackChange(Sender: TObject);
LightXTrackChange(Sender: TObject);
LightYTrackChange(Sender: TObject);
LightZTrackChange(Sender: TObject);
Button2Click(Sender: TObject);
UpdateBtnClick(Sender: TObject);
Add2BtnClick(Sender: TObject);
ProBodyEditKeyPress(Sender: TObject; var Key: Char);
CloseBtnClick(Sender: TObject);
FormCloseQuery(Sender: TObject; var CanClose: Boolean);
FormCreate(Sender: TObject);
CondBtnClick(Sender: TObject);
// Auflistung aller privaten Methoden als Prototypen
private
function SyntaxLineCheck(var production: String; var new_production: String) : String;
function SystemCheck : boolean;
function saveToCFG : boolean;
procedure StrToProduction(const str: string; var lcontext: string; var predessor: string;
var rcontext: string; var condition: string; var successor: string);
function TestingForBrackets(const succ: string) : string;
procedure TestingProductionHeads(const pred: string; var headlist: string;
var headdoublelist: string);
procedure TestingForExistingSymbols(const succ: string; const lcon: string;
const rcon: string; const headList: string; var missHeadList: string);
function TestingHeadParameterList(const pred: string; const cond: string;
const succ: string) : string;
public
{ Public-Deklarationen }
end;
{ ---------------------------------------------------------------------------Öffentliche globale Variablen
---------------------------------------------------------------------------- }
var
MDIChildMain: TMDIChildMain;
// Die Instanz von TMDIMain ist öffentlich für alle anderen
// Units erreichbar
{ ---------------------------------------------------------------------------IMPLEMENTATIONSTEIL DER UNIT
---------------------------------------------------------------------------- }
implementation
{ ---------------------------------------------------------------------------Compiler-Schalter
---------------------------------------------------------------------------- }
{$R *.dfm}
{ ---------------------------------------------------------------------------Liste aller private eingebundenen Units
---------------------------------------------------------------------------- }
uses MDIMainSource, Direct3DSource, TurtleComSource, CondFormSource, ConsoleFormSource;
{ ---------------------------------------------------------------------------Globale Variablen die nur in dieser Unit erreichbar sind
---------------------------------------------------------------------------- }
var updProduction: string;
// Speichert die Produktion die Bearbeitet werden soll
{ ---------------------------------------------------------------------------Methodenname:
Rückgabewert
Typ:
Bedeutung:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
TestingHeadParameterList
string
Gibt einen String mit einer Warnmeldung zurück oder
eine leere Zeichenkette
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
pred
Enthält den Produktionskopf einer Produktion
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
cond
233
H
Quelltexte
Bedeutung:
Typ:
Name:
Bedeutung:
Enthält den Bedingungsteil einer Produktion
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
succ
Enthält den Produktionskörper einer Produktion
Methodenbeschreibung:
Die Aufgabe dieser Methode ist, die Parameterliste des Produktionskopfs zu
prüfen, ob die angegebenen Parameter verwendet werden und ob Parameter
verwendet werden die nicht aufgelistet sind.
---------------------------------------------------------------------------- }
function TMDIChildMain.TestingHeadParameterList(const pred: string; const cond: string;
const succ: string) : string;
var parameterlist : string;
notfoundlist
: string;
s
parameter
: string;
: string;
i, j
bracket
: integer;
: boolean;
//
//
//
//
//
//
//
//
//
Speichert alle Variablen, die in der Parameterliste
enthalten sind
Enthält alle Variablen einer Produktion, die nicht
deklariert wurden
Zwischenspeicher
Kopie von der Parameterliste, um gefundene Variablen aus
der Liste zu löschen
Laufvariablen
Signalisiert das eine Parameterliste durchlaufen wird
begin
// Variablen Initialisierung
result := '';
bracket := false;
parameterlist := '';
notfoundlist := '';
i := Pos('(', pred);
if i = 0 then
Exit;
// Suche nachdem Anfang einer Parameterliste
// Ist keine Parameterliste vorhanden,
// dann Beende die Verarbeitung
// Schleife durchläufen die Parameterliste der Produktion und entnimmt die Variablen
for j := i+1 to length(pred)-1 do
// Leerzeichen und das Komma werden nicht beachtet
if not(pred[j] in [' ', ',']) then
parameterlist := parameterlist+pred[j]+',';
parameter := parameterlist;
// Zuweisung der Kopie der Parameterliste
// Schleife durchläuft den Bedingungsteil, um Variablen zu finden
for i := 1 to length(cond) do
// Ist das nächste Zeichen eine Variable und kein mögliches Zeichen für eine Ausdruck?
if not(cond[i] in ['=', '<', '>', '&', '|', '+', '*', '-', '/', ' ', '.', '0'..'9']) then
begin
j := Pos(cond[i], parameterlist);
// Suche nach der Position des Zeichens
// in der Parameterliste
// Wurde ein Eintrag gefunden, dann lösche die Variable aus der Liste
if j > 0 then
begin
s := Copy(parameterlist, 1, j-1) + Copy(parameterlist, j+2, length(parameterlist));
parameterlist := s;
end
// Ansonsten trage die Variable in die Liste der nicht deklarierten Parameter,
// sofern das Zeichen nicht schon dort eingetragen ist
else if Pos(cond[i], parameter) = 0 then
if Pos(cond[i], notfoundlist) = 0 then
notfoundlist := notfoundlist+cond[i]+',';
end;
// Durchlaufe den Produktionskörper, um in Argumentenlisten nach Variablen zu suchen
for i := 1 to length(succ) do
// Wurde der Anfang einer Parameterliste gefunden, dann setze die Variable bracket
if succ[i] = '(' then
bracket := true
// Wurde das Ende einer Parameterliste gefunden, dann deaktiviere die Variable bracket
else if succ[i] = ')' then
234
H
Quelltexte
bracket := false
// Wird gerade eine Parameterliste durchlaufen und die Symbole stellen keinen Ausdruck dar
// und damit eine Variable, dann Prüfe diese
else if not(succ[i] in ['=', '<', '>', '&', '|', '+', '*', '-', '/', ' ', '.', '0'..'9'])
and bracket then
begin
j := Pos(succ[i], parameterlist);
// Suche nach der Position des Zeichens
// in der Parameterliste
// Wurde ein Eintrag gefunden, dann lösche die Variable aus der Liste
if j > 0 then
begin
s := Copy(parameterlist, 1, j-1) + Copy(parameterlist, j+2, length(parameterlist));
parameterlist := s;
end
// Ansonsten trage die Variable in die Liste der nicht deklarierten Parameter,
// sofern das Zeichen nicht schon dort eingetragen ist
else if Pos(succ[i], parameter) = 0 then
if Pos(succ[i], notfoundlist) = 0 then
notfoundlist := notfoundlist+succ[i]+',';
end;
// Gebe eine entsprechende Nachricht zurück, wenn die Parameterliste noch
// Parameter enthält (damit nicht verwendet werden) oder wenn in der Variable
// notfoundlist Parameter enthalen sind (Parameter die nicht deklariert wurden)
if (length(parameterlist) > 0) and (length(notfoundlist) > 0) then
result := 'Folgende Parameter werden nicht in der Produktion benutzt: '+Copy(parameterlist,
1, length(parameterlist)-1)+'! Folgende Parameter sind nicht im'+
'+Produktionskopf enthalten: '+Copy(notfoundlist, 1, length(notfoundlist)-1)+'!'
else if length(parameterlist) > 0 then
result := 'Folgende Parameter werden nicht in der Produktion benutzt: '+Copy(parameterlist,
1, length(parameterlist)-1)+'!'
else if length(notfoundlist) > 0 then
result := 'Folgende Parameter sind nicht im Produktionskopf enthalten: '+Copy(notfoundlist,
1, length(notfoundlist)-1)+'!';
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
TestingForExistingSymbols
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
succ
Enthält den Produktionskörper
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
lcon
Enthält den linken Kontext einer Produktion
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
rcon
Enthält den rechten Kontext einer Produktion
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
headList
Ist eine Liste in der alle Symbole enthalten sind für die
bisher eine Produktion gefunden wurde
var string
missHeadList
Zeiger auf eine String-Liste in dem alle Symbole enthalten
sind für die keine Produktion gefunden wurde
Methodenbeschreibung:
Diese Methode prüft eine Produktion nach deren enthaltenen Symbolen und
ob es für die Symbole eine Produktion existiert.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.TestingForExistingSymbols(const succ: string; const lcon: string;
const rcon: string; const headList: string; var missHeadList: string);
var i
: integer;
bracket : boolean;
begin
bracket := false;
// Laufvariable
// Wird gesetzt, wenn im Produktionskörper eine Parameterliste
// gefunden wurde
// Initialisierung
235
H
Quelltexte
// Wenn es einen linken Kontext gibt, dann durchlaufe diesen und Prüfe ob ein Symbol
// enthalten ist, für das noch keine Produktion gefunden wurde und speichere es
// in der Liste missHeadList ab
if length(lcon) > 0 then
for i := 1 to length(lcon) do
if not((succ[i] in ['c', 'F', 'f', 'Z', 'z', 'g', '.', '+', '-', '&', '^', '/', '\',
'|', '%', '$', '~', 't', '"', #39, ';', ':', '?', '|', '[', ']', '{', '}', ' '])
or (Pos(succ[i], headList) > 0) or (Pos(succ[i], missHeadList) > 0)) then
missHeadList := missHeadList+succ[i]+',';
// Wenn es einen rechten Kontext gibt, dann durchlaufe diesen und Prüfe ob ein Symbol
// enthalten ist, für das noch keine Produktion gefunden wurde und speichere es
// in der Liste missHeadList ab
if length(rcon) > 0 then
for i := 1 to length(rcon) do
if not((succ[i] in ['c', 'F', 'f', 'Z', 'z', 'g', '.', '+', '-', '&', '^', '/', '\',
'|', '%', '$', '~', 't', '"', #39, ';', ':', '?', '|', '[', ']', '{', '}', ' '])
or (Pos(succ[i], headList) > 0) or (Pos(succ[i], missHeadList) > 0)) then
missHeadList := missHeadList+succ[i]+',';
// Durchlaufe den Produktionskörper und suche Symbolen
for i := 1 to length(succ) do
if succ[i] = '(' then
// Wurde der Anfang einer Parameterliste gefunden,
begin
// dann setze die Variable bracket
bracket := true;
continue;
end
else if succ[i] = ')' then
// Wurde das Ende einer Parameterliste gefunden,
begin
// dann deaktiviere die Variable bracket
bracket := false;
continue;
end
else if bracket then
// Überspringe den Inhalt einer Parameterliste
continue
// Prüfe ob das Zeichen ein Symbol darstellt und speichere es in der Liste
// missHeadList ab, wenn es keine Produktion dafür existiert
else if not((succ[i] in ['c', 'F', 'f', 'Z', 'z', 'g', '.', '+', '-', '&', '^', '/',
'\', '|', '%', '$', '~', 't', '"', #39, ';', ':', '?', '|', '[', ']',
'{', '}', ' '])
or (Pos(succ[i], headList) > 0) or (Pos(succ[i], missHeadList) > 0)) then
missHeadList := missHeadList+succ[i]+',';
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
TestingProductionHeads
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
pred
Enthält den Produktionskopf
var string
headList
Zeiger auf eine String-Liste mit allen vorhandenen
Produktionsköpfen
var string
headdoublelist
Zeiger auf eine String-Liste, in der alle Symbole enthalten
sind die mehr als eine Produktion aufweisen können
Methodenbeschreibung:
Prüft ob für ein Symbol mehr als eine Produktion existert.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.TestingProductionHeads(const pred: string; var headlist: string;
var headdoublelist: string);
var i : integer;
s : string;
// Zwischenspeicher für Ergebnisse
// Zwischenspeicher für Ergebnisse
begin
i := Pos('(', pred);
// Suche nachdem Anfang einer Parameterliste im Produktionskopf
if i > 0 then
// Wurde eine gefunden, dann kopiere das Symbol heraus
s := Copy(pred, 1, i-1)
else
// Wurde keine gefunden, dann übernehme den Produktionskopf
236
H
Quelltexte
s := pred;
i := Pos(s+',', headlist);
// Suche nach einem Eintrag des Symbols in der Liste headList
// Wurde ein Eintrag gefunden, dann speichere das Symbol in die headdoublelist Liste,
// ansonsten trage die Variable in die headList Liste ein
if i > 0 then
headdoublelist := headdoublelist + s+','
else
headlist := headlist+s+',';
end;
{ ---------------------------------------------------------------------------Methodenname:
Rückgabewert
Typ:
Bedeutung:
Parameter
Typ:
Name:
Bedeutung:
TestingForBrackets
string
Gibt einen String mit einer Warnmeldung zurück oder
eine leere Zeichenkette
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
succ
Enthält den Produktionskörper einer Produktion
Methodenbeschreibung:
Es wird geprüft, ob die Anzahl der öffnenden und schließenden, eckigen und
geschweifen Klammern gleich ist.
---------------------------------------------------------------------------- }
function TMDIChildMain.TestingForBrackets(const succ: string) : string;
var lbracket
rbracket
lpolygon
rpolygon
i
:
:
:
:
:
integer;
integer;
integer;
integer;
integer;
//
//
//
//
//
Enthält die Anzahl
Enthält die Anzahl
Enthält die Anzahl
Enthält die Anzahl
Laufvariable
der
der
der
der
eckigen öffnenden Klammern
eckigen schließenden Klammern
geschweiften öffnenden Klammern
geschweiften schließenden Klammern
begin
// Initialisierung von Variablen
lbracket := 0;
rbracket := 0;
lpolygon := 0;
rpolygon := 0;
result := '';
// Durchlaufe den Produktionskörper und erhöhe den Wert einer Variable, wenn
// eine Klammer entsprechend der Bedeutung der Variable gefunden wurde
for i := 1 to length(succ) do
if succ[i] = '[' then
inc(lbracket)
else if succ[i] = ']' then
inc(rbracket)
else if succ[i] = '{' then
inc(lpolygon)
else if succ[i] = '}' then
inc(rpolygon);
// Gibt eine entsprechende Nachricht zurück, wenn die Anzahl der öffnenden und
// schließenden, eckigen und geschweiften Klammern ungleich ist
if (lbracket <> rbracket) and (lpolygon <> rpolygon) then
result := 'Die Anzahl der schliessenden und öffnenden, eckigen und geschweiften Klammern+'
+' ist ungleich!'
else if lbracket <> rbracket then
result := 'Die Anzahl der schliessenden und öffnenden eckigen Klammern ist ungleich!'
else if lpolygon <> rpolygon then
result := 'Die Anzahl der schliessenden und öffnenden geschweiften Klammern ist ungleich!';
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
StrToProduction
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
237
H
Quelltexte
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
str
String der
var string
lcontext
Enthält am
var string
predessor
Enthält am
var string
rcontext
Enthält am
var string
condition
Enthält am
var string
successor
Enthält am
zerlegt werden soll
Schluss den linken Kontext
Schluss den Produktionskopf
Schluss den rechten Kontext
Schluss den Bedingungsteil
Schluss den Produktionskörper
Methodenbeschreibung:
Zerlegt einen gegebenen String, in die Einzelteile einer Produktion.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.StrToProduction(const str: string; var lcontext: string;
var predessor: string; var rcontext: string; var condition: string;
var successor: string);
var lcon
rcon
cond
prob
:
:
:
:
integer;
integer;
integer;
integer;
//
//
//
//
Enthält
Enthält
Enthält
Enthält
die
die
die
die
Position,
Position,
Position,
Position,
an
an
an
an
der
der
der
der
der
der
der
der
linke Kontext anfängt
recte Kontext anfängt
Bedingungsteil anfängt
Produktionskörper anfängt
begin
// Die Variablen werden mit den Werten deren Bedeutung belegt
lcon := Pos(' < ', str);
rcon := Pos(' > ', str);
cond := Pos(' : ', str);
prob := Pos(' -> ', str);
// Ist ein linker Kontext vorhanden und nicht Bestandteil eines arithmetischen
// Ausdruckes (weil es auch "kleiner" bedeuten kann), dann kopieren den String
// heraus und speichere es in lcontext ab
if (lcon > 0) and ((lcon < cond) or (cond = 0)) then
lcontext := Copy(str, 1, lcon-1)
else
// Ansonsten belege den Teil mit einem Wildcard
lcontext := '*';
successor := Copy(str, prob+4, length(str));
// Kopiere den Produktionskörper heraus
// Die folgenden if-Anweisungen lösen den Produktionskopf heraus, wobei beachtet
// werden muss (um die Position exakt zu bestimmen) welche Teile einer
// Produktion enthalten sind
if (rcon > 0) and ((rcon < cond) or (cond = 0)) then
if lcon = 0 then
predessor := Copy(str, 1, rcon-1)
else
predessor := Copy(str, lcon+3, rcon-3-lcon)
else if cond > 0 then
if (lcon = 0) or (lcon > cond) then
predessor := Copy(str, 1, cond-1)
else
predessor := Copy(str, lcon+3, cond-3-lcon)
else
if lcon = 0 then
predessor := Copy(str, 1, prob-1)
else
predessor := Copy(str, lcon+3, prob-3-lcon);
// Wenn ein rechter Kontext vorhaden ist, dann kopiere diesen heraus
if rcon > 0 then
if cond = 0 then
rcontext := Copy(str, rcon+3, prob-3-rcon)
else
rcontext := Copy(str, rcon+3, cond-3-rcon)
else
rcontext := '*';
// Ansonsten belege den Teil mit einem Wildcard
238
H
Quelltexte
// Wenn ein Bedingungsteil vorhaden ist, dann kopiere diesen heraus
if cond > 0 then
condition := Copy(str, cond+3, prob-3-cond)
else
condition := '*';
// Ansonsten belege den Teil mit einem Wildcard
end;
{ ---------------------------------------------------------------------------Methodenname:
Rückgabewert
Typ:
Bedeutung:
saveToCFG
boolean
Gibt zurück, ob die Verarbeitung erfolgreich war
Methodenbeschreibung:
Speichert die aktuelle Einstellung in der Info-Strukur in die CFG-Datei.
---------------------------------------------------------------------------- }
function TMDIChildMain.saveToCFG : boolean;
var cfg : TextFile;
// Zeiger auf die CFG-Datei
begin
result := true;
// Initialisierung des Rückgabewertes
// Dateiverarbeitung
AssignFile(cfg, 'LPROCESS.CFG');
{$i-} Rewrite(cfg); {$i+}
if IOResult <> 0 then
begin
result := false;
Exit;
end;
// Dateinamen den File-Handle zu zuweisen
// Öffnet Datei zum schreiben und schaltet
// zwischenzeitig die I/O-Prüfung aus
// Wurde die Datei erfolglos geöffnet,
// dann Beende die Verarbeitung
// Schreibe den Inhalt der Info-Struktur in die CFG-Datei
if Info.Bitmap = true then
WriteLn(cfg, 'Image=bmp')
else
WriteLn(cfg, 'Image=Targa');
if Info.AA = true then
WriteLn(cfg, 'AA=on')
else
WriteLn(cfg, 'AA=off');
if Info.Files = true then
WriteLn(cfg, 'Files=on')
else
WriteLn(cfg, 'Files=off');
WriteLn(cfg, 'Width='+IntToStr(Info.Width));
WriteLn(cfg, 'Height='+IntToStr(Info.Height));
WriteLn(cfg, 'POV='+Info.PathPOV);
WriteLn(cfg, 'QPOV='+Info.PathQPOV);
WriteLn(cfg, 'CamX='+FloatToStr(Info.CamX));
WriteLn(cfg, 'CamY='+FloatToStr(Info.CamY));
WriteLn(cfg, 'CamZ='+FloatToStr(Info.CamZ));
WriteLn(cfg, 'LightX='+FloatToStr(Info.LightX));
WriteLn(cfg, 'LightY='+FloatToStr(Info.LightY));
WriteLn(cfg, 'LightZ='+FloatToStr(Info.LightZ));
CloseFile(cfg);
end;
// Schließe den Zugriff auf die CFG-Datei
{ ---------------------------------------------------------------------------Methodenname:
Rückgabewert
Typ:
Bedeutung:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
SyntaxLineCheck
string
Gibt einen String mit einer Fehlermeldung zurück oder
eine leere Zeichenkette
var string
production
Enthält die ursprüngliche Produktion
var string
new_production
239
H
Quelltexte
Bedeutung:
Enthält die ursprüngliche Produktion formatiert
Methodenbeschreibung:
Prüft eine Produktion nach Fehlern und formatiert den String der Produktion.
---------------------------------------------------------------------------- }
function TMDIChildMain.SyntaxLineCheck(var production: String; var new_production: String)
: String;
var substr
pred
con
index1, index2
:
:
:
:
String;
String;
String;
integer;
//
//
//
//
Zwischenspeicher
Enthält den Produktionskopf
Enthält den Bedingungsteil
Zwischenspeicher
begin
result := '';
// Initialisierung
index1 := Pos('<', production);
substr := Copy(production, 1, index1-2);
// Sucht die Position des linken Kontext
// Kopiert den linken Kontext heraus
// Ist im linken Kontext ein Wildcard und dahiner befinden sich Zeichen, dann gib eine
// Fehlermeldung aus
if (Pos('*', substr) > 0) and (length(substr) > 1) then
begin
result := 'Der linke Kontext enthält neben dem *-Zeichen, noch weitere Zeichen!';
Exit;
end;
// Ist im linken Kontext eine runde Klammer, dann gib eine Fehlermeldung aus
if (Pos('*', substr) = 0) and ((Pos('(', substr) > 0) or (Pos(')', substr) > 0)) then
begin
result := 'Im Kontext sind keine Klammern erlaubt!';
Exit;
end;
// Wenn kein Wildcardsymbol enthalten ist, dann formatiere den String
if Pos('*', substr) = 0 then
new_production := substr+' < ';
index2 := Pos('>', production);
// Sucht die Position des rechten Kontext
pred := Copy(production, index1+2, index2-3-index1);
// Kopiert den Produktionskopf heraus
// Prüft, ob eine Klammer von der Parameterliste fehlt und gibt eine Fehlermeldung aus
if (Pos('(', pred) > 0) xor (Pos(')', pred) > 0) then
begin
result := 'Es fehlt eine Klammer!';
Exit;
end;
index1 := Pos(':', production);
// Sucht die Position des Bedingungsteils
substr := Copy(production, index2+2, index1-3-index2); // Kopiert den rechten Kontext heraus
// Ist im rechten Kontext ein Wildcard und dahiner befinden sich Zeichen, dann gib
// eine Fehlermeldung aus
if (Pos('*', substr) > 0) and (length(substr) > 1) then
begin
result := 'Der rechte Kontext enthält neben dem *-Zeichen, noch weitere Zeichen!';
Exit;
end;
// Ist im rechten Kontext eine runde Klammer, dann gib eine Fehlermeldung aus
if (Pos('*', substr) = 0) and ((Pos('(', substr) > 0) or (Pos(')', substr) > 0)) then
begin
result := 'Im Kontext sind keine Klammern erlaubt!';
Exit;
end;
// Wenn kein Wildcardsymbol enthalten ist, dann formatiere den String
if Pos('*', substr) = 0 then
substr := ' > '+substr
else
substr := '';
index2 := Pos('->', production);
// Sucht die Position des Produktionskörpers
con := Copy(production, index1+2, index2-3-index1);
// Kopiert den Bedingungsteil heraus
240
H
Quelltexte
// Ist im Bedingungsteil ein Wildcard und dahiner befinden sich Zeichen, dann gib
// eine Fehlermeldung aus
if (length(con) > 1) and (Pos('*', con) > 0) then
begin
result := 'Die Bedingung enthält neben dem *-Zeichen, noch weitere Zeichen!';
Exit;
end;
// Es wird geprüft, ob eine Parameterliste vorhanden ist, wenn ein Bedingungsteil
// vorhanden ist
if (Pos('*', con) = 0) and ((Pos('(', pred) = 0) or (Pos(')', pred) = 0)) then
begin
result := 'Für die Bedingung sind keine Parameter vorhanden!';
Exit;
end;
// Wenn ein Wildcardsymbol enthalten ist, dann
if Pos('*', con) > 0 then
con := ' ->'
else
begin
result := MDIMain.testingCondition(con);
con := ' : '+con+' ->';
if result <> '' then
wird kein Bedingungsteil angefügt
//
//
//
//
//
Prüfte ob die Bedingung fehlerfrei ist
Formatiere den Bedingungsteil
Wenn ein Fehler in der Bedingung
gefunden wurde,
dann Beende die Verarbeitung
exit;
end;
result := MDIMain.testingProductionHead(pred); // Prüfe den Produktionskopf
// Wurde keine Fehlermeldung erzeugt, dann Beende die Verarbeitung ohne die
// endgültige Speicherung des formatierten Strings
if result <> '' then
exit;
// Weist den endgültigen formatierten String zu
new_production := new_production + pred + substr + con + Copy(production, index2+2,
length(production));
end;
{ ---------------------------------------------------------------------------Methodenname:
Rückgabewert
Typ:
Bedeutung:
SystemCheck
boolean
Gibt zurück, ob die Verarbeitung erfolgreich war
Methodenbeschreibung:
Prüft die Produktionen, ob Schwächen vorhanden sind.
---------------------------------------------------------------------------- }
function TMDIChildMain.SystemCheck : boolean;
var i, k
lcon
pred
rcon
cond
succ
s
missProd
heads
headdoubles
:
:
:
:
:
:
:
:
:
:
integer;
string;
string;
string;
string;
string;
string;
string;
string;
string;
//
//
//
//
//
//
//
//
//
//
Laufvariable
Enthält den linken Kontext der Produktion
Enthält den Produktionskopf der Produktion
Enthält den rechten Kontext der Produktion
Enthält den Bedingungsteil der Produktion
Enthält den Produktionskörper der Produktion
Zwischenspeicher
Liste aller Symbole die keine Produktionen haben
Liste aller Symbole die eine Produktion haben
Liste aller Symbole die mehrere Produktionen haben
begin
// Initialisierung der Variablen
result := true;
k := 0;
missProd := '';
heads := '';
headdoubles := '';
MessageList.Clear;
// Durchlaufe die Liste alle Produktionen
for i := 0 to ProductionList.Count-1 do
241
H
Quelltexte
begin
// Zerlege ein Produktionsstring in seine Einzelteile
StrToProduction(ProductionList.Items[i], lcon, pred, rcon, cond, succ);
// Teste den Produktionskopf auf Fehler
TestingProductionHeads(pred, heads, headdoubles);
// Teste die Produktionsteile nach Symbolen die keine Produktion haben
TestingForExistingSymbols(succ, lcon, rcon, heads, missProd);
// Teste die Produktion, nach der Anzahl deren Klammern
s := TestingForBrackets(succ);
if s <> '' then
// Ist die Klammernanzahl ungleich?
begin
// Gebe eine Nachricht aus
MessageList.Items.Add('Warnung in Regel '+IntToStr(i+1)+' !');
MessageList.Items.Add(s);
result := false;
end;
// Teste die Parameterliste, ob alle Symbole verwendet werden oder nicht
// deklarierte verwendet werden
s := TestingHeadParameterList(pred, cond, succ);
if s <> '' then
// Ist die Prüfung erfolgreich gewesen,
begin
// dann gebe eine Nachricht aus
MessageList.Items.Add('Warnung in Regel '+IntToStr(i+1)+' !');
MessageList.Items.Add(s);
result := false;
end;
end;
// Wurde für eine Symbol mehrere Produktionen gefunden?
if length(headdoubles) > 0 then
begin
// Dann gebe eine Nachricht aus
MessageList.Items.Add('Warnung: Für folgende Symbole gibt es mehrere Produktionen : '+
Copy(headdoubles, 1, length(headdoubles)-1));
result := false;
end;
s := '';
// Sind Symbole vorhanden, für die im ersten Durchgang keine Produktionen gefunden wurde?
if length(missProd) > 0 then
// Dann Durchlaufe die Liste und prüfe ob nach der Aufnahme aller Symbole, nun
// Produktionen für das Symbol vorhanden sind
for i := 1 to (length(missProd) div 2) do
begin
if i = 1 then
k := 1
else
k := k +2;
if Pos(missProd[k], heads) = 0 then
s := s+Copy(missProd, k, 2);
end;
missProd := s;
// Sind weiterhin Symbole vorhanden, für die keine Produktion existieren, dann
// gib eine Nachricht aus
if length(missProd) > 0 then
begin
MessageList.Items.Add('Warnung: Für folgende Symbole existiert keine Produktion : '+
Copy(missProd, 1, length(missProd)-1));
result := false;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
FormClose
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
var TCloseAction
Action
Gibt an, wie das Fenster geschlossen werden ssoll
242
H
Quelltexte
Methodenbeschreibung:
Methode wird beim Beenden des Formulars aufgerufen.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
if D3DActive then
D3DForm.Close;
if TurtleActive then
TurtleForm.Close;
if CondActive then
CondForm.Close;
Action := caFree;
end;
// Falls ein Direct3D-Fenster offen ist, dann schließe es
// Falls ein Fenster mit den Turtle-Kommandos offen ist,
// dann schließe es
// Falls ein Fenster mit den Ausdrücken für die Bedingung offen ist,
// dann schließe es
// Gib den Speicher frei
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Button1Click
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird ausgeführt, wenn der Button zum Löschen aller Produktionen
geklickt wird.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.Button1Click(Sender: TObject);
begin
// Wenn Produktionen vorhanden sind, dann Frage nach, ob diese wirklich
// gelöscht werden sollen
if ProductionList.Items.Count > 0 then
if MessageDlg('Wollen Sie wirklich alle Produktionen löschen?', mtConfirmation,
[mbYes, mbNo], 0) = mrYes then
begin
ProductionList.Clear;
// Da das L-System verändert wurde, wird der Button zum speichern freigegeben
// und das L-System als modifiziert gekennzeichnet
SaveBtn.Enabled := true;
if Caption[length(Caption)] <> '*' then
Caption := Caption+'*';
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
DelBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Die Methode wird aufgerufen, wenn der Button zum löschen einer einzelnen
Produktion geklickt wird.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.DelBtnClick(Sender: TObject);
begin
// Ist eine Produktion ausgewählt worden,
if ProductionList.ItemIndex > -1 then
begin
243
H
Quelltexte
// dann lösche diese
ProductionList.Items.Delete(ProductionList.ItemIndex);
// Da das L-System verändert wurde, wird der Button zum speichern freigegeben
// und das L-System als modifiziert gekennzeichnet
SaveBtn.Enabled := true;
if Caption[length(Caption)] <> '*' then
Caption := Caption+'*';
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
AddBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Die Methode wird aufgerufen, wenn eine Produktion hinzugefügt werden soll und
auf den Button "Hinzufügen" geklickt wird.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.AddBtnClick(Sender: TObject);
var production
new_production
msg
i
:
:
:
:
String;
String;
String;
integer;
//
//
//
//
Produktion aus der Eingabemaske
Formatierte Produktion für die Produktionsliste
Nachricht für Fehlermeldungen die ausgegeben werden
Laufvariable
begin
// Prüfe, ob in allen Editierfelder etwas eingetragen wurde
if (LeftContextEdit.Text = '') or (RightContextEdit.Text = '') or (ProHeadEdit.Text = '')
or (ProBodyEdit.Text = '') or (ConditionEdit.Text = '') then
begin
MessageDlg('Die Produktion ist nicht vollständig!', mtError, [mbOk], 0);
Exit;
end;
// Baue den String für die Produktion zusammen
production := trim(LeftContextEdit.Text)+' < '+trim(ProHeadEdit.Text)+' > '+
trim(RightContextEdit.Text)+' : '+trim(ConditionEdit.Text)+' -> '+
trim(ProBodyEdit.Text);
// Prüfe die Produktion
msg := SyntaxLineCheck(production, new_production);
// Wurde kein Fehler gefunden, dann Prüfe ob der String schon existiert in
// der Produktionsliste, ansonsten füge den String ein
if msg = '' then
begin
for i := 0 to ProductionList.Count-1 do
// Durchlaufen der Produktionen
begin
if CompareText(new_production, ProductionList.Items[i]) = 0 then
begin
MessageDlg('Diese Produktion existiert schon!', mtWarning, [mbOk], 0);
Exit;
end;
end;
ProductionList.Items.Add(new_production);
end
// Wurde ein Fehler gefunden, dann gib diesen aus und Beende die Verarbeitung
else
begin
MessageDlg(msg, mtError, [mbOk], 0);
Exit;
end;
// Markiere, dass das L-System verändert wurde
if Caption[length(Caption)] <> '*' then
Caption := Caption+'*';
LeftContextEdit.Text := '*';
244
H
Quelltexte
RightContextEdit.Text := '*';
ConditionEdit.Text := '*';
ProHeadEdit.Text := '';
ProBodyEdit.Text := '';
SaveBtn.Enabled := true;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
EditBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Wenn eine Produktion bearbeitet werden soll, dann Rufe diese Funktion auf.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.EditBtnClick(Sender: TObject);
var lcon
pred
rcon
cond
prob
i
:
:
:
:
:
:
string;
string;
string;
string;
string;
integer;
//
//
//
//
//
//
Enthält den linken Kontext der Produktion
Enthält den Produktionskopf der Produktion
Enthält den rechten Kontext der Produktion
Enthält den Bedingungsteil der Produktion
Enthält den Produktionskörper der Produktion
Laufvariable
begin
// Speicher die zu bearbeitende Produktion zwischen, damit UpdateBtnClick
// auf den String zugreifen kann
updProduction := ProductionList.Items[ProductionList.ItemIndex];
// Zerlege die Produktion in seine Einzelteile und zeige diese mit den
// entsprechenden Editierfelder an
StrToProduction(ProductionList.Items[ProductionList.ItemIndex], lcon, pred, rcon,
cond, prob);
LeftContextEdit.Text := lcon;
ProHeadEdit.Text := pred;
RightContextEdit.Text := rcon;
ConditionEdit.Text := cond;
ProBodyEdit.Text := prob;
UpdateBtn.Visible := true;
Add2Btn.Visible := true;
AddBtn.Visible := false;
// Aktiviere die alternative Hinzufüge-Funktion
// Deaktiviere die Standard Hinzufüge-Funktion
// Passe die Größe des Editierfeldes für den Produktionskörper
// an die Größe des Strings an
i := Canvas.TextWidth(pred);
if i > ProBodyEdit.Width then
begin
ProBodyEdit.Width := i+8;
UpdateBtn.Left := ProBodyEdit.Width + 311;
AddBtn.Left := UpdateBtn.Left;
Add2Btn.Left := UpdateBtn.Left + 88;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
RecursEditChange
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Diese Methode wird von allen Editierfeldern verwendet und wird ausgeführt
wenn der Inhalt eines Editierfeldes sich ändert.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.RecursEditChange(Sender: TObject);
245
H
Quelltexte
begin
// Wenn das aufrufende Objekt ein TEdit-Objekt ist, dann markiere das Fenster
// das sich der Inhalt verändert hat
if (Sender As TEdit).Modified = true then
begin
SaveBtn.Enabled := true;
if Caption[length(Caption)] <> '*' then
Caption := Caption+'*';
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
RecursEditKeyPress
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
var char
Key
Zeiger auf das Zeichen, das eingegeben wurde
Methodenbeschreibung:
Bei jeder Zeicheneingabe für Editierfelder die nur Zahlen akzeptieren,
wird diese Methode aufgerufen.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.RecursEditKeyPress(Sender: TObject; var Key: Char);
begin
// Lasse nur Zahlen und den Rücklauf als Eingabe zu
if (Key in ['0'..'9']) or (Key = #8) then
Key := Key
else
Key := #0;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
CheckBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Führt die Prüfung der gesamten Produktionen aus, wenn auf den Button
"Prüfen" geklickt wird.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.CheckBtnClick(Sender: TObject);
begin
SystemCheck;
end;
// Führe die Prüfung aus
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
SaveBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Speichert den Inhalt des Kindfensters in die LS-Datei ab.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.SaveBtnClick(Sender: TObject);
246
H
Quelltexte
var filename
s
old_file
new_file
can_open
abort
i
:
:
:
:
:
:
string;
string;
TextFile;
TextFile;
boolean;
boolean;
: integer;
//
//
//
//
//
//
//
//
Enthält den Dateinamen der LS-Datei
Zwischenspeicher
File-Handle auf die LS-Datei
File-Handle auf die LS-Datei mit den neuen Inhalt
Speichert, ob old_file geöffnet werden konnte
Gibt an ob das Schreiben in die Datei abgebrochen
werden soll
Laufvariable
begin
// Prüfe, ob der Inhalt verändert wurde
if Caption[length(Caption)] <> '*' then
Exit;
// Führe ein Test des L-Systems aus
if SystemCheck = false then
// Falls eine Warnung vorliegt, dann Frage den Anwender ob er wirklich speicher möchte
if MessageDlg('Es liegen Hinweise zum Lindenmayer-System vor,'+#13+'wollen Sie trotzdem'
+' fortfahren?', mtInformation, [mbYes, mbNo], 0) = mrNo then
Exit;
// Falls es eine neue Datei ist, dann wird eine Dialogbox geöffnet, um den
// Namen für das zu speicherne L-System einzugeben
if Pos('Unbenannt', Caption) > 0 then
begin
if SaveDialog.Execute = false then
Exit;
filename := SaveDialog.FileName;
end
// Ansonsten entnehme, den Namen aus der Überschrift des Kindfensters
else
filename := Copy(Caption, 1, length(Caption)-1);
// Öffnen der alten Datei
AssignFile(old_file, filename);
{$i-} Reset(old_file); {$i+}
if IOResult <> 0 then
can_open := false
else
can_open := true;
//
//
//
//
//
//
Namen den File-Handle zuweisen
Öffnen der Datei zum lesen und zwischenzeitige
Deaktivierung der I/O-Prüfung
War das öffnen erfolglos,
dann setze nicht die Variable can_open
Ansonsten setze die Variable
// Öffnen der neuen Datei
AssignFile(new_file, 'temp_VisualL.ls');
// Namen den File-Handle zuweisen
{$i-} Rewrite(new_file); {$i+}
// Öffnen der Datei zum schreiben und zwischenzeitige
// Deaktivierung der I/O-Prüfung
if IOResult <> 0 then
// War das öffnen erfolglos,
begin
// dann gebe eine Meldung aus und Beende
// die Verarbeitung
MessageDlg('Datei kann nicht gespeichert werden!', mtError, [mbOk], 0);
Exit;
end;
// Konnte die alte Datei geöffnet werden?
if can_open then
begin
abort := false;
// Durchlauf die alte Datei bis zum ersten Ausdruck des L-Systems und
// übernehme alle define-Anweisungen und Kommentare in die neue Datei
while (not eof(old_file)) or (abort = false) do
begin
ReadLn(old_file, s);
s := trim(s);
if (Pos('#', s) = 1) or (Pos('/*', s) = 1) then
WriteLn(new_file, s)
else if length(s) = 0 then
continue
else
abort := true;
end;
end;
// Schreibe in die neue Datei die Daten für das L-System
247
H
Quelltexte
WriteLn(new_file, RecursEdit.Text);
WriteLn(new_file, BasisAngleEdit.Text);
WriteLn(new_file, BasisThickEdit.Text);
WriteLn(new_file, AxiomEdit.Text);
WriteLn(new_file);
for i := 0 to ProductionList.Items.Count-1 do
WriteLn(new_file, ProductionList.Items[i]);
CloseFile(new_file);
// Schließe die neue Datei
// Konnte die alte Datei geöffnet werden, dann schließe und lösche diese
// und benenne die neue um
if can_open then
begin
CloseFile(old_file);
DeleteFile(filename);
RenameFile('temp_VisualL.ls', filename);
end
// Benenne die neue Datei nur um in die Zieldatei
else
RenameFile('temp_VisualL.ls', filename);
Caption := filename;
SaveBtn.Enabled := false;
// Aktualisiere die Überschrift des Kindfensters
// Deaktiviere den Speicher Button
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
SaveAsBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Diese Methode wird aufgerufen, wenn auf den Button "Speichern unter"
geklickt wird.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.SaveAsBtnClick(Sender: TObject);
begin
// Markiert das L-System das es modifiziert wurde, damit es gespeichert werden kann
if Pos('Unbenannt', Caption) <> 1 then
Caption := 'Unbenannt*';
SaveBtn.Click;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
RunBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Wenn auf den Button "Bild erzeugen" geklickt wird, dann wird diese Methode
aufgerufen.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.RunBtnClick(Sender: TObject);
begin
// Führe einen Check des L-Systems durch und wenn Warnungen vorhanden sind
// Frage den Benutzer nach, ob der ein Bild erzeugen möchten
if SystemCheck = false then
if MessageDlg('Es liegen Hinweise zum Lindenmayer-System vor,'+#13+'wollen Sie trotzdem'
+' fortfahren?', mtInformation, [mbYes, mbNo], 0) = mrNo then
Exit;
248
H
Quelltexte
// Speicher den Inhalt der Info-Struktur in die CFG-Datei
if saveToCFG then
begin
// Speicher das L-System, wenn es modifiziert wurde
if Caption[length(Caption)] = '*' then
SaveBtn.Click;
// Wenn das L-System nicht gespeichert werden konnte, dann Verlasse die Methode
if Caption[length(Caption)] = '*' then
Exit;
// Initialisiere die JvCreateProcess-Komponente mit dem String für den
// Aufruf der Fassade und setze das Tag, dass es eine Bilderzeugung ist
MDIMain.JvCreateProcess1.CommandLine := 'lprocess '+ExtractFilename(Caption);
MDIMain.JvCreateProcess1.Tag := 1;
// Baue den Namen für die Bilddatei zusammen
if Info.Bitmap then
PicName := Copy(ExtractFilename(Caption), 1, length(ExtractFilename(Caption))-2)+'bmp'
else
PicName := Copy(ExtractFilename(Caption), 1, length(ExtractFilename(Caption))-2)+'tga';
ConsoleFom.ListBox1.Clear;
MDIMain.JvCreateProcess1.Run;
end
// Lösche die Konsolenausgabe
// Starte JvCreateProcess
// Gib eine Meldung aus, wenn die Einstellungen nicht gespeichert werden konnten
else
MessageDlg('Einstellungen konnten nicht in die CFG-Datei gespeichert werden!',
mtError, [mbOk], 0);
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
DelAllMsgBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Wird auf den Button zum Löschen aller Nachrichten geklickt, dann wird diese
Methode aufgerufen.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.DelAllMsgBtnClick(Sender: TObject);
begin
// Lösche alle Nachrichten, wenn welche vorhanden sind
if MessageList.Items.Count > 0 then
MessageList.Clear;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Direct3DBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Die Methode wird ausgeführt, wenn auf den Button "3D-Ansicht" geklickt wird.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.Direct3DBtnClick(Sender: TObject);
begin
// Erzeuge ein Instanz des Direct3D-Fensters, wenn noch keine existiert
249
H
Quelltexte
if D3DActive = false then
begin
D3DForm := TDirect3DForm.Create(Application);
D3DActive := true;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
CamXTrackChange
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird aufgerufen, wenn an der TrackBar-Komponente der Wert verändert
wird. Diese Methode gleicht den Wert mit der Info-Struktur ab.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.CamXTrackChange(Sender: TObject);
begin
// Wenn 0 eingestellt wird, dann gebe dieses direkt an
if CamXTrack.Position = 0 then
begin
CamXLabel.Caption := '0';
Info.CamX := 0;
end
// Ansonsten berechne den neuen Wert
else
begin
CamXLabel.Caption := FloatToStrF((CamXTrack.Position * 0.5), ffFixed, 1, 1);
Info.CamX := CamXTrack.Position * 0.5;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
CamYTrackChange
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird aufgerufen, wenn an der TrackBar-Komponente der Wert verändert
wird. Diese Methode gleicht den Wert mit der Info-Struktur ab.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.CamYTrackChange(Sender: TObject);
begin
// Wenn 0 eingestellt wird, dann gebe dieses direkt an
if CamYTrack.Position = 0 then
begin
CamYLabel.Caption := '0';
Info.CamY := 0;
end
// Ansonsten berechne den neuen Wert
else
begin
CamYLabel.Caption := FloatToStrF((CamYTrack.Position * 0.5), ffFixed, 1, 1);
Info.CamY := CamYTrack.Position * 0.5;
end;
end;
{ ----------------------------------------------------------------------------
250
H
Quelltexte
Methodenname:
Parameter
Typ:
Name:
Bedeutung:
CamZTrackChange
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird aufgerufen, wenn an der TrackBar-Komponente der Wert verändert
wird. Diese Methode gleicht den Wert mit der Info-Struktur ab.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.CamZTrackChange(Sender: TObject);
begin
// Wenn 0 eingestellt wird, dann gebe dieses direkt an
if CamZTrack.Position = 0 then
begin
CamZLabel.Caption := '0';
Info.CamZ := 0;
end
// Ansonsten berechne den neuen Wert
else
begin
CamZLabel.Caption := FloatToStrF((CamZTrack.Position * 0.5), ffFixed, 1, 1);
Info.CamZ := CamZTrack.Position * 0.5;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
LightXTrackChange
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird aufgerufen, wenn an der TrackBar-Komponente der Wert verändert
wird. Diese Methode gleicht den Wert mit der Info-Struktur ab.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.LightXTrackChange(Sender: TObject);
begin
// Wenn 0 eingestellt wird, dann gebe dieses direkt an
if LightXTrack.Position = 0 then
begin
LightXLabel.Caption := '0';
Info.LightX := 0;
end
// Ansonsten berechne den neuen Wert
else
begin
LightXLabel.Caption := FloatToStrF((LightXTrack.Position * 0.5), ffFixed, 1, 1);
Info.LightX := LightXTrack.Position * 0.5;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
LightYTrackChange
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird aufgerufen, wenn an der TrackBar-Komponente der Wert verändert
wird. Diese Methode gleicht den Wert mit der Info-Struktur ab.
251
H
Quelltexte
---------------------------------------------------------------------------- }
procedure TMDIChildMain.LightYTrackChange(Sender: TObject);
begin
// Wenn 0 eingestellt wird, dann gebe dieses direkt an
if LightYTrack.Position = 0 then
begin
LightYLabel.Caption := '0';
Info.LightY := 0;
end
// Ansonsten berechne den neuen Wert
else
begin
LightYLabel.Caption := FloatToStrF((LightYTrack.Position * 0.5), ffFixed, 1, 1);
Info.LightY := LightYTrack.Position * 0.5;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
LightZTrackChange
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird aufgerufen, wenn an der TrackBar-Komponente der Wert verändert
wird. Diese Methode gleicht den Wert mit der Info-Struktur ab.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.LightZTrackChange(Sender: TObject);
begin
// Wenn 0 eingestellt wird, dann gebe dieses direkt an
if LightZTrack.Position = 0 then
begin
LightZLabel.Caption := '0';
Info.LightZ := 0;
end
// Ansonsten berechne den neuen Wert
else
begin
LightZLabel.Caption := FloatToStrF((LightZTrack.Position * 0.5), ffFixed, 1, 1);
Info.LightZ := LightZTrack.Position * 0.5;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Button2Click
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Die Methode öffnet die Tabelle für die Turtle-Kommandos, wenn auf den Button
"Turtle-Kommandos" geklickt wird. Vorausgesetzt es gibt nicht schon eine
Instanz des Fensters.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.Button2Click(Sender: TObject);
begin
if TurtleActive = false then
begin
TurtleForm := TTurtleComForm.Create(Application);
TurtleActive := true;
252
H
Quelltexte
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
UpdateBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Aktualisiert eine Produktion, wenn auf den Button "Aktualisieren" geklickt
wird.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.UpdateBtnClick(Sender: TObject);
var i: integer;
// Zwischenspeicher
begin
// Durchläuft die Produktionsliste nach identischen String
// Wurde keiner gefunden, dann wird der String in die Liste aufgenommen
for i := 0 to ProductionList.Count-1 do
if CompareText(updProduction, ProductionList.Items[i]) = 0 then
begin
ProductionList.Items.Delete(i);
break;
end;
// Deaktiviere die Buttons zum Aktualisieren einer Produktion
Add2Btn.Visible := false;
AddBtn.Visible := true;
UpdateBtn.Visible := false;
AddBtn.Click;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Add2BtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Fügt eine Produktion hinzu, wenn der Button "Aktualisiere" aktiv ist.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.Add2BtnClick(Sender: TObject);
begin
Add2Btn.Visible := false;
AddBtn.Visible := true;
UpdateBtn.Visible := false;
AddBtn.Click;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
ProBodyEditKeyPress
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Diese Funktion wird ausgeführt, wenn ein Zeichen im Editierfeld für den
Produktionskörper eingegeben wird.
---------------------------------------------------------------------------- }
253
H
Quelltexte
procedure TMDIChildMain.ProBodyEditKeyPress(Sender: TObject;
var Key: Char);
var i : integer;
// Zwischenspeicher
begin
// Berechne die Breite des Textes des Editierfeldes mit dem neuen Zeichen
i := Canvas.TextWidth((ProBodyEdit.Text+key));
// Wurde RETURN gedrückt,
if (key = #13) and (AddBtn.Visible = true) then
begin
AddBtn.Click;
// dann füge die Produktion hinzu
ProHeadEdit.SetFocus;// Gebe den Eingabefokus an den Editierfeld des Produktionskopf
end
else if key = #13 then
begin
UpdateBtn.Click;
// dann aktualisiere die Produktion, wenn diese zum
// bearbeiten angeklickt wurde
ProHeadEdit.SetFocus;// Gebe den Eingabefokus an den Editierfeld des Produktionskopf
end
// Wird ein Zeichen gelöscht und die berechnete Breite der Zeichenkette ist immer
// noch größer, als das Editierfeld, dann passe die Größe an
else if (key = #8) and (i > 169) then
begin
ProBodyEdit.Width := i;
UpdateBtn.Left := ProBodyEdit.Width + 311;
AddBtn.Left := UpdateBtn.Left;
Add2Btn.Left := UpdateBtn.Left + 88;
end
// Wird ein Zeichen hinzugefügt, dann Passe die Breite des Editierfeldes, an
// die Breite des Strings
else if i+9 > ProBodyEdit.Width then
begin
ProBodyEdit.Width := i+8;
UpdateBtn.Left := ProBodyEdit.Width + 311;
AddBtn.Left := UpdateBtn.Left;
Add2Btn.Left := UpdateBtn.Left + 88;
end
// Ansonsten gebe die Standardwerte
else
begin
ProBodyEdit.Width := 169;
UpdateBtn.Left := 480;
AddBtn.Left := 480;
Add2Btn.Left := 568;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
CloseBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Schließt das Kindfenster, wenn auf den Button "Schließen" geklickt wird.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.CloseBtnClick(Sender: TObject);
begin
Close;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
FormCloseQuery
254
H
Quelltexte
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
var Boolean
CanClose
Gibt an, ob das Formular geschlossen werden soll.
Methodenbeschreibung:
Methode wird vor dem Schließen des Formulars aufgerufen.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.FormCloseQuery(Sender: TObject;
var CanClose: Boolean);
var i : integer;
// Zwischenspeicher
begin
CanClose := true;
// Wurde der Inhalt des L-Systems modifiziert, dann wird gefragt, ob die
// Änderungen gespeichert werden sollen.
if Pos('*', Caption) > 0 then
begin
i := MessageDlg('Das Lindenmayer-System wurde geändert, aber noch nicht gepspeichert.'+
#13+'Möchten Sie es jetzt Speichern?', mtInformation,
[mbYes, mbNo, mbCancel], 0);
if i = mrYes then
SaveBtn.Click
else if i = mrCancel then
CanClose := false;
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
FormCreate
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird beim erzeugen des Formulars aufgerufen und Initialisiert
Variablen.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.FormCreate(Sender: TObject);
begin
D3DActive := false;
TurtleActive := false;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
CondBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Die Methode öffnet die Tabelle für die Ausdrücke im Bedingungsteil, wenn auf
den Button "Turtle-Kommandos" geklickt wird. Vorausgesetzt es gibt nicht schon
eine Instanz des Fensters.
---------------------------------------------------------------------------- }
procedure TMDIChildMain.CondBtnClick(Sender: TObject);
begin
if CondActive = false then
begin
CondForm := TCondForm.Create(Application);
255
H
Quelltexte
CondActive := true;
end;
end;
end.
Dateiname: MDIMainSource.pas
{ ---------------------------------------------------------------------------Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
05. 06. 04
[email protected]
VisualL
1.0
Klassenname:
TMDIMain
Version:
1.0
Kurzbeschreibung:
Diese Klasse repräsentiert das MDI-Elternfenster mit einige
öffentlichen Methode die hilfreiche Funktionen für andere Klassen darstellen.
---------------------------------------------------------------------------- }
unit MDIMainSource;
{ ---------------------------------------------------------------------------BESCHREIBUNG DER SCHNITTSTELLE DER UNIT
---------------------------------------------------------------------------- }
interface
{ ---------------------------------------------------------------------------Liste alle öffentlich eingebundenen Units
---------------------------------------------------------------------------- }
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ToolWin, ActnMan, ActnCtrls, ActnMenus, ActnList, StdActns,
ComCtrls, ImgList, Menus, StdCtrls, MDIChildSource, Direct3DSource, TurtleComSource,
CondFormSource, JvComponent, JvSysComp;
{ ---------------------------------------------------------------------------Deklaration eigener Datentypen
---------------------------------------------------------------------------- }
type
{ ---------------------------------------------------------------------------Klassenbeschreibung für TMDIMain
---------------------------------------------------------------------------- }
TMDIMain = class(TForm)
// Auflistung aller eingebundenen Komponenten der Klasse
ActionManager1: TActionManager;
FileNew: TAction;
EditCut1: TEditCut;
EditCopy1: TEditCopy;
EditPaste1: TEditPaste;
EditSelectAll1: TEditSelectAll;
EditUndo1: TEditUndo;
EditDelete1: TEditDelete;
HelpAbout: TAction;
FileOpen1: TFileOpen;
FileExit1: TFileExit;
WindowClose1: TWindowClose;
WindowCascade1: TWindowCascade;
WindowTileHorizontal1: TWindowTileHorizontal;
WindowTileVertical1: TWindowTileVertical;
WindowMinimizeAll1: TWindowMinimizeAll;
WindowArrange1: TWindowArrange;
ActionMainMenuBar: TActionMainMenuBar;
ActionToolBar1: TActionToolBar;
CoolBar1: TCoolBar;
StatusBar1: TStatusBar;
ImageList1: TImageList;
256
H
Quelltexte
OptionsCFG: TAction;
ImportLS: TAction;
OpenDialog1: TOpenDialog;
JvCreateProcess1: TJvCreateProcess;
// Auflistung aller Methoden für die Ereignisverarbeitung
procedure HelpAboutExecute(Sender: TObject);
procedure FileNewExecute(Sender: TObject);
procedure OptionsCFGExecute(Sender: TObject);
procedure FileOpen1Accept(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure ImportLSExecute(Sender: TObject);
procedure JvCreateProcess1Read(Sender: TObject; const S: String);
procedure JvCreateProcess1Terminate(Sender: TObject;
ExitCode: Cardinal);
// Auflistung aller privaten Methoden als Prototypen
private
procedure CreateMDIChild(const Name: string);
function checkLSLine(var line: string) : string;
procedure defaultCFG;
procedure fillInfo(var cfg : TextFile);
procedure setOptionsForChild(var Child : TMDIChildMain);
function convertRealForTracker(value: real48) : integer;
procedure openLS(const filename: string);
// Auflistung aller öffentlichen Methoden als Prototypen
public
function testingProductionHead(const ProHead: string) : string;
function testingCondition(const Condition: string) : string;
end;
{ ---------------------------------------------------------------------------Deklaration der Datenstruktur für die CFG-Datei
---------------------------------------------------------------------------- }
TInfo = record
Bitmap : boolean;
AA : boolean;
Files : boolean;
Width : integer;
Height : integer;
CamX : real48;
CamY : real48;
CamZ : real48;
LightX : real48;
LightY : real48;
LightZ : real48;
PathPOV : string;
PathQPOV : string;
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
Gibt an, ob die Grafik als BMP gespeichert werden soll
Gibt an, ob Anti-Aliasing bei Render verwendet werden soll
Gibt an, ob alle Dateien aus den Zwischenschritten
gelöscht werden sollen (wird von Visual L nicht verwendet)
Enthält die Breite der Grafik in Pixel
Enthält die Höhe der Grafik in Pixel
Enthält die Zahl zur relativen Positionierung der Kamera
für die x-Achse
Enthält die Zahl zur relativen Positionierung der Kamera
für die y-Achse
Enthält die Zahl zur relativen Positionierung der Kamera
für die z-Achse
Enthält die Zahl zur relativen Positionierung des Lichtes
für die x-Achse
Enthält die Zahl zur relativen Positionierung des Lichtes
für die y-Achse
Enthält die Zahl zur relativen Positionierung des Lichtes
für die z-Achse
Speichert den Pfad zu POV-Ray als Zeichenkette
Speichert den Pfad zu QuietPOV als Zeichenkette
end;
{ ---------------------------------------------------------------------------Öffentliche globale Variablen
---------------------------------------------------------------------------- }
var
MDIMain: TMDIMain;
D3DActive: boolean;
D3DForm: TDirect3DForm;
TurtleActive: boolean;
TurtleForm: TTurtleComForm;
CondForm: TCondForm;
CondActive: boolean;
//
//
//
//
//
//
//
//
//
//
PicName: string;
// Globe Variable zum speichern von Zeichenketten (Name der
Info: TInfo;
Die Instanz von TMDIMain ist öffentlich für alle anderen
Units erreichbar
Die Datenstruktur ist öffentlich für alle anderen Units
erreichbar
Gibt an, ob eine Instanz von TDirect3DForm erzeugt wurde
Globale Instanz der Klasse TDirect3DForm
Gibt an, ob die Instanz von TTurtleComForm erzeugt wurde
Globale Instanz der Klasse TTurtleComForm
Globale Instanz der Klasse TCondForm
Gibt an, ob die Instanz von TCondForm erzeugt wurde
257
H
Quelltexte
// Bilddatei)
{ ---------------------------------------------------------------------------IMPLEMENTATIONSTEIL DER UNIT
---------------------------------------------------------------------------- }
implementation
{ ---------------------------------------------------------------------------Compiler-Schalter
---------------------------------------------------------------------------- }
{$R *.dfm}
{ ---------------------------------------------------------------------------Liste aller private eingebundenen Units
---------------------------------------------------------------------------- }
uses AboutSource, CFGSource, ConsoleFormSource, ShowPicSource;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
openLS
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
filename
Dateiname der LS-Datei
Methodenbeschreibung:
Öffnet eine LS-Datei, prüft den Inhalt und übergibt die Informationen an ein
MDI-Kindfenster. Wurden Fehler gefunden, werden diese im Nachrichtenfeld des
Kindfensters erscheinen.
---------------------------------------------------------------------------- }
procedure TMDIMain.openLS(const filename: string);
var Child
f
s, t
i
state
errorLine
fl
:
:
:
:
:
:
:
TMDIChildMain;
TextFile;
string;
integer;
integer;
integer;
single;
//
//
//
//
//
//
//
Instanz für ein neues Kindfenster
Variable zur Verarbeitung der LS-Datei
Zwischenspeicherung von Zeichenketten
Laufvariable
Speichert den Zustand der Verarbeitung der Datei
Zählt die Regeln, um eine Fehlerhafte Regel zu benennen
Wird zum testen eines Float-Wertes benötigt
begin
// Initialisierung von Variablen
state := 0;
errorLine := 0;
// Datei öffnen
AssignFile(f, filename);
{$i-} Reset(f); {$i+}
// Zuweisung des Dateinamens an die Variable
// Datei wird versucht zu öffnen um diese Auszulesen
// (I/O-Prüfung des Compilers wird dazu deaktiviert)
if IOResult <> 0 then
// Konnte die Datei nicht geöffnet werden?
begin
// Gib eine Fehlermeldung aus und Beende die weitere
// Verarbeitung
MessageDlg('Datei "'+filename+'" konnte nicht geöffnet werden!', mtError, [mbOk], 0);
Exit;
end;
// Erzeuge Kindfenster
Child := TMDIChildMain.Create(Application);
Child.Caption := filename;
// Verarbeitungsschleife für die LS-Datei
while not eof(f) do
begin
readln(f, s);
// Hol eine Zeile aus der Datei
s := trim(s);
// Schneide Leerzeichen am Anfang und am Ende ab
if length(s) > 1000 then
// Ist die Zeile zu lang?
begin
// Dann überspringe die Zeile und schreib eine Nachricht
// ins Kindfenster
Child.MessageList.Items.Add('Die Zeile ist zu lang!');
continue;
end;
258
H
Quelltexte
// Enthält die Zeile ein Kommentar, Leerzeile oder eine Präprozessor-Anweisung,
// dann überspringe die Zeile
if (Pos('/*', s) = 1) or (length(s) = 0) or (Pos('#', s) = 1) then
continue;
i := Pos('/*', s);
if i > 1 then
begin
s := Copy(s, 1, i-1);
s := trim(s);
end;
// Suche nach ein Kommentarzeichen in einer Zeile mit
// einem Ausdruck
// Ist ein Kommentar gefunden worden,
// dann Schneide den Kommentar ab und
// entferne die Leerzeichen
// Die case-Anweisung prüft die Anfangswerte des L-Systems, die am Anfang
// der Datei stehen.
case state of
0: try
// Erster gültiger Ausdruck (kein Kommentar oder Leerzeile)
// Es wird erwartet, dass der Ausdruck die Zahl für die
// Rekursionstiefe repräsentiert
i := StrToInt(s);
// Der String wird versucht in eine Zahl zu wandeln
// Ist es gelungen, wird der Wert an das Kindfenster
// weitergegeben und der Zustand erhöht.
Child.RecursEdit.Text := IntToStr(i);
Inc(state);
continue;
except
on EConvertError do // Bei einem Konvertierungsfehler, wird eine Nachricht an
begin
// das Kindfenster übergeben
Child.MessageList.Items.Add('Keine Rekursionstiefe gefunden!');
Child.MessageList.Items.Add('Keinen Basiswinkel gefunden!');
Child.MessageList.Items.Add('Keine Basisstärke gefunden!');
state := 3;
// Da keine gültige Zahl gefunden wurde, wird als nächstes
// nach einem Axiom gesucht
end;
end;
1: try
// Zweiter gültiger Ausdruck
// Es wird erwartet, dass der Ausdruck die Zahl für den
// Basiswinkel repräsentiert
fl := StrToFloat(s);// Der String wird versucht in eine Zahl zu wandeln
// Ist es gelungen, wird der Wert an das Kindfenster
// weitergegeben und der Zustand erhöht.
Child.BasisAngleEdit.Text := FloatToStr(fl);
Inc(state);
continue;
except
on EConvertError do // Bei einem Konvertierungsfehler, wird eine Nachricht an
begin
// das Kindfenster übergeben
Child.MessageList.Items.Add('Keinen Basiswinkel gefunden!');
Child.MessageList.Items.Add('Keine Basisstärke gefunden!');
state := 3;
// Da keine gültige Zahl gefunden wurde, wird als nächstes
// nach einem Axiom gesucht
end;
end;
2: try
// Dritter gültiger Ausdruck
// Es wird erwartet, dass der Ausdruck die Zahl für die
// Basisstärke repräsentiert
i := StrToInt(s);
// Der String wird versucht in eine Zahl zu wandeln
// Ist es gelungen, wird der Wert an das Kindfenster
// weitergegeben und der Zustand erhöht.
Child.BasisThickEdit.Text := IntToStr(i);
Inc(state);
continue;
except
on EConvertError do // Bei einem Konvertierungsfehler, wird eine Nachricht an
begin
// das Kindfenster übergeben
Child.MessageList.Items.Add('Keine Basisstärke gefunden!');
state := 3;
// Da keine gültige Zahl gefunden wurde, wird als nächstes
// nach einem Axiom gesucht
end;
end;
end;
// An dieser Stelle wird nach einem gültigen Startaxiom gesucht
if state = 3 then
begin
259
H
Quelltexte
state := 4;
// Erhöhe den Zustand, um danach Produktionen zu verarbeiten
// Enthält die Zeichenkette Symbole die auf eine Produktion hinweisen?
if (Pos('<', s) > 0) or (Pos('>', s) > 0) or (Pos(':', s) > 0) or
(Pos('->', s) > 0) then
Child.MessageList.Items.Add('Kein Axiom gefunden')
else
begin
Child.AxiomEdit.Text := s;
// Wenn nicht, dann übernehme den Ausdruck
// als Startaxiom
continue;
end;
end;
// An dieser Stelle werden die Ausdrücke nach einer gültigen Produktion geprüft
if state = 4 then
begin
Inc(errorLine);
t := checkLSLine(s);
// Prüfe die Zeichenkette, ob es eine gültige
// Produktion ist
// Wird keine Fehlermeldung zurückgegeben, dann übernehme den String als Produktion.
if t = '' then
Child.ProductionList.Items.Add(s)
// Enthält der String eine Fehlermeldung,dann gib diese aus
else
begin
Child.MessageList.Items.Add('Fehler in Regel '+IntToStr(errorLine)+' !');
Child.MessageList.Items.Add(t);
end;
end;
end;
// Ende der Verarbeitungsschleife
CloseFile(f);
setOptionsForChild(Child);
// Schließe die Datei
// Setze die Einstellungen für das Kindfenster aus
// der Info-Struktur
end;
{ ---------------------------------------------------------------------------Methodenname:
Rückgabewert
Typ:
Bedeutung:
Parameter
Typ:
Name:
Bedeutung:
testingCondition
string
Gibt einen String mit einer Fehlermeldung zurück oder
eine leere Zeichenkette
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
Condition
Enthält den Bedingungsteil einer Produktion
Methodenbeschreibung:
Die Methode prüft, ob ungültige Zeichen im Bedingungsteil enthalten sind.
---------------------------------------------------------------------------- }
function TMDIMain.testingCondition(const Condition: string) : string;
var i : integer;
// Laufvariable
begin
result := '';
// Rückgabewert wird mit dem Leerstring initialisiert
// Durchlauf den kompletten String und Prüfe jedes Zeichen auf seine Gültigkeit
for i := 1 to length(Condition) do
if not(Condition[i] in ['a'..'z', 'A'..'Z', '0'..'9', '<'..'>', '-'..'/', ' ',
'+', '*']) then
begin
result := 'Im Bedingungsteil befindet sich mindestens ein nicht erlaubtes Zeichen!';
Exit;
end;
end;
260
H
Quelltexte
{ ---------------------------------------------------------------------------Methodenname:
Rückgabewert
Typ:
Bedeutung:
testingProductionHead
string
Gibt einen String mit einer Fehlermeldung zurück oder
eine leere Zeichenkette
Parameter
Typ:
Name:
Bedeutung:
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
ProHead
Enthält den Produktionskopf einer Produktion
Methodenbeschreibung:
Prüft, ob der Produktionskopf einen gültigen Ausdruck darstellt.
---------------------------------------------------------------------------- }
function TMDIMain.testingProductionHead(const ProHead: string) : string;
var leftBracket
rightBracket
i
ParameterList
begin
result := '';
:
:
:
:
integer;
integer;
integer;
string;
//
//
//
//
//
Stellt die Position der öffnenden runden Klammer da
Enthält die Position der schließenden runden Klammer
Laufvariable
Ist ein Teilstring des Produktionskopf und enthält
die Parameterliste
// Initialisierung des Rückgabewerts mit dem Leerstring
leftBracket := Pos('(', ProHead);
rightBracket := Pos(')', ProHead);
// Zuweisung der Position der öffnenden Klammer
// Zuweisung der Position der schließenden Klammer
// Wurde nur eine Klammer gefunden?
if (leftBracket > 0) xor (rightBracket > 0) then
begin
// Gib Fehlermeldung zurück und Beende die weitere Verarbeitung
result := 'Im Bedingungsteil befindet sich mindestens ein nicht erlaubtes Zeichen!';
Exit;
end
// Ist die linke Klammer hinter der rechten Klammer?
else if leftBracket > rightBracket then
begin
// Gib Fehlermeldung zurück und Beende die weitere Verarbeitung
result := 'Rechte Klammer steht vor der linken Klammer!';
Exit;
end
// Befindet sich hinter der rechten Klammer noch etwas?
else if (rightBracket <> length(ProHead)) and (rightBracket > 0) then
begin
// Gib Fehlermeldung zurück und Beende die weitere Verarbeitung
result := 'Hinter der rechten Klammer steht noch ein Ausdruck!';
Exit;
end
// Enthält der Produktionskopf eine Parameterliste?
else if ((leftBracket+1) = rightBracket) then
begin
// Gib Fehlermeldung zurück und Beende die weitere Verarbeitung
result := 'In der Klammer befindet sich nichts!';
Exit;
end
// Prüfe die Parameterliste
else if (leftBracket > 0) and (rightBracket > 0) then
begin
// Zuweisung der Parameterliste und abschneiden der Leerzeichen am Anfang und Ende
ParameterList := trim(Copy(ProHead, leftBracket+1, rightBracket-1-leftBracket));
// Ist die Parameterliste leer?
if length(ParameterList) = 0 then
begin
// Gib Fehlermeldung zurück und Beende die weitere Verarbeitung
result := 'In der Klammer befindet sich nichts!';
Exit;
end;
// Durchlauf die Parameterliste und prüfe, ob diese nur aus gültigen Zeichen besteht
for i := 1 to length(ParameterList) do
if not(ParameterList[i] in['a'..'z', 'A'..'Z', ',', ' ']) then
begin
result := 'In der Klammer befindet sich mindestens ein nicht erlaubtes Zeichen!'
261
H
Quelltexte
+'Erlaubt sind nur Buchstaben und das Komma!';
Exit;
end;
end;
// Ende der If-Anweisung
end;
{ ---------------------------------------------------------------------------Methodenname:
Rückgabewert
Typ:
Bedeutung:
Parameter
Typ:
Name:
Bedeutung:
convertRealForTracker
integer
Gibt eine Ganzzahl für die Tracker-Komponente zurück
real48
value
Fließkommazahl aus der CFG-Datei
Methodenbeschreibung:
Rechnet eine Fließkommazahl aus der CFG-Datei um, in eine ganze Zahl für die
Tracker-Komponente. Dabei ist die Auflösung 0.5 und geht von +10 bis -10.
---------------------------------------------------------------------------- }
function TMDIMain.convertRealForTracker(value: real48) : integer;
begin
// Wenn die Zahl größer 10 ist, dann weise das Maximum zu und beende
if value > 10 then
begin
result := 20;
Exit;
end
// Wenn die Zahl kleiner -10 ist, dann weise das Minimum zu und beende
else if value < -10 then
begin
result := -20;
Exit;
end
// Wenn die Zahl gleich 0 ist, dann weise die 0 zu und beende
else if value = 0 then
begin
result := 0;
Exit;
end;
// Multipliziere die Fließkommazahl mit zwei und runde den Wert auf, bzw. ab.
result := round(value * 2);
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
setOptionsForChild
var TMDIChildMain
Child
Zeiger auf ein erzeugtes Kindfenster
Methodenbeschreibung:
Initialisiert das Kindfenster mit den Werten aus der Info-Struktur.
---------------------------------------------------------------------------- }
procedure TMDIMain.setOptionsForChild(var Child : TMDIChildMain);
begin
with Child do
begin
AACheck.Checked := Info.AA;
TargaCheck.Checked := not Info.Bitmap;
// Anti-Aliasing setzen?
// Targa oder Bitmap
262
H
Quelltexte
WidthEdit.Text := IntToStr(Info.Width);
HeightEdit.Text := IntToStr(Info.Height);
// Breite der Grafik
// HöHe der Grafik
// Setzen der Werte für die Achsen der Lichtquelle und der Kamera
CamXTrack.Position := convertRealForTracker(Info.CamX);
CamXLabel.Caption := FloatToStr(CamXTrack.Position * 0.5);
CamYTrack.Position := convertRealForTracker(Info.CamY);
CamYLabel.Caption := FloatToStr(CamYTrack.Position * 0.5);
CamZTrack.Position := convertRealForTracker(Info.CamZ);
CamZLabel.Caption := FloatToStr(CamZTrack.Position * 0.5);
LightXTrack.Position := convertRealForTracker(Info.LightX);
LightXLabel.Caption := FloatToStr(LightXTrack.Position * 0.5);
LightYTrack.Position := convertRealForTracker(Info.LightY);
LightYLabel.Caption := FloatToStr(LightYTrack.Position * 0.5);
LightZTrack.Position := convertRealForTracker(Info.LightZ);
LightZLabel.Caption := FloatToStr(LightZTrack.Position * 0.5);
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
fillInfo
var Textfile
cfg
Zeiger auf eine geöffnete CFG-Datei
Methodenbeschreibung:
Liest den Inhalt einer CFG-Datei aus und füllt den Inhalt der Info-Struktur
mit den Daten aus.
---------------------------------------------------------------------------- }
procedure TMDIMain.fillInfo(var cfg : TextFile);
// Variablen
const bmp
aa
files
width
height
pov
qpov
camx
camy
camz
lightx
lighty
lightz
var
s
key
value
i, j
mit einem
: boolean
: boolean
: boolean
: boolean
: boolean
: boolean
: boolean
: boolean
: boolean
: boolean
: boolean
: boolean
: boolean
:
:
:
:
Wert vorinitialisieren
= false;
// Die Variablen werden auf true
= false;
// gesetzt, wenn deren gleichnamige
= false;
// Option in der CFG-Datei gefunden wurde
= false;
= false;
= false;
= false;
= false;
= false;
= false;
= false;
= false;
= false;
string;
string;
string;
integer;
//
//
//
//
Zwischenspeicher für ein String
Speichert den Bezeichner einer Option
Speichert den Wert einer Option
Laufvariablen
begin
// Durchlaufe die CFG-Datei bis zum Ende
while not eof(cfg) do
begin
ReadLn(cfg, s);
trim(s);
j := Pos('//', s);
i := Pos('=', s);
//
//
//
//
Speicher eine Zeile in die Variable s
Schneide Leerzeichen am Anfang und am Ende ab
Suche, ob die Zeile ein Kommentar enthält
Suche, ob die Zeile eine Option enthält
// Ist die Zeile eine Leerzeile oder eine Kommentarzeile, dann überspringe diese
if (j = 1) or (length(s) = 0) or ((j > 0) and (i > j)) then
continue;
key := trim(Copy(s, 1, i-1));
if (j <> 0) then
value := trim(Copy(s, i+1, j-1-i))
// Zuweisung des Bezeichners der Option
// Zuweisung des Wertes und abschneiden
// eines Kommentars
else
263
H
Quelltexte
value := trim(Copy(s, i+1, length(s)));
// Zuweisung des Wertes
// Ist es der Bezeichner Image?
if CompareText(key, 'Image') = 0 then
begin
bmp := true;
// Option gefunden
// Wenn der Wert bmp ist, dann setze die Variable in der Info-Struktur
if CompareText(value, 'bmp') = 0 then
Info.Bitmap := True
// Wenn der Wert targa ist, dann setze nicht die Variable
else if CompareText(value, 'targa') = 0 then
Info.Bitmap := False
// Falls nichts zutrifft, dann liegt kein gültiger Wert vor
else
bmp := false;
end
// Ist es der Bezeichner Height?
else if CompareText(key, 'Height') = 0 then
begin
try
Info.Height := StrToInt(value);
// Wandel den String in eine Zahl um
height := true;
// Option wurde gefunden
except
on EConvertError do ;
// Bei einem Konvertierungsfehler, mache nichts
end;
end
// Ist es der Bezeichner Width?
else if CompareText(key, 'Width') = 0 then
begin
try
Info.Width := StrToInt(value);
// Wandel den String in eine Zahl um
width := true;
// Option wurde gefunden
except
on EConvertError do ;
// Bei einem Konvertierungsfehler, mache nichts
end;
end
// Ist es der Bezeichner AA?
else if CompareText(key, 'AA') = 0 then
begin
aa := true;
// Option gefunden
// Wenn der Wert on ist, dann setze die Variable in der Info-Struktur
if CompareText(value, 'on') = 0 then
Info.AA := True
// Wenn der Wert off ist, dann setze nicht die Variable
else if CompareText(value, 'off') = 0 then
Info.AA := False
// Falls nichts zutrifft, dann liegt ein ungültiger Wert vor
else
aa := false;
end
// Ist es der Bezeichner POV?
else if CompareText(key, 'POV') = 0 then
begin
// Ist es keine leere Zeichenkette?
if length(value) <> 0 then
begin
Info.PathPOV := value;
// Übernehme den String
pov := true;
// Option gefunden
end;
end
// Ist es der Bezeichner QPOV?
else if CompareText(key, 'QPOV') = 0 then
begin
// Ist es keine leere Zeichenkette?
if length(value) <> 0 then
264
H
Quelltexte
begin
Info.PathQPOV := value;
qpov := true;
end;
end
// Übernehme den String
// Option gefunden
// Ist es der Bezeichner Files?
else if CompareText(key, 'Files') = 0 then
begin
files := true;
// Option gefunden
// Wenn der Wert on ist, dann setze die Variable in der Info-Struktur
if CompareText(value, 'on') = 0 then
Info.Files := True
// Wenn der Wert off ist, dann setze nicht die Variable
else if CompareText(value, 'off') = 0 then
Info.Files := False
// Falls nichts zutrifft, dann liegt ein ungültiger Wert vor
else
files := false;
end
// Ist es der Bezeichner CamX?
else if CompareText(key, 'CamX') = 0 then
begin
try
Info.CamX := StrToFloat(value);
// Wandel den String in eine Fließkommazahl um
camx := true;
// Option gefunden
except
on EConvertError do ;
// Bei einem Konvertierungsfehler, mache nichts
end;
end
// Ist es der Bezeichner CamY?
else if CompareText(key, 'CamY') = 0 then
begin
try
Info.CamY := StrToFloat(value);
// Wandel den String in eine Fließkommazahl um
camy := true;
// Option gefunden
except
on EConvertError do ;
// Bei einem Konvertierungsfehler, mache nichts
end;
end
// Ist es der Bezeichner CamZ?
else if CompareText(key, 'CamZ') = 0 then
begin
try
Info.CamZ := StrToFloat(value);
// Wandel den String in eine Fließkommazahl um
camz := true;
// Option gefunden
except
on EConvertError do ;
// Bei einem Konvertierungsfehler, mache nichts
end;
end
// Ist es der Bezeichner LightX?
else if CompareText(key, 'LightX') =
begin
try
Info.LightX := StrToFloat(value);
lightx := true;
except
on EConvertError do ;
end;
end
// Ist es der Bezeichner LightY?
else if CompareText(key, 'LightY') =
begin
try
Info.LightY := StrToFloat(value);
lighty := true;
except
on EConvertError do ;
end;
end
0 then
// Wandel den String in eine Fließkommazahl um
// Option gefunden
// Bei einem Konvertierungsfehler, mache nichts
0 then
// Wandel den String in eine Fließkommazahl um
// Option gefunden
// Bei einem Konvertierungsfehler, mache nichts
265
H
Quelltexte
// Ist es der Bezeichner LightZ?
else if CompareText(key, 'LightZ') =
begin
try
Info.LightZ := StrToFloat(value);
lightz := true;
except
on EConvertError do ;
end;
end;
end;
0 then
// Wandel den String in eine Fließkommazahl um
// Option gefunden
// Bei einem Konvertierungsfehler, mache nichts
// Ende der While-Schleife
// Prüfe ob alle Optionen gefunden, bzw. einen gültigen Wert enthalten.
// Ist es nicht der Fall, dann initalisiere diese mit Standardwerten und gib
// eine Meldung an den Benutzer aus.
if not bmp then
begin
Info.Bitmap := True;
MessageDlg('Für den Schlüssel "Image" wurde kein gültiger Wert gefunden!'
+' Es wird der Standwert "bmp" verwendet!', mtWarning, [mbOK], 0);
end
else if not width then
begin
Info.Width := 640;
MessageDlg('Für den Schlüssel "Width" wurde kein gültiger Wert gefunden! '
+'Es wird der Standwert "640" verwendet!', mtWarning, [mbOK], 0);
end
else if not height then
begin
Info.Height := 480;
MessageDlg('Für den Schlüssel "Height" wurde kein gültiger Wert gefunden!'
+' Es wird der Standwert "480" verwendet!', mtWarning, [mbOK], 0);
end
else if not files then
begin
Info.Files := false;
MessageDlg('Für den Schlüssel "Files" wurde kein gültiger Wert gefunden!'
+' Es wird der Standwert "false" verwendet!', mtWarning, [mbOK], 0);
end
else if not pov then
begin
Info.PathPOV := 'POV=c:\programme\pov-ray for windows v3.5\bin\';
MessageDlg('Für den Schlüssel "POV" wurde kein gültiger Wert gefunden!'
+' Es wird ein Standwert verwendet!', mtWarning, [mbOK], 0);
end
else if not qpov then
begin
Info.PathQPOV := 'QPOV=c:\programme\pov-ray for windows v3.5\guiext\quietpov\';
MessageDlg('Für den Schlüssel "QPOV" wurde kein gültiger Wert gefunden!'
+' Es wird ein Standwert verwendet!', mtWarning, [mbOK], 0);
end
else if not aa then
begin
Info.Bitmap := false;
MessageDlg('Für den Schlüssel "AA" wurde kein gültiger Wert gefunden!'
+' Es wird der Standwert "off" verwendet!', mtWarning, [mbOK], 0);
end
else if not camx then
begin
Info.CamX := 2.0;
MessageDlg('Für den Schlüssel "CamX" wurde kein gültiger Wert gefunden!'
+' Es wird der Standwert "2.0" verwendet!', mtWarning, [mbOK], 0);
end
else if not camy then
begin
Info.CamY := 2.0;
MessageDlg('Für den Schlüssel "Camy" wurde kein gültiger Wert gefunden!'
+' Es wird der Standwert "2.0" verwendet!', mtWarning, [mbOK], 0);
end
else if not camz then
begin
Info.CamZ := 2.0;
MessageDlg('Für den Schlüssel "CamZ" wurde kein gültiger Wert gefunden!'
+' Es wird der Standwert "2.0" verwendet!', mtWarning, [mbOK], 0);
end
else if not lightx then
266
H
Quelltexte
begin
Info.LightX := 1.0;
MessageDlg('Für den Schlüssel "LightX" wurde kein gültiger Wert gefunden!'
+' Es wird der Standwert "1.0" verwendet!', mtWarning, [mbOK], 0);
end
else if not lighty then
begin
Info.LightY := 1.0;
MessageDlg('Für den Schlüssel "LightY" wurde kein gültiger Wert gefunden!'
+' Es wird der Standwert "1.0" verwendet!', mtWarning, [mbOK], 0);
end
else if not lightz then
begin
Info.LightZ := 1.0;
MessageDlg('Für den Schlüssel "LightZ" wurde kein gültiger Wert gefunden!'
+' Es wird der Standwert "1.0" verwendet!', mtWarning, [mbOK], 0);
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
defaultCFG
Methodenbeschreibung:
Schreibt eine CFG-Datei mit Standardwerten.
---------------------------------------------------------------------------- }
procedure TMDIMain.defaultCFG;
var cfg : TextFile;
// File-Handle für die CFG-Datei
begin
// Erzeugen der CFG-Datei
AssignFile(cfg, 'LPROCESS.CFG');
{$i+} Rewrite(cfg); {$i+}
// File-Handle den Namen zuweisen
// Öffnen der Datei zum schreiben und I/O-Prüfung
// des Compilers temporär ausschalten
if IOResult <> 0 then
// Falls die Datei nicht geöffnet werden konnte,
begin
// dann gib eine Fehlermeldung an den Benutzer
// aus und Beende die Funktion.
MessageDlg('Das Programm kann nicht gestartet werden, weil keine CFG-Datei erstellt'
+' werden kann!', mtError, [mbOk], 0);
Close;
end;
// Schreibt die Standardwerte in die neue CFG-Datei
WriteLn(cfg, 'Image=bmp');
WriteLn(cfg, 'Height=480');
WriteLn(cfg, 'Width=640');
WriteLn(cfg, 'AA=off');
WriteLn(cfg, 'POV=c:\programme\pov-ray for windows v3.5\bin\');
WriteLn(cfg, 'QPOV=c:\programme\pov-ray for windows v3.5\guiext\quietpov\');
WriteLn(cfg, 'Files=off');
WriteLn(cfg, 'CamX=2.0');
WriteLn(cfg, 'CamY=2.0');
WriteLn(cfg, 'CamZ=2.0');
WriteLn(cfg, 'LightX=1.0');
WriteLn(cfg, 'LightY=1.0');
WriteLn(cfg, 'LightZ=1.0');
// Initialisiert die Info-Struktur ebenfalls mit den Standardwerten
Info.Bitmap := true;
Info.AA := false;
Info.Files := false;
Info.Width := 640;
Info.Height := 480;
Info.CamX := 2.0;
Info.CamY := 2.0;
Info.CamZ := 2.0;
Info.LightX := 1.0;
Info.LightY := 1.0;
Info.LightZ := 1.0;
Info.PathPOV := 'POV=c:\programme\pov-ray for windows v3.5\bin\';
Info.PathQPOV := 'QPOV=c:\programme\pov-ray for windows v3.5\guiext\quietpov\';
267
H
Quelltexte
CloseFile(cfg);
// Schließen der CFG-Datei
end;
{ ---------------------------------------------------------------------------Methodenname:
Rückgabewert
Typ:
Bedeutung:
checkLSLine
string
Gibt einen String mit einer Fehlermeldung zurück oder
eine leere Zeichenkette
Parameter
Typ:
Name:
Bedeutung:
var string
line
Zeiger auf eine Zeile
Methodenbeschreibung:
Prüft eine Zeile aus einer LS-Datei, ob diese eine gültige Produktion ist.
---------------------------------------------------------------------------- }
function TMDIMain.checkLSLine(var line: string) : string;
var s
production
lcon
pred
rcon
cond
succ
i
j
k
l
:
:
:
:
:
:
:
:
:
:
:
string;
string;
string;
string;
string;
string;
string;
integer;
integer;
integer;
integer;
//
//
//
//
//
//
//
//
//
//
//
Zwischenspeicher für ein String
Enthält die fertige Produktion
Linker Kontext der Produktion
Produktionskopf der Produktion
Rechter Kontext der Produktion
Bedingungsteil der Produktion
Produktionskörper der Produktion
Speichert die Position des linken Kontext Zeichens
Speichert die Position des Anfangs des Produktionskörpers
Speichert die Position des rechten Kontext Zeichens
Speichert die Position des Bedingungszeichens
begin
result := '';
// Rückgabewert mit einem Leerstring initialisieren
i := Pos('<', line);
k := Pos('>', line);
l := Pos(':', line);
// Zuweisung der Position des linken Kontext Zeichens
// Zuweisung der Position des rechten Kontext Zeichens
// Zuweisung der Position des Bedingungszeichens
// Falls das linke Kontext Zeichen ganz links außen steht,
// dann ist die Produktion nicht richtig formuliert.
if i = 1 then
begin
result := 'Linker Kontext fehlt!';
Exit;
end;
s := trim(Copy(line, 1, i-1)); // Der linke Kontext wird aus dem String herauskopiert
// Ist das Wildcardzeichen enthalten und weitere Zeichen?
if (Pos('*', s) > 0) and (length(s) > 1) then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'Der linke Kontext enthält neben dem *-Zeichen, noch weitere Zeichen!';
exit;
end;
// Ist das Wildcardzeichen nicht vorhanden,
if (Pos('*', s) = 0) and ((Pos('(', s) > 0)
begin
// Gebe Fehlermeldung zurück
result := 'Im Kontext sind keine Klammern
Exit;
end;
aber runde Klammern?
or (Pos(')', s) > 0)) then
und Beende die Verarbeitung
erlaubt!';
// Ist das linke Kontext Zeichen in echt ein Teil des Bedingungsteils?
if (l > 0) and (i > l) then
begin
s := '*';
i := 0;
end;
// Ist kein linker Kontext vorhanden?
if ((Pos('*', s) > 0) and (length(s) = 1)) or (length(s) = 0) then
lcon := ''
268
H
Quelltexte
else
// ansonsten baue den linken Kontext zusammen
lcon := s+' < ';
j := Pos('->', line); // Zuweisung der Position des Anfangs des Produktionskörpers
s := trim(Copy(line, j+2, length(line)));
// Kopiere den Produktionskörper heraus
// Enthält die Zeichenkette etwas?
if length(s) = 0 then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'Es existiert kein rechter Produktionsteil!';
Exit;
end;
succ := ' -> '+s;
// Baue den Produktionskörper zusammen
// Existiert kein Bedingungsteil und kein rechter Kontext?
if (k = (j+1)) and (l = 0) then
begin
rcon := '';
cond := '';
pred := trim(Copy(line, i+1, j-1-i));
// Kopiere den Produktionskopf heraus
end
// Existiert ein rechter Kontext, aber kein Bedingungsteil?
else if (k <> (j+1)) and (l = 0) then
begin
s := trim(Copy(line, k+1, j-1-k));
// Kopiere den rechten Kontext heraus
// Existiert ein Inhalt für den rechten Kontext?
if length(s) = 0 then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'Rechter Kontext fehlt!';
exit;
end;
// Sind weitere Zeichen außer dem Wildcardzeichen im rechten Kontext enthalten?
if (Pos('*', s) > 0) and (length(s) > 1) then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'Der linke Kontext enthält neben dem *-Zeichen, noch weitere Zeichen!';
exit;
end;
// Sind Klammern im rechten Kontext enthalten?
if (Pos('*', s) = 0) and ((Pos('(', s) > 0) or (Pos(')', s) > 0)) then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'Im Kontext sind keine Klammern erlaubt!';
Exit;
end;
// Enthält der rechte Kontext keinen Ausdruck?
if ((Pos('*', s) > 0) and (length(s) = 1)) or (length(s) = 0) then
rcon := ''
else
// Ansonsten baue einen rechten Kontext zusammen
rcon := ' > '+s;
cond := '';
pred := trim(Copy(line, i+1, k-1-i));
// Kopiere den Produktionskopf heraus
end
// Existiert ein Bedingungsteil, aber kein rechter Kontext?
else if (k > l) and (l > 0) then
begin
s := trim(Copy(line, l+1, j-1-l));
// Kopiere den Bedingungsteil heraus
pred := trim(Copy(line, i+1, l-1-i));
// Kopiere den Produktionskopf heraus
rcon := '';
// Existiert ein Inhalt für den Bedingungsteil?
if length(s) = 0 then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'Bedingung fehlt!';
exit;
end;
// Sind weitere Zeichen außer dem Wildcardzeichen im Bedingungsteil enthalten?
if (Pos('*', s) > 0) and (length(s) > 1) then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'Die Bedingung enthält neben dem *-Zeichen, noch weitere Zeichen!';
exit;
end;
269
H
Quelltexte
// Sind Klammern im Bedingungsteil enthalten?
if (Pos('*', s) = 0) and ((Pos('(', s) > 0) or (Pos(')', s) > 0)) then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'In der Bedingung sind keine Klammern erlaubt!';
Exit;
end;
// Enthält der Bedingungsteil keinen Ausdruck?
if ((Pos('*', s) > 0) and (length(s) = 1)) or (length(s) = 0) then
cond := ''
else
// Ansonsten baue einen Bedingungsteil zusammen
cond := ' : '+s;
end
// Es existert sowohl ein Bedingungsteil, als auch ein rechter Kontext!
else
begin
pred := trim(Copy(line, i+1, k-1-i));
// Kopiere den Produktionskopf heraus
s := trim(Copy(line, l+1, j-1-l));
// Kopiere den Bedingungsteil heraus
// Existiert ein Inhalt für den Bedingungsteil?
if length(s) = 0 then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'Bedingung fehlt!';
exit;
end;
// Sind weitere Zeichen außer dem Wildcardzeichen im Bedingungsteil enthalten?
if (Pos('*', s) > 0) and (length(s) > 1) then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'Die Bedingung enthält neben dem *-Zeichen, noch weitere Zeichen!';
exit;
end;
// Sind Klammern im Bedingungsteil enthalten?
if (Pos('*', s) = 0) and ((Pos('(', s) > 0) or (Pos(')', s) > 0)) then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'In der Bedingung sind keine Klammern erlaubt!';
Exit;
end;
// Enthält der Bedingungsteil keinen Ausdruck?
if ((Pos('*', s) > 0) and (length(s) = 1)) or (length(s) = 0) then
cond := ''
else
// Ansonsten baue einen Bedingungsteil zusammen
cond := ' : '+s;
s := trim(Copy(line, k+1, l-1-k));
// Kopiere den rechten Kontext heraus
// Existiert ein Inhalt für den rechten Kontext?
if length(s) = 0 then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'Rechter Kontext fehlt!';
exit;
end;
// Sind weitere Zeichen außer dem Wildcardzeichen im rechten Kontext enthalten?
if (Pos('*', s) > 0) and (length(s) > 1) then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'Der rechte Kontext enthält neben dem *-Zeichen, noch weitere Zeichen!';
exit;
end;
// Sind Klammern im rechten Kontext enthalten?
if (Pos('*', s) = 0) and ((Pos('(', s) > 0) or (Pos(')', s) > 0)) then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'Im Kontext sind keine Klammern erlaubt!';
Exit;
end;
// Enthält der rechte Kontext keinen Ausdruck?
if ((Pos('*', s) > 0) and (length(s) = 1)) or (length(s) = 0) then
rcon := ''
else
// Ansonsten baue einen rechten Kontext zusammen
rcon := ' > '+s;
end;
// Existiert ein Inhalt für den Produktionskopf
270
H
Quelltexte
if length(pred) = 0 then
begin
// Gebe Fehlermeldung zurück und Beende die Verarbeitung
result := 'Es existert kein Produktionskopf!';
exit;
end;
result := testingProductionHead(pred); // Lass den Produktionskopf prüfen auf
// seine Gültigkeit
if result <> '' then
// Ist der Produktionskopf ungültig?
exit;
if cond <> '' then
// Existiert ein Bedingungsteil?
begin
result := testingCondition(Copy(cond, 4, length(cond)));
// Prüfen des Bedingungsteils
// auf seine Gültigkeit
if result <> '' then // Ist der Bedingungsteil ungültig?
exit;
end;
production := lcon + pred + rcon + cond + succ;
// Zusammensetzen der Produktion
line := production;
// Produktion zurückgeben
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
CreateMDIChild
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
Name
Enthälten den Titel für das neue Kindfenster
Methodenbeschreibung:
Erzeugt ein neues Kindfenster mit dem übergebenen Namen.
---------------------------------------------------------------------------- }
procedure TMDIMain.CreateMDIChild(const Name: string);
var Child :
TMDIChildMain;
// Zu erzeugende Instanz des Kindfensters
begin
Child := TMDIChildMain.Create(Application);
Child.Caption := Name;
setOptionsForChild(Child);
end;
// Erzeugen des Kindfensters
// Titel dem Kindfenster übergeben
// Einstellungen ans Kindfenster übergeben
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
HelpAboutExecute
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Erzeugt das Info-Fenster und zeigt es an.
---------------------------------------------------------------------------- }
procedure TMDIMain.HelpAboutExecute(Sender: TObject);
var about : TAboutForm; // Zu erzeugende Instanz des Info-Fensters
begin
about := TAboutForm.Create(Application);
about.ShowModal;
about.Release;
end;
// Erzeuge Instanz des Info-Fensters
// Fenster modal anzeigen
// Speicher freigeben, wenns geschlossen wird
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
FileNewExecute
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
271
H
Quelltexte
Methodenbeschreibung:
Erzeugt ein neues und leeres Kindfenster.
---------------------------------------------------------------------------- }
procedure TMDIMain.FileNewExecute(Sender: TObject);
begin
CreateMDIChild('Unbenannt'+IntToStr(MDICHildCount+1)); // Erzeuge neues Kindfenster
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
OptionsCFGExecute
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Erzeugt das Einstellungs-Formular und zeigt es an.
---------------------------------------------------------------------------- }
procedure TMDIMain.OptionsCFGExecute(Sender: TObject);
var CFG : TCFGForm;
// Zu erzeugende Instanz des Einstellungs-Formulars
begin
CFG := TCFGForm.Create(Application);
CFG.ShowModal;
end;
// Erzeuge Instanz
// Zeige das Formular modal an
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
FileOpen1Accept
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Öffnet eine Datei und erzeugt ein Kindfenster mit dem Inhalt der Datei.
---------------------------------------------------------------------------- }
procedure TMDIMain.FileOpen1Accept(Sender: TObject);
begin
openLS(FileOpen1.Dialog.FileName);
end;
// Erzeuge Kindfenster mit dem Inhalt der Datei
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
FormCreate
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Methode wird beim erzeugen des Formulars aufgerufen. Es läd die Info-Struktur
mit den notwendigen Informationen.
---------------------------------------------------------------------------- }
procedure TMDIMain.FormCreate(Sender: TObject);
var cfg : TextFile;
// File-Handle für die CFG-Datei
begin
DecimalSeparator := '.';
// Setzt fest, dass der Punkt den Nachkommateil symbolisiert
AssignFile(cfg, 'LPROCESS.CFG');
{$i-} Reset(cfg); {$i+}
// Weise den File-Handle den Namen der Datei zu
// Öffne Datei zum lesen und deaktiviere temporär
272
H
Quelltexte
// die I/O-Prüfung
// Konnte die Datei nicht geöffnet werden?
if IOResult <> 0 then
begin
defaultCFG;
Exit;
end;
fillInfo(cfg);
CloseFile(cfg);
end;
// Dann erzeuge eine Standard CFG-Datei
// und Verlasse die Methode
// Füll den Inhalt der Info-Struktur mit den Daten der CFG-Datei
// Schließe die CFG-Datei
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
FormClose
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
var TCloseAction
Action
Gibt an, wie das Fenster geschlossen werden ssoll
Methodenbeschreibung:
Methode wird beim Beenden des Formulars aufgerufen.
---------------------------------------------------------------------------- }
procedure TMDIMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
WinExec('lprocess -e', SW_HIDE);
// Beende eine laufende POV-Ray Instanz
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
FormCloseQuery
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
var Boolean
CanClose
Gibt an, ob das Formular geschlossen werden soll.
Methodenbeschreibung:
Methode wird vor dem Schließen des Formulars aufgerufen.
---------------------------------------------------------------------------- }
procedure TMDIMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var cfg : TextFile;
// File-Handle für die CFG-Datei
begin
CanClose := true;
// Applikaton kann geschlossen werden
AssignFile(cfg, 'LPROCESS.CFG');
{$i-} Rewrite(cfg); {$i+}
if IOResult <> 0 then
begin
// Weise dem File-Handle den Namen der Datei zu
// Öffne die Datei zum lesen
// Konnte Datei nicht geöffnet werden?
// Soll die Applikation wirklich beendet werden?
if MessageDlg('Die Einstellungen für die CFG-Datei können nicht gespeichert werden!'+
#13+'Soll die Anwendung trotzdem geschlossen werden?', mtError,
[mbYes, mbNo], 0) = mrNo then
CanClose := false;
Exit;
end;
// Speicher die Daten aus der Info-Struktur in die CFG-Datei
if Info.Bitmap = true then
WriteLn(cfg, 'Image=bmp')
else
WriteLn(cfg, 'Image=Targa');
if Info.AA = true then
WriteLn(cfg, 'AA=on')
else
273
H
Quelltexte
WriteLn(cfg, 'AA=off');
if Info.Files = true then
WriteLn(cfg, 'Files=on')
else
WriteLn(cfg, 'Files=off');
WriteLn(cfg, 'Width='+IntToStr(Info.Width));
WriteLn(cfg, 'Height='+IntToStr(Info.Height));
WriteLn(cfg, 'POV='+Info.PathPOV);
WriteLn(cfg, 'QPOV='+Info.PathQPOV);
WriteLn(cfg, 'CamX='+FloatToStr(Info.CamX));
WriteLn(cfg, 'CamY='+FloatToStr(Info.CamY));
WriteLn(cfg, 'CamZ='+FloatToStr(Info.CamZ));
WriteLn(cfg, 'LightX='+FloatToStr(Info.LightX));
WriteLn(cfg, 'LightY='+FloatToStr(Info.LightY));
WriteLn(cfg, 'LightZ='+FloatToStr(Info.LightZ));
CloseFile(cfg);
// Schließe die CFG-Datei
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
ImportLSExecute
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Konvertiert den Inhalt einer LS-Datei um und zeigt den Inhalt in einem neuen
Kindfenster an.
---------------------------------------------------------------------------- }
procedure TMDIMain.ImportLSExecute(Sender: TObject);
var s, t : string;
i
: integer;
// Temporäre Zwischenspeicherung von Strings
// Zwischenspeicher
begin
// Öffne eine Standard-Dialogbox zum öffnen einer Datei.
// Ist Ausführung erfolgreich gewesen (gültige Auswahl einer Datei?)
if OpenDialog1.Execute then
begin
// Soll der Inhalt in eine neue Datei gespeichert werden?
if MessageDlg('Soll eine neu Datei angelegt werden, in der die Daten in Version 5'
+' vorliegen?'+#13+'Andernfalls werden die Daten in der alten Datei'
+' überschrieben!', mtInformation, [mbYes, mbNo], 0) = mrYes then
begin
// Rufe eine InputeBox auf, damit der Anwender den Dateinamen eingeben kann
s := InputBox('Dateiname eingeben!', 'Bitte geben Sie ein Dateinamen an, in der'
+' die neuen Daten gespeichert werden soll!', '');
// Wenn kein Dateiname angegeben wurde, dann Beende die Methode
if s = '' then
Exit;
i := length(s);
// Hole die Länge der Zeichenkette
// Prüfte die Dateinamenerweiterung
if not((s[i-2] = '.') and (s[i-1] = 'l') and (s[i] = 's')) then
s := s+'.ls';
PicName := GetCurrentDir+'\'+s;
// Definiere den vollständigen Pfad
FileOpen1.Dialog.FileName := s;
// Zuweisung des neuen Dateinamens
t := ExtractFileName(OpenDialog1.FileName);
// Hole Dateinamen der zu
// konvertierenden Datei
// Definiere die Zeichenkette für den Kommandointerpreter
JvCreateProcess1.CommandLine := 'lprocess -c '+Copy(t, 1, length(t)-3)+' '+s;
end
else
begin
FileOpen1.Dialog.FileName := OpenDialog1.FileName;
t := ExtractFileName(OpenDialog1.FileName);
// Hole Dateinamen der zu
// konvertierenden Datei
274
H
Quelltexte
// Definiere die Zeichenkette für den Kommandointerpreter
JvCreateProcess1.CommandLine := 'lprocess -c '+Copy(t, 1, length(t)-3);
PicName := OpenDialog1.FileName;
end;
JvCreateProcess1.Tag := 2;
// Signalisiere, dass eine Umwandlung
// stattfinden wird
ConsoleFom.ListBox1.Clear;
// Lösche den Inhalt der Konsolenausgabe
JvCreateProcess1.Run;
// Führe die Anweisung aus
end;
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
JvCreateProcess1Read
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
string (konstante Zeichenkette, Geschwindigkeitsoptimierung)
S
Enthält die Konsolenausgabe
Methodenbeschreibung:
Methode wird ausgeführt, wenn Zeichenketten von der Standardeingabe
an die Komponente übergeben werden.
---------------------------------------------------------------------------- }
procedure TMDIMain.JvCreateProcess1Read(Sender: TObject; const S: String);
begin
// Schreibe die Konsolenausgabe in die Listbox des Formulars für die Konsolenausgabe
ConsoleFom.ListBox1.Items.Add(s);
end;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
JvCreateProcess1Terminate
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Cardinal
ExitCode
Enthält den Exitcode der ausgeführten Anwendung
Methodenbeschreibung:
Führt eine bestimmte Anwendung aus, die vorher in dem Attribut CommandLine
definiert wurde.
---------------------------------------------------------------------------- }
procedure TMDIMain.JvCreateProcess1Terminate(Sender: TObject;
ExitCode: Cardinal);
var showpic : TShowPicForm;
// Zu erzeugende Instanz eines Formulars zum Anzeigen
// eines Bildes
begin
// Ist die Anwendung nicht erfolgreich beendet worde und ist die Ausführung
// eine Bilderzeugung?
if (ExitCode <> 0) and (JvCreateProcess1.Tag = 1) then
begin
// Gib eine Fehlermeldung aus und zeige dem Anwender die Konsolenausgabe
MessageDlg('Bild konnte nicht erzeugt werden, weil ein Fehler vor lag!'+#13
+'Die Ausgabe der Konsole wird eingeblendet!', mtError, [mbOk], 0);
ConsoleFom.ShowModal;
ConsoleFom.Release;
end
// Ist die Ausführung eine Bilderzeugung und ist diese
else if JvCreateProcess1.Tag = 1 then
begin
showpic := TShowPicForm.Create(Application);
showpic.Image1.Picture.LoadFromFile(PicName);
showpic.Height := showpic.Image1.Picture.Height;
275
erfolgreich?
// Erzeuge Instanz
// Lade das Bild
// Anpassen der Größe des Formulars
H
Quelltexte
showpic.Width := showpic.Image1.Picture.Width;
showpic.ShowModal;
end
// an die Größe des Bildes
// Anzeige des Formulars modal
// Ist die Anwendung nicht erfolgreich beendet worde und ist die Ausführung
// eine Konvertierung?
else if (ExitCode <> 0) and (JvCreateProcess1.Tag = 2) then
begin
// Gib eine Fehlermeldung aus und zeige dem Anwender die Konsolenausgabe
MessageDlg('Datei konnte nicht importiert werden!'+#13+'Die Ausgabe der Konsole'
+' wird eingeblendet!', mtError, [mbOk], 0);
ConsoleFom.ShowModal;
ConsoleFom.Release;
end
// Ist die Ausführung eine Konvertierung und ist diese erfolgreich?
else if JvCreateProcess1.Tag = 2 then
openLS(PicName);
// Öffne die Datei und zeigen den Inhalt in einem
// neuen Kindfenster
end;
end.
Dateiname: ShowPicSource.pas
{ ---------------------------------------------------------------------------Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
05. 06. 04
[email protected]
VisualL
1.0
Klassenname:
TShowPicForm
Version:
1.0
Kurzbeschreibung:
Diese Klasse repräsentiert das Fenster, das zum Anzeigen der Grafik benutzt
wird.
---------------------------------------------------------------------------- }
unit ShowPicSource;
{ ---------------------------------------------------------------------------BESCHREIBUNG DER SCHNITTSTELLE DER UNIT
---------------------------------------------------------------------------- }
interface
{ ---------------------------------------------------------------------------Liste alle öffentlich eingebundenen Units
---------------------------------------------------------------------------- }
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls;
{ ---------------------------------------------------------------------------Deklaration eigener Datentypen
---------------------------------------------------------------------------- }
type
{ ---------------------------------------------------------------------------Klassenbeschreibung für TShowPicForm
---------------------------------------------------------------------------- }
TShowPicForm = class(TForm)
// Auflistung aller eingebundenen Komponenten der Klasse
Image1: TImage;
// Auflistung aller Methoden für die Ereignisverarbeitung
procedure Image1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private-Deklarationen }
public
276
H
Quelltexte
{ Public-Deklarationen }
end;
{ ---------------------------------------------------------------------------Öffentliche globale Variablen
---------------------------------------------------------------------------- }
var
ShowPicForm: TShowPicForm;
ren
// Die Instanz von TShowPicForm ist öffentlich für alle ande// Units erreichbar
{ ---------------------------------------------------------------------------IMPLEMENTATIONSTEIL DER UNIT
---------------------------------------------------------------------------- }
implementation
{ ---------------------------------------------------------------------------Compiler-Schalter
---------------------------------------------------------------------------- }
{$R *.dfm}
{ ---------------------------------------------------------------------------Liste aller private eingebundenen Units
---------------------------------------------------------------------------- }
uses GraphicEx;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Image1Click
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Schließt das Formular, wenn auf das Bild gelickt wird.
---------------------------------------------------------------------------- }
procedure TShowPicForm.Image1Click(Sender: TObject);
begin
Close;
end;
// Schließe das Formular
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
FormClose
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
var TCloseAction
Action
Gibt an, wie das Fenster geschlossen werden ssoll
Methodenbeschreibung:
Methode wird beim Beenden des Formulars aufgerufen.
---------------------------------------------------------------------------- }
procedure TShowPicForm.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
Action := caFree;
end;
// Speicher freigeben
end.
277
H
Quelltexte
Dateiname: TurtleComSource.pas
{ ---------------------------------------------------------------------------Autor:
Datum:
Kontakt:
Programmname:
Version:
Jan Derer
05. 06. 04
[email protected]
VisualL
1.0
Klassenname:
TTurtleComForm
Version:
1.0
Kurzbeschreibung:
Diese Klasse repräsentiert das Fenster, das zum Anzeigen der
Turtle-Kommandos benutzt wird.
---------------------------------------------------------------------------- }
unit TurtleComSource;
{ ---------------------------------------------------------------------------BESCHREIBUNG DER SCHNITTSTELLE DER UNIT
---------------------------------------------------------------------------- }
interface
{ ---------------------------------------------------------------------------Liste alle öffentlich eingebundenen Units
---------------------------------------------------------------------------- }
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons;
{ ---------------------------------------------------------------------------Deklaration eigener Datentypen
---------------------------------------------------------------------------- }
type
{ ---------------------------------------------------------------------------Klassenbeschreibung für TTurtleComForm
---------------------------------------------------------------------------- }
TTurtleComForm = class(TForm)
// Auflistung aller eingebundenen Komponenten der Klasse
GroupBox1: TGroupBox;
Label1: TLabel;
Label3: TLabel;
Label9: TLabel;
Label11: TLabel;
Label5: TLabel;
Label7: TLabel;
GroupBox2: TGroupBox;
CloseBtn: TBitBtn;
Label13: TLabel;
Label14: TLabel;
Label15: TLabel;
Label16: TLabel;
Label17: TLabel;
Label18: TLabel;
Label19: TLabel;
GroupBox3: TGroupBox;
Label20: TLabel;
Label21: TLabel;
Label22: TLabel;
Label23: TLabel;
Label24: TLabel;
Label25: TLabel;
Label26: TLabel;
Label27: TLabel;
Label28: TLabel;
Label29: TLabel;
Label30: TLabel;
Label31: TLabel;
Label32: TLabel;
278
H
Quelltexte
Label33: TLabel;
GroupBox4: TGroupBox;
Label34: TLabel;
Label35: TLabel;
Label36: TLabel;
Label37: TLabel;
GroupBox5: TGroupBox;
Label38: TLabel;
Label39: TLabel;
Label40: TLabel;
Label41: TLabel;
Label42: TLabel;
Label43: TLabel;
Label44: TLabel;
Label45: TLabel;
Label46: TLabel;
Label2: TLabel;
Label4: TLabel;
Label6: TLabel;
// Auflistung aller Methoden für die Ereignisverarbeitung
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure CloseBtnClick(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
{ ---------------------------------------------------------------------------Öffentliche globale Variablen
---------------------------------------------------------------------------- }
var
TurtleComForm: TTurtleComForm;
alle anderen
// Die Instanz von TTurtleComForm ist öffentlich für
// Units erreichbar
{ ---------------------------------------------------------------------------IMPLEMENTATIONSTEIL DER UNIT
---------------------------------------------------------------------------- }
implementation
{ ---------------------------------------------------------------------------Compiler-Schalter
---------------------------------------------------------------------------- }
{$R *.dfm}
{ ---------------------------------------------------------------------------Liste aller private eingebundenen Units
---------------------------------------------------------------------------- }
uses MDIMainSource;
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
Typ:
Name:
Bedeutung:
FormClose
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
var TCloseAction
Action
Gibt an, wie das Fenster geschlossen werden ssoll
Methodenbeschreibung:
Methode wird beim Beenden des Formulars aufgerufen.
---------------------------------------------------------------------------- }
procedure TTurtleComForm.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
TurtleActive := false;
// Signalisieren, dass es keine Instanz mehr gibt
279
H
Quelltexte
Action := caFree;
end;
// Speicher freigeben
{ ---------------------------------------------------------------------------Methodenname:
Parameter
Typ:
Name:
Bedeutung:
CloseBtnClick
TObject
Sender
Enthält das Objekt, das diese Methode aufruft
Methodenbeschreibung:
Schließt das Formular, wenn auf "Schließen" gelickt wird.
---------------------------------------------------------------------------- }
procedure TTurtleComForm.CloseBtnClick(Sender: TObject);
begin
Close;
end;
// Schließe das Formular
end.
280
Was this manual useful for you? yes no
Thank you for your participation!

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project

Download PDF

advertisement