Konzeption & Realisierung eines prozeduralen Ansatzes zur Erzeugung von Gebäuden Masterarbeit von Sven Janusch Fachbereich Informatik Hochschule Darmstadt Darmstadt, 2007 Referentin: Prof. Dr. Elke Hergenröther Korreferent: Prof. Dr. Wolf-Dieter Groch Eidesstattliche Erklärung Hiermit erkläre ich an Eides Statt, dass ich die vorliegende Masterarbeit selbständig und ohne fremde Hilfe verfasst habe. Diese Arbeit hat in gleicher oder ähnlicher Form noch keiner Prüfungsbehörde vorgelegen. _____________________ Sven Janusch Kelkheim, den 25.04.2007 Danksagung Mein erster Dank geht an Prof. Dr. Elke Hergenröther für die Betreuung dieser Arbeit und Hilfe bei der Gliederung. Der nächste Dank geht an die gesamte 3D-Abteilung von Related Designs. Darunter besonders an Burkhard Ratheiser, der die Idee für dieser Arbeit hatte und für Betreuung innerhalb der Firma zuständig war und an Wolfgang Klose für die tatkräftige Unterstützung bei der MFC-Programmierung. Weiterhin danke ich Andreas Jupe für die Fertigung von grafischen Inhalten und Siegfried Janusch, Dr. Gerd Schrader sowie Prof. Dr. Kurt-Ulrich Witt für das Korrekturlesen der Arbeit. Inhaltsverzeichnis 1. Einleitung.......................................................................................................................... 1 1.1 Anforderungen......................................................................................................... 2 1.2 Historischer Rückblick............................................................................................ 3 1.3 Verwandte Arbeiten................................................................................................. 8 1.4 Lösungsansatz und Überblick.................................................................................. 14 2. Architektonische Grundlagen.......................................................................................... 15 2.1 Merkmale................................................................................................................. 15 2.2 Dächer..................................................................................................................... 16 2.2.1 Begriffe....................................................................................................... 16 2.2.2 Dachformen................................................................................................ 17 2.2.3 Dachneigung............................................................................................... 17 2.2.4 Dachausmittlung......................................................................................... 18 3. Lindenmayer-Systeme...................................................................................................... 19 3.1 Deterministisch kontextfreie Lindenmayer-Systeme.............................................. 20 3.2 Deterministisch kontextsensitive Lindenmayer-Systeme........................................ 21 3.3 Verzweigende Lindenmayer-Systeme..................................................................... 23 3.4 Stochastische Lindenmayer-Systeme...................................................................... 24 3.5 Parametrische Lindenmayer-Systeme..................................................................... 25 3.6 Umgebungssensitive und offene Lindenmayer-Systeme........................................ 26 4. Straight-Skeleton.............................................................................................................. 28 4.1 Ereignisse................................................................................................................ 29 4.2 Distanz..................................................................................................................... 30 4.3 Definition................................................................................................................. 31 4.4 Algorithmus............................................................................................................. 31 4.5 Berechnung der Ereignisse...................................................................................... 32 4.5.1 Edge Events................................................................................................ 32 4.5.2 Split Events................................................................................................. 33 4.5.3 Vertex Events............................................................................................... 34 4.6 Gewichtung............................................................................................................. 35 5. Konzept.............................................................................................................................. 37 5.1 Regelbasierte System.............................................................................................. 39 5.1.1 Variablen..................................................................................................... 40 5.1.2 Funktionen.................................................................................................. 40 5.1.3 Konstanten.................................................................................................. 41 5.1.4 Produktionen............................................................................................... 42 5.1.5 Wahrscheinlichkeiten.................................................................................. 43 5.1.6 Bedingungen............................................................................................... 45 5.1.7 Transformation .......................................................................................... 45 5.1.8 Verzweigungen............................................................................................ 47 5.1.9 Ausführung.................................................................................................. 48 5.2 Grammatiken........................................................................................................... 49 5.2.1 Mass-Grammar........................................................................................... 50 5.2.1.1 Raum............................................................................................. 50 5.2.1.2 Anwendung.................................................................................... 50 5.2.2 Facade-Grammar....................................................................................... 52 5.2.2.1 Raum............................................................................................. 52 5.2.2.2 Anwendung.................................................................................... 52 5.2.3 Border-Grammar........................................................................................ 54 5.2.3.1 Raum............................................................................................. 54 5.2.3.2 Anwendung.................................................................................... 54 5.2.4 Profile-Grammar........................................................................................ 56 5.2.4.1 Raum............................................................................................. 57 5.2.4.2 Texturierung.................................................................................. 57 5.2.4.3 Anwendung.................................................................................... 57 5.2.5 Kommunikation........................................................................................... 60 5.3 Primitive.................................................................................................................. 61 5.3.1 Wand-Primitive........................................................................................... 61 5.3.1.1 Formen.......................................................................................... 61 5.3.1.2 Fassaden....................................................................................... 62 5.3.1.2 Ecken............................................................................................. 62 5.3.1.3 Texturierung.................................................................................. 63 5.3.1.4 Funktionen.................................................................................... 63 5.3.2 Dach-Primitive........................................................................................... 64 5.3.2.1 Formen.......................................................................................... 64 5.3.2.2 Dachdurchdringung...................................................................... 64 5.3.2.3 Fassaden....................................................................................... 67 5.3.2.4 Kanten........................................................................................... 69 5.3.2.5 Texturierung.................................................................................. 69 5.3.2.6 Funktionen.................................................................................... 70 5.4 Gruppen................................................................................................................... 71 5.4.1 Modell-Gruppen......................................................................................... 71 5.4.1.1 Funktion........................................................................................ 72 5.4.1.2 Schnitt............................................................................................ 72 5.4.1.3 CutOut-Modell.............................................................................. 73 5.4.1.4 Anwendung.................................................................................... 74 5.4.2 Textur-Gruppen........................................................................................... 74 5.5 Subdivision und Symmetrie.................................................................................... 75 5.5.1 Subdivision.................................................................................................. 75 5.5.1.1 SubDiv........................................................................................... 76 5.5.1.2 Part................................................................................................ 76 5.5.1.3 Split............................................................................................... 77 5.5.2 Symmetrie................................................................................................... 78 5.6 Umwelt.................................................................................................................... 79 5.6.1 Globale Umwelt.......................................................................................... 79 5.6.2 Lokale Umwelt............................................................................................ 80 5.6.2.1 Umgebungstypen........................................................................... 81 5.6.2.2 Abtastung....................................................................................... 81 5.6.2.2.1 Punkt-Abtastung.................................................................. 82 5.6.2.2.2 Flächen-Abtastung.............................................................. 82 5.6.2.2.3 Kanten-Abtastung................................................................ 83 5.6.2.2.4 Punkt-Gerade-Abtastung..................................................... 84 5.6.2.3 Anfrage-Funktionen...................................................................... 84 6. Resultate............................................................................................................................. 87 7. Schlussbemerkungen........................................................................................................ 91 7.1 Fazit......................................................................................................................... 91 7.2 Ausblick................................................................................................................... 92 7.3 Zusammenfassung................................................................................................... 93 8. Literaturverzeichnis......................................................................................................... 94 1. Einleitung Die vorliegende Arbeit wurde für das Computerspiel Anno 4 (Working Title) konzeptioniert, basierend auf den Erkenntnissen durch dessen Vorgängertitel Anno 1701. Das von Related Designs1 hergestellte Anno 1701 ist ein Wirtschaftssimulations- und Aufbauspiel, bei dem die Kolonialisierung, die Diplomatie, der Handel und der Aufbau einer Stadt im Vordergrund stehen. Begonnen wird meist mit einem Schiff, durch dessen Hilfe die erste Insel besiedelt werden kann, um dort eine Kolonie zu errichten. Je größer die Kolonie wird, desto mehr Waren fordert die Bevölkerung, was unabwendbar zur Ausweitung der Kolonie führt. Im Laufe der Zeit entwickelt Abb. 1.1 Anno 1701 sich die kleine Kolonie zur einer Großstadt und schließlich zu einem riesigen Imperium. Dieses erstreckt sich über mehrere Inseln, besitzt Handelsstrukturen, Straßennetze und vor allem jede Menge Produktionsgebäude, Wohnhäuser und sonstige Bauwerke. Obwohl bereits über einhundert unterschiedliche Gebäude in dem Spiel integriert sind, lässt es sich nicht vermeiden, dass immer wieder gleiche Gebäude und Strukturen zu erkennen sind. Diese Wiederholungen vermitteln ein unrealistisches Stadtbild. Zwar sind auch in der realen Welt häufig Gebäude vom gleichen Typ (z.B. Fachwerkbau) vorhanden, dessen ungeachtet sieht jedes Gebäude jedoch einzigartig aus, von den Reihenhäusern der NeuAbb. 1.2 Stadt aus Anno 1701 zeit einmal abgesehen. Aus diesem Grund ist es wichtig, dass sich jedes Gebäude von den anderen unterscheidet und somit eine lebendigere und organische Stadt entsteht. Um dies zu erreichen, müssten noch mehr Gebäude für das Spiel modelliert werden, wodurch allerdings die Arbeitszeiten und die Kosten steigen. Eine andere Möglichkeit ist, dass die Modellierung der kompletten Gebäude oder einzelnen Gebäudeteilen ­automatisch berechnet wird. Abb. 1.3 No Man‘s Land Related Designs ist eine der führenden Entwicklerfirmen von Computerspielen in Deutschland. Bekannte Projekte sind unter anderem America, No Man’s Land, Castle Strike und Anno 1701. 1 An dieser Stelle kommt ein prozeduraler Ansatz ins „Spiel“. Prozedural bedeutet, dass ein Gebäude nicht mehr bis ins kleinste Detail vormodelliert wird, sondern der Computer dieses anhand bestimmter Vorgaben und Regeln berechnet. Während dieser Berechnung kann der Computer Varia­tionen in die einzelnen Gebäude einbauen, wodurch eine größere Vielfalt entsteht und damit kein Gebäude mehr dem anderen gleicht. Durch die größere Vielfalt könnte wiederum die Langzeitmotiva­tion beim Spielen steigen, weil es immer wieder etwas Neues zu sehen und zu entdecken gibt. Außerdem entsteht für den Spieler ein neues Spielerlebnis, da der Spieler eine einzigartige Stadt aufbauen kann und dies jedes Mal, wenn er das Spiel von vorne beginnt. Obwohl das System vorerst für Computerspiele entwickelt wurde, sind auch andere Bereiche denkbar, in denen es eingesetzt werden könnte, beispielsweise zur Unterstützung in der Filmindustrie oder als erster Entwurf bei der Städteplanung. 1.1 Anforderungen Das Hauptaugenmerk dieser Arbeit liegt auf dem Entwurf möglichst detailreicher, vielfältiger und ansprechender Gebäude. Dazu muss ein System entwickelt werden, über das verschiedene Gebäudetypen anhand von Regeln formuliert werden können. Aus diesen Regeln kann das System dann beliebig viele Varianten des Typs erzeugen. Damit diese in einem Computerspiel einsetzbar sind, existieren selbstverständlich Restriktionen in punkto Speicher und Performance. D.h. das Gebäude sollte zwar detailreich und vielfältig sein, trotz alldem müssen gewisse Richtlinie bzgl. des Speichers und der Berechnungszeit eingehalten werden. Zu dem sollte eine einfache und möglichst gezielte Steuerung der Gebäudegenerierung vorhanden sein, damit die einzelnen Gebäudetypen einfach und schnell erstellt, verändert und angepasst werden können. Im Vergleich zu natürlichen Phänomenen (wie beispielsweise Landschaften, Flüsse oder Bäume) ist die prozedurale Generierung bzw. die Formulierung von Gebäuden sehr viel komplexer. Ein Gebäude besteht aus den verschiedensten Materialien, der Zusammensetzungen von unterschiedlichen Teilen, besitzt Symmetrien, und vor allem ist das Äußere stark geprägt durch den künstlerischen Einfluss des Architekten. Das folgende Kapitel 1.2 Historischer Rückblick beschreibt einen kleinen Überblick bisheriger Forschungen sowie die Vor- und Nachteile prozeduraler Ansätze. 1.2 Historischer Rückblick Eine der ersten prozeduralen Methoden wurde bereits in den vierziger Jahren des 20. Jahrhunderts entwickelt. Damals entwarf John von Neumann nach einer Idee von Stanislaw Ulam den ersten zellularen Automaten. Auf der Suche nach einem dynamischen System, das Selbst-Replikationen bilden kann, wies Stanislaw Ulam von Neumann auf eine mathematische Abstraktion hin, die Ulam bereits in seiner Arbeit zur Simulation von Kristallwachstum verwendete. Von Neumann erweiterte die Idee zu einem allgemeinen System und entwickelte den zellularen Automaten. Dieser besteht aus einer Anordnung meist quadratischer Zellen, einer Zustandsmenge und mehreren Überführungsfunktionen. Jede Zelle des Zellraums kann einen Zustand annehmen und wechselt diesen abhängig von seinem eigenen Zustand und denen seiner Nachbarzellen. In einem iterativen Verfahren verändert sich das System anhand der Funktionen und Zustände, und kann so beispielsweise Pflanzenwachstum simulieren. Ein bekanntes Anwendungsbeispiel stellte der Mathematiker John Conway im Jahr 1970 vor. In seinem Spiel des Lebens simuliert ein zweidimensionaler zellularer Automat die Evolution von Lebewesen. Jede Zelle entspricht dabei einem Lebewesen, das sich über die Zeit hinweg weiterentwickelt. Bei jedem Schritt kann entweder eine tote Zelle lebendig werden, eine bereits lebendige Zelle normal weiterleben oder eine lebendige Zelle ihr Leben aufgrund von Überbevölkerung oder Isolation verlieren. Der Spieler gibt lediglich den StartzuAbb. 1.4 Spiel des Lebens (Bild: [4]) stand der Zellen vor und stößt die erste Iteration an. Danach kann er die Evolution seines Systems beobachten. Ein weiterer sehr bekannter Begriff ist der des Fraktals. Benoît Mandelbrot leitete diesen 1975 vom dem lat. fractus ab, was übersetzt soviel wie „gebrochen“ bedeutet. Fraktale besitzen einen großen Stellenwert in der computergenerierten Erstellung von Inhalten, werden allerdings häufig missverstanden. Grundlegend bezeichnen Fraktale geometrische Muster und Formen, die einen gewissen Grad an Selbstähnlichkeit1 besitzen und sich durch eine meist reelle Dimension2 auszeichnen. Selbstähnlichkeit bedeutet, dass Teile eines Objektes, kleineren Teile des gleichen Objektes ähneln, die wiederum kleineren Teile ähneln, usw. Das Objekt besitzt demnach einen rekursiven Charakter. Dabei spielt es keine Rolle ob die selbstähnlichen Teile zwei Stufen oder unendlich oft fortführbar sind. Die zwei folgenden Zitate definieren noch einmal genau den Begriff des Fraktals. “A rough or fragmented geometric shape that can be subdivided in parts, each of which is (at least approximately) a reduced/size copy of the whole.” [Man82] “The main point is this: as long as something displays self-similarity over some, albeit perhaps small, range of scale, it may qualify as fractal.” [EBE+03, S. 434] Für den Begriff der Selbstähnlichkeit wird oft der aus der Mathematik und Physik stammende Begriff Skaleninvarianz verwendet. 2 Nicht-ganzzahlige Dimensionen, z.B. deren Hausdorff-Besikowitsch-Dimension größer ist als ihre topologische Dimension. 1 Sehr viele Fraktale besitzen eine schöne Ästhetik, weshalb sie häufig in der digitalen Kunst zum Einsatz kommen. Eines der bekanntesten Beispiele, das sogenannte Apfelmännchen, wird in Abb. 1.5 dargestellt. Des Weiteren eignen sich Fraktale zur Beschreibung von Landschaften oder sonstigen natürlichen Phänomenen wie Wasser, Küsten, Flüssen, Rauch, etc. All diese Phänomene besitzen einen hohen Grad an Selbstähnlichkeit und können aus diesem Grund als Fraktal bezeichnet werden. Zur Definition und Ermittlung von Fraktalen kommen verschiedene Systeme in Frage, vornehmlich zählen zu diesen die Abb. 1.5 Mandelbrot-Menge (Bild: [3]) mehrfache Iteration von Funktionen, dynamische Systeme oder grammatik- bzw. regelbasierende Systeme. Zu letzterem zählen wiederum die bekannten L-Systeme. All diese Systeme können auf verschiedene Arten, aber mit einfachen Mitteln rekursive oder iterative Objekte beschreiben. Nun stellt sich die Frage, kann ein Bauwerk als Fraktal bezeichnet werden? Diese Frage kann nicht so einfach beantwortet werden. Im Normalfall würde die Antwort „nein“ lauten, allerdings sind die selbstähnlichen Teile eines Objekts nicht immer offensichtlich zu erkennen. [Lor02] erforschte diese Problematik in seiner Arbeit, die im späteren Kapitel 1.3 Verwandete Arbeiten noch genauer vorgestellt wird. Ein weiterer Meilenstein stellt das 1968 von Aristid Lindenmayer entwickelte Textersetzungssystem dar. Diese nach seinem Erfinder benannten Lindenmayer-Systeme vereinen formale Grammatiken und später Turtle-Grafiken1 [AS82], um Pflanzen bzw. biologische Systeme prozedural zu generieren und darzustellen. Ähnlich wie eine Grammatik verwendet ein L-System Regeln, um die Zeichen eines Wortes durch neue Zeichen zu ersetzen. Durch diese Substitution kann ein Anfangswort in ein sehr komplexes Endwort, hier ein Synonym für Pflanze, gewandelt werden. Die Zeichen des erzeugten Wortes werden schließlich interpretiert und grafisch dargestellt. Diese Interpretation wird auch als TurtleMetapher oder Turtle-Grafik bezeichnet. Durch die von A. Lindenmayer, P. Prusinkiewicz und anderen später erforschten Weiterentwicklungen können L-Systeme Verzweigungen erzeugen, stochastisch arbeiten oder Einfluss durch die Umwelt erhalten. Obwohl L-System hauptsächlich bei pflanzenähnlichen Strukturen zur Anwendung kommen, eignen diese sich auch zum Beschreiben Abb. 1.6 Pflanzen generiert durch L-Systeme [DHM+98] von Fraktalen und anderer Formen. Der Ausdruck Turtle ist üblich, da sich die Interpretation ähnlich wie eine Schildkröte verhält. Die so lange strickt geradeaus läuft, bis eine Änderung der Richtung notwendig wird. 1 Mitte der achtziger Jahre entwickelten sich prozedurale Methoden speziell im Bereich der Computer-Grafik rasch weiter. Im Vordergrund stand anfangs hauptsächlich die Generierung von Texturen. Zum Beispiel die Erforschung iterierter Funktionen, die prozedural Texturen für Holzmaserungen, Marmormaserungen oder ähnliche Strukturen erzeugen. Vorreiter in diesem Gebiet waren unter anderem Ken Perlin, Darwyn Peachey und Geoffrey Gardner. Ein Teil ihrer Forschung bestand aus der Erfindung der so genannten Solid Textures. Diese stellen eine Alternative zu den üblichen zweidimensionalen Texturen dar und arbeitet mit drei Dimensionen. Traditionelle Texturen benötigen sehr komplizierte Verfahren, um eine Textur (z.B. die einer Holzmaserung) auf ein komplexes Objekt abzubilden, und führen in den meisten Fällen zu unrealistischen Abbildungen. Durch Solid Textures können Maserungen für beliebige Objekte prozedural generiert werden, ohne komplizierte Texturkoordinaten erstellen zu müssen. Ken Perlin wurde ebenfalls bekannt durch seine berühmte Zufallsfunktion, die so genannte Perlin Noise Funktion. Die Zufallsfunktion verwendet eine feste Permutationstabelle, Hash-Funktionen und Interpolationen, um einen kontinuierlichen und kontrollierbaren Zufall zu erhalten. Es handelt es sich deswegen mehr um eine pseudo-random Funktion. Abb. 1.7 Solid Textures (Bild: [Jag04]) Abb. 1.8 Cirrus clouds von David S. Ebert In den letzten Jahrzehnten gewann der prozedurale Entwurf in vielen Gebieten neue Erkenntnisse. Vor allem im Bereich der Fraktale wie Landschaften [EBE+03], Wolken [Gar85][EBE+03] und Partikelsystem für Rauch [FSJ01], Feuer [FKM+06] und ähnliches. Die meisten Verfahren zum Erzeugen von Partikeleffekten verwenden einfache Zufallsfunktionen. Diese beeinflussen die Eigenschaften wie z.B. Position, Farbe oder Größe, und animieren die Partikel zufällig. Weiterhin wurden sehr viele Ergebnisse im Bereich der Generierung und Tesselierung von Geometrien erreicht. Dies geht von der computergesteuerten Generierung einfacher Kugeln bis hin zur dynamischen Tesselierung komplexer geometrischer Objekte. Neben den typischen Computer-Grafik Bereichen wurde auch in anderen Gebieten geforscht, zum Beispiel im Bereich der Musik und Komposition[Hol81][Pru86][WS05]. Dort werden Ersetzungssysteme (z.B. Lindenmayer-Systeme) eingesetzt, um Musik prozedural zu komponieren. Die Interpretation erfolgt in diesen Fällen nicht grafisch, sondern akustisch. In diesem Kapitel wird nur ein sehr kleiner Ausschnitt aller Arbeiten und Forschungen beschrieben, die im Bereich der prozeduralen Generierung tätig waren und sind. Vor allem in den 80er, 90er und den letzten Jahren haben prozedurale Ansätze einen großen Schub erhalten. Da die Rechner immer leistungsfähiger wurden und die zu erzeugenden Objekte immer komplexer werden sollen, gewannen prozedurale Ansätze an Attraktivität. Anstatt Texturen, Modelle, Animationen und vieles mehr von einem Programmierer bzw. Grafiker kreieren zu lassen, werden einmalig prozedurale Systeme erzeugt, die dann den größten Teil dieser Arbeit bewältigen. Dies gilt vor allem für Objekte, die einen hohen Grad an Selbstähnlichkeit aufweisen. Was bedeutet prozedural und welche Vorteile bzw. Nachteile liegen in einem solchen Ansatz? „Procedural techniques are code segments or algorithms that specify some characteristic of a computergenerated model or effect.“ [EBE+03, S. 1] [EBE+03] formuliert prozedurale Methoden als Programmcode bzw. Algorithmen, die einige charakteristische Merkmale von computergenerierter Modellen1 beschreiben. So wird, um ein Beispiel zu nennen, kein Foto zur Darstellung einer Pflanze verwendet. Vielmehr wird die Pflanze zuerst anhand von Regeln definiert. Aus diesen Regeln wird schließlich eine virtuelle Pflanze erzeugt und dargestellt. Mit anderen Worten, ein prozeduraler Ansatz verwendet Prozeduren, um die Objekte zu generieren. Prozeduren sind in der Datenverarbeitung eine Kette von Anweisungen, demnach Algorithmen. D.h. ein Großteil der Komplexität eines Objektes wird nun vom einem Computer durch Prozeduren erzeugt und nicht von dem Programmierer bzw. Grafiker. Diese werden durch einen prozeduralen Ansatz entlastet. Als stärksten Vorteil führt [EBE+03] die Abstraktion prozeduraler Verfahren auf. Durch prozedurale Methoden können komplexe Modelle in Funktion bzw. Algorithmen abstrahiert werden. Aus diesem Grund ist es nicht notwendig, ein komplexes Modell zu speichern, sondern nur dessen Abstraktion in Form einer Funktion, woraus ein enormer Speichervorteil entsteht. Aus dem geringen Speicherverbrauch kann wiederum eine bessere Ladezeit resultieren. Dies ist natürlich stark von der Berechnungszeit der prozeduralen Funktion abhängig. Durch die Abstraktion kann variabel entschieden werden, wie komplex ein Modell durch den Algorithmus erzeugt werden soll. Als geeignetes Beispiel für diese Flexibilität kann die Auflösung einer Textur angesehen werden. Normalerweise muss ein Grafiker zu Beginn entscheiden, in welcher Auflösung eine Textur gestaltet wird. Dies ist bei einem prozeduralen System nicht notwendig. Die Textur kann nach Belieben in ihrer Genauigkeit variieren und für ein gegebene Situation passend erzeugt werden, auch direkt zur Laufzeit. Weiterhin verfügen Prozeduren über eine parametrische Kontrolle, durch die sich die zu erzeugenden Inhalte einfach steuern und variieren lassen. So könnte die Größe eines Baums, die Anzahl der Äste und sonstige Eigenschaften über mehrere Parameter kontrolliert werden. Smith bezeichnet dies auch als Data-base amplification [Smi84]. Es bedeutet, dass komplexe Strukturen aus geringen Eingabedaten generiert werden können. Wurden die Parameter festgelegt, so können diese entweder durch den Nutzer gesetzt werden, der damit das Aussehen der Inhalte steuert. Oder aber, damit kommen wir schon zum nächsten Vorteil, die Parameter werden von dem Programm stochastisch erzeugt. D.h. die Parameter oder sonstige Einstellungen werden von dem Programm bzgl. vorgegebener Wahrscheinlichkeiten während der Berechnung ermittelt. Dafür verwendet das Programm ein Zufallsgenerator und generiert für jede Ausführung einen neuen Inhalt. Durch stochastische Mittel erzeugen prozedurale Methoden eine sehr große Vielfalt, die durch handgefertigte Modellierung nicht erreicht werden kann. So mächtig ein stochastischer Ansatz ist, beinhaltet dieser auch einen großen Nachteil und bildet die Achillesferse dieser Systeme. Was durch prozedurale, vor allem stochastische Methoden an Vielfalt gewonnen wird, verliert der Nutzer an Genauigkeit der Ergebnisse. Es entsteht ein Wechselspiel zwischen Flexibilität/Vielfalt und gezielten Ergebnissen. Das bedeutet, wenn ein Künstler eine exakte Vorstellung von einem Ergebnis hat, kann dieses mit Hilfe prozeduraler Methoden meist nicht erreicht werden. Dafür können sehr viele Ergebnisse erschaffen werden, die alle der exakten Vorstellung im gewissen Maße ähneln. Zu dem kommt, dass viele prozedurale Verfahren schwer nachvollziehbar sind, vor allem für Nutzer, die nur geringe Vorkenntnisse besitzen. Außerdem sind die meisten Systeme schwer zu programmieren und fehleranfällig. Je nach System und zu erzeugendem Inhalt bzw. Komplexität des Ergebnisses können lange Berechnungszeiten entstehen. Vor allem bei Objekten, die Verzweigungen erzeugen, kann die Komplexität und damit die Berechnungszeit sehr schnell expandieren. 1 Modelle ist in diesem Fall ein Platzhalter für Texturen, Geometrien, Animationen, usw. Oliver Deussen unterteilt in seinem Buch [Deu03] prozedurale Ansätze in „prozedurale“ und regelbasierte Verfahren. Prozedurale Verfahren sind nach seiner Definition parametrisierte Algorithmen, die meist speziell für eine Aufgabe konzipiert werden, zum Beispiel eine rekursive Funktion zum Erzeugen einer speziellen Baumart. Im Gegensatz dazu wird in regelbasierten Systemen eine formale Regelbasis verwendet. Diese ermöglicht eine allgemeine Spezifikation des Systems und ordnet dem System keine genaue Aufgabe zu. Ein sehr bekanntes Beispiel sind die so genannten Lindenmayer-Systeme, die als Textersetzungssystem eine Untermenge der regelbasierten Systeme bilden. Zusätzlich listet Deussen die iterierten Funktionssysteme und Graphen mit entsprechenden Abarbeitungsmethoden als weitere Regelsysteme auf. In regelbasierten Systemen lassen sich durch den Einsatz von einfachen Regeln aufwändige und komplexe Systeme definieren. Der größte Vorteil von regelbasierten System gegenüber üblichen prozeduralen Verfahren liegt klar auf der Hand. Durch das Austauschen der Regelbasis kann mit geringem Aufwand ein komplett unterschiedliches Modell definiert werden. Anfangs noch ziemlich langsam, benötigen die regelbasierende Systeme durch die heutige Rechenleistung der Computer nur noch einen Bruchteil der Verarbeitungszeit und sind somit fast uneingeschränkt einsetzbar, auch zum Erstellen sehr komplexer Strukturen. 1.3 Verwandte Arbeiten Nach dem im vorhergehenden Kapitel eine allgemeine Zusammenfassung von prozeduralen Verfahren aufgelistet wurde, wenden wir uns in diesem Kapitel noch einmal genauer dem Bereich des prozeduralen Städte- und Gebäudeentwurfs zu. Auch hier wird nur ein kleiner, aber bedeutsamer Ausschnitt der Arbeiten erläutert, die auf diesem Gebiet bereits erforscht wurden. Michael Hansmeyer beschreibt in seiner Arbeit [Han] fünf Ansätze, bei denen Algorithmen in der Architektur unterstützend angewendet werden können. Im ersten Ansatz dient das System zur Simulation von Personenströmen. Mit Hilfe dieser Simulation können Wege nach Effizienz und Sicherheit beurteilt und verbessert werden. Im zweiten Ansatz können mit der Hilfe genetischer Algorithmen Optimierungen im Entwurf der Gebäude vorgenommen werden. Abb. 1.9 Verzweigtes L-System (Bild: [Han]) Dazu zählen zum Beispiel eine optimale Aufteilung des Grundrisses in Korridore und unterschiedliche Wohnungen. Die Aufteilung verfolgt dabei das Ziel einen möglichst hohen Markpreis zu erzielen. Das dritte Verfahren behandelt die Erzeugung von Formen über die Angabe von vordefinierten Parametern hinaus. Dabei findet das System über Permutationen immer wieder neue Formen. Im vierten Ansatz steht die Generierung eines Gebäudes, basierend auf den Regeln eines LSystems. Es geht um die Frage, ob natürliche Wachstumsprozesse auch in der Architektur Anwendung finden, siehe Abb. 1.9. Im letzten Teil der Arbeit werden die Parameter eines Gebäudes direkt in das Abbild des Gebäudes übertragen und dort dargestellt. Dazu werden mit Hilfe von Transformationen die Farbe, Geometrie und ähnliche Komponenten verändert und dadurch die Parameter über das Bauwerk sichtbar gemacht. Abb. 1.10 Gebäudevariante erzeugt aus vordefinierten Entwurfsregeln (Bild: [Han]) W. Lorenz untersucht in seiner Arbeit „Fractal and Fractal Architecture“ [Lor02] die Grundrisse von Bauwerken und deren Fassaden nach selbstähnlichen Strukturen und damit nach Fraktalen. Auch wenn sich die Arbeit nicht direkt mit dem prozeduralen Entwurf von Bauwerken befasst, gibt sie aufschlussreiche Erkenntnisse darüber, ob Gebäude als Fraktale bezeichnet werden können. Innerhalb der Arbeit werden Beispiele für selbstähnliche Struktur in Städten erläutert. Zum Beispiel weisen die Straßennetze einer Stadt, Ähnlichkeiten mit denen eines Stadtviertels auf und diese wiederum mit den Netzen eines Einfamilienhauses. Auch wenn es sich nur um eine statistische Selbstähnlichkeit handelt und nicht um eine exakte Selbstähnlichkeit, können die Straßennetze einer Stadt als Fraktal bezeichnet werden. Mit Hilfe der Box-Counting Dimension, einer der möglichen Varianten, fraktale Dimensionen zu beschreiben, werden in der Arbeit die Dimensionen von Grundrissen und Fassaden berechnet. Das Box-Counting Verfahren kann auf beliebige Schwarz-Weiß-Zeichnungen ausgeführt werden und eignet sich deshalb besonders gut für die Zeichnungen von Gebäudegrundrissen. Mit Hilfe des Verfahrens werden die Fassaden und Grundrisse verschiedener Gebäude untersucht und so der Grad an Selbstähnlichkeit festgestellt. Dabei soll bestimmt werden, ob ein Gebäude aufgrund der Fassadenstruktur, Symmetrien oder ähnlichen Eigenschaften „schön“ wirkt oder nicht. Es folgt eine Analyse mehrerer Bauwerke, dessen Ergebnisse in Statistiken Abb. 1.11 Kathedrale von Lincoln, England (Bild: [Lor02]) zusammengefasst werden. Die Abbildung 1.12 zeigt ein offensichtliches Beispiel für Selbstähnlichkeiten eines Gebäudegrundrisses. Es handelt sich um das von Kaiser Friedrich II erbaute Castel del Monte, auch die „steinernen Krone Apuliens“ genannt. In Abbildung 1.11 verdeutlicht [Lor02] Selbstähnlichkeiten zwischen den einzelnen Fenstern der Kathedrale von Lincoln. Neben den Ähnlichkeiten der einzelnen Fenster weisen die Muster der Fensterscheiben ebenfalls selbstähnliche Strukturen auf. Abb. 1.12 Castel del Monte in Apulia, Italien (Bild: [Lor02]) Pascal Müller beschreibt in seiner Arbeit [Mül99] den Entwurf eines Systems, das Straßennetze einer Großstadt prozedural verlegt und die einzelnen Bauwerke an die erzeugten Netze anpasst. Dabei liegt der Schwerpunkt nicht im Entwurf der Formen und Details von Gebäuden, sondern eher auf dem gesamten Erscheinungsbild der Stadt. Als Werkzeug für die Straßennetze verwendet [Mül99] ein sequentiell selbstsensitives LindenmayerSystem. Lindenmayer-Systeme wurden eigentlich entwickelt, um Verzweigungen von Pflanzen zu generieren. Diese wachsen, sobald ein Ast sich aufspaltet, immer getrennt voneinander weiter. Straßennetze dagegen bilden Verzweigungen, die wieder aufeinander treffen und damit Zyklen bilden. Um solche Gegebenheiten durch ein System erzeugen zu lassen, erkennt das von [Mül99] aufgestellte selbstsensitive L-System Straßen, die auf bereits erzeugte Segmente/Straßen stoßen, und verbindet diese miteinander (siehe Abb. 1.13). Weiterhin reagieren die Straßen auf Kollisionen mit Wasserbereichen oder Waldgebieten und werden entsprechend umgeleitet. Nach dem das Straßennetz verlegt wurde, müssen in den Zwischenbereichen des Netzes passende Gebäude erstellt Abb. 1.13 Selbstsensitives L-System (Bild: [Mül99]) werden. Dazu ermittelt das System die möglichen Bereiche, spaltet deren Flächen mehrfach und zerlegt diese damit in einzelne Häusergrundrisse. [Mül99] bezeichnet diesen Vorgang als Subdivision. Durch die Parzellierung werden die einzelnen Häuserblöcke mit mehreren Bauwerken belegt. Die einzelnen Bauwerke werden dann entsprechend ihrer Position innerhalb der Stadt texturiert und extrudiert (in die Höhe gezogen). Die Hochhäuser variieren noch nicht in ihren Formen und erhalten noch keine besonderen Merkmale. Abbildung 1.15 präsentiert eine von dem System Abb. 1.14 Subdivision eines Polygons (Bild: [Mül99]) generierte Großstadt, die das heutige Manhattan widerspiegeln soll. Abb. 1.15 Virtuelles Manhattan (Bild: [Mül99]) 10 In seiner späteren Arbeit [Mül01] verbessert P. Müller das System an den bisherigen Schwachstellen, erweitert es um neue Funktionen und entwickelt daraus die so genannte City Engine. Diese vereint die bereits in seiner vorherigen Arbeit [Mül99] entwickelten selbstsensitive L-Systeme, das Verfahren zur Parzellierung von Häuserblöcken und die neu entwickelte Grammatik zum Erzeugen der einzelnen Gebäudestrukturen. Die prozedurale Generierung der Gebäude erfolgt über eine so genannte Shape Grammar. Diese kann auf alle beliebigen durch das Straßennetz und die Subdivisionen erzeugten Grundformen angewendet werden, d.h. die Grammatik ist formunabhängig. Angewendet auf einen Grundriss kann die Shape Grammar diesen verändern, darauf Extrusionen1 anwenden und innerhalb diesen volumetrische Grundformen erzeugen. Wie bei Lindenmayer-Systemen können Verzweigung erzeugt werden und daraus hierarchische Strukturen entstehen. Abb. 1.16 veranschaulicht den von einer Shape Grammar erzeugten Sears Tower aus Chicago. Mit Hilfe der Shape Grammar werden in der City Engine verschiedene Gebäudetypen entworfen, die dann zufällig innerhalb der Stadt platziert werden. Im Gegensatz zur vorherigen Arbeit gewinnen die Gebäude an Struktur und Vielfalt, wodurch die Stadt einen realistischeren und lebendigeren Eindruck erhält. Mit Hilfe der City Engine können die einzelnen Schritte zum Entwerfen einer Stadt bearbeitet und angezeigt werden. Dies beinhaltet die Einstellung von Parametern, Aufstellen der verschiedenen Grammatiken für Straßennetze und Gebäudestrukturen, usw. Obwohl eine größere Vielfalt und ein höherer Detailgrad für die Gebäude-Grundrisse sowie die Grundformen erzielt wurde, besitzen die Bauwerke noch keine Feinheiten wie Regenrinnen, Fensterrahmen, Treppen oder Ähnliches. Abb. 1.16 Sears Tower (Bild: [Mül01]) Extrusion wird abgeleitet aus dem lat. extrusus von extrudere ”hinausdrängen, -stoßen“. Die Gebäude werden förmlich aus ihrem Grundriss „herausgepresst“ und expandieren in die Höhe. 1 11 [WWS+03] widmen sich in ihrer Arbeit ausschließlich der prozeduralen Erstellung von Fassaden. Im Gegensatz zur City Engine sollen die Gebäude einen noch höheren Detailgrad erhalten, z.B. sollen Fenster, Türen, Säulen und sonstige Formen über das System formuliert und daraus generiert werden. Um eine möglichst große Vielfalt zu erhalten, wurden zwei verschiedene Grammatiken implementiert, die sogenannte Split Grammar und die sogenannte Control Grammar. Die Split Grammar ist eine veränderte Variante einer Shape Grammar. Durch die Grammatik können Fassaden in verschiedene Teile gespalten bzw. durch mehrere Formen ersetzt werden und dadurch Strukturen, Fenster und sonstige Formen an den Häusern erzeugen. Abbildung 1.17 verdeutlicht die Regeln und den Prozess einer Split Grammar. Die weißen Abb. 1.17 Split Grammar (Bild: [WWS+03]) Kästen repräsentieren nichtterminale Symbole bzw. Formen, die restlichen Formen beenden die Ersetzung. Beginnend mit einer Grundform, also einer Wand, wird diese sukzessive durch neue Formen ersetzt bzw. unterteilt. Dieser Vorgang wird so lange fortgesetzt, bis ausschließlich Formen existieren, die keine weitere Ersetzung ermöglichen. Bei der erwähnten Control Grammar handelt es sich um eine einfache kontextfreie Grammatik. Im Gegensatz zur Split Grammar soll die Control Grammar keine Formen erzeugen, sondern anhand von architektonischen Richtlinien und Regeln die räumliche Aufteilung der Split Grammar steuern und deren Attribute (z.B. Material) entsprechend setzen. Ziel der Arbeit ist es, komplexe Fassaden für beliebige Gebäude zu erstellen, wobei die meisten Gebäude bis zu 100.000 Polygone besitzen. Diese hohe Komplexität schließt die Nutzung für Echtzeit-Systeme aus, zumindestens für größere Städte mit mehreren hundert Gebäuden. Durch den Einsatz der Control Grammar können sehr große Regelwerke sinnvoll verwaltet und auf die einzelnen Gebäude angewandt werden. Dadurch erzeugt das System eine große Vielfalt zwischen den Gebäuden, und es entsteht ein lebendigeres und organischeres Bild der Stadt. Abb. 1.18 Virtuelle Fassaden (Bild: [WWS+03]) 12 In den Arbeiten [MVU+05] und [MWH+06] wird die bereits beschriebene City Engine erneut erweitert. Durch den Einsatz einer verbesserten Shape Grammar kann eine gezielte und effizientere Generierung des Masse-Modells erreicht werden. Zum Masse-Modell gehören die grundlegenden Teile eines Bauwerks, die aus transformierten Würfeln, Kugeln und sonstigen Formen zusammengesetzt werden. Zusätzlich kann die Grammatik alle Formen auf ihre Flächen, Kanten und Punkte herunterbrechen und auf diese Subdivisionen ausführen. Ähnlich wie in der von [WWS+03] eingeführten Split Grammar können damit die Fassaden des Masse-Modells in verschiedene Stockwerke und Bereiche unterteilt werden. In diesen Bereichen können Fenster, Türen oder sonstige Elemente durch Formen oder vorgefertigte Modelle erzeugt werden. Ebenfalls ist es möglich, vorgefertigte Objekte zu laden, z.B. das Objekt einer Feuerleiter oder sonstige komplexe Objekte, die nicht mit Hilfe des Systems generiert werden können. Alle Grundrissverformungen, Extrusionen, Generierungen, Transformationen, Unterteilungen, Splits oder sonstige Operationen werden innerhalb einer einzigen Grammatik angegeben. Durch diesen Ansatz können schnell sehr große und unübersichtliche Grammatiken entstehen. Wie in der Arbeit von [WWS+03] weisen die hier erzeugten Gebäude-Modelle mehrere tausend Polygone auf. Allerdings kann der Detailgrad, in der Computer-Grafik bekannt als Level of Detail, über das System verändert werden und ermöglicht so ein interaktives arbeiten. Die Abbildung 1.19 zeigt ein mit Hilfe der neuen Grammatik generiertes Haus. Ebenfalls zu erkennen sind die einzelnen Unterteilungen, die durch spezielle Split-Befehle innerhalb der Grammatik erreicht werden. In der Arbeit beschreibt [MVU+05], wie die Stadt Pompeji virtuell durch die City Engine nachgestellt wird, siehe Abb. 1.20. Abb. 1.19 Shape Grammar (Bild: [MWH+06]) Abb. 1.20 Virtuelles Pompeji (Bild: [MWH+06]) 13 1.4 Lösungsansatz und Überblick Auf den vorherigen Seiten wurde eine kurze Zusammenfassung einiger verwandter Arbeiten aufgestellt. Vor allem die Arbeiten von [WWS+03] und [MWH+06] erzielen bereits sehr realistische und vielfältige Ergebnisse. Aus diesem Grund wurden die beiden Arbeiten als Vorlage für das zu implementierende System verwendet, d.h. einige der Ideen wurden übernommen und teilweise angepasst oder erweitert. Das entwickelte System zum prozeduralen Generieren von Gebäuden besitzt im Ansatz große Ähnlichkeit mit dem von P. Müller aufgestellten System [MWH+06]. Wie bei [MWH+06] bildet die Basis ein verändertes Lindenmayer-System, allerdings wurde dafür eigens eine neue Syntax und eine neue Definition für das L-System aufgestellt. Diese machen die Grammatiken übersichtlicher, verständlicher und ermöglichen eine effizientere Transformation der Formen. Über die Grammatik des L-Systems können Regeln aufgestellt werden, aus denen das System Formen für ein Masse-Modell anfertigt. Allerdings wird anders als bei [MWH+06] kein Straßennetz erzeugt und deshalb auch keine Parzellierung von Häuserblöcken ermittelt. D.h. die einzelnen Häuser besitzen keine speziellen Grundrissflächen, aus denen die Gebäude extrahiert werden. Die Masse-Modelle werden lediglich durch Transformation eigens benannter Primitive erzeugt, also aus Würfeln, Zylindern, etc. Ebenfalls anders als bei [MWH+06] wird das Masse-Modell nicht innerhalb der Grammatik auf seine einzelnen Flächen und Kanten heruntergebrochen. Dieser Vorgang wird vom System automatisch ausgeführt, dabei kann jeder Fläche und Kante eine neue Grammatik zugewiesen werden. Anstatt nun das komplette Gebäude (inklusive Fassaden) von einer einzigen Grammatik erzeugen zu lassen, werden die Teile des Gebäudes auf verschiedene Grammatiken aufgeteilt. Dadurch verringert sich die Komplexität der Grammatiken und sorgt für eine klare Struktur zwischen den einzelnen Bereichen des Gebäudes. Zum Beispiel fertigt eine Masse-Grammatik das Grundmodell des Bauwerks an und eine Fassaden-Grammatik die einzelnen Fassaden des erzeugten Grundmodells. Ein weiterer Vorteil dieser Aufspaltung entsteht durch die Wiederverwendbarkeit der einzelnen Grammatiken. Eine Grammatik, die Regeln für Fassadenstrukturen besitzt, kann mit jeder beliebigen Grammatik zum Erzeugen eines Masse-Modells kombiniert werden. Zusätzlich können Bereiche der einzelnen Grammatiken (z.B. der Bereich einer Fassade) in verschiedene Teile gespalten werden, ähnlich wie bei der Split Grammar von [WWS+03] und [MWH+06]. D.h. die Grammatiken verfügen über verschiedene Subdivision- und Split-Methoden. Diese wurden insofern erweitert, so dass mit einfachen Mitteln Symmetrien formuliert werden können, wie z.B. das symmetrische Muster eines Fachwerkbaus. Wie in dem System von [MWH+06] können auch vorgefertigte Objekte geladen und platziert werden. Allerdings wurden auch hier einige Veränderungen und Erweiterungen eingeführt, um dadurch die Vielfalt zu steigern. Zusätzlich ist das eigens aufgestellte System erstmalig umgebungssensitiv im Bereich der Gebäudegenerierung, d.h. die Gebäude, Fassaden und erzeugten Modelle können Einfluss durch die Umwelt erhalten. Dazu kann die Umwelt durch verschiedene Methoden analysiert werden und so z.B. die Höhe des Hauses, die Größe und Position von Fenster und vieles mehr beeinflussen. Die nächsten drei Kapitel beschreiben die Grundlagen des Systems. Dazu gehören die in Kapitel 2 Architektonische Grundlagen beschriebenen architektonischen Grundkenntnisse und Begriffe. Diese werden vermittelt und benötigt, damit zum einen das Verständnis für die Fachbegriffe entsteht. Und zum anderen, um aufzuzeigen, dass das Systeme einfache Regeln aus der Architektur umsetzen kann. Darauf folgend, werden in Kapitel 3 Lindenmayer-Systeme verschiedene Varianten der LindenmayerSysteme dargelegt. Diese bilden die Basis zum Formulieren der Regeln, aus denen ein Gebäude erbaut werden soll. Im vierten Kapitel Straight-Skeleton folgt ein Algorithmus, mit dem Dachflächen für beliebige Grundrisse ermittelt werden können. Der Algorithmus ermöglicht eine einfache Formulierung der Dachstrukturen in Form von Polygonen. Im Kapitel 5 Konzept folgt schließlich das Konzept des entwickelten Systems. D.h. es wird erklärt, wie alle Komponenten zusammenspielen, welche Veränderungen an den Lindenmayer-Systemen vorgenommen werden, wie das Straight-Skeleton eingesetzt wird, wie die Analyse der Umwelt realisiert wird, etc. 14 2. Architektonische Grundlagen In diesem Grundlagenkapitel werden typische charakteristische Merkmale von Bauwerken, insbesondere Wohnhäuser, beschrieben. Zusätzlich werden einige Grundbegriffe aus der Architektur erläutert. Ein besonderes Augenmerk liegt auf den Grundlagen und Begriffen der Dachkonstruktionen, da hier viele der Begriffe innerhalb der Arbeit benötigt werden. Das Kapitel soll verdeutlichen, das einfache Regeln und Merkmale über das System formuliert und schließlich umgesetzt werden können, und keinen tiefgründigen Einblick in die Architektur oder Statik geben. 2.1 Merkmale Auffallend an den meisten Gebäuden sind die Unterschiede bzw. Gleichheiten der einzelnen Stockwerke. Das unterste Stockwerk besitzt meist eine andere Fassade als die darüber liegenden Stockwerke, bei denen jedes Stockwerk häufig die gleiche Struktur und Fassade besitzt. Ein Beispiel wird in Abb. 2.1 dargestellt. In der Abbildung ist zu erkennen, dass das unterste Stockwerk große Fenster und eine weiße Fassade besitzt und die darüber liegenden Stockwerke ein Fachwerk-Muster aufweisen sowie Fenster, die sich an den gleichen Stellen innerhalb der einzelnen Stockwerke befinden. Zur damaligen Zeit wurde hauptsächlich zwischen Stein- und ­Holzbauten unterschieden. In der Moderne kamen dann die Stahlund Betonbauten hinzu. Während die Mauern mehrstöckiger Steinbauten direkt aufeinander sitzen und nach oben hin häufig dünner werden, können die Wände von Holzkonstruktionen etagenweise vortreten. Diese Eigenschaft bildet ein wesentliches Abb. 2.1 Fachwerkhaus charakteristisches Merkmal der Holzbauten gegen über den Steinbauten. Ein weiteres Merkmal, das sehr häufig von Architekten eingesetzt wird, bildet die Symmetrie. Ohne Symmetrie würden die meisten Gebäude chaotisch wirken und ihre schöne Ästhetik verlieren. Ein typisches Beispiel für die Symmetrie eines Gebäudes verdeutlicht das „Weiße Haus“. Wie in der Abbildung 2.2 zu erkennen ist, kann in der Mitte des „Weißen Hauses“ eine vertikale Achse gelegt werden. An dieser Achse kann die linke bzw. rechte Hälfte des Gebäudes gespiegelt werden und erhält dadurch genau die gegenüberliegende Seite. Auch das Gebäude aus Abb. 2.1 besitzt ein symmetrisches Fachwerkmuster. Man könnte sagen, dass bei fast allen Gebäuden Symmetrien zu erkennen sind und dies eines der wesentlichsten Merkmale darstellt, egal aus welcher Region, Zeitalter oder Kultur das Bauwerk stammt. An dieser Stelle könnten unzählige Regeln erläutert werden, z.B. die optimale Größe von Fenster bzgl. der Wand- und Raumgröße [BFH+80]; doch dies würde zu weit führen. Möchte ein Nutzer ein Gebäude entwerfen, kann er sich vorher erkundigen welche Größe sein Haus besitzen soll, wie die einzelnen Stockwerke sich verhalten, ob Symmetrien innerhalb des Gebäudes existieren, usw. All diese Regeln können durch das realisierte System formuliert werden und bilden damit die Grundpfeiler des Gebäudes. Abb. 2.2 „Weiße Haus“ 15 2.2 Dächer Das Dach bildet den oberen Abschluss eines Gebäudes, es besteht aus Dach-Tragewerk (Dach-Konstruktion) und Dach-Haut (Dach-Deckung und Hilfskonstruktionen). Die Dach-Konstruktion ist die Gesamtheit der tragenden Bauteile. [Bro88, S. 74] Das Dach dient in seiner praktischen Ausübung, vor allem als Schutz vor Niederschlägen, Hitze und Kälte. Die Konstruktion des Dachs muss aus diesem Grund gut überlegt sein und erfordert viel Erfahrung über Deckmaterialien, Balkenkonstruktionen, Statik etc. All diese Punkte sollen im Folgenden außer Acht gelassen werden, der wichtigste Punkt ist, die Dachkonstruktion möglichst realistisch und optisch ansprechend vom dem System erzeugen zu lassen. Auch in der Architektur wird die Dachkonstruktion, speziell die Dachausmittlung, nicht nur auf ihre Funktion beschränkt, sondern bestimmt zu einem großen Teil die Optik des Gebäudes: „Nicht allein in technischer, sondern auch in formaler Beziehung ist das Dach als wesentlicher Teil eines Bauwerks von großer Bedeutung, da seine Form einen großen Einfluss auf die äußere Erscheinung des Gebäudes ausübt.“ [War00, S. 129] Dieses Kapitel wird die Begriffe, die verschiedenen Formen sowie die Problematik bei der Erstellung einer Dachkonstruktion erläutern und verdeutlichen. Im späteren Kapitel 4 Straight-Skeleton dieser Arbeit folgt die Erklärung eines grundlegenden Algorithmus zur Generierung der Dächer, sowie in Kapitel 5 Konzept schließlich ein Konzept zum Erzeugen prozeduraler Dächer. 2.2.1 Begriffe In der Architektur wird ein Dach üblicherweise in 14 Komponenten [Pre99] gegliedert. Dazu zählen die in Abb. 2.3 dargestellten Bezeichnungen: First, Traufe, Ortgang, Hauptdach, Nebendach, Walm, Krüppelwalm, Grat, Giebel, Kehle, Verfallung und Anfallspunkt. Zusätzlich sind noch die Elemente Gaube und Dachflächenfenster gebräuchlich. Abb. 2.3 Dach-Begriffe Der Giebel bzw. die Giebelseite definiert die Seite eines Gebäudes, an der sich keine geneigte Dachfläche befindet (meist die kürzere Seite des Hauses), siehe Abb. 2.5 in der Spalte Giebeldach. Wenn diese Seite des Hauses ebenfalls eine geneigte Dachfläche aufweist, ist von einem Walm bzw. von einem Walmdach die Rede. Wenn der Walm nicht direkt bei der Traufe beginnt, entstehen der so genannte Krüppelwalm und Trapezgiebel. Die Längsseite ist das genaue Gegenteil der Giebelseite, sie bezeichnet die meist längeren Walmseiten. Der First beschreibt die oberste horizontale Kante eines geneigten Dachs. Im Gegensatz zur Traufe, die die unteren Kanten und damit den Beginn des Dachs definiert. Der Ortgang bezeichnet die seitlichen Schnittkanten einer Dachfläche mit einer Giebelwand und verbindet damit die Traufe mit dem First. Treffen zwei Dachflächen aufeinander, wird die Kante nicht als Ortgang, sondern als Grat bzw. Kehle bezeichnet. Wenn die Kante eine Außenkante bildet, wird der Begriff Grat verwendet und unter der Bedingung, dass die Kante eine Innenkante bildet, der Begriff Kehle. Ein Grat, der nicht bis zur Traufe verläuft, trägt die Bezeichnung Verfallung. Eine Verfallung entsteht meist beim Zusammenfassen von Haupt- und Nebendach und verläuft vom First des Hauptdachs bis zum First des Nebendachs. Genaueres zur Zusammenfassung von Dächern, in der Architektur auch als Dachausmittlung bezeichnet, wird im Kapitel 2.2.4 Dachausmittlung beschrieben. 16 Dachflächenfenster sind flach auf einer Walmbzw. Längsfläche liegende Fenster. Handelt es sich um ein stehendes Dachfenster, ist der Ausdruck Dachgaube (Dachgaupe) üblich. Die verschiedenen Formen für Dachgauben [Pre99] werden in Abbildung 2.4 vorgestellt, dazu zählen die Schleppgaube (a), Gaube mit Satteldach (b) und deren spezielle Ableitung in Form eines Dreiecks (Spitzgaube) (d), Gaube mit Walmdach (c), Fledermausgaube (e) und Ochsenauge (f). Abb. 2.4 Dachgauben (Bild: [Pre99]) 2.2.2 Dachformen Die Form eines Dachs ist meist, zumindestens in früheren Zeiten, abhängig von den zu Verfügung stehenden Deckmaterialien, den klimatischen Bedingungen und natürlich der jeweiligen Region und Kultur. Die gängigsten Dachformen werden in Abb. 2.5 tabellarisch aufgelistet, dazu zählen die Dachformen: Sattel-, Mansard- und Pultdach [BFH+80] [Pre99][War00], zusätzlich existiert noch das Flachdach [Pre99], das Zeltdach bzw. Regeldach bei einem Kreis [War00] und das Tonnendach [War00]. Das Satteldach besteht aus zwei entgegengesetzt geneigten Längsflächen, dabei befindet sich an der Vorderseite meist ein Giebel, Walm oder Krüppelwalm. Im Gegensatz zum Satteldach Abb. 2.5 Dachformen verfügt ein Pultdach nur über eine einzige geneigte Dachfläche. Die Vorderseite eines Pultdachs entspricht in den meisten Fällen einer Giebelwand. Weist die Dachkonstruktion einen Knick auf, so dass die unteren Teile steiler sind als die oberen, handelt es sich um ein Mansarddach. Das Mansarddach geht auf den französischen Architekten Mansart (1625-1708) zurück, dieser entwarf seiner Zeit ebenfalls das Versailler Schloss. Die Besonderheit des Mansarddachs liegt in dem zusätzlichen Wohnraum, der durch die spezielle Dachkonstruktion geschaffen wird. Dieser Wohnraum, die sogenannten Mansarden, besaßen zur damaligen Zeit Steuervorteile, da eine Mansarde nicht als vollwertiges Stockwerk zählte. Sind die Längs- und Giebelseite gleich lang oder besitzen die Seiten eine passende Dachneigung, so dass nur ein Anfallspunkt für das gesamte Dach entsteht. Dann trägt diese spezielle Form des Sattel- oder Mansarddachs auch die Bezeichnung Zeltdach. 2.2.3 Dachneigung Die Dachneigung gibt die Neigung oder das Gefälle der Dachflächen an. Sie wird als Verhältnis der Dachhöhe zur Breite des von der betreffenden Dachseite überdachten Gebäudeteils in Prozent oder in Winkelgraden angegeben. [Bro88] Die Dachneigung darf, je nach Verwendung des Materials für Dachkonstruktion und Dachdeckung, nicht unter- oder überschritten werden. Weiterhin unterteilt [Pre99] die Dächer je nach Neigung in drei verschiedene Kategorien. Als da wäre das Flachdach, für Dächer mit einer Dachneigung zwischen 0° und 8°, das flach geneigte Dach ab einem Neigungswinkel von 8° bis zu einem Winkel von 30° und schließlich das Steildach mit einer Neigung von 30° bis 60°. 17 2.2.4 Dachausmittlung Die Dachausmittlung bestimmt zu einem vorgegebenen Hausgrundriss die Zusammensetzung des Dachs und damit die Lage und Neigung der einzelnen Dachflächen. Dabei handelt es sich um ein Problem aus der darstellenden Geometrie (Projektionslehre): dreidimensionale Raumgebilde als zweidimensionale Flächengebilde auf die Grundrissebene zu projizieren. Ziel der Dachausmittlung ist die Ermittlung der Form, Lage, Neigung und Abgrenzung der einzelnen Dachflächen. Zur Berechnung der Dachausmittlung existieren zwei Standard-Vorgehensweisen [Pre99][BFH+80]. Beide setzen voraus, dass die Traufe der zu verbindenden Dachtrakte jeweils auf gleicher Höhe liegt. Für den in Abb. 2.6 gezeigten Fall, wird keine gemeinsame Dachausmittlung ermittelt, da sich die Traufe des Nebentrakts nicht auf gleicher Höhe mit der des Haupttrakts befindet. In der ersten Variante werden die Dachflächen so angelegt, dass der First von allen Dächern auf gleicher Höhe liegt. Daraus folgt zwangsläufig, dass die Dachneigung zwischen Haupt- und Nebendach unterschiedlich ausfallen kann. Man kann versuchen, dies an den Walmflächen optisch einzuschränken, indem die Walmfläche des Nebendachs flacher konstruiert wird als das anschließende Satteldach (in Abb. 2.7 durch die gestrichelte Linien dargestellt), allerdings wirkt dadurch „das eine peinlicher als das andere“. [BFH+80, S. 33] Diese Variante ist in der Architektur eher verpönt und gilt als einfache, nicht stilvolle Lösung. Abb. 2.6 Ungleiche Traufe Abb. 2.7 Gleiche Firsthöhe, ungleiche Dachneigung Die zweite Variante ist schwieriger zu konstruieren, was sowohldieProjektionalsauchdiespätereBalkenkonstruktion angeht. Die Variante sieht vor, dass die Dachneigung aller Dachflächen gleich ist und aus diesem Grund die Firsthöhen Abb. 2.8 Ungleiche Firsthöhe, gleiche zwischen den einzelnen Dächern ­ verschieden ausfallen Dachneigung können. Die korrekte Dachausmittlung ergibt dann eine Verfallung, d.h. ein kurzes Stück Grat, das den höheren mit dem niedrigeren Dachfirst verbindet. In vielen Fällen sieht die Verfallung unangenehm aus und das umso mehr, je kürzer sie ist, siehe Abb. 2.9 (b). Aus diesem Grund sollte der Haupttrakt sich deutlich von den Nebentrakten unterscheiden und erkennbar sein, siehe Abb. 2.9 (a). Die Dachausmittlung stellt einen nichttrivialen Teil bei der Generierung eines Dachs dar. Ein geeigneter Algorithmus zur Ermittlung der Dachausmittlung bzgl. der oben beschriebenen zweiten Variante, wird innerhalb des Grundlagen-Kapitels 4 Straight-Skeleton vorgestellt. Abb. 2.9 (a) Korrekte Dachausmittlung (b) Kurze Verfallung 18 3. Lindenmayer-Systeme Der ungarischer Biologe Aristid Lindenmayer schuf 1968 einen mathematischen Formalismus zur Simulation biologischer Entwicklungen, basierend auf einem Ersetzungssystem. Anfang der 90er veröffentlichen A. Lindenmayer und P. Prusinkiewicz das Buch „The algorithmic beauty of plants“ [LP90]. Dieses zählt heute zu den Klassikern der Computer-Grafik und beschreibt, wie durch Kombination von formalen Grammatiken und Turtle-Grafiken ein System entsteht, mit dem Pflanzen und biologische Systeme prozedural generiert und dargestellt werden können. Diese Systeme sind nach ihrem Erfinder benannt und tragen deshalb den Namen Lindenmayer-Systeme, oder kurz L-Systeme. Im Laufe der Zeit wurden die L-Systeme von A. Lindenmayer selbst und anderen weiterentwickelt. Diese Entwicklung führte einmal zur Generierung immer realistischerer Pflanzen, und zum anderen wurde immer mehr Wissenschaftlern bewusst, wie mächtig diese Systeme sind. Dies führte wiederum dazu, dass Lindenmayer-Systeme zur Erzeugung vieler Fraktale genutzt werden. Obwohl L-Systeme vornehmlich zu Beschreibung von Pflanzen, Fraktalen oder ähnlichen Formen verwendet werden, eignen sich die Systeme ebenfalls zu Formulierung von Bauwerken. Auch diese weisen in manchen Fällen rekursive Charakteristika auf (s. Kapitel 1.3 Verwandte Arbeiten), die durch einfache Regeln eines L-Systems verfasst werden können. Allerdings werden L-Systeme, die Gebäude generieren, meistens nicht ihr gesamtes Potenzial ausnutzen können, und zwar aufgrund der niedrigen oder oft ganz fehlenden Selbstähnlichkeit eines Gebäudes. Trotzdem stellen Ersetzungssysteme ein geeignetes Mittel dar, um Regeln für ein Bauwerk und dessen Fassaden aufzustellen. Als praktisches Beispiel können die Shape-Grammar [MWH+06], sowie die Split- und Control-Grammar [WWS+03] aufgezählt werden, siehe Kapitel 1.3 Verwandte Arbeit. L-Systeme gehören zu der Gruppe der regelbasierten Systeme und zur Untergruppe der Textersetzungssysteme. In einem Textersetzungssystem wird ein gegebenes Startwort mit Hilfe von Regeln sukzessive in ein neues Wort gewandelt. Dabei kann pro Iteration jedes Zeichen des Wortes, auch Variable genannt, mit Hilfe einer passenden Regel durch ein oder mehrere Zeichen ersetzt werden. Durch diese Substitution wächst das Wort immer weiter an und wird komplexer. Dies geschieht so lange, bis keine passende Regel mehr gefunden werden kann oder eine festgelegte Anzahl von Schritten erreicht wurde. Im Gegensatz zu den formalen Grammatiken von N. Chomsky arbeiten Lindenmayer-Systeme parallel und nicht sequentiell. Begründet wird dies durch das parallele Wachstum sowie die parallele Zellteilung von Pflanzen. Und genauer heißt dies, dass innerhalb eines Schrittes alle Variablen gleichzeitig durch eine passende Regel ersetzt werden. Anders als bei formalen Grammatiken, wo in einer Iteration lediglich die erste Variable von links beginnend ersetzt wird. Sobald das erzeugte Wort nicht mehr weiter anwachsen soll oder kann, wird es interpretiert. Der Interpreter durchläuft das erzeugte Wort von links beginnend und führt für jedes Symbol eine festgelegte Aktion aus, z.B. das Zeichnen einer Linie. Abbildung 3.1 zeigt die berühmte Von-Koch-Kurve bzw. Koch’sche Schneeflocke, benannt nach dem schwedischen Mathematiker Helge von Koch. Die Von-Koch-Kurve zählt zu den bekanntesten Fraktalen und kann durch ein L-System mit einer einzigen Regel erzeugt werden. Abb. 3.1 Koch’sche Schneeflocke beim Start (a), nach einer Iteration (b) und nach zwei Iterationen (c) 19 Bevor auf die einzelnen Systeme genauer eingegangen wird, werden noch ein paar Grundkenntnisse aus der Mengenlehre und theoretischen Informatik erläutert. Diese werden benötigt, um die Definition der verschiedenen Lindenmayer-Systeme nachvollziehen zu können. Ein Alphabet Σ ist eine endliche und nicht leere Menge. Die Elemente eines Alphabets werden auch Symbole oder Zeichen genannt. Ein Symbol a Σ bezeichnet genau ein Element aus der Menge Σ, während ω Σ+ ein beliebiges, nicht leeres Wort aus dem Alphabet beschreibt. Gilt ω Σ*, so ist ω ein beliebiges Wort, inklusive dem leeren Wort ε. Für ein Wort ω bezeichnet |ω| die Länge des Wortes, also die Anzahl der Zeichen, die in dem Wort enthalten sind. So gilt zum Beispiel |ε| = 0. 3.1 Deterministisch kontextfreie Lindenmayer-Systeme Deterministisch kontextfreie L-Systeme, kurzgefasst D0L-Systeme, sind die einfachste Form dieses Textersetzungssystems. Die Grundlage wird durch eine formale Grammatik G festgelegt, die durch das Tripel G = (V, ω, P) definiert wird, wobei gilt: (1) (2) (3) (4) V ist eine endliche und nichtleere Menge, genannt Alphabet ω V + ist das nichtleere Ausgangswort oder auch Axiom genannt P ist eine endliche Menge, genannt Produktionen (Ersetzungsregeln) Eine Produktion (φ, χ) P wird geschrieben als φ → χ, wobei gilt φ, χ |φ| = 1 in einem kontextfreien L-System V+ und außerdem Die linke Seite einer Produktion (auch Predecessor genannt) gibt das zu ersetzende Zeichen an, während die rechte Seite (auch Successor genannt) das Wort definiert, durch welches der Predecessor ersetzt werden soll. Zeichen, für die keine Produktion angegeben wird, verwenden die Identitätsproduktion (φ → φ). Sie bleiben deshalb unverändert und lösen keine weitere Textersetzung aus. Aus diesem Grund werden Zeichen ohne Ersetzungsregeln auch als Konstanten oder als Terminalzeichen angesehen. In einem deterministischen System existiert zu einem φ höchstens eine Regel mit φ auf der linken Seite. Das bedeutet, dass zu einem gegebenen Ausgangswort das Ergebnis eindeutig berechnet wird und immer gleich ist. Um die Funktionsweise eines kontextfreien L-System zu verdeutlichen, wird das häufig beschriebene Beispiel der Von-Koch-Kurve erklärt. Gegeben sei das Alphabet V = {F, +, -}, das Axiom ω = F--F--F und die Produktionsmenge P = {F → F+F--F+F}. Durch die parallele Abarbeitung des L-Systems werden in der ersten Iteration die drei F Symbole des Axioms jeweils durch die Produktion von F ersetzt und bilden damit die folgende Ableitungsfolge. 1. Schritt 2. Schritt 3. Schritt F--F--F F+F--F+F--F+F--F+F--F+F--F+F F+F--F+F+F+F--F+F--F+F--F+F+F+F--F+F--F+F--F+F+F+F--F+F--F+F--F+F+F+F-- F+F--F+F--F+F+F+F--F+F--F+F--F+F+F+F--F+F An dieser Stelle wird der Vorgang einfach abgebrochen. Bis zum jetzigen Zeitpunkt existiert noch kein Unterschied zu einer formalen Grammatik, einzig und allein die parallele statt sequentielle Abarbeitung stellt einen Unterschied dar. Aus diesem Grund wurden in einer Iteration alle Symbole gleichzeitig bzgl. ihrer Ersetzungsregel substituiert. Wie bereits erwähnt wurde, verwenden L-Systeme eine graphische Interpretation ihrer erzeugten Wörter. Dieser Vorgang wird als Turtle-Metapher [AS82] bezeichnet. In diesem Beispiel entspricht die Schildkröte einem Vektor mit dem Format (x, y, α), dabei bestimmen x und y die Position und α die Ausrichtung der Schildkröte. Ähnlich wie eine reale Schildkröte, bewegt sich die virtuelle Schildkröte ausschließlich in ihre aktuelle Bewegungsrichtung und zwar so lange bis eine Richtungsänderung notwendig wird. Nach 20 dem das Endwort mit Hilfe der Ersetzungsregeln aus dem Axiom gebildet wurde, läuft der Interpreter jedes Zeichen von links beginnend ab und führt je nach Art des Zeichens eine festgelegte Funktion aus. Für dieses Beispiel würde die Interpretation des Alphabets so aussehen [Deu03]. F Bewege Turtle um d in aktueller Richtung, zeichne Linie: (x, y, α) → (x + d cos α, y + d sin α, α) + Erhöhe aktuellen Winkel um δ: (x, y, α) → (x, y, α + δ) - Erniedrige aktuellen Winkel um δ: (x, y, α) → (x, y, α - δ) Wenn nun die Länge d und der Winkel δ festgelegt werden, sagen wir d = 1 und δ = 60°, können die gebildeten Wörter mit Hilfe der Befehle interpretiert werden. Das interpretierte Axiom erzeugt das in Abb. 3.1 (a) dargestellte Dreieck und nachdem eine Iteration durchlaufen wurde, wird der in Abb. 3.1 (b) gezeigte Stern gezeichnet. Mit jedem weiteren Schritt wird die Struktur feiner und nähert sich dem Aussehen einer Schneeflocke, Abb. 3.1 (c). Mit Hilfe eines kleinen Alphabets, der Angabe einer einzigen Ersetzungsregel sowie eines Axioms kann ein beliebig detailreiches Objekt erzeugt werden. 3.2 Deterministisch kontextsensitive Lindenmayer-Systeme Kontextsensitive Systeme bieten dem Nutzer die Möglichkeit, die Ersetzungsregel eines Zeichens abhängig von dessen lokaler Umgebung zu machen. Wird dem Predecessor einer Ersetzungsregel ein linker und/oder rechter Kontext1 zugeteilt, kann die Regel nur ausgeführt werden, wenn der angegebene Kontext im Wort auch wirklich das Zeichen umschließt. Ein kontextsensitives System ist die Erweiterung eines kontextfreien Systems und kann demnach mindestens alle Wörter erzeugen, die mit einem kontextfreien System erzeugt werden können. In einem kontextsensitiven System verändern sich das Alphabet sowie das Axiom nicht, der einzige Unterschied besteht in der Angabe der Produktionen. Dabei wird genau definiert, im Gegensatz zu den formalen Grammatiken von Chomsky, welches Zeichen auf der linken Seite ersetzt werden soll und welche Zeichen den Kontext bilden. Eine Produktion (σ, τ, φ, χ) P hat demnach die Form σ < φ > τ → χ, wobei gilt φ V, χ V+ und σ, τ V*. Durch Einklammern (mit Hilfe der Größer- und Kleinerzeichen) des zu ersetzenden Zeichens φ wird klar festgelegt, welches Zeichen (nämlich φ) ersetzt werden soll und welche Zeichen (σ, τ) den Kontext bilden. D.h. das Symbol φ kann nur ersetzt werden, wenn innerhalb des Wortes links bzw. rechts neben φ, genau der Kontext σ bzw. τ steht. Bei einem D1L-System kann entweder ein linker oder ein rechter Kontext angeben werden, bei der Klasse der D2L-Systeme werden immer ein linker und ein rechter Kontext angeben und bei (k, l)L-Systemen wird auf der linken Seite ein Kontext der Länge k und auf der rechten Seiten ein Kontext der Länge l platziert. Ein Kontext, aus dem lat. contextus für ”Zusammenhang“, bezeichnet den ein Wort umgebenden Text, durch den oft die Bedeutung erst klar wird. 1 21 Wieder wird an dieser Stelle auf ein Standard-Beispiel zurückgegriffen. Dabei wird mit Hilfe einer kontextsensitiven Produktion ein Symbol durch ein Wort geleitet. Gegeben sei das Alphabet V = {a, b}, das Axiom ω = baaaaa und die Produktionsmenge P = {b < a → b, b → a}. 1.Schritt 2.Schritt 3.Schritt 4.Schritt … baaaaa abaaaa aabaaa aaabaa Dadurch, dass a in der ersten Produktion im Kontext zu b steht, wird in jedem Schritt das rechts von einem b liegende a durch ein b ersetzt. Gleichzeitig wird das bereits vorhandene b durch ein a ersetzt (zweite Produktion). Für alle a Symbole, die kein b als linken Nachbar besitzen, trifft die erste Produktion nicht zu, und es wird stattdessen die Identitätsproduktion ausgeführt. Damit wandert der Buchstabe b von der linken zur rechten Seite des Wortes. Da es sich weiterhin um ein deterministisches System handelt, ist das Ergebnis immer gleich, obwohl diesmal für jedes φ V mehrere Regeln angegeben werden können. Allerdings nur unter der Bedingung, dass sie einen verschiedenen Kontext besitzen und damit eine eindeutige linke Seite erhalten. Diese Bedingung schließt immer noch nicht die Möglichkeit aus, dass mehrere Regeln für ein Zeichen angewendet werden können. In solchen mehrdeutigen Fällen wird immer die Regel mit dem größten Kontext angewendet. Die Abbildung 3.2 zeigt drei unterschiedlich verzweigende kontextsensitive L-Systeme, entwickelt von [HH74]. Wie Verzweigungen innerhalb eines L-Systems erzeugt werden können, wird im nächsten Kapitel 3.3 Verzweigende Lindenmayer-Systeme erklärt. Abb. 3.2 Kontextsensitive L-Systeme (Bilder: [3]) 22 3.3 Verzweigende Lindenmayer-Systeme Die bisherigen L-Systeme können ausschließlich zusammenhängende Gebilde erzeugen; da allerdings die meisten Pflanzen und biologischen Systeme Verzweigungen besitzen, werden die L-Systeme um eine neue Eigenschaft erweitert. Das System ist nun imstande, den aktuellen Zustand der Schildkröte zu speichern und zu einem späteren Zeitpunkt wieder zu laden. Realisiert wird dies mit der Hilfe eines Stacks1. Ein Stack ist ein spezieller Typus von Speicher, bei dem der Zugriff lediglich auf zwei Arten erfolgen kann. Dies wäre zum einen der Push-Befehl, der den aktuellen Zustand auf die oberste Speicherstelle legt. Und zum anderen der Pop-Befehl, dieser liest den obersten Zustand vom Speicher und löscht ihn danach. Damit wird der zuletzt gespeicherte Zustand immer zuerst geladen. Um den Stack verwirklichen zu können, müssen zwei Änderungen eingeführt werden. Als Erstes wird das Alphabet um zwei Zeichen erweitert, meistens werden dafür die öffnende und schließende eckige Klammer verwendet. Des Weiteren muss der Interpreter die beiden neuen Zeichen ausführen können. Dazu verhält sich der Interpreter wie folgt: Liest der Interpreter die öffnende eckige Klammer [ innerhalb des erzeugten Wortes, so wird der aktuelle Zustand der Schildkröte auf den Stack geschrieben (Push). Wenn der Interpreter die schließende eckige Klammer ] identifiziert, wird der oberste Zustand vom Speicher entnommen, gelöscht und als aktueller Zustand für die Schildkröte gesetzt (Pop). Damit wird die Schildkröte quasi an einen früheren Zeitpunkt zurückversetzt und kann in eine andere Richtung weiter marschieren. Es folgt ein kleines Beispiel, diesmal zur Verdeutlichung der verzweigten L-Systeme. Dazu ist wieder ein Alphabet V = {A, F, +, -, [, ]} gegeben, nur diesmal werden die beiden zusätzlichen Zeichen zur Abfrage des Stacks sowie eine weitere Variable A dem Alphabet beigefügt. Zusätzlich sind das Axiom ω = A und die Produktionen P = {A → F[+A][-A]FA, F → FF} gegeben. 1. Schritt 2. Schritt 3. Schritt … A F[+A][-A]FA FF[+F[+A][-A]FA][-F[+A][-A]FA]FFF[+A][-A]FA Das Zeichen A (für Apex) dient lediglich zum Erzeugen eines neuen Astes, d.h. der Interpreter führt beim Lesen des Zeichens keine besondere Aktion aus. Die Ersetzungsregel für A erzeugt drei Verzweigungen und damit eine baumähnliche Struktur. Während die Ersetzungsregel für F nur dazu dient, die Länge der bereits bestehenden mittleren Äste zu verdoppeln. Mit Hilfe dieses Lindenmayer-Systems entsteht nach 7 Iterationen der in Abb. 3.3 dargestellte Baum. Abb. 3.3 Verzweigtes L-System 1 Stack zu Deutsch „Stapel“, bezeichnet einen Stapel- oder Kellerspeicher. 23 3.4 Stochastische Lindenmayer-Systeme Wie in den Kapiteln 3.1 - 3.3 festgestellt wurde, sind die bisher vorgestellten Lindenmayer-Systeme deterministisch und liefern zu einem gegebenen Axiom immer das gleiche Ergebnis. Um der Vielfalt der Natur gerecht zu werden, können die Systeme auf zwei Wege in ein indeterministisches System ausgebaut werden. Eine Möglichkeit dafür, stellen die in diesem Kapitel erklärten stochastischen Systeme dar und eine weitere Möglichkeit, die in Kapitel 3.5 erklärten parametrischen L-Systeme. Beide Variationen können kombiniert werden und ein kontextfreies oder -sensitives System erweitern. In einem stochastischen System ist die Angabe mehrerer Ersetzungsregeln mit identisch linker Seite erlaubt. D.h. für jedes Zeichen der Sprache können beliebig viele verschiedene Regeln angegeben werden. Allerdings muss das System bei einem Ersetzungsschritt eine dieser Regeln auswählen. Diese Auswahl geschieht mit Hilfe einer Wahrscheinlichkeit, die jeder Regel zugeteilt werden muss. Stochastische Systeme stellen demnach eine Ergänzung der bisherigen Systeme dar, indem die formale Grammatik zu einem 4-Tupel G = (V, ω, P, π) erweitert wird. Dabei gelten die bereits in den vorherigen Kapiteln erklärten Eigenschaften für kontextfreie bzw. kontextsensitive L-Systeme, wobei eine Menge von Wahrscheinlichkeiten hinzukommt. Die Wahrscheinlichkeitsverteilung π: P → (0, 1] bildet die Menge der Wahrscheinlichkeiten auf die Menge der Produktionen ab. Oder anders gesagt, π ordnet jeder Regel eine Wahrscheinlichkeit zu, die einen Wert größer 0 und kleiner gleich 1 besitzt. D.h. π enthält genauso viele Elemente wie die Menge der Produktionen P und ordnet jeder dieser Regel genau ein Element zu. Addiert man die Wahrscheinlichkeiten aller Produktionen mit identisch linker Seite (gleiches Ersetzungszeichen (Predecessor) sowie gleicher Kontext, falls vorhanden), so muss die Summe all dieser Wahrscheinlichkeiten eine 1 ergeben. Durch diese Bedingung wird festgelegt, dass das System immer eine Regel auswählen kann. Eine Produktion (φ, χ, υ) P hat für ein kontextfreies System schließlich folgende Notation: φ → χ : υ. Dabei sind wie gewohnt φ, χ V+ mit |φ| = 1. Neu angegeben wird υ π, hier bestimmt υ die Wahrscheinlichkeit mit der die Produktion zwischen allen passenden Produktionen ausgewählt wird. Kurz zusammengefasst, für jedes Zeichen φ V können in einem stochastischen System mehrere Regeln angeben werden. Dabei muss jeder Regel eine Wahrscheinlichkeit zwischen 0 und 1 (einschließlich 1) zugewiesen werden. Die Wahrscheinlichkeiten aller Ersetzungsregeln, die über eine identische linke Seite verfügen, müssen in der Summe 1 ergeben. Daraus folgt, dass immer eine Produktion ausgewählt werden kann. Soll in einem Ersetzungsschritt nun das Zeichen φ ersetzt werden, so wird durch einen Zufallsgenerator eine der passenden Regeln anhand ihrer Wahrscheinlichkeiten ausgewählt und φ schließlich entsprechend der ermittelten Regel ersetzt. Da stochastische L-Systeme mit Hilfe eines Zufallsgenerators arbeiten, ist nicht mehr garantiert, dass für ein gegebenes Axiom immer dasselbe Ergebnis erzeugt wird. Aus diesem Grund sind stochastische Systeme indeterministisch. Weiterhin wird durch die Auswahl verschiedener Regeln die grundlegende Topologie des Ergebnisses verändert. Dies führt zu verschiedenen Verzweigungsstrukturen, wie in Abb. 3.4 zu erkennen ist. Jede der verschiedenen Pflanzen aus der Abbildung wurde durch die gleichen Regeln erzeugt. Die zufällige Auswahl der Regeln führt schließlich zu den verschiedenen Resultaten. 24 Abb. 3.4 Ergebnisse eines stochastisches L-Systems 3.5 Parametrische Lindenmayer-Systeme Parametrische L-Systeme sind eine weitere Variante, mehr Abwechslung von einem Lindenmayer-System erzeugen zu lassen. Im Gegensatz zu den vorher beschriebenen stochastischen Systemen verändern paramterische Systeme nicht die topologische Struktur des Ergebnisses. Ein weiterer Vorteil von parametrischen Systemen liegt in der einfachen Kontrolle der einzelnen Parameter. Mit deren Hilfe können z.B. die maximale Länge von Ästen, die Anzahl der Verzweigungen und vieles mehr innerhalb der aufgestellten Regeln festgelegt werden und sind damit nicht mehr konstant definiert. In einem parametrischen L-System können allen Zeichen zusätzlich ein oder mehrere Parameter angehängt werden, diese Kombination wird als Modul bezeichnet. Ein Modul A(s0, s1, s2, …, sn) besteht demnach aus dem Zeichen A V und den formalen Parametern1 s0, s1, s2, …, sn Σ. Auf die Parameter kann innerhalb der Regel zurückgegriffen werden, außerdem können einfache arithmetische Operationen (z.B. Addition und Multiplikation) auf die Parameter ausgeführt werden. Eine weitere Neuerung stellen die eingeführten Bedingungen dar, die jeder Regel angehängt werden können. Eine Regel mit einer Bedingung kann nur ausgeführt werden, wenn die Bedingung erfüllt ist. Definiert wird ein parametrisches L-System durch ein 4-Tupel G = (V, Σ, ω, P). Dabei ist wie gehabt V die Menge der Variablen und ω das Axiom. Σ ist eine endliche Menge formaler Parameter. Für das Axiom gilt immer noch ω V+. Mit dem Unterschied, dass die formalen Parameter von Modulen durch reelle Zahlen ersetzt werden müssen. Eine Produktion φ : μ → χ erhält nun die Bedingung μ, diese kann aus formalen sowie reellen Parameter bestehen. Es dürfen außerdem arithmetische und logische Ausrücke auf die Parameter angewendet werden. Verdeutlichen lässt sich die Funktionsweise schnell anhand eines Beispiels. Gegeben ist das Alphabet V = {A(x)}, das Axiom ω = A(4) und die Produktionen P = {A(x) : x > 0 → A(x-1)}. Zu beachten ist, dass das Symbol A zusätzlich den formalen Parameter x erhält und mit diesem zusammen ein Modul bildet. Der Parameter x ist ein Platzhalter für beliebige reelle Zahlen, im Axiom A(4) beispielsweise die reelle Zahl 4. Durch einen Doppelpunkt nach dem Predecessor wird eine Bedingung für die Regel eingeleitet, in diesem Fall die Bedingung x > 0. Wird nun die Produktion auf das Axiom angewendet, entsteht die nachfolgende Ersetzungssequenz. 1. Schritt 2. Schritt 3. Schritt 4. Schritt A(4) A(3) A(2) A(1) In jedem Schritt wird durch Anwendung der Regel der Parameter x um 1 dekrementiert, und zwar solange die Bedingung erfüllt bleibt. Im 5. Schritt kann die Bedingung x > 0 nicht mehr erfüllt und die Regel deshalb nicht mehr ausgeführt werden. Das Beispiel zeigt ein mögliches Anwendungsgebiet der parametrischen L-Systeme, in dem die Parameter als Abbruchbedingung eingesetzt werden. Ein weiteres Anwendungsgebiet wäre z.B. die Beeinflussung des Interpreters über die Parameter. Dabei kann der Interpreter die Parameter auslesen, anhand dessen den Zustand der Schildkröte verändern und dadurch z.B. die Äste eines Baums (die bisher immer gleich lang waren) mit voranschreitender Verzweigung kürzer werden lassen. Sind die Parameter fest gewählt, bleibt das System deterministisch. Werden die Parameter allerdings zufällige oder dynamisch zur Laufzeit berechnet, wird das System indeterministisch und bietet somit große Variationsmöglichkeiten. Formale Parameter dienen zur Spezifikation eines Moduls. Reelle Parameter sind definierte Parameter, d.h. Parameter denen eine reelle Zahl zugewiesen wurde. 1 25 Ein weiteres Beispiel wird in Abbildung 3.5 dargestellt. Das dort gezeigte parametrische L-System wird durch das Axiom ω = A(1) sowie die Produktion P = {A(s) → F(s) [+A(s/1.456)] [-A(s/1.456)]} erzeugt. In jedem Schritt erzeugt das System zwei Verzweigungen, die mit jeder fortschreitenden Iteration ihre Länge und Ausrichtung ändern. Diese werden beide über den formalen Parameter s bestimmt. Der Parameter s wird bei jeder Ersetzung von A durch einen festen Wert dividiert und verringert dadurch die Abweichung und Größe. Auch hier wird durch eine einzige Ersetzungsregel eine sehr komplexe und beliebig detailreiche Geometrie formuliert. Abb. 3.5 Parametrisches L-System (Bild: [LP90]) 3.6 Umgebungssensitive und offene Lindenmayer-Systeme Die bisher vorgestellten Systeme bieten bereits eine große Bandbreite von Optionen an, möglichst realistische und ansprechende Pflanzen zu erschaffen. So können mit kontextsensitiven Systemen zwar Pflanzen auf ihre eigene lokale Umgebung zurückgreifen und diese zu weiteren Ausbreitung verwenden, allerdings bleibt die wirkliche Umwelt von dessen Ausbreitung völlig ungenutzt. Um dieses Verhältnis zwischen Pflanze und Umwelt simulieren zu können, wurden von [MPJ95] die so genannten umgebungssensitiven bzw. umweltsensitiven Systeme eingeführt. Bei diesen handelt es sich um eine Erweiterung der parametrischen Systeme, wobei ausschließlich eine einseitige Kommunikation stattfindet. D.h. das System kann lediglich Information von der Umgebung anfordern, aber verändert diese nicht. In den später erforschten offenen Systemen [MP96] findet eine beidseitige Kommunikation statt, damit verändert das L-System auch die Umgebung und beeinflusst über diese andere Systeme. In einem umgebungssensitiven L-System wird die Umwelt in zwei Eigenschaftsklassen gegliedert, die globalen und lokalen Eigenschaften. Die globalen Eigenschaften definieren globale, nicht an einen kleinen Ort festgelegte Eigenschaften. Dazu zählen zum Beispiel die Länge der Tageszeit oder die minimalen und maximalen Temperaturen während des Tages. Die lokalen Eigenschaften der Umgebung stellen dagegen meist physische und in der näheren Umgebung der Pflanze existierende Eigenschaften dar. Zu diesen zählen z.B. feste Objekte, die das Wachstum der Pflanzen stören können, oder die Dichte und Beschaffenheit des Erdbodens. Umgebungssensitive Systeme bauen auf paramterischen L-Systemen auf, dabei werden neue sogenannte Anfrage-Module1 eingeführt. Der Unterschied zu den in Kapitel 3.5 Parametrische Lindenmayer-Systeme beschriebenen parametrischen Modulen besteht darin, dass die Parameter der Anfrage-Module während der Ersetzung noch nicht mit konkreten Werten belegt werden. Sie bleiben also vorerst undefiniert. Nun wird nach jedem Ersetzungsschritt das aktuelle Wort sofort interpretiert, allerdings noch nicht grafisch dargestellt. Durch diese Interpretation werden die Position und Orientierung (Zustand) der Schildkröte wie gewohnt verändert. Sobald ein Anfrage-Modul interpretiert werden soll, wird die Umgebung mit 1 in Englisch query modules. 26 Hilfe des aktuellen Zustands der Schildkröte abgefragt. Diese Anfrage an die Umgebung liefert Werte für die undefinierten Parameter zurück und weist diesen damit konkrete Werte zu, die wiederum in der nächsten Iteration eingesetzt werden können. Wenn das endgültige Wort erreicht wurde, wird erneut das gesamte Wort interpretiert und diesmal auch grafisch dargestellt, woraus dann das Abbild der Pflanze resultiert. Offene L-System können im Vergleich zu umgebungssensitiven L-Systemen nicht nur Anfragen an die Umgebung stellen, sondern aufgrund einer beidseitig Kommunikation auch Änderungen an die Umwelt senden, deshalb erweitern sich die vorherigen Anfrage-Module zu Kommunikations-Modulen2. Im Grunde verändert sich nichts an dem Verfahren des bereits beschriebenen umgebungssensitiven Systems, außer dass die Parameter eines Kommunikations-Moduls nicht zwangsläufig undefiniert bleiben müssen. D.h. ein Kommunikations-Modul kann über definierte Parameter Eingaben an die Umwelt schicken und durch undefinierte Parameter Einflüsse von der Umwelt erhalten. Bei beiden Modulen (Anfrage- und Kommunikations-Modul) wird den Modulen ?E(s0, s1, s2, …, sn) lediglich ein Fragezeichen vorausgesetzt. Damit kann das System die speziellen Module von normalen parametrischen Modulen unterscheiden. An dieser Stelle sparen wir uns eines der sehr komplexen und abstrakten Beispiele und zeigen stattdessen direkt zwei Ergebnisse, siehe Abbildungen 3.6 und 3.7. In der ersten Abbildung wird gezeigt, wie eine rankenartige Pflanze sich der Geometrie eines Objektes anpasst. In der zweiten Abbildung reagieren die beiden Bäume zusätzlich auf globale Eigenschaften. Mit dem Bestreben möglichst viel Licht zu erhalten, interagieren die beiden Bäume miteinander und passen ihr Wachstum dementsprechend an. Abb. 3.6 Umgebungssensitives L-System (Bild: [MPJ95]) Abb. 3.7 Offenes L-System (Bild: [MP96]) 1 in Englisch communication modules. 27 4. Straight-Skeleton Wie sich im späteren Verlauf der Arbeit herausstellen wird, werden die Dächer eines Gebäudes lediglich durch ein Polygon definiert. Dieser Linienzug bildet die Traufe der Dächer und besitzt noch keinerlei innere Struktur, d.h. die Lage und Ausrichtung der einzelnen Dachflächen ist noch nicht festgelegt. Es wird demnach ein Verfahren benötigt, das die Dachausmittlung für eine Traufe berechnen kann, und aus dieser die einzelnen Dachflächen ermittelt. Da die Traufe eines Gebäudes beliebig verlaufen kann, muss das Verfahren die Dachausmittlung für jedes Polygon bestimmten können, egal, ob es sich um ein konvexes oder konkaves Polygon handelt. Das zurzeit bekannteste und robusteste Verfahren zur Ermittlung einer Dachausmittlung wurde 1995 von Aichholzer und anderen vorgestellt. Die Arbeit [Aic+95] beschreibt eine neue Form der Darstellung von inneren Strukturen einfacher Polygone, auch kurz als Straight-Skeleton bezeichnet. Dabei wird die Form des Polygons durch ein topologisches Skelett widergespiegelt und stellt eine Alternative zu dem bekannten Medial-Axis Verfahren, im deutschen Mittelachsen oder Medial-Achsen, dar. Im Gegensatz zu den Mittelachsen enthält das Straight-Skeleton als Repräsentationsform ausschließlich geradlinige Segmente und keinerlei Kurven. Kurvenelemente sind durch die spätere Darstellung des Dachs aus Flächen nicht zweckdienlich, weshalb das Mittelachsen-Verfahren ungeeignet für die Dachausmittlung ist. Weiterhin beruht die Berechnung des Straight-Skeletons nicht auf der Basis von Abständen, wie es bei den Mittelachsen der Fall ist. Vielmehr wird bei der Ermittlung des Straight-Skeletons auf eine Art „Schrumpfprozess“ (im engl. shrinking process) zurückgegriffen. Dabei bedienen sich die Autoren der Idee, dass das Polygon gleichmäßig in sich zusammenschrumpft. D.h. dass alle Punkte des Polygons gleichmäßig in Richtung ihrer Winkelhalbierenden laufen und aus diesem Grund schließlich zusammenfallen. Oder anders gesagt, alle Kanten des Polygons laufen mit konstanter Geschwindigkeit ins innere des Polygons. Dabei verlaufen die Kanten immer parallel zur ihrer Ursprungskante und verändern während der Bewegung nur ihre Länge. Das Polygon schrumpft so lange, bis sein Flächeninhalt eine Größe von Null aufweißt bzw. die Kantenlängen auf Null sinken. Beide Verfahren bilden ein und dasselbe Ergebnis unter der Bedingung, dass das Polygon konvex ist [Aic+95] [Gör04]. Besitzt das Polygon konkave Stellen, so enthalten die Mittelachsen parabolische Elemente und sind aus diesem Grund nicht geeignet für die Dachausmittlung. Die Struktur des Straight-Skeletons ist identisch mit einer von vielen Möglichkeiten der Dachausmittlung. Dabei wird als Polygon einfach der Grundriss des Hauses bzw. die Traufe des Dachs verwendet. Für diese Traufe berechnet das Straight-Skeleton die innere Struktur, woraus anschließend die einzelnen Dachflächen erzeugt werden können. Allerdings besitzt das Straight-Skeleton eine Einschränkung: Die innere Struktur (Synonym für Dachausmittlung) eines Polygons (Synonym für Traufe) wird immer so berechnet, als wenn alle Dachflächen eine Steigung von 45 Grad besitzen würden. Abb. 4.1 (a) Schrumpf-Prozess, (b) Straight-Skeleton und (c) Dach (Bilder (a)(b): [Aic+95]) 28 Die Abbildung 4.1 (a) zeigt mehrere Schritte des Schrumpfprozesses. Dabei ist zu erkennen, wie das Polygon immer kleiner wird, die Kanten aber trotzdem parallel zu ihrer Ausgangskante verlaufen. In Abb. 4.1 (b) wird schließlich das erzeugte Straight-Skeleton von Abb. 4.1 (a) durch die gestrichelten Linien dargestellt. Abbildung 4.1 (c) präsentiert schließlich die aus dem Straight-Skeleton erzeugten Dachflächen, demnach das fertige Dach. Die nachfolgenden Kapitel beschreiben zuerst die speziellen Ereignisse (Kapitel 4.1), die während des Schrumpfens auftreten können. Weiterhin wird eine Definition von Distanz (Kapitel 4.2) innerhalb des Straight-Skeletons definiert, sowie eine Definition für das Straight-Skeleton (Kapitel 4.3) selbst aufgeführt. Schließlich wird ein Algorithmus (Kapitel 4.4) zur Bestimmung des Straight-Skeletons erläutert. 4.1 Ereignisse Wie bereits beschrieben, laufen während des Schrumpfens alle Kanten mit gleicher Geschwindigkeit in das Innere des Polygons. Während dieser Bewegung können zwei verschiedene Ereignisse [Aic+95] auftreten. Nach Eppstein und Erickson [EE98] bei speziellen konkaven Polygonen noch ein drittes Ereignis. Bei jedem Ereignis verbinden sich zwei Kanten oder eine Kante wird in mehrere Teile gespalten. Dies hat zur Folge, dass sich an genau den Stellen, wo die Ereignisse auftreten, die Topologie des Straight-Skeletons verändert. Deshalb schrumpft das Polygon bis zum Auftreten des ersten Ereignisses, verändert dort seine Struktur und schrumpft weiter bis um nächsten Ereignis, usw. (mehr dazu in Kapitel 4.4 Algorithmus). (1) Edge Event Eine Kante schrumpft zu einem Punkt zusammen, besitzt damit die Länge null und verbindet nunmehr ihre Nachbar-Kanten miteinander. (2) Split Event Ein reflex Vertex1 trifft während des Schrumpfprozesses auf eine gegenüberliegende und entgegenlaufende2 Kante und spaltet diese, demnach auch das gesamte Polygon, in zwei Teile. Die beiden adjazenten Kanten des Reflex-Vertex sind nach dem Ereignis adjazent zu den beiden aufgesplitterten und damit neu erzeugten Kanten. Nachdem das Polygon in zwei neue Polygon gespalten wurde, schrumpft jedes der neuen Polygon rekursiv weiter. Da Reflex-Vertices nur in konkaven Polygonen entstehen, können Split Events auch nur in genau diesen auftreten. (3) Vertex Event In speziellen Fällen können zwei oder mehr simultane Ereignisse in genau demselben Punkt auftreten. In den meisten Fällen, und zwar, wenn es sich bei den Ereignissen um Edge Events handelt, stellen diese Ereignisse keine Ausnahme dar. Treffen allerdings zwei oder mehr Reflex-Vertices zeitgleich auf einem Punkt zusammen, so entsteht dadurch ein neues Ereignis: das Vertex Event. Bei einem Vertex Event kann das Polygon in mehr als zwei Teile gespaltet werden. Genauer gesagt, wird das Polygon in so viele Teile gespalten, die der Anzahl simultaner Ereignisse entspricht. [Aic+95][EE98] formulieren, dass jeder neue Knoten immer einen stumpfen Winkel besitzt und deshalb niemals ein neuer Reflex-Vertex entstehen kann. Nach [EE98] stellen die durch ein Vertex Event entstehenden Knoten die einzige Ausnahme dar, siehe Abb. 4.2 (c). Die Autoren benennen Punkte die einen überstumpfen Winkel besitzen als Reflex-Vertex, abgeleitet von dem englischen mathematischen Begriff „reflex Angle“, für einen überstumpfen Winkel. 2 Wie sich im späteren Verlauf herausstellen wird, handelt es sich dabei nicht immer um entgegenlaufende Kanten. Bei entarteten Polygonen existieren Ausnahmen, die der Einfachheit halber erst einmal ignoriert werden. 1 29 Die Abbildung 4.2 (a) zeigt, wie zeitgleich zwei Kanten des Polygons auf eine Länge von Null zusammenschrumpfen und deshalb simultan zwei Edge Events auslösen (gekennzeichnet durch die zwei Kreise). Das durch die Ereignisse entstehende und geschrumpfte Polygon wird durch die roten Linien verdeutlicht. Abbildung 4.2 (b) veranschaulicht, wie ein Reflex-Vertex beim Schrumpfen des Polygons in die gegenüberliegende Kante läuft und diese bzw. damit das gesamte Polygon in zwei Teile spaltet. Dadurch entstehen zwei neue Polygone, die beide getrennt voneinander weiter in sich zusammenschrumpfen. Das konkave Polygon aus Abb. 4.2 (c) löst ein Vertex Event aus, da sich zwei Reflex-Vertices in einem Punkt treffen. Oder anders gesagt, da jeder der beiden Vertices die Kante des anderen spaltet. Abb. 4.2 (a) Zwei zeitgleiche Edge Events, (b) Split Event und (c) Vertex Event (Bilder: [EE98]) 4.2 Distanz In einem Straight-Skeleton wird die Schrumpf-Distanz1 immer senkrecht zu den Kanten des Polygons angegeben. Dies ist darin begründet, dass während des Schrumpfprozesses alle Kanten des Polygons parallel zu ihrer Ausgangskante verlaufen, wie bereits beschrieben und verdeutlicht in Abb. 4.1 (a). Wie sich im späteren Verlauf heraus stellen wird, muss für jedes Ereignis eine Distanz angegeben werden. Diese beschreibt den Zeitpunkt, nach dem das Ereignis ausgelöst wird. Benötigt wird die Distanz, um die ver- Abb. 4.3 Distanz bei Edge Event schiedenen Ereignisse miteinander zu vergleichen und dadurch eine Reihenfolge der Ereignisse aufzustellen zu können. Würde zum Beispiel das in Abb. 4.3 dargestellte Edge Event auftreten, so wäre die Distanz, die das Polygon bis zum Eintritt des Ereignisses schrumpft, nicht gleich der Länge zwischen dem Ausgangspunkt und dem geschrumpften Punkt, sondern der senkrechte Abstand des geschrumpften Punktes bis zu einer der beiden Nachbar-Kanten des Ausgangspunktes. Bei einem Split Event (s. Abb. 4.4) entspräche die Distanz ebenfalls nicht der Länge zwischen dem Ausgangspunkt und dem geschrumpften Punkt. Die Distanz wird auch hier durch den senkrechten Abstand des geschrumpften Punktes bis zu einer der beiden Nachbar- Abb. 4.4 Distanz bei Split Event Linien des Ausgangspunktes ermittelt. Wichtig ist, dass der Abstand zur Linie der jeweiligen Kante (grau gestrichelte Linie in Abb. 4.4) berechnet wird und nicht der Abstand bis zur Kante (Strecke) selbst. 1 Im engl. shrink distance. 30 4.3 Definition „The straight skeleton, S(P), is defined as the union of the pieces of angular bisectors traced out by polygon vertices during the shrinking process. S(P) is a unique structure defining a polygonal partition of P. Each edge e of P sweeps out a certain area which we call the face of e. Bisector pieces are called arcs, and their endpoints which are not vertices of P are called nodes, of S(P).“ [Aic+95] Übersetzt man die Definition des Straight-Skeletons nach [Aic+95], ist das Straight-Skeleton die Vereinigung aller Winkelhalbierenden, die während des Schrumpfprozesses an den Punkten des Polygons entstehen. Weiterhin beinhaltet die Definition, dass jedes Straight-Skeleton einzigartig ist und für ein Polygon eindeutig bestimmt werden kann. Die Bezeichnung Node1 anstatt Vertices für die neuen Punkte, die an den Enden der kollidierenden Winkelhalbierenden entstehen, kommt daher, dass jeder der erzeugten Punkte auch als Knoten innerhalb eines Graphen angesehen werden kann. Diese Knoten besitzen in den meisten Fällen einen Grad von 3, nur unter speziellen Umständen2 können evtl. Knoten mit einem größeren Grad entstehen. Das StraightSkeleton bildet von diesem Gesichtspunkt aus einen Graph, dessen Knoten die innere Struktur des Polygons unterteilen und damit wiedergeben. Die Fläche die aus einer Ursprungskante des Polygons gebildet wird, quasi die spätere Dachfläche, wird auch als Face dieser Kante bezeichnet. 4.4 Algorithmus In diesem Kapitel wird ein Algorithmus zur Berechnung des Straight-Skeletons vorgestellt. Der hier beschriebene Algorithmus ist nicht sehr effizient, sowohl [EE98] als auch [Obd] stellen in ihren Arbeiten Methoden zur Optimierung vor. Diese werden hier allerdings nicht weiter berücksichtigt. Wie von [EE98] formuliert, werden alle möglichen Ereignisse berechnet und anhand ihrer Schrumpf-Distanz, also dem „Zeitpunkt“, nach dem sie ausgelöst werden, in eine Liste sortiert. Wenn alle Ereignisse berechnet wurden, wird das erste Ereignis aus der Liste entnommen und das Polygon bis zum Auftreten dieses Ereignisses geschrumpft. An dieser Stelle verändert das Polygon seine Struktur, indem z.B. zwei Kanten durch ein Edge Event in einem Punkt zusammenfallen. Diese Veränderung kann wieder neue Ereignisse zufolge haben und erfordert eine erneute Berechnung der Ereignisse. Ist dies geschehen, wird das Polygon erneut bis zum Auftreten des ersten Ereignisses geschrumpft. Bei jeder neuen Iteration wird das Polygon kleiner, bis es irgendwann einen Flächeninhalt von Null besitzt und der Algorithmus damit terminiert. Die genauen Schritte sehen folgendermaßen aus: 1. 2. 3. 4. 5. 1 2 Berechne die Winkelhalbierenden für alle Punkte des Polygons. Um die Ausrichtung der Winkelhalbierenden eindeutig bestimmen zu können, müssen die Punkte des Polygons gegen den Uhrzeigersinn angegeben werden. Berechne alle Edge, Split und Vertex Events, Genaures dazu in dem späteren Kapiteln 4.5. Füge das jeweilige Ereignis sortiert (anhand der Schrumpf-Distanz) in eine Ereignis-Liste ein. Dabei gilt, je kleiner die Distanz, desto höher die Priorität des Ereignisses. Entnehme das Ereignis bzw. die Ereignisse mit der höchsten Priorität aus der Liste und schrumpfe das Polygon entsprechend der/des Ereignisse/s, genaures in Kapitel 4.5. Berechne die Winkelhalbierenden für alle durch ein Split/Vertex Event neu entstandenen oder durch ein Edge Event zusammengefassten Punkte. Da der Schrumpfprozess keinerlei Einfluss auf die Winkelhalbierenden der anderen Punkte ausübt, müssen diese nicht noch einmal berechnet werden. Für das geschrumpfte Polygon und jedes eventuell durch ein Split oder Vertex Event neu entstandene Polygon führe erneut Schritt 2 aus. Es sei denn, der Flächeninhalt des Polygons ist Null. Zu Deutsch „Knoten“. Dies ist der Fall, wenn zeitgleich mehrere Ereignisse in einem Punkt auftreten, zum Beispiel bei einem Vertex Event. 31 Jedes Mal, wenn ein Polygon schrumpft, wird das unveränderte Polygon (Polygon vor dem Schrumpfen) beibehalten und nicht durch das geschrumpfte Polygon überschrieben. Jeder Punkt innerhalb des Ursprungspolygons erhält eine Verbindung (Zeiger) auf seinen geschrumpften Punkt in dem neu erzeugten und verkleinerten Polygon. Damit bilden das ursprüngliche Polygon und alle in den verschiedenen Iterationen geschrumpfte Polygone einen Graphen, das Straight-Skeleton. 4.5 Berechnung der Ereignisse 4.5.1 Edge Events Wie bereits beschrieben, tritt ein Edge Event ein, wenn die Kante zwischen zwei Punkten auf eine Länge von Null schrumpft und damit die beiden Nachbar-Punkte zu einem Punkt zusammenfallen. Die Berechnung aller Edge Events für ein Polygon erweist sich als relativ einfach. Hierzu muss lediglich für jeden Punkt des Polygons die Schnittpunkte zwischen der Winkelhalbierenden des Punktes und den Winkelhalbierenden der Nachbar-Punkte ermittelt werden. Konnte ein Schnittpunkt berechnet werden, wird die Schrumpf-Distanz (siehe Kapitel 4.2 Distanz) berechnet und der Schnittpunkt als Edge Event in die Ereignis-Liste sortiert. Falls zwei Schnittpunkte berechnet werden konnten, wird der Schnittpunkt mit der kürzeren Distanz verwendet. Konnte kein Schnittpunkt berechnet werden, löst der aktuelle Punkt kein Edge Event aus. Sollte das Edge Event die höchste Priorität besitzen, wird das Polygon bis zum Auftreten des Edge Events geschrumpft. Dazu werden alle Punkte entlang ihrer Winkelhalbierenden und entsprechend der Schrumpf-Distanz des Ereignisses verschoben. Dadurch fallen mindestens zwei Punkt zu einem Punkt zusammen, die nun durch einen gemeinsamen Knoten ersetzt werden. Abbildung 4.5 zeigt die drei möglichen Fälle bei der Berechnung eines Edge Events. In allen drei Zeichnungen wird der aktuelle Knoten, für den das Edge Event berechnet werden soll, durch einen roten Punkt gekennzeichnet. In Abb. 4.5 (a) entsteht nur ein Schnittpunkt zwischen den Winkelhalbierenden, dieser bildet damit das Edge Event für den roten Knoten. In Abb. 4.5 (b) entstehen zwei Schnittpunkte, da der Schnittpunkt mit dem rechten Nachbar deutlich früher auftritt, wird dieser verwendet, um als Edge Event in die Ereignis-Liste aufgenommen zu werden. Abb. 4.5 (c) zeigt schließlich ein Beispiel, in dem kein Schnittpunkt für den aktuellen Knoten berechnet werden kann und damit auch kein Edge Event für diesen stattfindet. Abb. 4.5 (a) Edge Event mit einem Schnittpunkt, (b) Edge Event mit zwei Schnittpunkten und (c) kein Edge Event 32 4.5.2 Split Events Zur Erinnerung, Split Events treten auf, wenn ein Reflex-Vertex während des Schrumpfprozesses in eine andere Kante des Polygons läuft und diese damit in zwei Teile spaltet, daher der Name „split“, zu Deutsch „gespalten“ oder „zweiteilig“. Berechnet werden die Split Events, indem jeder ReflexVertex des Polygons gegen alle Kanten des Polygons getestet wird. Ausgeschlossen davon sind die zwei Nachbar-Kanten des aktuellen Reflex-Vertex. Um schließlich den Auftrittspunkt bzw. Schnittpunkt eines möglichen Splits für ein Reflex-Vertex zu berechnen, werden in der Literatur zwei verschiedene Varianten erklärt. Bei der erste Variante [Obd] wird das Split Event im R2 berechnet, die zweite Variante [EE98] ermittelt das Split Event im R3. (1) Die Berechnung des Split Events beruht auf der Tatsache, dass der senkrechte Abstand des Schnittpunkts P zur Linie (nicht Strecke) der Kante E der gleiche ist wie zu den beiden Linien der Nachbar-Kanten des Reflex-Vertex V, siehe Kapitel 4.2 Distanz und Abb. 4.6 (a) und (c). Zuerst wird der Schnittpunkt R berechnet, dieser ist gegeben durch den Schnitt der Linie von E und einer der Nachbar-Kanten von V, die nicht parallel zur Kante E verlaufen darf. Wichtig, es wird der Schnittpunkt der beiden Linien und nicht der beiden Strecken berechnet. Wenn R ermittelt wurde, kann die Winkelhalbierende zu R berechnet werden. Diese wird durch die Kante E und der verwendeten Hilfskante von V ermittelt. Der endgültige Schnittpunkt P ergibt sich schließlich durch den Schnitt der Winkelhalbierenden von V und R. Die Abbildungen 4.7 (a) und (c) zeigen die graphische Ermittlung des Punktes P. Nachdem der Punkt P bestimmt wurde, muss geprüft werden, ob dieser innerhalb der möglichen Fläche (Face) liegt, die E während des Schrumpfens annehmen kann. Die Fläche ist durch E und die beiden Winkelhalbierenden B0 und B1 gegeben und bildet meist ein Dreieck, siehe Abb. 4.6 (b). Für [Obd] gelten Reflex-Vertices, die hinter der zu prüfenden Kante E liegen und damit nicht entgegenlaufen, als keine möglichen Kandidaten für ein Split Event. Allerdings haben [EE98] bewiesen, dass durch spezielle Konstellationen trotzdem Split Events entstehen können, siehe Abb. 4.6 (d). Selbst wenn eine solche Konstellation mit dem Verfahren geprüft werden würde, würde die oben beschriebene Berechnung kein korrektes Ergebnis liefern und einen falschen Punkt P berechnen, siehe erneut Abb. 4.6(d). Aus diesem Grund ist diese Art der Berechnung nicht ausreichend. Abb. 4.6 (a) Berechnung des Split Event Punktes P, (b) Mögliche Fläche der Kante E, (c) Berechnung des Split Event Punktes P, (d) Keine bzw. falsche Berechnung 33 (2) [EE98] verwenden zum Ermitteln des Split Events, die durch die beiden Winkelhalbierenden entstehende Fläche der Kante E. Diese Fläche wird im R3 angegeben, besitzt immer eine Steigung von 45° und wird durch die beiden Winkelhalbierenden B0 und B1 bestimmt. Abbildung 4.7 (a) und (b) zeigen die zwei möglichen Fälle der Fläche. Entweder bildet die Fläche ein Dreieck oder verläuft ins Unendliche, wobei dieser Unterschied für die Berechnung des Ereignisses keine Rolle spielt. Der Split-Punkt/Schnittpunkt P des aktuellen Reflex-Vertex V wird berechnet, indem der Schnitt zwischen der Winkelhalbierenden von V und der Ebene von E ermittelt wird. Wenn der Schnittpunkt berechnet werden konnte, muss festgestellt werden, ob dieser sich innerhalb der möglichen Fläche von E befindet. Liegt der Punkt außerhalb der Fläche, so kommt der Punkt als mögliches Split Event nicht in Frage. Das Verfahren zu Berechnung des Split Events funktioniert in allen Fällen, auch bei nicht entgegenlaufender Kante, wie das Beispiel aus Abb. 4.7 (b) verdeutlicht. Die Abbildung 4.7 (a) zeigt die dreidimensionale Darstellung eines Polygons, das ungefähr dem Polygon aus Abb. 4.2 (b) gleich kommt. Abb. 4.7 (b) zeigt eine dreidimensionale Variante ähnlich des Polygons aus Abb. 4.2 (c). Es ist zu erkennen, dass die Winkelhalbierende von V die Fläche der Kante E genau in der Winkelhalbierende B1 schneidet und dort ein Split Event auslöst. Abb. 4.7 Berechnung des Split Events im R3 Nachdem der Schnittpunkt berechnet wurde, muss ermittelt werden, wann das Split Event auftritt. Dazu wird die Schrumpf-Distanz des Schnittpunktes berechnet (siehe Kapitel 4.2 Distanz). Wenn alle Kanten gegen den aktuellen Reflex-Vertex getestet wurden, wird das Split Event mit der geringsten SchrumpfDistanz unter allen ermittelt und in die Ereignis-Liste sortiert eingefügt. Sollte ein Split Event die höchste Priorität innerhalb der Ereignis-Liste besitzen, so wird das Polygon in zwei Teile gespalten. Dabei wird der Reflex-Vertex, der das Ereignis auslöst, jeweils in beide Polygone eingefügt, siehe Abb. 4.2 (b). 4.5.3 Vertex Events Wie in Kapitel 4.1 Ereignisse bereits erläutert wurde, sind Vertex Events zwei oder mehr simultane Split Events, die zur gleichen Zeit im gleichen Punkt auftreten. D.h. konkret, wenn zwei Split Events im selben Punkt und bei gleicher Distanz ausgelöst werden, so werden diese Split Events zu einem Vertex Event zusammengefasst. Jedes neue Split Event, das ebenfalls in diesem Punkt zum gleichen Zeitpunkt (Distanz) auftritt, wird zusätzlich dem Vertex Event beigefügt. Die eigentliche Berechnung des Vertex Events beinhaltet also lediglich den Vergleich aller Split Events, und zwar anhand deren Auftrittspunkte und Schrumpf-Distanzen. 34 4.6 Gewichtung Wie auf letzten Seiten deutlich geworden ist, laufen beim Schrumpfen alle Kanten mit gleicher und konstanter Geschwindigkeit in das Innere des Polygons. Wurde das Straight-Skeleton auf die Weise ermittelt, werden aus dem entstandenen Skelett die Dachflächen erzeugt, die alle eine Steigung von 45° besitzen. Wie im Kapitel 2.2.2 Dachformen dargelegt wurde, existieren Dächer, deren Flächen an den Giebelseiten steil nach oben verlaufen und demnach über eine Steigung von 90° an den Giebelseiten verfügen. Solche Gegebenheiten können durch eine individuelle Gewichtung der Kanten erreicht werden. Erhält eine Kante des Polygons eine Gewichtung, so schrumpft die Kante entsprechend der Gewichtung langsamer als die Kanten ohne Gewichtung1. Dabei darf die Gewichtung nur zwischen 0 und 1 liegen, einschließlich 0 und 1. Zum Beispiel schrumpft eine Kante mit einer Gewichtung von 0.5 nur halb so schnell wie alle restlichen Kanten, falls diese über eine Gewichtung von 1 verfügen. Berücksichtigt werden muss die Gewichtung zum einen bei der Schrumpf-Distanz und zum anderen bei der Ermittlung der Winkelhalbierenden. Abbildung 4.8 (a) präsentiert ein einfaches Polygon mit einer Standardgewichtung für alle Kanten. Wird für die beiden kürzeren Seiten eine Gewichtung von 0.5 festgelegt, schrumpfen diese nur mit der halben Geschwindigkeit und es entsteht das Straight-Skeleton aus Abb. 4.8 (b). Bei einer Gewichtung von 0 für die linke und rechte Kante entsteht das Skelett aus Abb. 4.8 (c). Bei diesem verlaufen die Flächen der linken und rechten Kante steil nach oben, besitzen demnach eine Steigung von 90°. Abbildung 4.8 (a) entspricht der Dachausmittlung eines Walmdachs, während Abb. 4.8 (c) der Dachausmittlung eines Satteldachs mit Giebelseiten gleichkommt. Abb. 4.8 Gewichtung 1 Keine Gewichtung entspricht einer Gewichtung von 1. 35 36 5. Konzept In den vorherigen Kapiteln wurden einige Grundlagen erläutert, darunter waren die LindenmayerSysteme, das Straight-Skeleton, architektonische Grundlagen sowie einige Arbeiten, die bereits Ergebnisse auf dem Gebiet der prozeduralen Gebäudegenerierung erzielt haben. Im Laufe dieses Kapitels wird beschrieben, wie all diese Verfahren eingesetzt werden, um ein System zu schaffen, das Gebäude prozedural generiert. Die Grundlage des entwickelten Systems bildet ein regelbasierendes Ersetzungssystem, das stark auf den in Kapitel 3 beschriebenen Lindenmayer-Systemen beruht. Über das Ersetzungssystem, das im weiteren Verlauf der Arbeit meist nur als Grammatik bezeichnet wird, können Regeln aufgestellt werden, aus denen verschiedene Merkmale von Gebäuden erzeugt werden. Dazu werden einige Änderungen an den erklärten Lindenmayer-System vorgenommen, unter anderem z.B. an der Definition, an der Syntax, bei der Angabe von Wahrscheinlichkeiten und Bedingungen und vieles mehr. Diese Änderungen und Ausbauten ermöglichen die Aufstellung von übersichtlicheren Grammatiken und eine effizientere und gezielte Steuerung der Transformationen (Zustand der Schildkröte). Wie bei der von [MWH+06] entwickelten Shape-Grammar wird zuerst durch Kombination verschiedener Formen ein Masse-Modell [MWH+06] gebaut. Der Begriff Masse-Modell wird übernommen und bezeichnet das Grundmodell bzw. den Grundriss des Bauwerks. Im Unterschied zu [MWH+06] wird kein Straßennetz generiert, und als Initialgrundriss steht deshalb keine beliebige Form zur Verfügung, auf der Extrusionen angewendet werden könnten. Das Masse-Modell wird ausschließlich aus speziellen Körpern, im Fortlaufenden als Primitive bezeichnet, zusammengebaut. Um diesen Vorgang zu erleichtern, wurde für das System eine gezielte Steuerung von Transformationen entwickelt. Im Vergleich zu [MWH+06] wird das Gebäude nicht aus einer einziger Grammatik gefertigt, sondern auf verschiedene Grammatiken aufgeteilt. D.h. für die unterschiedlichen Teile des Gebäudes existieren verschiedene Grammatiken und Regelbasen. Eine Startgrammatik erstellt das oben beschriebene Masse-Modell. Den einzelnen Teilen des Masse-Modells können dabei andere Grammatiken zugeordnet werden. Diese werden z.B. an den Fassaden oder den Kanten der Gebäudeteile ausgeführt und erzeugen dort Fenster, Türen, Dachgauben, Regenrinnen oder sonstige Objekte. Zusätzlich wird ein sehr abstrakte Grammatik eingeführt, die es dem Nutzer ermöglicht, Polygone zu erstellen und anhand dieser die Geometrie des Masse-Modells zu verändern, um z.B. Säulen, Querbalken, Mansarddächer und vieles mehr zu generieren. Neben den Primitiven können vorgefertigte Modell geladen werden, ähnlich wie bei [MWH+06]. Auch hier wurde eine Erweiterung eingeführt, dabei handelt es sich um die sogenannten Gruppen. Gruppen verwalten Modelle oder Texturen, die logisch zu einer Kategorie gehören (z.B. verschiedene Arten von Türen). Die Grammatiken können über eine Gruppe Objekte aus dieser anfordern und beispielsweise auf den Wänden, Ecken oder Dachflächen platzieren. Wie bei einer Split-Grammar [WWS+03] kann das System die einzelnen Grundrisse, Fassaden und Kanten in verschiedene Bereiche aufspalten. Allerdings existiert für diesen Vorgang keine spezielle Split-Grammar, sondern Funktionen, die in die bereits vorhandenen Grammatiken integriert wurden. D.h. Grammatiken, die bereits das Masse-Modell oder die Fassaden generieren, können zusätzlich Splitbzw. Subdivision-Methoden ausführen und dadurch den Gebäudebereich bzw. die Fassade unterteilen. Eine Neuerung ermöglicht hier eine einfache Formulierung von Symmetrien bzgl. der durch die Aufteilung erzeugten Partitionen. Eine weitere Entwicklung ermöglicht erstmals die Beeinflussung von Gebäuden durch ihre Umwelt. So können z.B. die Größe eines Gebäudes, die Anzahl der Fenster, die Art der Fenster und sonstige Eigenschaften durch die lokale und globale Umgebung beeinflusst werden. Zu diesem Zweck wurden verschiedene Verfahren ermittelt, die die Umgebung möglichst schnell und effizient analysieren können. Über die Fertigung von Regeln, das Anlegen von Gruppen und durch den Einsatz bereits vorgegebener Funktionen lassen sich schließlich verschiedene Gebäudetypen definieren. Ein Gebäudetyp könnte zum 37 Beispiel Wohnhäuser aus dem 17. Jahrhundert beschreiben. Anhand dieser Definition können dann beliebig viele Gebäude prozedural generiert werden, die je nach Regeln und Gruppen mehr oder weniger unterschiedlich ausfallen. Das folgende erste Unterkapitel 5.1 Regelbasierte System wird hauptsächlich das eigens eingeführte regelbasierende Ersetzungssystem erklären. Darunter zählen unter anderem die Definition des Systems, die Erläuterung der Syntax und die Anwendung von Transformationen. Nach diesem sehr theoretischen Kapitel werden im Kapitel 5.2 Grammatiken verschiedene Grammatik-Arten beschrieben. Diese werden für die unterschiedlichen Bereiche des Gebäudes (Masse-Modell, Fassaden, Kanten) eingesetzt und anhand eines durchgängigen Beispiels erläutert. Das Kapitel 5.3 Primitive beschäftigt sich mit der genauen Definition der Primitive. D.h. welche Primitive stellt das System zum Bauen von Wänden und Dächern zu Verfügung, welche Bereiche benutzen die verschiedenen Primitive, um Objekte zu erzeugen, wie kann das Straight-Skeleton eingesetzt werden, um Dächer zu fertigen, usw. Die genaue Funktionsweise von Gruppen sowie die Methoden zum Anfordern von Objekten aus der Gruppe werden im Kapitel 5.4 Gruppen erklärt. Darauf folgend im Kapitel 5.5 Subdivison wird die Implementierung der verschiedenen Split-Verfahren erläutert und schließlich im Kapitel 5.6 Umwelt die Realisierung und Analyse der Umwelt. 38 5.1 Regelbasiertes System Wie bereits erwähnt wurde, bildet die Basis ein Ersetzungssystem, das auf den in Kapitel 3 LindenmayerSysteme beschriebenen L-Systemen aufbaut. Genauer handelt es sich um ein verändertes parametrisches, stochastisches, umgebungssensitives und kontextfreies System. Es wurde keine kontextsensitive Grammatik implementiert, da für diese keine Anwendungsgebiet ersichtlich war und zudem schwer verständlich und fehleranfällig sind. Selbstverständlich arbeitet das System stochastisch, d.h. den verschiedenen Regeln können Wahrscheinlichkeiten zugewiesen werden, damit auf einfache Weise eine sehr große Vielfalt erzeugt werden kann. Die Grammatik verwendet die Ansätze der parametrischen L-Systeme, damit noch mehr Variationen erzeugt werden können und eine einfache parametrische Kontrolle (s. Kapitel 1.2 Historischer Rückblick unter Vorteile eines prozeduralen Ansatzes) möglich ist. Außerdem ist in vielen Fällen der Einsatz von Bedingungen unablässig. Weiterhin sind die entwickelten Grammatiken umgebungssensitiv. Hier wurde allerdings lediglich die Grundidee, das Objekte Einfluss durch ihre Umwelt erhalten, übernommen. Die Implementierung basiert auf komplett eigenständigen Forschungen. Aufgrund der stochastischen Auswahl der Regeln sowie einiger Zufallsfunktionen innerhalb der Regel ist das System indeterminiert. Die eigentliche Definition der Grammatik wurde ebenfalls verändert und für die Modellierung von Gebäuden optimiert. Die Definition erfolgt durch das 7-Tupel G = {V, K, F, Σ, ω, P, π}, wobei gilt: (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) V ist eine endliche und nichtleere Menge, genannt Variablen K ist eine endliche Menge, genannt Konstanten oder Definitionen F ist eine endliche Menge, genannt Funktionen Σ ist eine endliche Menge formaler Parameter V K = Ø, V F = Ø, V Σ = Ø, K F = Ø, K Σ = Ø sowie F Σ = Ø müssen erfüllt sein. Damit darf kein Symbol doppelt innerhalb der Mengen der Variablen, Funktionen, Kostanten oder formalen Parametern existieren ω ist das nichtleere Ausgangswort, auch Axiom genannt ω V, das Ausgangswort ist genau ein Zeichen aus dem Alphabet der Variablen P ist eine endliche Menge, genannt Produktionen π ist eine endliche Menge, genannt Wahrscheinlichkeiten π : P → [0, ∞) weißt jeder Regel aus P genau eine Wahrscheinlichkeit zwischen 0 und unendlich zu (einschließlich 0) Die schwerwiegendste Veränderung gegenüber der Grammatik eines L-Systems ist die Aufteilung des Alphabets in Variablen und Funktionen. Die Menge der Variablen beinhaltet alle Zeichen des Alphabets, die ausschließlich zur Expansion des Wortes verwendet werden und sonst über keinerlei Funktion verfügen. Genauer heißt dies, dass Variablen als Predecessor einer Ersetzungsregel verwendet werden dürfen, aber nie vom Interpreter interpretiert werden. Funktionen bilden das Gegenteil von Variablen, d.h. Funktionen sind Symbole, die vom Interpreter ausgeführt werden müssen, aber keine Ersetzungsregel einleiten dürfen (dürfen nicht der Predecessor einer Produktion sein). Funktionen dürfen selbstverständlich auf der rechten Seite einer Produktion stehen und damit durch die Ersetzung von Variablen erzeugt werden. Konstanten können als globale, formale Parameter angesehen werden, denen einmalig zu Beginn der Grammatik ein Wert zugewiesen wird. Sie können dann als formaler Parameter innerhalb der Grammatik an allen Stellen verwendet werden, z.B. als Argument einer Funktion oder als Wahrscheinlichkeit einer Regel. Bei der Menge der formalen Parameter handelt es sich dagegen um lokale, formale Parameter. Diese können, genau wie bei einem parametrischen L-System (s. Kapitel 3.5 Parametrische LindenmayerSysteme), nur innerhalb einer bestimmten Produktion eingesetzt werden. 39 Die Definition des Axioms wurde ebenfalls verändert. Während bei einem L-System das Axiom ein beliebiges, nicht leeres Wort aus dem Alphabet sein durfte, besteht das Axiom nun nur noch aus einem einzigen Zeichen aus der Menge der Variablen. Falls kein Axiom definiert wurde, bildet die erste Variable das Axiom. Durch die Aufspaltung des Alphabets muss die Notation der Produktionen ebenfalls neu angegeben werden. Produktionen dürfen als Predecessor nur ein Element aus der Menge der Variablen enthalten, sonst nichts. Die rechte Seite darf beliebig aus Variablen und Funktionen gebildet werden, wobei auch das leere Wort erlaubt ist. D.h. die Produktion darf als Terminalzeichen dienen, in dem der Predecessor durch das leere Wort ersetzt wird. Alle hier beschriebenen und einige noch nicht beschriebenen Änderungen werden auf den nächsten Seiten noch einmal genauer erklärt und anhand von Beispielen verdeutlicht. 5.1.1 Variablen Variablen dienen ausschließlich zur Erzeugung von Regeln und damit zur Expansion des Wortes, d.h. mit der Hilfe von Ersetzungsregeln können Variablen substituiert werden. Dieses Recht bleibt ausschließlich Variablen vorbehalten, und weder Konstanten noch Funktionen dürfen durch eine Regel ersetzt werden. Weiterhin führt der Interpreter keine Aktion aus, falls er eine Variable liest. Der Einsatz von Variablen beschränkt sich damit ausschließlich auf die Erzeugung von Verzweigungen, Kapseln von anderen Variablen und Funktionen, Bildung von Schleifen, etc. Variablen können, wie in einem paramterischen L-System (siehe Kapitel 3.5 Parametrische LindenmayerSysteme), zu Modulen erweitert werden. Dazu kann jeder Variablen zusätzlich eine Liste formaler Parameter angehängt werden. Die Schreibweise wurde vollständig übernommen und hat demnach die Form v(s0, s1, s2, …, sn), wobei v V und s0 - sn Σ gilt. Innerhalb einer Regel (auf der rechten Seite einer Produktion) darf der Parameter eines Moduls nicht ausschließlich durch reelle und formale Parameter belegt werden, sondern durch beliebige Kombinationen von Konstanten, Funktionen, Zeichenketten1 oder reellen und formalen Parametern2, die jeweils durch einen arithmetischen oder logischen Ausdruck getrennt werden. Das Symbol bzw. der Name einer Variablen darf ein beliebiges und zusammenhängendes Wort aus Buchstaben und Ziffern sein. Allerdings muss der Name eindeutig sein, d.h. das Wort darf nicht bereits als Name einer anderen Variable, Konstante, Funktion oder eines anderen formalen Parameters existieren, siehe Kapitel 5.1 Regelbasierte System unter (5). 5.1.2 Funktionen Im Gegensatz zu Variablen dürfen Funktionen keine Regeln einleiten, d.h. sie sollen das Wort nicht erzeugen, sondern sind lediglich Bestandteil des erzeugten Wortes. Dafür besitzen alle Funktionen eine Aktion, die vom Interpreter ausgeführt wird, falls dieser das Symbol der Funktion liest. Solche Aktionen können zum Beispiel Transformationen auslösen (Zustand der Schildkröte ändern), Wände und Dächer erzeugen, trigonometrische Funktionen ausführen, Eigenschaften des Systems ändern, Zufallswerte generieren, usw. 1 2 in der Datenverarbeitung auch „Strings“ genannt, zum Beispiel die Zeichenkette "Test". reelle Parameter sind feste reelle Dezimalwerte, zum Beispiel die reelle Zahl 3.14159. 40 Funktionen weisen große Ähnlichkeiten mit den Modulen eines parametrischen L-Systems auf. Wie ein Modul besteht eine Funktion aus einem Namen mit angehängten Parametern, auch Argumente genannt. Die Notation wurde von den Modulen übernommen und hat demnach die Form f(s0, s1, s2, …, sn), wobei gilt; f F und s0-sn sind jeweils eine beliebige Kombination von formalen Parametern, Konstanten, Funktionen, reellen Parametern oder Zeichenketten, jeweils durch einen arithmetischen oder logischen Ausdruck getrennt. Damit sind Variablen die einzigen Symbole, die nicht Teil eines Argumentes sein dürfen. Zusätzlich dürfen Funktionen einen Rückgabewert liefern, wobei als zulässige Rückgabewerte nur reelle Zahlen oder Zeichenketten erlaubt sind. So liefert z.B. die Funktion rand(s0, s1) einen zufälligen reellen Dezimalwert zwischen s0 und s1 zurück. Weiterhin dürfen Funktionen beliebig tief geschachtelt werden und damit andere Funktionen als Argument enthalten. Ein Beispiel für eine verschachtelte Funktion könnte so aussehen: rand(rand(0,1), 2). Eine detaillierte Liste aller bisherigen Funktionen ist im Anhang zu finden. 5.1.3 Konstanten Konstanten bzw. Definitionen sind globale, formale Parameter. Global bedeutet, dass sie innerhalb der gesamten Grammatik eingesetzt werden können und damit eine einfache parametrische Kontrolle ermöglichen. Jeder Konstanten wird, noch bevor die erste Iteration auf das Axiom ausgeführt wird, einmalig ein Wert zugewiesen. Mit Hilfe der Konstante kann schließlich innerhalb der Grammatik jederzeit über diesen Wert verfügt werden. Zum Beispiel könnte eine Konstante das Argument einer Funktion sein oder als Teil einer Bedingung dienen. Wichtig ist, dass der Wert einer Konstante nach der Zuweisung zu keinem Zeitpunkt mehr verändert werden kann. Eine Konstante k K wird folgendermaßen deklariert und definiert: k = σ; Dabei gilt; σ ist eine beliebige Kombination von Funktionen, reellen Parametern, Zeichenketten oder bereits vorher definierter Konstanten, auch hier jeweils durch einen arithmetischen oder logischen Ausdruck getrennt. Die Zuweisung eines Wertes erfolgt ähnlich wie in bekannten Programmiersprachen, dazu stehen der Name der Konstanten links des Gleichheitszeichens und die eigentliche Zuweisung rechts davon. Abgeschlossen wird die Zuweisung mit einem Semikolon. Im Laufe der Zuweisung erkennt jede Konstante automatisch, welchen Typ sie besitzt, also ob sie eine reelle Zahl oder Zeichenkette enthält. Die nachfolgenden Zeilen enthalten einige Beispiele für die Definition von Konstanten. Test = "Test"; a = 0; b = (0 + 1) ∙ 2; c = rand(a, b); Der Konstante Test wird eine Zeichenkette zugewiesen, den Konstanten a und b die reellen Zahlen 0 und 2. Nach dem diese Konstanten berechnet wurden, wird die Konstante c ermittelt. Diese erhält einen zufälligen Wert zwischen a und b. 41 5.1.4 Produktionen Bei einem stochastischen L-System werden alle Ersetzungsregeln getrennt angeben, auch wenn sie einen identischen Predecessor besitzen. D.h. eventuell muss die linke Seite einer Produktion mehrmals definiert werden, obwohl es sich dabei immer um den gleichen Predecessor handelt. Dies kann schnell zu Fehlern führen, in dem beispielsweise eine Änderung am Predecessor vorgenommen wurde, aber vergessen wurde, diese Änderung bei allen zugehörigen Produktionen vorzunehmen. Ebenso entsteht ein erhöhter Zeitaufwand bei der Definition und Änderungen von Produktionen. Und als letzter Kritikpunkt kann die fehlende Übersichtlichkeit aufgeführt werden. Dadurch dass viele verschiedene Regeln für einen Predecessor angegeben werden, verliert der Nutzer schnell die Übersicht über all diese Regeln, deren Wahrscheinlichkeiten, deren Bedingungen, etc. Um dies zu umgehen, werden nun alle Regeln zusammen in einer Produktion angegeben. D.h. für einen Predecessor darf nur noch eine einzige Produktion aufgestellt werden, innerhalb dieser werden alle weiteren Regeln getrennt angeben. Dadurch verringert sich die Fehlerrate bei Änderungen, und die Übersichtlichkeit über die Regeln und deren Wahrscheinlichkeiten erhöht sich. Bisher war der Begriff Regel gleichgesetzt mit dem Begriff der Produktion. Da jetzt alle Regeln (bisher Synonym zu „Produktion“) innerhalb einer einzigen Produktion (bisher Synonym zu „Regel“) angeben werden, müssen die Begriffe neu definiert werden. Eine Produktion beschreibt ab jetzt die komplette linke sowie rechte Seite, während eine Regel nur noch eine der vielen Ersetzungen bezeichnet, die jeweils auf der rechten Seite einer Produktion stehen. Die genaue Form einer Produktion sieht dann folgendermaßen aus: φ→σ|υ|…; Dabei ist φ V der Predecssor der Produktion und σ, υ sind zwei verschiedene Regeln, die jeweils aus einer beliebigen Kombination von Variablen und Funktionen bestehen dürfen. Auch das leere Wort wird als Regel erlaubt, d.h. wurde keine Variable oder Funktion für eine Regel angegeben, entspricht dies dem leeren Wort. Die verschiedenen Regeln werden durch das Symbol | getrennt und alle innerhalb einer Produktion angeben. Abgeschlossen wird eine Produktion durch ein Semikolon. Es folgt ein kleines Beispiel, um die neue Schreibweise zu verdeutlichen. Gegeben ist die Menge der Funktionen F = {T(x0)}, die Variablen V={F} und deren Produktionsmenge P = {F → F T(5) F | ;}. Da für die Produktion zwei Regeln angegeben wurden, einmal F T(5) F und zum anderen das leere Wort, wird eine der beiden Regeln zufällig ausgewählt. Deshalb stellt die nachfolgende Ersetzungssequenz eine von vielen Möglichen dar. Genaueres zu Auswahl der Regeln kann im Kapitel 5.1.5 Wahrscheinlichkeiten nachgelesen werden. 1. Schritt 2. Schritt 3. Schritt … F F T(5) F T(5) F T(5) F Zur Erinnerung, da keine Startvariable angeben wurde, ist das Axiom durch die erste Variable aus der Menge aller Variablen gegeben, in diesem Beispiel die Variable F. Die Ersetzung folgt wie bei normalen L-System „parallel“ und führt zu den aufgelisteten Schritten. Weiterhin wird die Wahrscheinlichkeit einer Regel nicht mehr am Ende der Regel (durch einen Doppelpunkt getrennt) angeben, sondern ebenfalls auf der linken Seite einer Produktion, mehr dazu in Kapitel 5.1.5 Wahrscheinlichkeiten. Dasselbe gilt für die Bedingungen, die ebenfalls übersichtlich auf der linken Seite einer Produktion angeben werden, siehe Kapitel 5.1.6 Bedingungen. 42 5.1.5 Wahrscheinlichkeiten Die Menge der Wahrscheinlichkeiten π besitzt die gleiche Bedeutung wie in einem stochastischen LSystem. Die Wahrscheinlichkeit einer Regel definiert immer noch, mit welcher Wahrscheinlichkeit der Zufallsgenerator die Regel unter allen passenden Regeln auswählt. Anstatt die Wahrscheinlichkeit der jeweiligen Regel anzuhängen, werden nun alle Wahrscheinlichkeiten übersichtlich auf der linken Seite einer Produktion angegeben. Dadurch können die Wahrscheinlichkeiten schnell angepasst werden und außerdem behält der Nutzer den Überblick über das Verhältnis der Wahrscheinlichkeiten zueinander. φ [p0, p1, …, pn] → σ0 | σ1 | … | σn; Wie zu erkennen ist, werden die Wahrscheinlichkeiten aller Regeln innerhalb der eckigen Klammern definiert. Die erste Wahrscheinlichkeit wird dann der ersten Regel zugeordnet, die zweite Wahrscheinlichkeit der zweiten Regel, usw. Weiterhin können die Wahrscheinlichkeiten p0 - pn nicht nur durch reelle Zahlen bestimmt werden, sondern durch Kombinationen von Konstanten, Funktionen, formale und reelle Parameter, auch hier jeweils durch ein arithmetischen oder logischen Ausdruck getrennt. Da auch Funktionen oder formale Parameter die Wahrscheinlichkeit einer Regel bestimmen dürfen und diese sich zur Laufzeit ändern bzw. komplett zufällige Werte erhalten können (wir erinnern uns an die rand-Funktion), folgt, dass die Wahrscheinlichkeiten nicht konstant sind. Dies kann einige Probleme mit sich bringen, die im nächsten Absatz genauer erläutert werden. In einem stochastischen L-System musste immer darauf geachtet werden, dass die Summe aller Wahrscheinlichkeiten 1 ergibt, dies ist jetzt nicht mehr der Fall. Außerdem darf die Wahrscheinlichkeit für eine Ersetzungsregel auch Werte größer 1 und den Wert 0 annehmen. Die Begründung für diese Definition liegt in der Möglichkeit, dass sich Wahrscheinlichkeiten während der Laufzeit ändern können. Dies sind drastische Änderungen, wodurch eine Menge Fragen aufkommen. Wie verhält sich der Zufallsgenerator, wenn die Wahrscheinlichkeiten nicht die Summe von 1 erreichen? Was passiert wenn die Summe größer als 1 wird? ... Es existieren genau vier mögliche Fälle, die der Zufallsgenerator beachten muss. Entweder ergibt die Summe aller Wahrscheinlichkeiten für einen gleichen Predecessor genau eine 0, oder sie ist größer 0 und kleiner 1, oder sie ist genau 1, oder der letzte Fall tritt ein und die Summe ist sogar größer als 1. Eventuell muss das System in mancher dieser Fälle eingreifen und automatisch Anpassungen vornehmen. Wie der jeweilige Fall vom dem System behandelt wird, wird in der folgenden Liste festgelegt. (1) Summe gleich 0 Alle Ersetzungsregeln besitzen jeweils eine Wahrscheinlichkeit von 0, daraus folgt, dass keine Regel vom Zufallsgenerator ausgewählt wird und damit die zu ersetzende Variable durch das leere Wort (also nichts) ersetzt wird. (2) Summe kleiner 1 Eine oder mehrere Regeln besitzen Werte die kleiner 1 sind und auch in der Summe nicht 1 erreichen. In diesem Fall werden alle Werte entsprechend ihrer Verhältnisse zueinander so abgebildet, dass die Summe aller Wahrscheinlichkeiten nach der Abbildung genau 1 ergibt. Genauer gesagt, werden die Wahrscheinlichkeiten einfach mit der reziproken Summe multipliziert. Es sind z.B. drei Regeln mit ihren Wahrscheinlichkeiten p0, p1 und p2 gegeben. p0: 0.5 p1: 0.1 p2: 0.2 43 Werden die drei Wahrscheinlichkeiten addiert, ergibt dies eine Summe von 0.8 und liegt damit unter 1. Die reziproke Summe ist dann 1 / 0.8 = 1.25 und nachdem die Wahrscheinlichkeiten mit dieser multipliziert werden, entstehen die neuen Wahrscheinlichkeiten. (3) Summe gleich 1 (4) Summe größer 1 p0: 0.625 p1: 0.125 p2: 0.250 Nun ergibt die Summe eine 1, und der Zufallsgenerator kann immer eine der Regeln zufällig auswählen. Der Zufallsgenerator kann normal arbeiten und bestimmt zufällig eine der Ersetzungsregeln. Eine Wahrscheinlichkeit oder alle Wahrscheinlichkeiten ergeben in der Summe einen Wert, der größer als 1 ist. In diesem Fall bleiben die Wahrscheinlichkeiten unverändert, d.h. es wird keine Veränderung wie in Fall (2) durchgeführt. Die Regeln werden von vorne beginnend zufällig ausgewählt. Sobald die Summe größer 1 wird, können die dahinter definierten Regeln vom Zufallsgenerator nicht mehr ausgewählt werden. Daraus folgt, dass die Reihenfolge, in der die Regeln angegeben werden, eine große Rolle spielt und zuerst definierte Regeln bevorzugt behandelt werden. Dies hat einen triftigen Grund. Indem die Wahrscheinlichkeiten unverändert bleiben, können bestimmte Regeln vor Anderen erzwungen werden. Dazu ein einfaches Beispiel. Es seien die folgenden drei Wahrscheinlichkeiten für eine jeweilige Regel gegeben. p0: 0.9 p1: 0.2 p2: 0.4 Da die erste Regel mit einer neunzigprozentigen Wahrscheinlichkeit ausgewählt wird, kann die zweite Regel nur noch zu 10% ausgewählt werden, obwohl diese eine Wahrscheinlichkeit von 20% besitzt. Die Wahrscheinlichkeit p2 wird als Letztes angeben und hat momentan keine Chance, vom Zufallsgenerator bestimmt zu werden. Darf p0 nicht mehr ausgeführt werden, z.B. auf Grund einer nicht erfüllten Bedingung, fällt die Wahrscheinlichkeit p0 weg. Dies führt dazu, dass mit den Wahrscheinlichkeiten p1 und p2 wie gehabt verfahren wird. D.h. da deren Summe 0.6 ergibt und damit kleiner 1 ist, würde Fall (2) zur Anwendung kommen. Es bleibt nur noch zu klären, was mit Regeln passiert, für die keine Wahrscheinlichkeit angegeben wurde. Die Wahrscheinlichkeiten bleiben vorerst undefiniert und zwar so lange, bis der Zufallsgenerator eine Regel auswählen möchte. Wenn dies geschieht, werden die Wahrscheinlichkeiten aller Regeln (deren Bedingung erfüllt ist, siehe Kapitel 5.1.6 Bedingungen) addiert. Ist diese Summe größer oder gleich 1, erhalten alle undefinierten Wahrscheinlichkeiten den Wert 0 und können damit nicht ausgewählt werden. Liegt die Summe unter 1, wird der Rest gleichmäßig unter allen undefinierten Wahrscheinlichkeiten verteilt. Angenommen es existieren zu einer Variablen vier Regeln mit folgenden Wahrscheinlichkeiten. 44 p0: 0.5 p1: 0.1 p2: * p3: * Der Stern kennzeichnet die undefinierten Wahrscheinlichkeiten p2 und p3. Die Wahrscheinlichkeiten p0 und p1 ergeben in der Summe 0.6, der Rest von 0.4 bleibt undefiniert. Diese vierzig Prozent werden nun unter p2 und p3 gleichmäßig aufgeteilt, und beide erhalten damit 0.2, also jeweils eine zwanzigprozentige Chance vom Zufallsgenerator ausgewählt zu werden. Durch die automatische Anpassung der Wahrscheinlichkeiten wird gewährleistet, dass der Zufallsgenerator immer eine Regel auswählen kann, dass manche Regeln vor anderen erzwungen werden können und die Wahrscheinlichkeiten ohne große Probleme dynamisch verändert werden dürfen. Damit kann das System wie ein normales stochastisches L-System genutzt werden. Darüber hinaus wird dem Nutzer die Möglichkeit geben, dynamisch die Wahrscheinlichkeiten zu ändern, ohne selbst allzu viel bedenken zu müssen. 5.1.6 Bedingungen Die in Kapitel 3.5 Parametrische Lindenmayer-Systeme eingeführten Bedingungen eines parametrischen L-Systems werden ebenfalls entsprechend ihrer Funktionalität eingeführt. Allerdings verändert sich auch hier die Art und Weise, in der die Bedingungen angegeben werden, dazu erweitert sich eine Produktion wie folgt: φ [p0, p1, …, pn] {c0, c1, …, cn} → σ0 | σ1 | … | σn; φ, σ, υ und die Wahrscheinlichkeiten p0 - pn bleiben unverändert, hinzukommen die auf der linken Seite der Produktion angegebenen Bedingungen c0 bis cn. Diese werden mit Hilfe einer öffnenden und schließenden geschweiften Klammer gekennzeichnet. Genau wie bei den Wahrscheinlichkeiten wird auch hier jeweils die erste Bedingung der ersten Regel zugeordnet, die zweite Bedingung der zweiten Regel, usw. Ebenfalls wie bei den Wahrscheinlichkeiten ist die Definition der Bedingungen c0 – cn. Diese können beliebig aus Funktionen, Konstanten, lokalen Parameter usw. definiert werden. Ist das Ergebnis einer Bedingung 0, gilt diese als nicht erfüllt, und die entsprechende Regel kann nicht ausgewählt werden. Ist die Bedingung ungleich 0, gilt diese als erfüllt, und die Regel wird anhand ihrer Wahrscheinlichkeit vom Zufallsgenerator eventuell ausgewählt. Falls eine Regel keine Bedingung zugeordnet wurde, gilt die Bedingung standardmäßig als erfüllt, und die Regel besitzt immer die Chance, vom Zufallsgenerator ausgewählt zu werden. 5.1.7 Transformation Wie in der Einleitung des Konzepts (s. Kapitel 5 Konzept) erwähnt wurde, werden die Gebäude aus speziellen Primitiven (z.B. Würfel oder Zylinder) und vorgefertigten Modellen zusammengesetzt. D.h. anstatt eine Linie zu zeichnen, wie es bei den bisherigen L-Systemen (s. Kapitel 3 LindenmayerSysteme) der Fall ist, werden nun vom dem System Körper und Objekte erzeugt. Damit ein Gebäude durch die Kombination von mehreren Primitiven und Modellen erzeugt werden kann, müssen verschiedene Transformationen auf die einzelnen Teile des Gebäudes durchführbar sein. Dies beinhaltet eine Veränderung der Position, Ausrichtung und Größe des jeweiligen Objekts. Der Zustand der „Schildkröte“ (s. Kapitel 3 Lindenmayer-Systeme) ist aus diesen Gründen zu einer Transformation, bestehend aus einer dreidimensionalen Verschiebung, Rotation und Skalierung, erweitert worden. Genauer gesagt, ist der Zustand nicht nur eine Transformation, sondern eine Sequenz von Transformationen. Dabei kann jede Transformation (welche durch eine Matrix repräsentiert werden kann) der Sequenz jeweils Informationen für eine Translation, Rotation und Skalierung erhalten. Die Matrix einer Transformation ist so aufgebaut, dass zuerst die Skalierung ausgeführt wird, gefolgt von 45 der Rotation und zuletzt schließlich die Verschiebung. Daraus ergibt sich folgende Form für die Matrix einer Transformation: M= [ (cos α cos β) sx (cos α sin β sin γ – sin α cos γ) sx (cos α sin β cos γ + sin α sin γ) sx 0 (sin α cos β) sy (sin α sin β sin γ + cos α cos γ) sy (sin α sin β cos γ - cos α sin γ) sy 0 - sin β sz tx cos β sin γ sz ty cos β cos γ sz tz 0 1 ] Dabei beschreiben (sx, sy, sz) die Skalierung, (tx, ty, tz) die Verschiebung entlang der x-, y- und z-Achse und die Winkel (α, β, γ) die Rotation. Der Zustand bzw. die aktuelle Transformations-Matrix kann innerhalb der Grammatik über bereits vordefinierte Funktionen verändert werden. Einige der möglichen Funktionen werden hier aufgelistet, die restlichen sind im Anhang zu finden. T(tx, ty, tz) R(α, β, γ) S(sx, sy, sz) TR(rx, ry, rz) Führe eine zusätzliche Verschiebung aus Führe eine zusätzliche Rotation aus, dabei werden alle Winkel in Radien angegeben. Ersetze die aktuelle Skalierung Führe eine zusätzliche Verschiebung bzgl. der aktuellen Ausrichtung aus Wie bereits beschrieben wurde, entspricht der Zustand einer Sequenz von Transformationen bzw. deren Matrizen. Jede Funktion verändert immer nur die aktuelle Matrix. Eine neue Matrix kann durch die öffnende geschweifte Klammer eingeleitet werden. Mit der schließenden geschweiften Klammer wird die aktuelle Matrix gelöscht, und die vorherige Matrix wieder zur aktuellen Matrix, ähnlich wie bei einem Push- und Pop-Befehl. Eine Sequenz kann zum Beispiel dazu dienen, eine Rotation nach der Translation auszuführen. Dazu wird die Rotation in eine neue Matrix verschoben und damit erst später ausgeführt. Das folgende Beispiel verdeutlicht noch einmal die Funktionsweise der Transformations-Sequenz. Main → T(10, 0, 0) S(3, 1, 2) R(0, 3.14/2, 0) Box(); Wie beschrieben, spielt es keine Rolle, in welcher Reihenfolge die Funktionen ausgeführt werden, da alle Funktionen immer die aktuelle Transformation verändern und für diese eine feste Reihenfolge definiert wurde. Die Reihenfolge lautet, zuerst die Skalierung, dann die Rotation und zum Schluss schließlich die Verschiebung. Durch die Produktion entsteht der in Abb. 5.1.1 dargestellte Würfel. Dabei werden die verschiedenen Teile der Transformation von links nach rechts innerhalb der Abbildung aufgelistet. Abb. 5.1.1 Transformation eines Würfels 46 Wird durch eine geschweifte Klammer eine neue Transformation eingeleitet, so werden alle darauf folgenden Funktionen auf die neue Transformation ausgeführt. Main → T(10, 0, 0) S(3, 1, 2) { R(0, 3.14/2, 0) Box() }; Die Verschiebung und Skalierung werden wie im ersten Beispiel bestimmt, d.h. sie werden auf die erste Transformation innerhalb der Sequenz ausgeführt. Danach wird eine neue Transformation eingeleitet. Alle Funktionen, die nun folgen, werden auf eine neue Transformation (Matrix) ausgeführt, in diesem Fall eine Rotation um 90°. Daraus folgt, dass der Würfel zuerst durch die erste Transformation der Sequenz verändert wird, dargestellt durch die ersten drei Zeichnungen der Abbildung 5.1.2. Nachdem diese beendet ist, folgen alle weiteren Transformationen der Reihe nach, hier nur noch eine einzige weitere. Diese führt eine Rotation aus, diesmal allerdings nach der Verschiebung, siehe viertes Bild der Abb. 5.1.2. Abb. 5.1.2 Transformation eines Würfels Die Metapher der Schildkröte bzw. Turtle (s. Kapitel 3 Lindenmayer-Systeme) ist für die neue Art der Transformation nicht mehr passend. Dies liegt z.B. daran, dass Objekte in eine Richtung bewegt werden können, die nicht der aktuellen Ausrichtung entspricht. Deshalb wird hier eine neue und eher Gebäuden entsprechende Metapher eingeführt. Dabei kann man sich den aktuellen Zustand wie einen Kran vorstellen. Dieser kann, da er über einen frei schwenkbares Oberteil verfügt, in eine Richtung fahren, während er in eine beliebige andere Richtung ausgerichtet ist. Wurde der Bestimmungsort erreicht, platziert der Kran ein Stück des Gebäudes, das beliebig groß skaliert sein kann. Die Steuerung über die Transformations-Sequenz ermöglicht eine viel gezieltere und vielseitigere Veränderung des Zustands. Auf der anderen Seite sollte die Sequenz nicht zu groß ausfallen, weil ansonsten die Übersicht verloren geht und die Grammatik schwer nachvollziehbar wird. 5.1.8 Verzweigungen Wie in einem verzweigten L-System (siehe Kapitel 3.3 Verzweigende Lindenmayer-Systeme) verfügt das neue System auch über einen Stack. Mit dessen Hilfe kann der aktuelle Zustand gespeichert und zu einem späteren Zeitpunkt wieder geladen werden. Dafür werden ebenfalls die öffnende und schließende eckige Klammer verwendet. Falls ein Push-Befehl ausgeführt werden soll, müssen alle Transformationen der Sequenz auf dem Stack gespeichert werden. Und bei einem Pop-Befehl wird selbstverständlich wieder die gesamte Sequenz geladen und vom Stack gelöscht. Zusätzlich wird bei jeder Substitution einer Variablen durch eine Ersetzungsregel, automatisch ein Push-Befehl zu Beginn der Ersetzung und ein Pop-Befehl am Ende der Ersetzung ausgeführt. Über die Funktion AutoPP (siehe Anhang) kann dieser Vorgang aktiviert oder deaktiviert werden. D.h. sobald die Funktion ausgeführt wird, aktiviert oder deaktiviert das System sofort das automatische Push und Pop bei einer Ersetzung. Der neue Zustand gilt so lange, bis die Funktion erneut aufgerufen wird und der Status sich damit ändert. 47 5.1.9 Ausführung Zum Schluss dieses Unterkapitels noch ein paar Worte zur Ausführung der Grammatiken. Wie bei einem Lindenmayer-System werden alle Variablen innerhalb einer Iteration „gleichzeitig“ ersetzt. Bei einem umgebungssensitiven System lief der Interpreter bereits nach jeder Iteration einmal über das aktuelle Wort. Dadurch haben die undefinierten Parameter der Anfrage-Module konkrete Werte erhalten, siehe Kapitel 3.6 Umgebungssensitive und offene Lindenmayer-Systeme. Ein ähnliches Verhältnis liegt ebenfalls bei dem neu eingeführten System vor. Nur hier muss der Interpreter jedes Zeichen sofort interpretieren, d.h. während eines Ersetzungsschrittes, bei dem die Variablen durch eine passende Regel ersetzt werden, müssen alle Funktionen sofort ausgeführt werden. Die Ausführung muss sofort stattfinden, da es Funktionen gibt, die die Einstellungen des Systems verändern (z.B. die AutoPP-Funktion) und damit die fortlaufende Ersetzung beeinflussen können. Außerdem existieren spezielle Funktionen die Schleifen, Bedingungen oder Partitionen einleiten (z.B. die Funktionen SubDiv und Part), die ebenfalls eine sofortige Interpretation der Funktionen voraussetzen. 48 5.2 Grammatiken Wie in der Einleitung des Konzepts (s. Kapitel 5. Konzept) beschrieben wurde, sollen für unterschiedliche Gebäudebereiche verschiedene Grammatiken zum Einsatz kommen. Zum einen kann dadurch die Verständlichkeit der einzelnen Grammatiken gesteigert werden, da das gesamte Gebäude nicht vollständig innerhalb einer einzigen Grammatik formuliert werden muss. Und zum anderen sind die verschiedenen Grammatiken beliebig austauschbar, wodurch wiederum eine große Wiederverwendbarkeit entsteht. Eine einmal aufgestellte Grammatik kann so bei einer Vielzahl von Gebäudetypen zum Einsatz kommen. Insgesamt wurden eigens vier unterschiedliche Grammatiken entwickelt. Die sogenannte Mass-Grammar ist die zuerst ausgeführte Grammatik1. Der Begriff Mass steht dabei für das Masse-Modell [MWH+06] bzw. Grundmodell des Gebäudes und Grammar ist der englische Begriff für Grammatik. Die MassGrammar ist die Startgrammatik, erstellt das grundlegende Modell und ruft dann für die verschiedenen Wände und Kanten des Masse-Modells weitere Grammatiken auf. D.h. zusätzlich zu der bereits erwähnten Mass-Grammar existieren noch drei weitere Grammatiken. Bei diesen handelt es sich um die Facade-Grammar, Border-Grammar und Profile-Grammar. Mass-, Border- und Facade-Grammar besitzen die gleichen Funktionen, arbeiten allerdings alle in einem anderen Raum (Koordinatensystem). Die Profile-Grammar besitzt einen anderen Funktionssatz und arbeitet ebenfalls in einem anderen, sehr abstrakten Raum. (1) Mass-Grammar (2) Facade-Grammar (3) Border-Grammar (4) Profile-Grammar Die Grammatik ist für die Erstellung des Grundmodells (Masse-Modells) zuständig. Durch die Regeln und Funktionen der Grammatik werden zufällige Grundmodelle eines Gebäudetyps erstellt. Dies können beispielsweise die gebäudetypischen I-, L-, T-, H- oder U-Formen sein. Facade ist der englische Begriff für Fassade. Mit Hilfe dieser Grammatik sollen Regeln aufgestellt werden, die eine zufällige Fassade erzeugen. Dazu gehören z.B. Fenster, Türen, Fachwerkmuster, etc. Die Facade-Grammar behandelt nicht ausschließlich die Flächen von Hauswänden, sondern auch die Flächen des Dachs und erzeugt an diesen z.B. Schornsteine oder Dachgauben. Die Border-Grammar oder auch Edge-Grammar genannt wird an allen Kanten des Masse-Modells ausgeführt. Zu diesen gehören die Kanten der Hauswände sowie die des Daches. Dort erzeugt die Grammatik anhand ihrer Regelbasis zum Beispiel eine Regenrinne, die an der Traufe des Dachs entlang läuft. Oder einen Wetterhahn, der meistens zufällig auf dem Dachfirst angebracht wird. Der Begriff Profile bedeutet ins Deutsch übersetzt „Profil“ oder „Seitenansicht“. Die Grammatik generiert Profile von Wand- und Dachflächen und aus diesen die Geometrie des Masse-Modells. Damit können anhand der Regeln zum Beispiel neben den üblichen Walmdächern auch Mansarddächer geformt werden. Auf den nächsten Seiten werden alle aufgelisteten Grammatiken genauer erklärt, außerdem wird anhand eines durchgängigen Beispiels ein sehr einfaches Haus erzeugt. Zum einen wird dadurch der Unterschied zwischen den einzelnen Grammatiken klarer, und zum anderen wird das Verständnis für die Anwendbarkeit der Grammatiken erhöht. 1 auch Startgrammatik oder Initialgrammatik genannt 49 5.2.1 Mass-Grammar Die Mass-Grammar ist für die Erzeugung einer grundlegenden Gebäudestruktur zuständig, u. a. zählen dazu die einzelnen Wände und Dächer des Gebäudes. Diese werden aus sogenannten Primitiven erzeugt, Genaueres zu Primitiven kann im Kapitel 5.3 Primitive nachgelesen werden. Während die Grammatik den Haupttrakt, Nebentrakt, Türme oder sonstige Teile des Gebäudes aus Primitiven zusammenbaut, kann sie den einzelnen Teilen weitere Grammatiken zuteilen. Diese erzeugen dann die Fenster, Türen und andere Details des Gebäudes. Die von der Mass-Grammar anwendbaren Funktionen werden im Anhang aufgelistet. Zusätzlich besitzt jede Grammatik spezielle bereits vordefinierte Konstanten, diese werden ebenfalls im Anhang beschrieben. 5.2.1.1 Raum Zuerst muss geklärt werden, in welchen Raum die Mass-Grammar fungiert. D.h. wo werden bzw. dürfen überhaupt die Primitive innerhalb der Welt platziert werden. Jedes Gebäude besitzt zu Beginn einen rechteckigen Grundriss, quasi das Grundstück des Gebäudes. Dieses Grundstück besitzt eine Länge, eine Breite und ein Koordinatensystem, welches die Position und Ausrichtung des Grundstücks bestimmt. Der Ursprung des Koordinatensystems, welches vergleichbar mit dem Object-Space eines Objektes ist, befindet sich in der Mitte des Grundstücks, siehe Abb. 5.2.1. Die Ausrichtung der Achsen entspricht genau den World-Space Achsen, d.h. die x-Achse verläuft nach rechts, die zAchse nach hinten und die y-Achse steht senkrecht zu beiden und verläuft direkt nach oben, wodurch ein linkshändiges und orthogonales Koordinatensystem entsteht. Alle Transformationen, die innerhalb der Grammatik aufgerufen werden, finden in dem oben beschriebenen Raum statt. Die Länge und Breite des Grundstücks kann über vordefinierte Abb. 5.2.1 Koordinatensystem der Mass-Grammar Konstanten abgefragt werden, s. Anhang. 5.2.1.2 Anwendung Am einfachsten lässt sich die Grammatik anhand einer direkten Anwendung erklären. Die Grammatik aus Abb. 5.2.2 formuliert die Mass-Grammar eines Gebäudetyps. Die Grammatik soll zwei Stockwerke und ein Dach erzeugen. Dazu verwendet die Grammatik zwei Primitive (Würfel) und ein Dach-Primitiv, s. Kapitel 5.3 Primitive. Die Primitive bekommen während der Ausführung weitere Grammatiken zugeteilt, diese führt jedes Primitiv nach Berechnung der Mass-Grammar aus. cWidth = rand(20, 25); cDepth = rand(10, 15); cFloor = 8; //Axiom der Mass-Grammar Main -> S(cWidth, cFloor, cDepth) TY(-2*cFloor) Floor(0); //Die Produktion erzeugt mehrere Stockwerke und am Ende ein Dach Floor(nFloor) [1, 1] {nFloor < 2, 1} -> TY(2*cFloor) Box("PWall", "FWall", "") Floor(nFloor+1) | RoofBox("PRoof", "", "BRoof"); Abb. 5.2.2 Mass-Grammar 50 Da kein Axiom angegeben wurde, bildet die erste Variable das Axiom. Dies ist in der angegebenen Grammatik die Variable Main. Bevor jedoch die erste Iteration auf das Axiom ausgeführt wird, werden einmalig die drei Konstanten berechnet. Dabei erhalten die Konstanten cWidth und cDepth einen zufälligen Wert und die Konstante cFloor einen festen Wert von 8. Nachdem die Konstanten ihre Werte erhalten haben, wird die Produktion für das Axiom aufgerufen. Die Regel der Produktion führt zuerst eine Skalierung aus, d.h. die aktuelle Transformation wird um die Konstanten cWidth, cFloor und cDepth skaliert. Nach der Skalierung, welche die Breite, Tiefe und Höhe eines Stockwerks bestimmt, wird die Produktion des Moduls Floor aufgerufen. Das Modul soll nun einige Stockwerke erstellen und am Ende schließlich ein Dach erzeugen. Dafür besitzt die Produktion zwei Regeln. Die Erste zum Erzeugen der Stockwerke und die Zweite zum Erstellen des Dachs. Erreicht wird die Unterteilung durch das Erzwingen der ersten Regel, solange deren Bedingung erfüllt bleibt. Sowohl der ersten als auch der zweiten Regel wurde dafür jeweils eine Wahrscheinlichkeit von 1 zugeteilt. Die Bedingung nFloor < 2 bestimmt damit die Anzahl der Stockwerke. Steigt der formale Parameter nFloor auf 2, kann die Bedingung nicht weiter erfüllt werden und es wird stattdessen die zweite Regel ausgeführt. Durch diese wird ein Dach erzeugt und die Grammatik terminiert, weil keine weitere Variable existiert. Zum Erzeugen der Stockwerke wird die Funktion Box verwendet. Die Funktion erzeugt ein Primitiv in Form eines Würfels und ruft für die Flächen und Kanten des Würfels jeweils eine Facade- bzw. BorderGrammar auf. Bevor die Facade- und Border-Grammar ausgeführt werden, wird eine Profile-Grammar erstellt und damit die Geometrie des Primitiv’s bestimmt, s. Kapitel 5.2.4 Profile-Grammar. Die Argumente der Funktion geben dabei die Namen der aufzurufenden Grammatik an. Das erste Argument bestimmt die Profile-Grammar, das zweite Argument die Facade-Grammar und das dritte Argument die Border-Grammar. Die Funktion aus dem Beispiel führt als Profile-Grammar die Grammatik "PWall" aus, usw. Da für die Border-Grammar eine leere Zeichenkette gesetzt wurde, wird für die zwei erzeugten Würfel keine Border-Grammar ausgeführt. Das Dach wird ebenfalls über eine Funktion erstellt, anstatt der Funktion Box wird hier die Funktion RoofBox verwendet. Genaueres über die Argumente der Funktion kann im Anhang nachgelesen werden. Das erste Argument bestimmt auch hier welche Profile-Grammar für das Dach ausgeführt werden soll, in diesem Fall die Grammatik "PRoof". Auf den nächsten Seiten wird das angefangene Beispiel fortgeführt. D.h. bisher wurden zwei Würfel und ein Dach erzeugt, siehe Abb. 5.2.3. Die beiden Würfel sind momentan nicht zu unterscheiden, da sie sich direkt übereinander befinden und keinen Schnitt erzeugen. Den beiden Würfeln wurden jeweils eine Profile- und Facade-Grammar zugeordnet, diese werden nun nach Berechnung des Masse-Modells ausgeführt. Das Dach ruft ebenfalls eine Profile-Grammar auf und zusätzlich noch eine zugeteilte BorderGrammar. Abb. 5.2.3 Ergebnis der Mass-Grammar 51 5.2.2 Facade-Grammar Die Facade-Grammar ist für die Erstellung der Objekte zuständig, die entlang der einzelnen Wand- und Dachflächen angebracht werden. Oder anders gesagt, die Objekte die relativ zur jeweiligen Fassade oder Dachfläche platziert werden sollen. Zu diesen Objekten gehören beispielsweise Fenster, Türen, Dachgauben, usw. Eine Facade-Grammar kann nur den speziellen Primitiven sowie Dach-Primitiven zugewiesen werden und wird aus diesem Grund auch nur an deren Flächen ausgeführt. Dabei wird für jede Fläche eines Primitiv’s bzw. Dach-Primitiv’s die Facade-Grammar jeweils einmal aufgerufen, falls dem Primitiv eine Grammatik zugewiesen wurde. 5.2.2.1 Raum Auch hier muss zuerst geklärt werden, in welchen Raum und Bereich die Grammatik arbeitet. Im Gegensatz zu Mass-Grammar befindet sich die Position des Koordinatensystems nicht in der Mitte des Gebäudegrundstücks, und die Ausrichtung entspricht nicht der des Weltsystems. Der Ursprung des Systems1 liegt immer an der linken unteren Ecke der jeweiligen Fläche/ Fassade. Die x-Achse verläuft rechts entlang der Fläche und die y-Achse nach oben entlang der Fläche. Die z-Achse steht senkrecht zur xund y-Achse und bildet die inverse Normale der Fläche, da es sich um ein linkshändiges und orthogonales System handelt. Der Wirkungsbereich, in dem eine Facade-Grammar arbeiten darf, wird ebenfalls durch ein Rechteck bestimmt, das sich relativ zur Fassade befindet. Abb. 5.2.4 Mögliche Koordinatensysteme der Die Abb. 5.2.4 verdeutlicht noch einmal bildlich Facade-Grammar die Lage und Ausrichtung einiger FassadenKoordinatensysteme und deren Bereiche. D.h. alle Operationen und Transformation, die innerhalb der Grammatik ausgeführt werden, finden in dem jeweiligen oben beschriebenen System statt. Wird zum Beispiel eine Translation in x-Richtung ausgeführt, verläuft diese direkt nach rechts entlang der Fassade. Die Verschiebung wird also relativ zur Fassade ausgeführt. Dies ermöglicht eine einfache Modellierung und Aufstellung der Grammatiken. Die Fassaden der Dachflächen sind nicht so eindeutig zu bestimmen, da jede Fassade eine rechteckige Fläche besitzen muss. Die genaue Ermittlung der Fassaden für Primitive und Dach-Primitive wird im Kapitel 5.3 Primitive erklärt. 5.2.2.2 Anwendung Zur Erinnerung, zu Beginn des Beispiels wurden in der Mass-Grammar zwei Stockwerke (Würfel) und ein passendes Dach generiert. Den beiden Würfeln wurde jeweils ein Facade-Grammar zugeordnet, diese wird nun für jede Fläche der beiden Würfel jeweils einmal ausgeführt. Da die beiden Würfel unten und oben offen sind (s. Kapitel 5.3 Primitive), wird insgesamt achtmal die Facade-Grammar aufgerufen. Die Abb. 5.2.5 formuliert die Grammatik "FWall", die den beiden Würfeln zugewiesen wurde. Mit Hilfe der Grammatik sollen drei Fenster auf jeder Seite eines Stockwerks platziert werden. Dabei werden die Fenster nicht durch Primitive gebildet, sondern durch bereits vormodellierte 3D-Modelle. 1 Der Raum einer Fassade wird auch als Facade-Space bezeichnet. 52 Main -> TY(HEIGHT/3) T(WIDTH/4) ModelGroup("Window") T(WIDTH/4) ModelGroup("Window") T(WIDTH/4) ModelGroup("Window"); Abb. 5.2.5 Facade-Grammar "FWall" Die Variable Main ist die einzige Variable und damit das Axiom. Die Produktion verschiebt durch die TY Funktion alle Fenster um ein Drittel der Fassadenhöhe nach oben. HEIGHT und WIDTH sind zwei von dem System vordefinierte Konstanten. Die Konstante HEIGHT gibt dabei die Höhe der aktuellen Fassade an und die Konstante WIDTH die Breite der Fassade. Durch die beiden Konstanten lässt sich verhindern, dass Objekte außerhalb der Fassade platziert werden. Die Regel erzeugt dreimal in gleichmäßigen Abständen jeweils ein Fenster. Die dafür verwendete Funktion ModelGroup wird im Kapitel 5.4 Gruppen genauer vorgestellt. Kurz gesagt, kann mit Hilfe der Funktion ein Modell geladen und bzgl. der aktuellen Transformations-Sequenz platziert werden. In diesem einfachen Beispiel wurde die Grammatik nur für die Wände eines Würfels aufgerufen. Allerdings könnte dieselbe Grammatik auch für die Flächen eines Daches zum Einsatz kommen. Deshalb muss innerhalb der Grammatik die Möglichkeit existieren, festzustellen, welcher Typ von Fassade gerade generiert wird. Zu diesem Zweck wurde die vordefinierte Konstante TYPE eingeführt. Dieser wird eine spezielle Zeichenkette zugewiesen, die den aktuellen Typ der Fassade beschreibt. Mögliche Werte die TYPE annehmen kann, sind entweder "Wall", "Roof" oder "Gable". Besitzt die Konstante den Wert "Wall", so wird die Grammatik zum Erstellen einer Wand ausgeführt, d.h. die Fassade eines normalen Primitiv’s. Enthält TYPE die Zeichenkette "Roof", dann soll die Grammatik die Fassade einer Walmfläche erzeugen und bei dem Wert "Gable" die Fassade einer Giebelseite. Die Konstante kann nun beispielsweise als Teil einer Bedingung eingesetzt werden und damit entscheiden, welche Ersetzungsregel ausgeführt wird. In Abbildung 5.2.6 ist eines der möglichen Ergebnisse zu sehen, das durch die Mass-Grammar aus Kapitel 5.2.1.2 in Verbindung mit der in diesem Kapitel beschriebenen Facade-Grammar erstellt wurde. Für jede Seite eines Stockwerks wurden jeweils drei Fenster erzeugt. Ohne eine spezielle Transformation ausführen zu müssen, wurden die Fenster relativ zu jeweiligen Fassade platziert. Abb. 5.2.6 Ergebnis der Facade-Grammar 53 5.2.3 Border-Grammar Bisher wurde das Masse-Modell samt Dach und Wänden erzeugt und deren Fassaden mit Fenster belegt. Dafür kamen die Mass- sowie die Facade-Grammar zum Einsatz. Viele Häuser weisen noch spezielle Objekte und Auffälligkeiten an den Ecken der Hauswände oder den Kanten des Dachs auf. Mit einer Border-Grammar können diese an einer Kante verlaufenden Objekte, mit einfachen Mitteln realisiert werden. Zusätzlich zu jeder Fläche eines Primitivs, bei der eine Facade-Grammar ausgeführt wurde, wird nun für jede Kanten eines Primitivs eine Border-Grammar aufgerufen. 5.2.3.1 Raum Im Gegensatz zur Mass- und Facade-Grammar besitzt eine Kante keine rechteckige Fläche, weshalb bei der Ermittlung des Raums1 die benachbarten Flächen mitberücksichtigt werden müssen. Der Raum ist außerdem abhängig von Typ der Kante. Der Typ einer Kante ist durch die Lage der Kante definiert, bildet die Kante z.B. den First eines Dachs, den Teil einer Traufe, etc. Trotz der unterschiedlichen Typen sind zwei ­Definitionen bei allen Räumen gleich. Zum einen verläuft die x­Achse des Koordinatensystems immer genau entlang der Kante. Und zum anderen befindet sich der Ursprung des Systems immer im Startpunkt der jeweiligen Kante. Die y-Achse und damit auch die z-Achse sind abhängig vom jeweiligen Typ der Kante. Sie bilden aber immer ein orthogonales, linkshändiges System. Abbildung 5.2.7 Abb. 5.2.7 Mögliche Koordinatensysteme zeigt einige der vielen möglichen Koordinatensysteme der Border-Grammar der Kanten. Auch hier wird die x-Achse in Grün, die y-Achse in Blau und die z-Achse in Rot dargestellt. Eine genaue Beschreibung aller Kanten und deren Koordinatensysteme werden im Kapitel 5.3 Primitive beschrieben. 5.2.3.2 Anwendung Sowohl eine Facade-Grammar als auch eine Border-Grammar werden immer einem kompletten Primitiv zugewiesen, d.h. alle Flächen und Kanten erhalten dieselbe Grammatik. Da insbesondere im Bereich des Dachs viele verschiedene Kanten-Arten existieren, muss die Grammatik diese unterscheiden können. Innerhalb der Grammatik kann der Typ einer Kante über die vordefinierte Konstante TYPE abgefragt werden, wie bereits bei der Facade-Grammar. Die Konstante enthält eine der folgenden Zeichenketten: "Border", "Eave", "GableEave", "GableBoard", "Groin", "Coving" oder "Ridge". Enthält die Konstante die Zeichenkette "Border", so wurde die Grammatik einem Primitiv zugeordnet, welches gerade die Ecke einer Wand berechnet. Alle restlichen Typen beschreiben die Kanten eines Dach-Primitivs. Falls TYPE den Wert "Eave" enthält, entspricht die aktuelle Kante einer Traufe, bei "GableEave" einer Traufe die an einer Giebelseite verläuft, "GableBoard" einem Ortgang, "Groin" einem Grat, "Coving" einer Kehle und bei "Ridge" einem First. Die genaue Erklärung der Begriffe wurde im Grundlagenkapitel 2.2 Dächer erläutert. 1 Der Raum einer Kante wird auch als Border-Space bezeichnet. 54 Zurück zum Beispiel. Die Grammatik "BRoof" aus Abb. 5.2.8 wird für alle Kanten des Dachs ausgeführt. Mit Hilfe der Grammatik soll ein Schornstein in der Nähe des Dachfirsts generiert werden, d.h. die Grammatik muss den Typ der Kante erkennen und nur im Falle eines Firsts auch wirklich den Schornstein erzeugen. cDis = rand(-5, 5); Main { TYPE == "Ridge" } -> T(rand(0, WIDTH), -abs(cDis), cDis) ModelGroup("Chimney"); Abb. 5.2.8 Border-Grammar "BRoof" Die Border-Grammar "BRoof", besitzt nur eine Variable und für diese eine Produktion. Das größte Augenmerk liegt auf der Bedingung. Ohne die Bedingung würde die Grammatik für alle Kanten des Dachs (inklusive Traufe, Kehle, Grat, usw.) ausgeführt werden und damit mehrere Schornsteine erzeugen. Weil die Bedingung den Typ abfragt und sie nur erfüllt ist, wenn der Typ die Zeichenkette "Ridge" enthält, wird die Regel nur für den First ausgeführt. Wird die Ersetzungsregel aufgerufen, verschiebt sie zuerst die Position zufällig entlang der x-Achse (Kante) und zwar zwischen dem Start und dem Ende der Kante. Außerdem wird eine Verschiebung senkrecht zur Kante vorgenommen, diese wird zufällig ermittelt, um noch mehr Variationen zu erzeugen. Über die Funktion ModelGroup wird das einfache Modell eines Schornsteins geladen und auf dem Dach platziert. Abbildung 5.2.9 bildet ein mögliches Ergebnis der bisherigen Grammatiken ab. Zu diesen zählen die in Kapitel 5.2.1.2 erklärte Mass-Grammar, die in Kapitel 5.2.2.2 erklärte Facade-Grammar und die in diesem Kapitel erläuterte Border-Grammar. Letzteres erzeugt den in der Abbildung dargestellten Schornstein. Abb. 5.2.9 Ergebnis der Border-Grammar 55 5.2.4 Profile-Grammar Die letzte der vier Grammatik-Arten ist die so genannte Profile-Grammar. Diese soll zu jedem Primitiv und Dach-Primitiv eine variable Geometrie erzeugen. Wände können so zufällig Säulen oder Querbalken erhalten und Dächer können zum Beispiel zwischen einem Walmdach und einem Mansarddach variieren. Die Idee beruht darauf, dass ein zweidimensionaler Linienzug die Struktur einer Wand bzw. eines Dachs repräsentiert. Dazu stellt man sich vor, dass man die jeweilige Wand von der Seite anschaut und demnach das Profil der Wand sieht. Nun können mit Hilfe der Grammatik zusammenhängende Punkte erzeugt werden, also ein Polygon, das das Profil der Wände bzw. der Dachflächen formt. Zum Verständnis wird in Abb. 5.2.10 (a) ein fertiges Polygon dargestellt. Dieses wurde durch eine Profile-Grammar erzeugt, und aus diesem wurde die Geometrie der Wand von Abb. 5.2.10 (b) generiert. Neben vertikalen Profilen können auch horizontale Profile erzeugt werden. Ein Beispiel für ein horizontales Profil wird in Abb. 5.2.11 dargestellt. Oder aber, es wird ein vertikales sowie ein horizontales Profil berechnet, und beide werden nachträglich kombiniert. Abbildung 5.2.12 zeigt eine Kombination der beiden Profile aus den Abbildungen 5.2.10 und 5.2.11. Dabei wird ein vertikales Profil lediglich einmal für das gesamte Primitiv erstellt, während ein horizontales Profil jeweils einmal für jede Fläche eines Primitivs erzeugt wird. Zusätzlich zur variablen Erzeugung der Geometrie eines Primitivs, ist die Profile-Grammar ebenfalls für dessen Texturierung zuständig. D.h. mit Hilfe der ProfileGrammar können zufällige Texturen geladen und auf die einzelnen Abschnitte des Profils gelegt werden. Abb. 5.2.10 Vertikale Profile-Grammar Abb. 5.2.11 Horizontale Profile-Grammar Damit aus einem Polygon später passende Flächen generiert werden können, muss die Profile-Grammar mindestens zwei vertikale Punkte erzeugen. Zusätzlich können zwei bis beliebig viele horizontale Punkte erzeugt werden, diese sind aber nicht notwendig. Eine Profile-Grammar, die das Profil eines Dachs ermittelt, unterliegt einigen Einschränkungen. Zum einen können für Dächer lediglich vertikale Profile erstellt werden, und zum anderen kann das erzeugte Profil nur dann verwendet werden, wenn das Dach nur einen einzigen durchgängigen First besitzt. Abb. 5.2.12 Kombination aus horizontaler und vertikaler Profile-Grammar 56 5.2.4.1 Raum Wie bereits erwähnt wurde, arbeitet die Profile-Grammar in einem sehr abstrakten Raum. Die x-Achse gibt dabei eine Verschiebung senkrecht zur Fläche an. Diese Verschiebung wird auf der späteren Wandbzw. Dachfläche entlang der Normalen durchgeführt. Handelt es sich um ein vertikales Profil, definiert der y-Wert eines Profil-Punktes die Höhe, in der ein Punkt erzeugt wird. Im Gegensatz zum horizontalen Profil, wo die Breite über den y-Wert definiert wird. Da ein Profil zweidimensional angegeben wird, besitzt der Raum einer Profil-Grammar keine z-Achse. 5.2.4.2 Texturierung Sowohl den vertikalen als auch den horizontalen Punkten kann eine Textur zugewiesen werden. Dies geschieht mit Hilfe der TextureGroup-Funktion. Im Kapitel 5.4 Gruppen kann mehr über Gruppen und speziell über Textur-Gruppen nachgelesen werden. Sobald die Funktion aufgerufen wird, setzt diese eine zufällige Textur aus der Gruppe als aktuelle Textur. Alle Punkte, die nach dem Setzen der Textur erzeugt werden, bekommen die aktuelle Textur zugewiesen. Genauer gesagt, die aus dem Punkt erzeugte Fläche bekommt die Textur zugewiesen. Soll eine Fläche keine Textur verwenden, kann mit Hilfe der Reset-Funktion die aktuelle Textur zurückgesetzt werden. Alle Punkte, die nach der Funktion aufgerufen werden, erhalten keine Textur zugewiesen. Es sei denn, es wurde erneut die TextureGroup-Funktion aufgerufen. Welche Textur wird bei der Kombination eines vertikalen und horizontalen Profils verwendet? In diesen Fällen gilt immer, dass die Texturen von horizontalen Punkten vorrangig behandelt werden. Demnach wird immer die Textur eines horizontalen Punktes verwendet, um eine Fläche zu texturieren, aber nur, falls dem horizontalen Punkt eine Textur zugewiesen wurde. Wurde weder einem vertikalen noch einem horizontal Punkt eine Textur zugeteilt, bleibt die Fläche untexturiert. 5.2.4.3 Anwendung Die Profile-Grammar für die Wände und Dächer sind die letzten Grammatiken, die für das durchgängige Beispiel benötigt werden. Die Wände sollen jeweils an der linken und rechten Seite eine Säule erhalten und am Boden jedes Stockwerks einen kleinen Vorsprung bilden. Das Dach dagegen soll leicht nach innen verlaufen, d.h. das Anfangsstück des Dachs verläuft flacher als das Endstück. Es handelt sich quasi um ein inverses Mansarddach, bei dem normalerweise das Dach zuerst steil beginnt und dann nach oben hin flacher wird. Zuerst folgt die Grammatik "PRoof" (s. Abb. 5.2.13) zum Berechnen des vertikalen Dachprofils. Wie bereits erwähnt wurde, können Dächer nur vertikale Profile erstellen. d = rand(0.10*HEIGHT, 0.15*HEIGHT); Main -> TextureGroup("Roof") T( 0, 0.0*HEIGHT) PointV() T(-d, 0.4*HEIGHT) PointV() T( d, 0.6*HEIGHT) PointV(); Abb. 5.2.13 Profile-Grammar "PRoof" 57 Innerhalb einer Profile-Grammar existieren drei wichtige Funktionen: die Funktion TextureGroup und die Funktion PointV bzw. PointH. Die Funktion TextureGroup lädt eine zufällige Textur aus einer Gruppe und setzt diese als aktuelle Textur. Alle Punkte, die nach dem Setzen der Textur erzeugt werden, bekommen die aktuelle Textur zugewiesen. Die Funktion PointV bzw. PointH erzeugt einen neuen Punkt des Profil-Polygons. Dabei erzeugt die Funktion PointV einen neuen vertikalen Punkt und die Funktion PointH einen neuen horizontalen Punkt. Jeder weitere erstellte Punkt wird immer mit Abb. 5.2.14 (a) Vertikales Profil des Dachs und dem vorherigen Punkt verbunden und bildet (b) Profil bzgl. der Dachfläche später eine Fläche. Auf dieser Fläche wird die Textur gelegt, die zum Zeitpunkt der Erstellung des Punktes aktuell war. Die Grammatik aus Abb. 5.2.13 generiert drei Punkte. Der erste Punkt direkt beim Start des Dachs, der zweite Punkt bei vierzig Prozent der Dachhöhe und den letzten Punkt genau am Ende des Dachs. Der mittlere Punkt erhält eine zufällige Verschiebung zwischen 10% und 15% der Dachhöhe. Abbildung 5.2.14 (a) verdeutlich noch einmal das aus der Grammatik erzeugte vertikale Profile. Wird dieses Profil relativ zur Dachausrichtung gesehen, entsteht die Abbildung 5.2.14 (b). Da die Funktion TextureGroup zu Beginn aufgerufen wurde und sonst zu keinem weiteren Zeitpunkt, erhalten alle Flächen die gleiche Textur. Diese wird zufällig aus der Gruppe "Roof" bestimmt. Nun folgt die Profile-Grammar der Wände für die beiden Würfel-Primitive. Hier wird mit Hilfe eines horizontalen Profils, an der linken und rechten Seite einer Wand jeweils eine Säule ermittelt. Außerdem wird mit Hilfe eines vertikalen Profils, am Anfang jedes Stockwerks ein kleiner Vorsprung erstellt. Main [1, 1] {PTYPE == VERTICAL} -> TextureGroup("Brick") T(0.0, 0.0) PointV() T(0.0, 2.0) PointV() TextureGroup("Wall") T(0.5, 0.1) PointV() T(0.0, HEIGHT-2.1) PointV() TextureGroup("Brick") T(-0.5, 0.0*WIDTH) PointH() T( 0.0, 0.1*WIDTH) PointH() Reset() T( 0.5, 0.0*WIDTH) PointH() TextureGroup("Brick") T( 0.0, 0.8*WIDTH) PointH() T(-0.5, 0.0*WIDTH) PointH() T( 0.0, 0.1*WIDTH) PointH(); Abb. 5.2.15 Profile-Grammar "PWall" 58 | Die Grammatik besitzt lediglich eine Produktion und innerhalb dieser zwei Regeln. Die erste Regel soll einmalig das vertikale Profil für alle Wände erzeugen und die zweite Regel jeweils ein horizontales Profil für jede Wand. Über eine Bedingung und mit Hilfe der vordefinierten Konstanten PTYPE wird innerhalb der Grammatik eine Abfrage eingeleitet. Diese Abfrage prüft, ob die Grammatik ein vertikales oder ein horizontales Profil berechnen soll. Im Falle eines vertikalen Profils werden vier vertikale Punkte generiert. Die ersten beiden Punkte bilden hier einen kleinen Vorsprung. Die beiden hinteren Punkt die restliche Wand bis zum Ende. Im Falle eines horizontalen Profils werden sechs Punkte erstellt. Die vorderen und hinteren zwei Punkte bilden jeweils eine Säule an den Enden der Wand. Die beiden mittleren Punkte verbinden die beiden Säulen. Zusätzlich erhalten die vier Punkte der Säule eine spezielle Backstein-Textur aus der Gruppe "Brick". Auf Grund der Reset-Funktion erhält die Fläche zwischen den beiden Säulen keine Textur. Da dieser Fläche keine Textur zugewiesen wurde, verwendet sie die Textur des vertikalen Profils, siehe Kapitel 5.2.4.2 Texturierung. Der restliche Teil bedarf keiner weiteren Erklärung. Die Figuren 5.2.16 bis 5.2.18 zeigen eines von vielen möglichen Ergebnissen, das aus den Grammatiken der vorherigen Kapitel erzeugt wurde. Bei jeder neuen Ausführung der Grammatiken variiert das Haus. Bisher allerdings noch relativ schwach, da keine wirklichen Veränderungen am Masse-Modell, den Fassaden und der Dachstruktur vorgenommen werden. Anhand des Beispiels ist außerdem die Wiederverwendbarkeit der einzelnen Grammatiken gut zu erkennen. So könnte beispielsweise die Grammatik für das Masse-Modell gegen eine andere Mass-Grammar ausgetauscht werden, ohne eine Veränderung an den sonstigen Grammatiken durchführen zu müssen. D.h. die Facade-, Border- und Profile-Grammar blieben unverändert und könnten auf jedes beliebige Primitiv angewendet werden. Abb. 5.2.17 Fertiges Gebäude mit Texturen Abb. 5.2.16 Fertiges Gebäude ohne Texturen Abb. 5.2.18 Nahansicht des Gebäudes 59 5.2.5 Kommunikation Im Laufe dieses Kapitels wurde gezeigt, dass die Mass-Grammar sogenannte Primitive erzeugen kann. Dabei kann jedem Primitiv eine Profile-, Facade- und/oder eine BorderGrammar zugeteilt werden. Diese werden dann für jede Fläche bzw. Kante des Primitivs einmal ausgeführt und erzeugen an diesen Objekte. Es ist ebenfalls möglich, innerhalb einer Facade- oder Border-Grammar erneut Primitive zu erstellen, die wiederum neue Profile-, Facade- und Border-Grammars zugeteilt bekommen und diese ausführen. Abb. 5.2.19 Grammatik-Kommunikation Anderseits ist es ebenso möglich, innerhalb der Mass-Grammar vormodellierte 3D-Modelle zu laden. Dies könnten zum Beispiel fertiggestaltete Bäume oder gar komplett vordefinierte Stockwerke sein. Es bleibt den Nutzer freigestellt, wie er das Gebäude aus Primitiven und Modellen zusammenbaut. Die Profile-Grammar ist die einzige Grammatik, die keinen neuen Primitiven und Modelle erstellen kann. In manchen Fällen ist es notwendig, dass die Grammatik bestimmte Parameter an eine andere Grammatik weiterleitet. Zum Beispiel könnte eine Mass-Grammar die Nummer des Stockwerks an eine Profile-Grammar übergeben. Diese verwendet die Nummer, um eine passende Textur für dass jeweilige Stockwerk zu finden. Diese einseitige Kommunikation findet über so genannte Register statt. Es handelt sich um eine einseitige Kommunikation, da nur die aufrufende Grammatik Daten an die aufzurufenden Grammatiken weiterleiten kann und nicht umgekehrt. Ein Register wird definiert durch einen eindeutigen Namen und erhält als Daten eine einzelne reelle Zahl oder Zeichenkette. Innerhalb der Grammatiken kann ein Register über die Funktion Out bzw. In angelegt bzw. ausgelesen werden. Zum Beispiel erzeugt die Funktion Out(3, "Floor") ein Register mit dem Namen "Floor" und weist diesem Register einen Wert von 3 zu. Sobald nun ein Primitiv erzeugt wird, werden alle aktuell angelegte Register an alle drei Grammatiken (Facade-, Border- und Profile-Grammar) des Primitiv’s weitergeleitet. Diese Grammatiken können das Register über die Funktion In("Floor") wieder auslesen. Der Rückgabewert der Funktion würde in diesem Beispiel 3 betragen. 60 5.3 Primitive Der Begriff Primitiv ist abgeleitet aus dem latein. primitivus für „einfach“. Primitive sind demzufolge einfache, grundlegende und bereits vordefinierte Körper. Das System stellt verschiedene Typen von Primitiven mit jeweils unterschiedlichen Formen zur Verfügung. Diese bilden dann Teile eines Gebäudes und können über die Grammatiken erzeugt werden. Im vorherigen Kapitel wurden Primitive bereits zur Generierung des Masse-Modells verwendet. Dabei hat sich herausgestellt, das Primitive in zwei Kategorien eingeteilt werden können. Dies wäre zum einen die Wand-Primitive1, die zur Erzeugung der Hauswände dienen. Und zum anderen die Dach-Primitive zum Erstellen der Dachflächen. Weiterhin wurde bereits verdeutlicht, dass jedem Primitiv drei verschiedene Grammatiken zugeteilt werden kann. Dies sind die im Kapitel 5.2 Grammatiken erläuterten Profile-, Facade- und Border-Grammar. Ein Primitiv führt diese jeweils einmal für jede Fläche bzw. Kante aus und erzeugt damit die Details des Gebäudes. Aber an welchen Flächen bzw. Kanten werden die Grammatiken konkret ausgeführt? Welche Arten von Primitiven stellt das System zu Verfügung? Wie können die verschiedene Primitive erzeugt werden? ... In den beiden nächsten Unterkapiteln werden diese Fragen beantwortet. Dabei wird zuerst auf die zur Erzeugung der Wände benötigten Primitive eingegangen und darauf folgend auf die für die Dachstrukturen benötigten Primitive. 5.3.1 Wand-Primitive Wand-Primitive sind einfache, konvexe Körper und werden durch eine oder mehrere Flächen definiert. Bislang wurden nur konvexe Körper implementiert, weil bei diesen eventuell anfallende SchnittAlgorithmen, schnell und einfach durchführbar sind. Durch Kombinationen verschiedener Primitive soll der Grundriss eines Gebäudes entworfen werden. Dazu zählen zum Beispiel die typische T, L oder U-Form eines Wohnhauses, die aus zwei bzw. drei Würfeln zusammengestellt werden kann. 5.3.1.1 Formen Das System verfügt über fünf unterschiedliche Formen. Zu diesen zählen der Würfel, der Zylinder, der Halb-Würfel, der Halb-Zylinder und eine einzige, unabhängige Fläche. Die Abbildung 5.3.1 verdeutlicht noch einmal bildlich die verschiedenen Primitive. Der Halb-Würfel und Halb-Zylinder besitzen, wie der Name bereits verrät, nur eine Hälfte des Würfels bzw. Zylinders. Dies ist in beiden Fällen die vordere Hälfte. Der Einsatz von halben Primitiven ist für den Zusammenbau mit ganzen Körpern vorgesehen. Beispielsweise kann eine T-Form mit Hilfe eines ganzen Würfels und einem angehängten halben Würfel realisiert werden. Alle erwähnte Primitive sind oben und unten offen. Diese Flächen werden nicht benötigt, da der obere Teil über ein Dach-Primitiv geschlossen werden kann und der untere Teil im Normalfall sowieso nicht sichtbar ist. Abb. 5.3.1 Primitive: (a) Würfel, (b) Zylinder, (c) Halb-Würfel, (d) Halb-Zylinder und (e) Wand 1 Im Gegensatz zu Dach-Primitive werden Wand-Primitive häufig einfach nur als Primitive bezeichnet. 61 5.3.1.2 Fassaden Im Kapitel 5.2.2 Facade-Grammar wurde bereits notdürftig erläutert, an welchen Stellen eines Primitivs die zugewiesene Facade-Grammar ausgeführt wird. Im Grunde entspricht es genau den eigenen Vorstellungen und Vermutungen: Jede Fläche eines Primitiv’s entspricht genau einer Fassade. Dabei befindet sich der Ursprung der Fassade in der linken, unteren Ecke der jeweiligen Fläche. Die x- und y-Achsen des Koordinatensystems liegen direkt auf der Fläche. D.h. die x-Achse verläuft direkt nach rechts entlang der unteren Kante einer Fläche. Während die y-Achse entlang der linken Kante nach oben verläuft. Da jedes Primitiv nur über rechteckige Flächen verfügt, stehen die beiden Achsen immer orthogonal zueinander. Dies erleichtert die Vorstellungen einer Fassade und ermöglicht die einfache Aufstellung einer Facade-Grammar. Die z-Achse des Fassaden-Koordinatensystems steht senkrecht zu beiden Achsen und bildet mit diesen ein linkshändiges System. Die Breite einer Fassade entspricht demnach genau der Länge der unteren Kante, durch welche bereits die x-Achse definiert wurde, während die Höhe einer Fassade durch die Länge der linken Kanten bestimmt wird. Zur Erklärung zeigt Abbildung 5.3.2 die Koordinatensysteme einiger Fassaden, die jeweils bläulich dargestellt werden. Die x-Achse des Systems wird in Grün dargestellt, die y-Achse in Blau und die z-Achse in Rot. Egal, für welches Primitiv eine Fassade bestimmt wird, der Raum bildet immer ein orthogonales, linkshändiges System. Abb. 5.3.2 Facade-Space (a) Würfel, (b) Zylinder, (c) Halb-Würfel, (d) Halb-Zylinder und (e) Wand 5.3.1.2 Ecken Innerhalb des Kapitels 5.2 Grammatiken wurde notdürftig erklärt, dass für jede Kante eines Primitivs jeweils eine Border-Grammar aufgerufen wird. Dies entspricht nicht ganz der Wahrheit. Vielmehr wird nur für alle inneren Kanten eines Primitivs, die zugewiesene Border-Grammar ausgeführt. Daraus folgt, dass die oberen sowie unteren Kanten keine Grammatiken aufrufen. Der Ursprung des Koordinatensystems eine Border-Grammar liegt an dem unteren Punkt der Kante, siehe Abb. 5.3.3. Die x-Achse des Systems verläuft genau entlang der Kante, d.h. wird eine Verschiebung in x-Richtung vorgenommen, so verläuft diese immer auf der Kante. Die z-Achse entspricht der inversen Normale der darauf folgenden Fläche. Die y-Achse steht schließlich senkrecht zur x- und z-Achse und bildet mit diesen ein linkshändiges System. Abbildung 5.3.3 verdeutlicht noch einmal die Koordinatensysteme einiger Kanten. Auch hier wird die x-Achse in Grün, die y-Achse in Blau und die z-Achse in Rot dargestellt. Die zugehörige Kante wird leicht bläulich unterstrichen und so sichtbar gemacht. 62 Abb. 5.3.3 Border-Space (a) Würfel, (b) Zylinder, (c) Halb-Würfel, (d) Halb-Zylinder und (e) Wand 5.3.1.3 Texturierung Die Texturierung erfolgt relativ einfach, indem die Textur flach auf jede Fläche des Primitivs gelegt wird, siehe Abb. 5.3.4 (a). Damit der Nutzer nachträglich Einfluss auf die Koordinaten erhält, können mittels der Funktion TexCoord(u, v) innerhalb der Profile-Grammar die Texturkoordinaten der einzelnen Abschnitte faktorisiert werden. D.h. die Funktion setzt jeweils einen unabhängigen Faktor für die u- und v-Koordinate, der allen darauf folgenden Punkten bzw. deren Flächen zugeordnet wird, ähnlich wie bei der TextureGroup-Funktion (s. Kapitel 5.2.4.2 Texturierung). Beispielsweise könnte eine Textur gestaucht werden, in dem beide Argumente eine 2 erhalten und dadurch die Texturkoordinaten verdoppelt werden, siehe Abb. 5.3.4 (b). (a) 5.3.1.4 Funktionen Innerhalb der Grammatiken können die vorgestellten Primitive über Funktionen erzeugt werden, wie bereits geschehen in Kapitel 5.2 Grammatiken. Diese Funktionen können innerhalb (b) einer Mass-, Facade- und Border-Grammar ausgeführt werden. Deshalb bleibt es diesen drei Grammatiken vorbehalten, Primitive zu erzeugen. Mögliche Funktionen sind dabei Box, Cylinder, HalfCylinder, HalfBox und Wall. Logischerweise erstellt jede der Funktionen eines der oben beschriebenen Primitive. Bevor das jeweilige Primitive erstellt wird, wird es vorher bzgl. der aktuellen Transformations-Sequenz transformiert. D.h. der Abb. 5.3.4 Texturierung (a) normal aktuelle Zustand beeinflusst die Größe, Ausrichtung und Pound (b) gestaucht sition der Primitive. Die Argumente der Funktionen werden durch drei Zeichenketten bestimmt. Das erste Argument enthält den Namen der auszuführenden Profile-Grammar, das zweite Argument, den der auszuführenden Facade-Grammar und das dritte Argument schließlich den Namen der Border-Grammar. Daraus folgt, dass die Grammatiken immer einem kompletten Primitiv zugeteilt werden. Es ist nicht möglich einzelnen Flächen oder Kanten verschiedene Grammatiken zu zuteilen. Allerdings ist es möglich, nach dem Namen der Grammatik, durch einen Doppeltpunkt getrennt, das Axiom (Namen einer Variablen) anzugeben, z.B. "FWall:Axiom". Eine weitere Möglichkeit, eines der fünf Primitiven zu erstellen, ist durch die allgemeine Funktion Prim gegeben. Das erste Argument gibt hier nicht den Namen der Profile-Grammar an, sondern den Namen des zu erzeugenden Primitivs. Das zweite Argument gibt schließlich den Namen der Profile-Grammar an, gefolgt von den Argumenten für die Namen der Facade- und Border-Grammar. 63 5.3.2 Dach-Primitive Die soeben beschriebenen Primitive, die der Erstellung von Wänden bzw. des Grundrisses dienen, sind aus Flächen definierte konvexe Körper. Im Gegensatz dazu stehen die im Folgenden beschriebenen Dach-Primitive. Diese werden zu Beginn lediglich als Linienzug definiert, der an den oberen Kanten der einzelnen Primitiven entlangläuft und damit die Traufe des Gebäudes bestimmt. Auf dieses Polygon (Synonym für Traufe) kann der im Kapitel 4 Straight-Skeleton erklärte Algorithmus des Straight-Skeletons angewendet werden. Das Verfahren ermittelt die innere Struktur des Dachs und daraus dessen Flächen. Wurde die Dachausmittlung und damit die Flächen des Dachs berechnet, wird für das gesamte Dach einmalig eine vertikale Profile-Grammar ausgeführt. Diese bestimmt den Typ und die Form des Dachs. Nach dem das Dach erzeugt wurde, werden alle möglichen Fassaden und Kanten berechnet und an diesen, die zugewiesene Facade- bzw. Border-Grammar ausgeführt. Durch die Ausführung der Grammatiken werden schließlich Dachfenster, Gauben, Schornsteine, Regenrinnen und weitere Details generiert. 5.3.2.1 Formen Im Gegensatz zu Wand-Primitiven sind Dach-Primitive nicht aus Flächen definiert. Ein Dach-Primitiv wird am Anfang nur als Linienzug angegeben. Dieser läuft an den oberen Kanten eines Primitivs entlang und bildet damit die passende Traufe zum jeweiligen Wand-Primitiv. Dem einzigen Wand-Primitiv, dem kein Dachelement zugeordnet werden kann, ist das Primitiv der unabhängigen Wand, siehe Abb. 5.3.1 (e). Da dieses Primitiv nur über eine einzige Kante verfügt und deshalb keinen geschlossenen Linienzug bildet, ist es nicht möglich, ein Dach für dieses zu bestimmen. Obwohl der Halb-Zylinder und Halb-Würfel keine geschlossenen Polygone formen, kann für beide Primitive ein Dach ermittelt werden. Dazu werden die Linienzüge beider Primitive einfach als geschlossen angenommen und die beiden Endpunkte miteinander verbunden. In Abbildung 5.3.5 werden alle DachPrimitive, zu den jeweilig passenden Wand-Primitiven, durch die grünen Linien gekennzeichnet. Abb. 5.3.5 Dach-Primitive: (a) Würfel, (b) Zylinder, (c) Halb-Würfel, (d) Halb-Zylinder 5.3.2.2 Dachdurchdringung Eine neue Eigenschaft der Dach-Primitive liegt darin, dass mehrere dieser Primitive miteinander verbunden werden können. Schneiden sich zwei oder mehr Dach-Primitive an ein oder mehreren Stellen, so werden diese zu einem Polygon zusammengefasst. Wie bei der Dachausmittlung (s. Kapitel 2.2.4 Dachausmittlung) werden nur Polygone die auf gleicher Höhe liegen miteinander verbunden. Das System berechnet demnach das Straight-Skeleton erst, nachdem alle Dach-Primitive zusammengefasst wurden und dadurch alle Dachdurchdringungen feststehen. Die Ermittlung der Dachdurchdringungen findet in zwei Phasen statt, weshalb zwei Algorithmen eigens dafür entwickelt wurden. Die erste Phase bestimmt unter allen erzeugten Dach-Primitiven diejenigen, die sich schneiden und auf gleicher Höhe befinden, und ordnet diese einer Gruppe zu. In der zweiten Phase werden alle Primitive einer Gruppe zu einem Dach-Primitiv bzw. Polygon zusammengefasst. 64 Abbildung 5.3.6 gibt das Bild einiger Wand-Primitive mit passenden Dach-Primitiven, auch hier wieder durch die grünen Linien gekennzeichnet. Die Grundrissprojektion von Abb. 5.3.6 wird in der Zeichnung 5.3.7 dargestellt. Da sich die zwei mittleren Würfel und der Zylinder durchdringen, sollen diese miteinander verbunden werden. Der kleinere Würfel im Bild schneidet zwar ebenfalls einen der mittleren Körper, aber besitzt nicht die gleiche Höhe. Aus diesem Grund soll dieser sowie der freistehende Würfel jeweils ein eigenständiges Dach erhalten. In den nächsten Abschnitten wird ausführlich erklärt, wie die Dachdurchdringung und Verbindung der Dächer berechnet werden kann. Abb. 5.3.6 Dachdurchdringung Abb. 5.3.7 Grundriss Im ersten Schritt werden die Durchdringungen aller Dach-Primitive ermittelt. Dazu werden die Polygone aller Primitive gegeneinander verglichen, aber nur, falls sie auf gleicher Höhe liegen. Ist dies gegeben, so wird das erste Primitiv in eine Gruppe eingeordnet. Danach werden die Schnitte des ersten Primitivs mit allen anderen Primitiven berechnet. Falls das Polygon des Primitivs ein oder mehrere andere Polygone schneidet, werden diese der Gruppe zugeordnet. Nun werden wiederum die Primitive, die der Gruppe neu zugeordnet wurden, gegen alle Primitive, die noch keiner Gruppe angehören, getestet. Konnten erneut Durchdringungen gefunden werden, werden die neu geschnittenen Primitive ebenfalls der Gruppe zugewiesen. Dieser Vorgang wird so lange fortgesetzt, bis kein Schnitt mehr gefunden werden kann. Existieren danach noch Primitive, die keiner Gruppe angehören, so wird mit diesen erneut von vorne begonnen. Das heißt, das nächste freie Polygon wird in eine neue Gruppe eingefügt, und es wird der Schnitt mit allen Primitiven berechnet, die noch keiner Gruppe zugewiesen wurden. Und so weiter. Durch den Algorithmus werden die Primitive schrittweise in ein bis mehrere Gruppen eingeordnet und als Ergebnis erhält das System alle Primitive, die sich schneiden und in gleicher Höhe befinden. Abb. 5.3.8 Iterationen zur Bestimmung Die Abbildung 5.3.8 verdeutlicht die einzelnen Iterationen aller Dachdurchdringungen des Algorithmus. Begonnen wird mit dem blauen Polygon aus Zeichnung 5.3.8 (a). Im zweiten Schritt (b) konnte der Zylinder der blauen Gruppe zugeteilt werden. In der dritten Iteration (c) konnte das nächste Primitiv in die Gruppe eingeordnet werden. Danach existiert kein weiteres Objekt, das der blauen Gruppe angehört. Das heißt, es wird in der vierten Iteration die rote Gruppe erzeugt und in der fünften Iteration die grüne Gruppe, beide in Abb. 5.3.8 (d) dargestellt. 65 Nachdem alle Primitive den Gruppen zugeordnet wurden, kann in der zweiten Phase für jede Gruppe ein verbundenes Polygon ermittelt werden. D.h. der nachfolgende Algorithmus wird für jede Gruppe einmal ausgeführt. Zuerst wird ein Startpunkt für die Gruppe ermittelt. Dieser darf nicht innerhalb eines der Gruppe angehöriger Polygone liegen. Da alle Polygon die Traufe eines konvexen Wand-Primitivs bilden, sind diese ebenfalls alle konvex. Weiterhin werden alle Polygone gegen den Uhrzeigersinn angegeben. Durch diese zwei Voraussetzungen lässt sich schnell und einfach ermitteln, ob sich ein Punkt innerhalb eines Polygons befindet. Wurde der Startpunkt bestimmt, wird von diesem beginnend das Polygon gegen den Uhrzeigersinn verfolgt. Dafür wird ein Strahl, ausgehend von dem aktuellen Punkt, in Richtung der zum Punkt gehörigen Kante geschickt. Trifft der Strahl auf ein anderes Polygon, so ergibt der Schnittpunkt einen neuen Punkt des verbundenen Polygons. Trifft der Strahl auf kein anderes Polygon, so bestimmt der nächste Punkt des aktuellen Polygons den neuen Punkt des verbundenen Polygons. Dies wird so lange fortgesetzt, bis der Startpunkt wieder erreicht wurde und damit alle Primitive miteinander verbunden wurden. Abb. 5.3.9 (a) zeigt die ersten fünf Iterationen zum Verbinden der Primitive. In Abb. 5.3.9 (b) gibt ein Bild des Ergebnisses, also das verbundene Polygon. Abb. 5.3.9 (c) verdeutlicht durch die gestrichelten Linien zusätzlich das Straight-Skeleton. Abb. 5.3.9 (a) Verbinden der Primitive, (b) Ergebnis der Zusammenfassung und (c) Ergebnis mit Straight-Skeleton Durch verbinden der durchdringenden Dach-Primitive wurde für den Haupttrakt ein einziges, zusammenhängendes Dach berechnet, Abb. 5.3.10. Das frei stehende Gebäude sowie der kleinere Nebentrakt erhalten jeweils ein eigenes unabhängiges Dach. Ein Problem beim Verbinden mehrerer Dach-Primitive könnte dadurch entstehen, dass den verAbb. 5.3.10 Dachverbindung schiedenen Dach-Primitiven auch verschiedene Profile-Grammars zugewiesen werden. Dies stellt insofern ein Problem dar, als dass durch diese Kombination Lücken innerhalb der Dachflächen entstehen könnten. Aus diesem Grund verwenden zusammengefasste Dach-Primitive immer die Grammatik des größten Primitivs. Das größte Primitiv stellt den Haupttrakt des Gebäudes dar und bestimmt damit die Form und das Aussehen des Dachs. 66 5.3.2.3 Fassaden Bei einem Wand-Primitiv entsprach jede Fläche genau einer Fassade. Die Bestimmung der Fassaden für ein Dach kann nicht so trivial beschrieben werden. Zum einen bildet eine Dachfläche in den wenigsten Fällen ein Rechteck. Und zum anderen existieren oft Dachflächen, an denen es nicht sinnvoll wäre, eine Facade-Grammar aufzurufen. Aus diesem Grund wurden eigens zwei Bedingungen aufgestellt, die eine Fassade (eine Fläche an der die Facade-Grammar ausgeführt wird) erfüllen muss. Erstens, die Fläche muss immer rechteckig sein, und zweitens muss sie direkt bei der Traufe beginnen und bis zum First verlaufen. Kann für eine Dachfläche kein Rechteck mit den passenden Kriterien gefunden werden, wird für diese Fläche auch keine Facade-Grammar ausgeführt. Warum müssen die beiden Kriterien erfüllt sein? Zur Erinnerung, eine Fassade definiert den Wirkungsbereich einer Facade-Grammar. Diese soll Objekte entlang der Dachflächen platzieren, z.B. Dachfenster, Schornsteine oder Dachgauben. Nun existieren viele Bereiche innerhalb einer Dachkonstruktion, an denen Objekte nicht platziert werden sollten bzw. in der Realität nicht platziert werden können. Beispielsweise in der linken unteren Ecke einer geneigten Walmfläche, in Abb. 5.3.11 verdeutlicht durch den grünen Punkt. An dieser Stelle würden normalerweise keine Dachfenster, keine Gauben und auch keine sonstigen Objekte platziert werden, da diese direkt auf die Balkenkonstruktion stoßen würden. Die verbotenen Bereiche existieren nicht an den Stellen, die strikt und durchgängig von der Traufe bis zum First verlaufen. Werden all diese Stellen zusammengefasst, erhält man ein Rechteck, das von der Traufe bis zum First verläuft und damit die Fassade der Dachfläche abgrenzt. Die nächsten Abschnitte beschreiben einen Algorithmus, mit dem rechteckige Fassaden für alle Formen von Dachflächen bestimmt werden können. Dazu muss zuerst die genaue Struktur des Dachs berechnet werden. Hierfür wird das Straight-Skeleton verwendet (s. Kapitel 4 Straight-Skeleton), allerdings dürfen die Zwischenschritte des Straight-Skeletons nicht verworfen werden. Die verschiedenen Iterationen werden benötigt, um möglichst einfach ein oder eventuell mehrere passende Rechtecke für die Dachflächen zu erhalten. D.h. zuerst werden für die einzelnen Iteration geeignete Rechtecke berechnet, die dann in einem zweiten Durchgang miteinander verbunden und evtl. an den Seiten gekürzt werden. Ein Beispiel für die verschiedenen Iterationen des Straight-Skeletons wird in Abb. 5.3.11 durch die gestrichelten Linien dargestellt. Abb. 5.3.11 Straight-Skeleton Im ersten Durchlauf muss der entwickelte Algorithmus für jede Fläche einer Straight-Skeleton Iteration, jeweils ein Rechteck bestimmen. Da diese Flächen entweder ein Trapez, Parallelogramm oder Dreieck bilden, wie in Abb. 5.3.11 zu erkennen ist, muss für jede dieser Formen ein Rechteck berechnet werden. Das Rechteck muss innerhalb der Form liegen und von der unteren bis zur oberen Kante verlaufen. Außerdem müssen die untere und obere Kante des Rechtecks parallel zur oberen bzw. unteren Kante der Form liegen. Kann für eine Form kein passendes Rechteck bestimmt werden, so wird auch keine Fassade für diese Fläche angegeben. Diese Fälle sind allerdings nur bei Parallelogrammen möglich, siehe Abb. 5.3.12 (c). Für ein Trapez und ein Dreieck kann immer ein passendes Rechteck gefunden werden, siehe Abb. 5.3.12 (a) und (b). Bei einem Dreieck nimmt das Rechteck immer die Form einer Linie an, dies spielt für den späteren Verlauf des Algorithmus keine Rolle. Es handelt sich einfach um ein Rechteck mit einer Breite von null. 67 Abb. 5.3.12 Bestimmung des Rechtecks Zum Verständnis wird in Abb. 5.3.13 (a) die erste Iteration des Algorithmus angezeigt, dabei konnte für das Parallelogramm des linken Nebentrakts kein Rechteck bestimmt werden, zur Erklärung siehe Abb. 5.3.12 (c). Für die restlichen Flächen konnte jeweils ein passendes Rechteck ermittelt werden. Diese werden innerhalb der Abbildung durch die blauen Flächen verdeutlicht. In der zweiten Iteration (b) werden die Flächen grün dargestellt und in der dritten Iteration (c) rot. Wurden die Rechtecke für alle Stufen des Straight-Skeletons bestimmt, können diese als nächstes schrittweise zusammengefasst werden. Dabei wird wieder mit der untersten Stufe begonnen. Diese wird mit dem darüber liegenden Level verbunden. Damit zwei Flächen verbunden werden dürfen, müssen sie eine Schnittlinie besitzen, d.h. die obere Kante des unteren Rechtecks muss die untere Kante des oberen Rechtecks schneiden. Als Ergebnis entsteht ein minimales Rechteck, dessen Längsseite durch die Schnittlinie bestimmt wird. Abbildung 5.3.13 (d) zeigt, wie die erste Stufe mit der zweiten Stufe zusammengefasst wird. Dabei können die beiden Rechtecke des rechten Nebentrakts nicht miteinander verbunden werden, weil sie keinen gemeinsamen Schnitt besitzen. Das Gleiche gilt für die Frontseite des Haupttrakts. Da hier der erste mit dem zweiten Level nicht verbunden werden konnte, kann auch nicht der zweite mit dem dritten Level verbunden werden. Abbildung 5.3.13 (e) zeigt schließlich die letzte Iteration, bei der erneut das Rechteck des Haupttrakts zusammengefasst wird. Wie zu erkennen ist, verlaufen alle Rechtecke von der Traufe bis zum First des jeweiligen Abb. 5.3.13 Iterationen zur Bestimmung der Dachs und grenzen die Fassade ab. Fassaden Innerhalb der berechneten Bereiche dürfen nun von einer Facade-Grammar Objekte platziert werden. Damit diese Platzierung gesteuert werden kann, muss zu guter Letzt für jede Fassade ein Koordinatensystem berechnet werden. Wie bei den Fassaden der Wand-Primitive befindet sich der Ursprung an der linken unteren Ecke des Rechtecks. Die x-Achse verläuft entlang der unteren Kante des Rechtecks und die y-Achse entlang der linken Außenkante. Die z-Achse steht auch hier senkrecht auf beiden Achsen und erzeugt ein linkshändiges System. Für Rechtecke, die eine Breite von Null besitzen, da sie z.B. durch ein Dreieck erzeugt wurden, existiert eigentlich keine untere Kante. D.h. die x-Achse kann nicht durch das Rechteck ermittelt werden. Allerdings kann immer noch auf die Kante des Straight-Skeletons zurückgegriffen werden, um in diesen Fällen die x-Achse zu bestimmen. 68 5.3.2.4 Kanten Bei einem Dach-Primitiv wird, im Gegensatz zu den Wand-Primitiven, an jeder Kante die zugewiesene Border-Grammar ausgeführt. Allerdings entstehen hier Unterschiede bei der Angabe der einzelnen Koordinatensysteme, die abhängig von dem Typ der jeweiligen Kante sind. Die verschiedenen Typen wurden im Grundlagenkapitel 2.2.1 Dachbegriffe definiert. Trotz einiger Unterschiede besitzen die Systeme der verschieden Kanten auch gleiche Eigenschaften. So liegt der Ursprung des Koordinatensystems bei allen Kanten im Startpunkt der jeweiligen Kante. Außerdem verläuft die x-Achse immer genau entlang der Kante, egal bei welchem Typ. Handelt es sich bei der Kante um den Teil einer Traufe oder eines Firsts, so steht die y-Achse genau senkrecht nach oben, siehe Abb. 5.3.14 (a). Ist die Kante dagegen vom Typ Grat oder Kehle, läuft die y-Achse senkrecht zur x-Achse und bildet die Winkelhalbierende der beiden benachbarten Flächen. Bei einem Ortgang steht die y-Achse ebenfalls senkrecht auf der x-Achse und der benachbarten Walmseite. Siehe Abbildung 5.3.14 (b) für die Koordinatensysteme eines Ortgangs, eines Grats und einer Kehle. Die zAbb. 5.3.14 Border-Space für (a) Traufe und First, Achse wird für alle Typen orthogonal (b) Ortgang, Kehle und Grat zur x- und y-Achse erzeugt und bildet mit diesen das Linkshandsystem der Kante. 5.3.2.5 Texturierung Im Gegensatz zu Wand-Primitiven existieren bei Dach-Primitiven zwei verschiedene Arten, die Texturierung durchzuführen. Dabei wird die Art der Texturierung anhand der Steigung des Dachs festgelegt. Handelt es sich um ein flach geneigtes Dach oder um ein Steildach (siehe Kapitel 2.2.3 Dachneigung), werden die Texturen wie bei einem Wand-Primitive auf die Flächen gelegt, siehe Abb. 5.3.14. Bei einem Flachdach (siehe Kapitel 2.2.3 Dachneigung) dagegen würde eine solche Texturierung zu falschen Eindrücken führen, siehe Abb. 5.3.15. Denn Flachdächer sind in den meisten Fällen aus Stein, Beton oder ähnliches direkt zusammengebaut und nicht mit Dachziegel belegt. D.h. es ist im Normalfall kein Schnitt zwischen den Dachflächen zu erkennen, da diese übergehend durch Steine oder Sonstiges verlegt werden. Aus diesem Grund wird die Textur gleichmäßig direkt von oben auf alle Flächen projiziert, wie in der Abbildung 5.3.16 zu erkennen ist. Abb. 5.3.14 Texturierung eines Steildachs Abb. 5.3.15 Falsche Texturierung eines Flachdachs 69 Wie bereits bei den Wand-Primitiven erläutert wurde, können die Texturkoordinaten (in beiden Fällen) über die TexCoord-Funktion faktorisiert werden. Dadurch kann die Textur auf den Dachflächen gestaucht oder gestreckt werden, je nachdem, ob der Faktor größer oder kleiner 1 übergeben wird. Abb. 5.3.16 Texturierung eines Flachdachs 5.3.2.6 Funktionen Um ein Dach-Primitiv durch eine Grammatik erzeugen zu lassen, wurden die Befehle RoofBox, RoofCylinder, RoofHalfBox und RoofHalfCylinder implementiert. Auch hier erzeugt die Funktion das zum Namen passende Dach-Primitiv. Wie bei den Wand-Primitiven bestimmen die ersten drei Argumente die Namen der Profile-, Facade- und Border-Grammar. Auch hier kann optional durch ein Doppelpunkt getrennt, nach dem Namen das Axiom festgelegt werden. Zusätzlich besitzen die Funktionen noch bis zu vier weitere Argumente. Das vierte Argument bestimmt die Steigung des Dachs. Diese wird in Radien angegeben und beträgt im Normalfall 45 Grad. Über das fünfte Argument kann die Überlauflänge des Dachs angegeben werden, d.h. ist der Wert größer als 0, bestimmt dieser die Länge, die das Dach über die Wand hinausläuft. Wie im Kapitel 4.6 Gewichtung erklärt wurde, können die Kanten eines Polygons gewichtet werden und durch diese entsprechend langsamer schrumpfen. Durch Gewichten der vorderen und hinteren Kante eines Dach-Primitivs können so Giebelseiten erzeugt werden. Als sechstes Argument kann dieser Wert zur Gewichtung der Giebelseiten übergeben werden. In Normalfall ist der Wert gleich 1, das heißt, die Giebelseiten besitzen die gleiche Steigung wie die Längsseiten des Dachs. Das letzte Argument kann entweder den Wert 0 oder 1 annehmen. Falls eine 1 übergeben wird, wird das Dach nach dem Algorithmus von Kapitel 5.3.2.2 Dachdurchdringung mit anderen Dach-Primitiven verbunden. Bei einer 0 wird das Primitiv mit keinem anderen Dach-Primitiv verbunden, auch wenn die Primitive die gleiche Höhe besitzen und eine Durchdringung stattfindet. Das System behandelt das Dach immer als eigenständig und unabhängig. Wie bereits bei den Wand-Primitiven (s. Kapitel 5.3.1.4 Funktionen) existiert eine allgemeine Funktion Roof, deren erstes Argument den Namen des Primitivs enthält. Damit werden alle weiteren Argumente um eins nach hinten verschoben, d.h. der Namen der Profile-Grammar wird als zweites Argument übergeben, der Namen der Facade-Grammar als drittes, usw. 70 5.4 Gruppen Allein durch die Zusammensetzung mehrerer Primitive ist es möglich, komplette Gebäude samt Details zu entwerfen. Allerdings würde durch einen solchen Ansatz die Komplexität der Grammatiken drastisch steigen, was wiederum längere Berechnungszeiten mit sich bringen würde. So würde ein rein aus Primitiven bestehendes Gebäude eine möglichst große Vielfalt mit sich bringen, allerdings würde der Nutzer so gut wie jede Kontrolle über das exakte Aussehen des Gebäudes verlieren. Der Zeitaufwand wäre ebenfalls beträchtlich. Je feiner und strukturierter das Gebäude werden soll, umso komplexer werden die Grammatiken und der damit verbundene Zeitaufwand sowie die Fehleranfälligkeit steigen. Aus diesem Grund ist es möglich, bereits vorgefertigte Modelle zu laden. Der Nutzer kann dadurch gewisse von ihm bestimmte Teile des Gebäudes fest definieren. Außerdem wird so ein Großteil der Komplexität aus den Grammatiken ausgelagert. Zum Beispiel ist ratsam, Details (wie Fensterrahmen, Türen, Blumenkästen und Ähnliches) als bereits vormodellierte Objekte zu laden und durch die Grammatiken zufällig platzieren zu lassen. Aber wie kann trotz der Nutzung vorgefertigter Modelle immer noch eine möglichst große Vielfalt erzeugt werden? Um dies zu erreichen, wurden sogenannte Modell-Gruppen eingeführt. ModellGruppen sind eine Ansammlung von Objekten, die alle zu einer Kategorie von Typen gehören sollten. Beispielsweise könnte eine Gruppe verschiedene Modelle von Türen enthalten. Sobald die Grammatik ein Objekt von der Gruppe anfordert, liefert die Gruppe eine zufällige Tür zurück und die Grammatik kann diese platzieren. Gruppen unterteilen sich in zwei unterschiedliche Klassen. Dies sind zum einen die Modell-Gruppen und zum anderen die Textur-Gruppen. Modell-Gruppen wurden weiter oben bereits ausführlich behandelt. Textur-Gruppen besitzen dieselben Eigenschaften, nur anstatt der Modelle enthalten sie Texturen. Zum Beispiel könnte eine Textur-Gruppe verschiedene Arten von Holz-Texturen enthalten und bei jeder Anfrage zufällig eine der Texturen zurückgeben. Das Prinzip von Gruppen ist einfach, aber sehr effektiv, und lagert einen großen Teil der Komplexität aus den Grammatiken aus. Wie auf Gruppen innerhalb der Grammatik zugegriffen werden kann, sowie einige Besonderheiten von Modellen, wird auf den nächsten Seiten innerhalb dieses Kapitels erklärt. 5.4.1 Modell-Gruppen Eine Modell-Gruppe enthält mehrere Variationen des gleichen Typs, beispielsweise verschiedene Arten von Türen. Jede Modell-Gruppe verfügt demnach über eine bestimmte Anzahl an Objekten und erhält zusätzlich einen eindeutigen Namen. Mit Hilfe des Namens kann über eine Funktion auf die Gruppe zugegriffen werden. Bei jedem Zugriff bestimmt die Gruppe ein zufälliges Objekt und liefert dieses zurück. Die Grammatik transformiert dieses Objekt anhand des aktuellen Transformations-Zustands und erzeugt schließlich das Modell. 71 5.4.1.1 Funktion Wurde einer Gruppe der Name und die Objekte zugewiesen, kann über die Funktion ModelGroup("Name: Index", UndoRotate, IntersectSetting) ein Objekt aus der Gruppe angefordert werden. Nur eine Mass-, Facade- oder Border-Grammar darf eine Gruppe laden, damit bleibt die Profile-Grammar als einzige Grammatik ausgeschlossen, s. Kapitel 5.2.5 Kommunikation. Die Bedeutung der einzelnen Argumente wird durch die folgende Tabelle erklärt. Name:Index Das erste Argument gibt den Namen der Modell-Gruppe an. Zusätzlich kann dem Namen, durch einen Doppelpunkt getrennt, ein Index mit übergeben werden. Wird kein Index angegeben, dann liefert die Gruppe ein zufälliges Objekt zurück. Wird allerdings ein Index angehängt, lässt sich ein bestimmtes Modell aus der Gruppe erzwingen. So würde ein Index von 1 zur Folge haben, dass immer das zweite Objekt (da die Indexierung bei 0 beginnt) aus der Gruppe geladen wird. UndoRotate Wird fast ausschließlich beim Laden von Objekten auf Dach-Fassaden benötigt. Falls das Argument eine 1 erhält, wird die Rotation der Fassade bzw. Kante rückgängig gemacht. IntersectSetting Das Argument bestimmt die Einstellungen für den Schnitt mit Primitiven. Mögliche Werte die das Argument annehmen kann, sind 0, 1, oder 2. Genaueres über den Schnitt von Modellen wird Kapitel 5.4.1.2 Schnitt erläutert. 5.4.1.2 Schnitt In manchen Fällen kann es vorkommen, dass die Flächen eines Primitivs teilweise oder gar komplett innerhalb des Volumens eines anderen Primitivs liegen. In solchen Fällen liegen dann ebenfalls häufig, die an den Fassaden bzw. Kanten erzeugte Modelle vollständig oder teils innerhalb des Primitivs. Über die ModelGroup-Funktion kann nun entschieden werden, wie mit solchen Modellen weiterverfahren werden soll. Dabei können drei verschiedene Fälle für ein Objekt eintreten. Die erste Möglichkeit wäre, dass sich das Modell komplett außerhalb jedes Primitivs befindet. Die zweite, das Modell befindet sich vollständig in einem oder mehreren Primitiven. Oder aber, der dritte Fall tritt ein und das Modell schneidet ein oder mehrere Primitive, aber befindet sich in keinem vollständig. Zur Demonstration präsentiert Abb. 5.4.1 die drei verschiedenen Möglichkeiten. Die Fenster-Modelle wurden jeweils von dem Haupttrakt erzeugt und befinden sich evtl. vollständig (rot) oder teilweise (grün) innerhalb des Nebentrakts. Abb. 5.4.1 Schnitt 72 Das dritte Argument der ModelGroup-Funktion steuert in welchen der Fälle die Objekte angelegt werden. Wie in Kapitel 5.4.1.1 Funktion erklärt wurde, kann an das Argument entweder eine 0, 1 oder 2 übergeben werden. Wurde eine 0 gesetzt, wird das erzeugte Objekt verworfen, wenn es sich innerhalb eines Primitivs befindet oder dieses nur schneidet. Bei einer 1 wird das Modell nur verworfen, wenn es sich komplett innerhalb eines anderen Primitivs befindet. Beträgt der Wert des Arguments eine 2, dann wird das Modell immer platziert, auch wenn es vollständig oder teilweise innerhalb eines Primitivs liegt. 5.4.1.3 CutOut-Modell Im bisherigen Verlauf der Arbeit wurde gezeigt, dass beliebige Modelle an den Fassaden oder sonstigen Bereichen platziert werden können. Einige Modelle verlaufen dabei ins Innere des Gebäudes, z.B. Fenster, Türen, Nischen, etc. Da die Wände des Masse-Modells nicht aufgespaltet werden und deshalb keine Lücke enthalten, durchlaufen sie häufig die einzelnen Modelle. Das entstehende Problem kann in Abb. 5.4.2 beobachtet werden. Eine mögliche Lösung für dieses Problem wäre, für jedes einzelne Modell eine Lücke innerhalb der Wand zu formen. Diese Variante birgt allerdings einige Nachteile in sich. Zum einen müssen die Schnitte zwischen den eventuell sehr komplexen Modellen und den Flächen der Primitive ermittelt werden. Zum anderen muss ein Algorithmus entwickelt werden, der die Wand inklusive der Lücken geeignet trianguliert. Beide Verfahren benötigen sehr komplizierte Algorithmen, welche die Laufzeit, die Anzahl der Polygone und den Speicherverbrauch drastischen steigern. Anstatt die Geometrie der Wände zu unterteilen, wird auf einen kleinen grafischen Trick zurückgegriffen. Jedes Modell mit Bereichen, die in das Innere einer Wand verlaufen, wird zusätzlich ein spezielles CutOut-Modell zugeteilt. Das CutOut-Modell benötigt keine Textur und soll nur bestimmte Bereiche des Tiefenbuffers „ausschneiden“. Damit können die Wände an den Stellen dieser speziellen Modelle nicht gezeichnet werden. Abb. 5.4.2 Fehler aufgrund einer durchlaufenden Wand Damit das Verfahren funktioniert, werden die Modelle und Primitive in einer bestimmten Reihenfolge gezeichnet. Zuerst müssen alle Modelle wie gewohnt in den Z-Buffer und Color-Buffer gezeichnet werden. Nach diesem Schritt können die zugewiesenen CutOut-Modelle gezeichnet werden, allerdings nur auf den Z-Buffer und nicht auf den Color-Buffer. Infolge dessen verändern die Modelle zwar den Tiefenbuffer, sind aber nicht sichtbar, da keine Farbinformationen gespeichert werden. Werden nun im letzten Schritt die Flächen der Primitive gezeichnet, und zwar wie gehabt auf den Z-Buffer und Color-Buffer, geschieht dies an allen Stellen, außer an denen durch die CutOut-Modelle veränderten Bereiche. In Abbildung 5.4.3 wird das Modell eines Fensters dargestellt. Zusätzlich wird in der Abbildung ein passendes CutOut-Modell in Rot angezeigt. Dieses Modell wird verwendet, um den Tiefenbuffer zu verändern und damit die dahinter liegende Wand auszuschneiden. Abb. 5.4.3 CutOut-Modell 73 5.4.1.4 Anwendung Die Mass-Grammar aus Abb. 5.4.4 soll in gleichmäßigen Abständen drei Fenster erstellen. Dazu wurde eine Modell-Gruppe mit der Bezeichnung "Window" angelegt und danach zwei Modelle zugeordnet. Die Grammatik erzeugt die in Abbildung 5.4.5 dargestellten Fenster. Das linke Fenster wird dabei zufällig aus der Gruppe ausgewählt und verändert sich jedes Mal, wenn die Grammatik erneut ausgeführt wird. Das zweite Fenster erhält zusätzlich einen Index von 1, der bewirkt, dass immer das zweite Fenster aus der Gruppe geladen wird. Das dritte Fenster wurde zuerst einer Konstanten zugewiesen, diese erhält zu Beginn ein zufälliges Objekt aus der Gruppe. Die Konstante kann schließlich als Argument an die Funktion ModelGroup übergeben werden und bewirkt damit, dass immer das Modell geladen wird, das ihr zu Beginn zugewiesen wurde. Alle Fenster werden normal durch die Transformations-Sequenz beeinflusst und umgewandelt, in diesem Fall eine Skalierung und Translation. cModel = ModelGroup("Window"); Main -> S( 2, T( 0, T(20, T(20, 2, 0, 0, 0, 2) 0) ModelGroup("Window") 0) ModelGroup("Window:1") 0) ModelGroup(cModel); Abb. 5.4.4 Mass-Grammar Abb. 5.4.5 Ergebnis der Mass-Grammar 5.4.2 Textur-Gruppen Textur-Gruppen enthalten Varianten von Texturen, die einer Kategorie zugeordnet werden können, zum Beispiel verschiedene Marmor-Texturen. Wie bei einer Modell-Gruppe erhält jede Textur-Gruppe einen eindeutigen Bezeichner. Dieser wird auch hier benötigt um Gruppe zu identifizieren. Textur-Gruppen können ausschließlich von einer Profile-Grammar geladen werden. Verwendet wird dafür die Funktion TextureGroup("Name:Index"). Wie bei der bereits beschriebenen ModelGroupFunktion gibt auch hier das erste Argument den Namen der Gruppe und eventuell ein Index an. Falls ein Index angehängt wird, bestimmt dieser, welche Textur geladen werden soll (die Indexierung beginnt auch hier bei 0). D.h. bei Angabe eines Index wird die Textur nicht mehr zufällig aus der Gruppe ermittelt, sondern immer die, durch den Index bestimmte Textur, geladen. Die drei Wand-Texturen aus Abbildung 5.4.6 veranschaulichen eine beispielhafte Gruppe für die MauerAbschnitte eines Bauwerks. Über die Gruppe kann nun zufällig oder gezielt eine dieser Texturen geladen und auf den Abschnitten einer Profile-Grammar gesetzt werden. Abb. 5.4.6 Textur-Gruppe für Mauerstrukturen 74 5.5 Subdivision und Symmetrie Aufgrund der räumlichen Aufteilung von Häusern und vor allem durch die Trennung in einzelne Stockwerke besitzen die meisten Fassaden eine dementsprechende Anordnung der Fenster, Türen und sonstigen Merkmalen bzgl. dieser Aufteilung. Dahingehend werden die Fassaden in Zeilen und Spalten strukturiert, und die Objekte innerhalb einer Zeile bzw. Spalte verfügen meist über eine exakte oder ähnliche Anordnung. Zusätzlich treten bei vielen Gebäuden häufig Symmetrien innerhalb des Grundmodells und der Fassaden auf, als Beispiel kann man das „Weiße Haus“ benennen. Wird dieses in der Mitte geteilt, bildet die linke Hälfe eine gespiegelte Variante der rechten Hälfte. Als Beispiel für symmetrische Fassaden müssen nur die meisten Fachwerkbauten betrachtet werden. Diese besitzen meistens ein symmetrisches Fachwerkmuster. Ein geeignetes Mittel, solche Aufteilungen durch Regeln zu formulieren, wurde von [WWS+03] mit einer Split-Grammar umgesetzt. Diese Idee wurde übernommen und ähnlich realisiert. Allerdings wird dazu keine komplett eigenständige Grammatik (Split-Grammar) definiert, sondern wie in dem Ansatz von [MWH+06] werden einige Methoden in die bereits bestehende Grammatik integriert. Mit Hilfe dieser Funktionen können Unterteilungen an den Fassaden oder Kanten vorgenommen werden. Außerdem wurde ein Verfahren hinzugefügt, welches dem Nutzer ermöglicht, mit einfachen Mitteln Symmetrien zu erzeugen. 5.5.1 Subdivision Wie bei der von [MWH+06] definierten Shape-Grammar können vertikale und horizontale Unteilungen an allen Fassaden (Bereich einer Facade-Grammar), aber auch Grundstücken (Bereich einer Mass-Grammar) oder Kanten (Bereich einer Border-Grammar) vorgenommen werden. Anders als bei [MWH+06] wird dabei nicht die Geometrie der Wände unterteilt bzw. durch Formen ersetzt, sondern nur der Bereich, in dem eine Grammatik arbeitet. Abbildung 5.5.1 (a) zeigt die Fassaden der beiden Stockwerke aus dem durchgängigen Beispiel von Kapitel 5.2 Grammatiken. Wird nun eine horizontale Subdivision in drei Teile1 vollführt, entstehen die in Abbildung 5.5.1 (b) dargestellten Fassaden. Der Vorgang könnte nun für jede einzelne Unterteilung erneut horizontal oder vertikal durchgeführt werden und damit weitere Teilungen erzeugen. Innerhalb der Grammatik können die verschieden Partitionen durch Funktionen erzeugt werden. Um die Handhabung möglichst einfach zu halten, wurden vier verschiedene Funktionen implementiert, dies sind die Funktionen SubDiv, Part und SplitX bzw. SplitY. Alle Funktionen können vertikale und horizontale Spaltungen durchführen und für jeden neu entstandenen Teil eine Produktion aufrufen. Allerdings wird die Anzahl und die Größe der Teilung bei jeder Funktion anders formuliert. Auf den nachfolgenden Seiten werden die vier Funktionen genauer erläutert. Abb. 5.5.1 Subdivision 1 Im fortlaufenden auch Zelle oder Partition genannt. 75 5.5.1.1 SubDiv Die Funktion SubDiv("Production", d, "Type", SameRule) unterteilt den Bereich, in dem eine Grammatik arbeitet, und ruft danach für jeden Teil eine Produktion auf. Die Produktion wird durch das erste Argument bestimmt. Bei der Unterteilung geht die Funktion so vor, dass sie die Größe der jeweiligen Zellen festlegt und nicht die Anzahl der Unterteilungen. D.h. jede Zelle besitzt eine genau festgelegte Größe d, und der Bereich wird in soviel Zellen geteilt, wie Zellen der Größe d in den Bereich hineinpassen. Dabei kann die letzte Zelle eventuell nicht die volle Größe besitzen, sondern nur den Rest des Bereichs erhalten, siehe Abb. 5.5.3. Die Größe (Breite oder Höhe, je nach dem ob eine vertikale oder horizontale Unterteilungen vorgenommen wird) wird durch das zweite Argument d definiert. Das dritte Argument bestimmt die Art der Subdivision. Hier kann entweder die Zeichenkette "X" oder die Zeichenkette "Y" übergeben werden. "X" veranlasst eine horizontale Teilung, während "Y" eine vertikale Unterteilung festlegt. Das vierte Argument bestimmt, ob alle Zellen die gleiche Regel der zugeteilten Produktion ausführen, für den Unterschied zwischen einer Regel und einer Produktion siehe Kapitel 5.1.4 Produktionen. D.h. falls eine 1 übergeben wird, wird zu Beginn der Subdivision zufällig eine Regel aus der Produktion ausgewählt und dann für alle Zellen ausgeführt. Bei einer 0 dagegen wird für jede Zelle zufällig eine Regel aus der Produktion gewählt. Wofür wird dieses Argument benötigt? Sehr viele Gebäude, vor allem Hochhäuser, besitzen innerhalb eines Stockwerks (Zeile) oder über mehrere Stockwerke hinaus (Spalte) die gleichen Merkmale, z.B. Fenster vom gleichen Typ. Für solche Gegebenheiten wird das Argument auf 1 gesetzt, damit wird für alle Bereiche (z.B. Synonym für Stockwerke) die gleiche Regel (Synonym für Merkmal) ausgeführt. Hier ein kleines Beispiel. Die Facade-Grammar aus Abb. 5.5.2 unterteilt eine Fassade in Zellen der Größe 10. Da die Fassade in diesem Fall eine Breite von 45 besitzt, kann die letzte Zelle nur eine Breite von 5 erhalten. Es entsteht die Unterteilung aus Abbildung 5.5.3. Zusätzlich wurde das letzte Argument auf 1 gesetzt, d.h. nach der Subdivision wird aus der Produktion Tile, zufällig eine der beiden Regel A oder B ausgewählt, siehe Kapitel 5.1.5 Wahrscheinlichkeiten. Diese Regel wird für alle Zellen aufgerufen, in diesem Fall rein zufällig A. Gekennzeichnet wird dies innerhalb der Abb. 5.5.3, durch die Buchstaben in den Zellen. Main -> SubDiv("Tile", 10, "X", 1); Tile -> A | B; A -> ...; B -> ...; Abb. 5.5.2 SubDiv Grammatik Abb. 5.5.3 SubDiv Ergebnis 5.5.1.2 Part Im Gegensatz zur Funktion Subdiv, bei der die Größe der einzelnen Zellen festgelegt wurde, definiert die Funktion Part("Production", n,"Type", SameRule) die Anzahl der Zellen. D.h. ein Bereich wird in n Partitionen gespalten, wobei jede Partition die gleiche Größe besitzt. Nach der Unterteilung wird für jede Zelle die Produktion "Production" aufgerufen, die eventuell für alle die gleiche Regel ausführt, falls SameRule gleich 1 ist (s. Kapitel 5.5.1.1 SubDiv). Abbildung 5.5.4 verdeutlicht die erweiterte Facade-Grammar von Abb. 5.5.2. Nachdem die Subdivision durch die Funktion SubDiv ausgeführt (s. Abb. 5.5.3) und für alle Zellen die Produktion A aufgerufen 76 wurde, wird erneut eine Unterteilung durchgeführt. Dabei werden die bereits erzeugten Zellen in 4 gleich große Partitionen gespalten. Dieser Vorgang wird in Abb. 5.5.5 dargestellt. Diesmal wurde zur Unterteilung die Funktion Part verwendet, welche eine vertikale statt einer horizontalen Teilung durchführt. Außerdem wurde das letzte Argument auf 0 gesetzt, dies bedeutet, dass für jede Zelle eine zufällige Regel C oder D ermittelt wird. Abbildung 5.5.5 verdeutlicht deshalb eines von sehr vielen möglichen Ergebnissen, die durch die zwei Funktionen (SubDiv und Part) erzielt werden können. Main -> SubDiv("Tile1", 10, "X", 1); Tile1 -> A | B; Tile2 -> C | D; A B C D -> -> -> -> Part("Tile2", 4, "Y", 0); ...; ...; ...; Abb. 5.5.4 Part Grammatik Abb. 5.5.5 Part Ergebnis 5.5.1.3 Split Die dritte Variante, den Bereich einer Mass-, Facade- oder Border-Grammar aufzuspalten, wird durch die Funktion SplitX bzw. SplitY ermöglicht. Die Funktion SplitX führt eine horizontale Spaltung durch, während die Funktion SplitY eine vertikale Spaltung ausführt. Anders als bei den vorherigen Methoden (s. Kapitel 5.5.1.1 SubDiv und Kapitel 5.5.1.2 Part) können hier variabel viele Argumente übergeben werden. Die genaue Angabe der Argumente wird durch die folgende Notation bestimmt. SplitX("Production0", p0, "Production1", p1, ...) SplitY("Production0", p0, "Production1", p1, ...) Anstatt nun die Größe oder die Anzahl der Zellen festzulegen, werden hier prozentual die Bereiche definiert. Wird beispielsweise für p0 der Wert 0.3 übergeben, werden die ersten 30% des Bereichs in eine Zelle gespalten. Für diese Zelle wird die Produktion "Production0" aufgerufen. Erhält danach p1 den Wert 0.7, werden die hinteren 70% in eine zweite Zelle gespalten, für welche diesmal die Produktion "Production1" aufgerufen wird. Dieser Vorgang ist variabel, und es können beliebig viele Argumente übergeben werden und damit der Bereich in beliebig viele Zellen gespalten werden. Selbstverständlich müsse die Werte immer zwischen 0 und 1 liegen und dürfen in der Summe nicht 1 überschreiten, da ansonsten der Bereich der Grammatiken verlassen wird. Ein Beispiel für den Einsatz einer Split Funktion wird in Abb. 5.5.6 und Abb. 5.5.7 dargelegt. Das Beispiel bedarf keiner weiteren Erklärung, da die Seite hier endet. Main -> SplitX("A", 0.1, "B", 0.6, "C", 0.3); A -> ...; B -> ...; C -> ...; Abb. 5.5.6 Split Grammatik Abb. 5.5.7 Split Ergebnis 77 5.5.2 Symmetrie Kommen wir zur Formulierung von Symmetrien innerhalb der Subdivisionen. Die nachfolgenden Erweiterungen betreffen nur die Funktionen SubDiv (s. Kapitel 5.5.1.2 SubDiv) und Part (s. Kapitel 5.5.1.2 Part) und können über ein zusätzliches Argument aktiviert werden. Wird beiden Funktionen als zusätzliches fünftes Argument eine 1 übergeben, so wird damit eine symmetrische Unterteilung eingeleitet. D.h. anstatt für alle Zellen eine zufällige Regel aus der Produktion zu ermitteln (s. Kapitel 5.5.1.2 SubDiv und Kapitel 5.5.1.2 Part) oder für alle Zellen eine einzige zufällige Regel zu berechnen, werden nun nur noch für die linke Hälfte der Unterteilung (einschließlich der mittleren Zelle, falls vorhanden) zufällig Regeln ermittelt. Die rechte Hälfte erhält dann die gespiegelte Variante der linken Hälfte. Die Vorgehensweise sieht folgendermaßen aus. Die Funktion beginnt von links bzw. unten und ermittelt für jede Zelle eine zufällige Regel aus der Produktion. Dies geschieht so lange, bis die mittlere Partition erreicht wurde. Bei jeder erzeugten Partition wird die Regel auf einen Stack gelegt, den sogenannten Symmetry-Stack. Auch hier werden die neuen Daten immer ganz oben auf dem Speicher platziert (Push) und es wird immer der oberste Datensatz vom Speicher geladen (Pop), wie bei einem normalen Stack. Wurde die linke Seite der Subdivisionen zufällig ermittelt, wird die rechte Seite durch die Regeln auf dem Symmetry-Stack bestimmt. Das heißt, jede Zelle, die sich rechts von der Mitte befindet, wählt ihre Regeln nicht selbst aus, sondern holt sich die Regel von Symmetry-Stack. Dadurch wird auf der rechten Hälfte eine Spiegelung erreicht. Zusätzlich wird während des Prozesses ständig ein Status verändert. Dieser Status kann über die vordefinierte Konstante SYM abgefragt werden. SYM erhält den Wert 0, falls eine Zelle der linken Hälfte erzeugt wird. Den Wert 1, wenn eine Zelle der rechten Hälfte generiert wird und eine 2, falls eine ungerade Anzahl an Zellen existiert und die mittlere Zelle generiert wird. Die Abb. 5.5.8 bildet eine neue Facade-Grammar ab. Die Grammatik führt eine horizontale Unterteilung in 7 gleiche große Partitionen aus, siehe Abb. 5.5.9. Durch Übergabe einer 1 an das letzte Argument der Part-Funktion wird eine symmetrische Unterteilung eingeleitet. Die ersten drei Zellen erhalten eine zufällige Regel aus der Produktion Tile. Jede der ausgewählten Regeln wird auf den Symmetry-Stack gelegt, so lange, bis die Mitte erreicht wird. Die Zelle der Mitte erhält ebenfalls eine zufällige Regel, legt diese allerdings nicht auf den Stack. Sobald die erste Zelle der rechten Hälften erzeugt wird, wählt diese nicht eine zufällige Regel aus, sondern holt sich die Regel vom Symmetry-Stack und löscht sie danach. Das Resultat dieser Vorgehensweise ist in Abb. 5.5.9 zu erkennen, dabei Main -> Part("Tile", 7, "X", 0, 1); bildet die Zeichnung eine von vielen Tile -> A | B | C; möglichen Lösungen ab. Die Variable B verdeutlicht zusätzlich A -> .../*Blue*/; den Einsatz der vordefinierten Konstante SYM. Die Konstante wird innerB [1, 1, 1] {SYM == 0, SYM == 2, SYM == 1} -> .../*Green*/ | .../*Red*/ | .../*Green*/; halb der Bedingungen eingesetzt und sorgt dafür, dass je nach Status eine C -> .../*Orange*/; andere Regel ausgewählt wird. Aus diesem Grund wird die mittlere Zelle Abb. 5.5.8 Symmetrie Grammatik rot dargestellt, während die beiden äußeren Zellen von B, in grün dargestellt werden. Abb. 5.5.9 Symmetrie Ergebnis 78 5.6 Umwelt Wie Pflanzen erhalten auch Gebäude Einfluss durch ihre lokale sowie globale Umwelt, beziehungsweise werden Gebäude in den meisten Fällen schon während des Entwurfs der Umgebung angepasst und dementsprechend gebaut. Zum Beispiel werden die Türen eines Gebäudes häufig in die Nähe der Straße gelegt. Oder, je dichter die Population innerhalb eines Gebietes ist, desto mehr Wohnraum wird benötigt. Dies führt zu größeren und höheren Wohnhäusern. Aber auch kleine Details können die Informationen der Umgebung verwenden. So könnte eine Wand von Efeu bedeckt sein, wenn diese sich in der Nähe eines Waldes befinden. Grenzt die Wand allerdings an eine Straße, so zieren stattdessen Blumenkästen die Fenster. In Gegensatz zu den in Kapitel 3.6 Umgebungssensitive und offene Lindenmayer-Systeme erklärten umweltsensitiven L-Systemen werden hier keine speziellen Anfrage-Module benötigt, stattdessen kann die Umwelt über normale Funktionen Anfragen beantworten. Im Laufe dieses Kapitels wird die Funktionsweise sowie Abfrage der globalen und lokalen Eigenschaften erklärt und anhand praktischer Beispiele verdeutlicht. 5.6.1 Globale Umwelt Die globalen Eigenschaften beschreiben die nicht an einen lokalen Ort gebundenen Eigenschaften der Umwelt, wie bereits in Kapitel 3.6 Umgebungssensitive und offene Lindenmayer-Systeme aufgeführt. Es wurden drei Eigenschaften der globalen Umwelt implementiert, zu diesen zählen die Temperatur, der Niederschlag und die Bevölkerungsdichte. (1) Temperatur Diese Eigenschaft bestimmt die minimale, maximale und durchschnittliche Temperatur der Region. Die Eigenschaft beschreibt den minimalen, maximalen und mittleren Nieder(2) Niederschlag schlag innerhalb der Region. (3) Populationsdichte Die Eigenschaft bestimmt die durchschnittliche Bevölkerungsdichte je Quadratkilometer. Um eine Vorstellung davon zu erhalten, in welchen Größenverhältnissen sich die Werte dieser Eigenschaften bewegen, folgen ein paar realistische Werte [Bro882]: Im Bereich Frankfurt beträgt die mittlere Niederschlagsmenge 55 mm im Monat Mai und die mittlere tägliche Temperatur 14,5° C (über das gesamte Jahr). Die Bevölkerungsdichte in Mainz liegt bei 182 Einwohnern je km2. Die Populationsdichte kann beispielsweise die Größe eines Hauses und dessen Anzahl an Stockwerke beeinflussen. Der maximale Niederschlag bestimmt dagegen, ob die Gebäude eine Regenrinne an der Traufe erhalten. Die Abfrage innerhalb der Grammatik erfolgt über die Funktion GEnv("Property"). Das einzige Übergabeargument ist eine Zeichenkette, die angibt welche globale Eigenschaft abgerufen werden soll. Mögliche Werte für das Argument sind "MinTemperature", "MaxTemperature", "AvgTemperature", "MinRainfall", "MaxRainfall", "AvgRainfall" und "PopulationDensity". Wird z.B. "AvgRainfall" als Argument übergeben, liefert die Funktion den Wert für die mittlere Niederschlagsmenge zurück. Dies wäre zum Beispiel der oben erwähnte Wert von 55 mm. Die Umsetzung und Anfrage ist demnach sehr einfach, verständlich und erfolgt über eine einzige Funktion. Diese kann innerhalb der Grammatik beliebig eingesetzt werden, z.B. als Teil einer Bedingung, als Faktor einer Regel-Wahrscheinlichkeit, usw. Die Eigenschaften lassen sich sehr leicht in das System integrieren und könnten noch um zusätzliche Eigenschaften ausgebaut werden, beispielsweise, um die relative Luftfeuchtigkeit, die mittlere Windgeschwindigkeit, die tägliche Sonnenscheindauer oder ähnliche Eigenschaften. 79 5.6.2 Lokale Umwelt Die lokale Umwelt spiegelt die meist physischen an einen Ort gebundenen Eigenschaften der Umgebung wieder. Zum Beispiel die in der Nähe eines Gebäudes verlaufenden Straßen oder Flüsse. Aber auch beispielsweise die Position der anderen Gebäude, wodurch ermittelt werden kann, ob das Gebäude vollständig von anderen Gebäuden umzingelt ist und sich deshalb in einem Wohnblock befindet. Die eigentliche Realisierung der lokalen Umgebung wurde mit Hilfe einer Rasterung erreicht, wodurch eine schnelle Analyse der Umgebung erzielt werden kann. Folglich wird ein Raster über die Umgebung gelegt. Diese wird entsprechend dem Raster unterteilt und ordnet jedem Feld1 eine Information bzw. einen Typ zu. In Abbildung 5.6.1 ist eine Landschaft aus dem Spiel Anno 1701 zu sehen. Wird über diese Landschaft ein Umgebungsraster gelegt, entsteht das Beispielmuster aus Abb. 5.6.2. Die verschiedenen Farben der Kacheln stellen jeweils einen anderen Umgebungstyp dar, die unterschiedlichen Typen werden im Kapitel 5.6.2.1 Typen erklärt. Im Gegensatz zu den globalen Eigenschaften ist die Abfrage der lokalen Eigenschaften nicht mehr so einfach. Um eine Information über die naheliegende Umgebung zu erhalten, muss die lokale Umgebung (Raster) abgetastet werden. Zu diesem Zweck wurden verschiedene Abtast-Algorithmen eingeführt. Die verschiedenen Algorithmen werden in Kapitel 5.6.2.2 Abtastung beschrieben und die Funktionen zur Abfrage schließlich in Kapitel 5.6.2.3 Anfrage-Funktionen. 1 im fortlaufenden auch Kachel genannt. 80 Abb. 5.6.1 Umgebung Abb. 5.6.2 Umgebungsraster 5.6.2.1 Umgebungstypen Wie bereits beschrieben wurde, wird die Umgebung in Kacheln aufgeteilt, und jeder Kachel wird ein Umgebungstyp zugewiesen. Dieser Typ wird durch die Merkmale innerhalb des Bereichs der Kachel bestimmt. Befindet sich beispielsweise ein Baum innerhalb einer Kachel, so wird dieser der Typ Flora zugewiesen. Zurzeit existieren sechs verschiedene Umgebungstypen: Wasser, Flora, Gebirge, Straßen, Gebäude und freie Flächen. (1) Freie Fläche Die Kachel weist keinerlei besondere Merkmale auf, d.h. keine der darauf folgenden Typen kann dieser Fläche zugewiesen werden. Innerhalb der Kachel befinden sich Bäume, Sträucher oder ähnliche Pflanzen. (2) Flora Große Steine, Felsen oder Teile eines Gebirges füllen den größten Teil der Kachel aus. (3) Gebirge Die Kachel liegt zum größten Teil im Wasser. Dies können Teile des Meers, eines (4) Wasser Flusses, eines Teichs oder Ähnliches sein. Feldwege, gepflasterte Straßen oder sonstige Pfade füllen die Kachel aus und bil(5) Straßen den damit das Merkmal dieser Kachel. Innerhalb der Kachel befinden sich Teile eines Gebäudes, dies können Wände oder (6) Gebäude sonstige Details sein. Erneut wird auf die Abbildung 5.6.2 zurückgegriffen. Innerhalb der Abbildung werden bereits alle vorhandenen Umgebungstypen dargestellt. Die dunkelgrünen Kacheln stellen dabei freie Bereiche dar, die hellgrünen Kacheln stehen für Flora, die gelben für ein Gebirge, die blauen für Wasser, die hellgrauen Kacheln weisen Straßen auf und zu guter letzt die roten Felder, die das Vorkommen an Gebäuden kennzeichnen. 5.6.2.2 Abtastung Bei einer Anfrage an die lokale Umwelt kann die Umgebung durch verschiedene Verfahren analysiert werden. Möchte eine Grammatik zum Beispiel die Anzahl der nah verlaufenden Straßen erhalten, so könnte die Umwelt um einen Punkt gleichmäßig in alle Richtungen abgetastet werden. Durch diese Methode würde die Grammatik Informationen über die komplette, nahliegende Umgebung erhalten. Dies wäre nicht sinnvoll, wenn beispielsweise eine einzelne Wand, Auskunft darüber erhalten möchte, ob sie in der Nähe einer Straße liegt. Aus diesem Grund wurden vier verschiedene Abtast-Methoden eingeführt. Eine ist die bereits erwähnte Abtastung um einen Punkt herum, eine weitere die Abtastung entlang einer Wand/Fläche, die Dritte bzgl. einer Ecke und als Viertes, die Abtastung der Umwelt von einem Punkt ausgehend in eine beliebige gerade Richtung. Die jeweilige Analyse wird dadurch unterschieden, welche Startinformation für die Abtastung zu Verfügung steht. Dies ist entweder ein einzelner Punkt, eine Fläche, eine Ecke oder ein Punkt mit einer Geraden. Bei allen vier Abtast-Methoden werden beliebig viele Kacheln des Rasters durchlaufen. Für jede Kachel, die während des Abtastens durchlaufen wird, wird ein passender Zähler um eins inkrementiert. D.h. für jeden Umgebungstyp existiert ein Zähler, welcher vor dem Abtasten mit null initialisiert wird und nach dem Abtasten Auskunft darüber gibt, wie viele Kacheln des Typs sich in der Nähe befinden. Der direkte Wert des Zählers liefert eine absolute Information über die Umgebung, zum Beispiel, dass 5 Kacheln des Typs Gebirge gescannt wurden. Wird der Zähler durch die Anzahl aller durchlaufenden Kacheln dividiert, erhält man eine relative Information der Umgebung. Zum Beispiel, dass 50 % der abgetasteten Umgebung von einem Gebirge belegt wird. 81 5.6.2.2.1 Punkt-Abtastung Die einfachste Variante, die Umgebung zu scannen ist, alle um einen Punkt liegende Kacheln abzufragen. Dabei werden die Kacheln des eigenen Gebäudes ignoriert, da die Informationen über die eigenen Bereiche nicht sinnvoll wären. Schließlich will das Gebäude die Umgebung analysieren und nicht sich selbst. Abbildung 5.6.3 (a) zeigt die Iso-Sicht(Draufsicht) eines Gebäudes samt Umgebungsraster. Die roten Kacheln innerhalb der Abbildung verdeutlichen den Bereich des Gebäudes. Der Ausgangspunkt für die Abtastung wird durch den grünen Punkt dargestellt. Da die roten Kacheln beim Abtasten der Umgebung ignoriert werden, wird mit der ersten Kachel außerhalb des roten Bereichs begonnen. Das System läuft alle Felder rings um das Gebäude ab und zählt jeweils die Umgebungstypen, siehe Abb. 5.6.3 (b). Wurden alle Felder gezählt, werden die Werte entweder absolut oder relativ zurückgegeben. D.h. entweder wird direkt der absolute Wert verwendet, beispielsweise das 4 Straßen-Kacheln gezählt wurden. Oder aber, die Zähler der jeweiligen Typen werden durch die Anzahl aller gelesenen Felder dividiert, dadurch liegen alle Werte zwischen 0 und 1. Wurden z.B. 4 Kacheln von Typ Straße gezählt und insgesamt 16 Felder durchlaufen, ergibt dies einen relativen Wert von 0.25. Das bedeutet, dass fünfundzwanzig Prozent der Umwelt durch Straßen belegt werden. Nach einer Iteration muss der Vorgang nicht unbedingt beendet werden, es können noch weitere Stufen durchlaufen werden. Dazu werden alle Kacheln der nächsten Stufe gezählt, wie in Abb. 5.6.3 (c) zu sehen ist. Soll der absolute Wert berechnet werden, können die Zähler für die jeweiligen Umgebungstypen einfach erhöht werden. Soll der relative Wert berechnet werden, erhalten alle weiteren Kacheln eine Gewichtung. Die Gewichtung entspricht der reziproken Nummer der Iteration, d.h. in der ersten Iteration (s. Abb. 5.6.3 (a)) erhalten alle Kacheln eine Gewichtung von 1, in der zweiten Iteration (s. Abb. 5.6.3 (b)) erhalten die Kacheln eine Gewichtung von ½, usw. Normiert werden die Kacheln durch die Division mit der Summe aller Kacheln. Es gilt demnach, je weiter eine Kachel entfernt ist, desto schwächer wird deren Gewichtung. Dadurch üben naheliegende Kacheln einen größeren Einfluss auf das Gebäude aus, als weit entfernte Kacheln. Abb. 5.6.3 (a) Iso-Sicht der Umgebung, (b) erste Iteration der Punkt-Abtastung und (c) zweite Iteration der Punkt-Abtastung 5.6.2.2.2 Flächen-Abtastung Eine weitere Methode, die Abtastung der Umgebung durchzuführen, ist die Abtastung bzgl. einer Fläche. Dabei wird jeweils ein Strahl von den beiden äußeren Kanten der Fläche losgeschickt. Beide Strahlen laufen in die Richtung der Flächen-Normale, wobei auch hier die Kacheln des Gebäudes ignoriert werden. Damit bilden die beiden ersten, von den Strahlen getroffenen Kacheln (außerhalb des Gebäudebereichs) einen Start- und Endpunkt. Anstatt nun alle Kacheln außerhalb des Gebäudebereichs zu zählen, werden nur die Felder zwischen dem Start- und Endpunkt gezählt, siehe Abb. 5.6.4 (b). Wie 82 bei der Punkt-Abtastung können die Umgebungsinformation absolut oder relativ berechnet werden. Außerdem können weitere Iterationen ausgeführt werden, in dem die Strahlen bis zu den nächsten Kacheln weitergeschickt werden. Die so neu ermittelten Kacheln dienen wieder als Start- und Endpunkt, siehe Abb. 5.6.4 (c). Ebenfalls wie bei der Punkt-Abtastung werden die Kacheln bei jeder weiteren Iteration geringer gewichtet, falls eine relative Anfrage an die Umgebung gestellt wird. Wie die Abbildungen aus 5.6.4 verdeutlichen, werden nur die Kacheln abgetastet, die sich vor der Wand befinden. Eine Fläche erhält dadurch keine für sie unwichtigen Informationen von Kacheln, die sich hinter der Fläche befinden. Abb. 5.6.4 (a) Iso-Sicht der Umgebung, (b) erste Iteration der Flächen-Abtastung und (c) zweite Iteration der Flächen-Abtastung 5.6.2.2.3 Kanten-Abtastung Die Kanten- bzw. Ecken-Abtastung funktioniert ähnlich wie die Flächenabtastung, nur diesmal werden zwei Strahlen von einem Punkt ausgehend verschickt. Die Abb. 5.6.5 (a) zeigt eine Ecke, die durch einen grünen Punkt dargestellt wird. Für diese Ecke soll nun eine Abtastung der Umgebung durchgeführt werden. Wie bei der Flächenabtastung werden zwei Strahlen verschickt. Diese starten beide aus einem gemeinsamen Punkt, der dem Startpunkt der Kante bzw. Ecke entspricht. Die beiden Strahlen sind durch die Normalen der mit der Ecke verbundenen Flächen gegeben. Auch hier bilden die beiden ersten Kacheln, die von den Strahlen getroffen werden, einen Start- und Endfeld. Die Umgebungstypen aller Felder zwischen den beiden Kacheln werden gezählt und deren Ergebnis absolut oder relativ zurückgegeben, wie in Abb. 5.6.5 (b) gezeigt wird. Wie schon bei der Punkt- und Flächen-Abtastung können weitere Iterationen durchgeführt werden, siehe Abb. 5.6.5 (c). Abb. 5.6.5 (a) Iso-Sicht der Umgebung, (b) erste Iteration der Kanten-Abtastung und (c) zweite Iteration der Kanten-Abtastung 83 5.6.2.2.4 Punkt-Gerade-Abtastung Die letzte Variante zum Abtasten der lokalen Umgebung wird vom einen Punkt aus gestartet und verschickt einen Strahl in eine beliebige Richtung. Das erste Feld außerhalb des Gebäudebereichs, das vom Strahl getroffen wird, erhöht den Zähler des jeweiligen Umgebungstyps. Bei jeder Iteration wird nur ein Feld gelesen und deshalb jeweils nur ein Zähler inkrementiert. Ansonsten gelten alle üblichen Regeln für absolute bzw. relative Informationen, wie sie bereits bei den vorherigen Abtast-Methoden erklärt wurden. Mit Hilfe dieser Methode können einzelne Objekte Anfragen an die Umwelt stellen. Zum Beispiel könnte ein Fenster über diese Abfrage herausfinden, ob sich ein Baum, ein Gebäude oder Ähnliches direkt vor ihm befindet. Abb. 5.6.6 (a) Iso-Sicht der Umgebung, (b) erste Iteration der Punkt-Gerade-Abtastung und (c) zweite Iteration der Punkt-Gerade-Abtastung 5.6.2.3 Anfrage-Funktionen Dieses Unterkapitel beschreibt, wie die im vorherigen Kapitel erklärten Abtast-Methoden innerhalb der Grammatik aufgerufen werden können. Dazu wurden drei Funktionen implementiert: Env, EnvP und EnvPN. Alle drei Funktionen führen jeweils eine Abtastung der Umgebung durch und liefern als Rückgabe den absoluten oder relativen Wert eines Umgebungstypen-Zählers. Die Rückgabewerte können dann zum Beispiel die Größe eines Objektes verändern, die Wahrscheinlichkeit einer Regel beeinflussen etc. Im Gegensatz zu den umweltsensitiven L-Systemen aus Kapitel 3.6 Umgebungssensitive und offene Lindenmayer-Systeme werden keine speziellen Anfrage-Module benötigt. Da Funktionen in dem neuen System beliebige Rückgabewerte erzeugen können, müssen keine undefinierten Parameter durch ein spezielles Anfrage-Modul beschrieben werden. Die Funktion Env("EnvironmentType", ScanLevels, Absolute) ist die erste der drei lokalen Umgebungsfunktionen. Die Argumente der Funktion werden durch die folgende Tabelle erklärt. "EnvironmentType" 84 Durch die Zeichenkette wird bestimmt, nach welchem Typ die Umgebung gescannt werden soll. Genauer gesagt, welcher Zähler den Rückgabewert bildet. Zulässig sind die englischen Begriffe der in Kapitel 5.6.2.1 aufgelisteten Umgebungstypen, zu diesen zählen "Empty", "Nature", "Street", "Building", "Water" und "Mountain". ScanLevels Absolute Das Argument bestimmt die Anzahl der Iterationen, die beim Abtasten der Umgebung ausgeführt werden, und damit die Distanz, bis zu welcher das Umgebungsraster abgetastet wird. Durch das Argument wird festgelegt, ob die Funktion den relativen oder absoluten Wert des Zählers zurückgibt. Wird eine 0 übergeben, werden die Zähler relativ berechnet, entsprechend der Regel aus Kapitel 5.6.2.2.1 PunktAbtastung. Ansonsten werden die Werte absolut ermittelt und zurückgegeben. Als Rückgabe liefert die Funktion den relativen bzw. absoluten Zähler des angeforderten Umgebungstyps. Offen bleibt die Frage, welche Abtast-Methode von der Funktion verwendet wird. Die Art der Methode ist abhängig von der Grammatik, welche die Funktion ausführt. Handelt es sich um eine Mass-Grammar, so wird eine Punkt-Abtastung durchgeführt. Bei einer Facade-Grammar wird eine Flächen-Abtastung verwendet und bei einer Border-Grammar eine Kanten-Abtastung. Der Ausgangspunkt für die Punkt-Abtastung wird durch den Mittelpunkt des Gebäudes bestimmt. Bei einer Flächenabtastung sind die beiden Startpunkte die äußeren Punkte der Fassade und die beiden Strahlen die Normalen der Fassade. Bei einer Border-Grammar entspricht der Startpunkt für die Abtastung dem Ursprung der Border-Grammar. Die beiden Strahlen werden durch die Normalen der benachbarten Fassaden bestimmt. Die Funktion bleibt völlig unbeeinflusst von der aktuellen Transformation. Das heißt, die Funktion liefert immer das gleiche Ergebnis zurück, egal, wann sie innerhalb der Grammatik aufgerufen wird. Eine weitere Möglichkeit, Anfragen an die Umgebung zu stellen, wird durch die Funktion EnvP("EnvironmentType", ScanLevels, Absolute) ermöglicht. Die Argumente der Funktion besitzen die gleiche Bedeutung wie die der bereits beschriebenen Env-Funktion. Allerdings führt die Funktion immer eine Punkt-Abtastung (siehe Kapitel 5.6.2.2.1 Punkt-Abtastung) aus, unabhängig davon ob sie innerhalb eine Mass-, Facade- oder Border-Grammar aufgerufen wird. Im Gegensatz zu Env-Funktion wird diese Funktion durch die Transformations-Sequenz beeinflusst. Genauer gesagt, der Ausgangspunkt für die Punkt-Abtastung wird durch die Verschiebung des aktuellen Transformations-Zustands bestimmt, oder anders gesagt durch den Ursprung des aktuellen Koordinatensystems. Die dritte Anfrage-Funktion ist die EnvPN("EnvironmentType", ScanLevels, Absolute) Funktion. Auch hier besitzen die Argumente die gleiche Bedeutung wie die der Env-Funktion. D.h. "EnvironmentType" bestimmt den Rückgabe-Zähler, ScanLevels die Anzahl der Iterationen und Absolute definiert, ob ein relativer oder absoluter Wert zurückgegeben wird. Die EnvPN-Funktion verwendet immer eine Punkt-Gerade-Abtastung (siehe Kapitel 5.6.2.2.4 PunktGerade-Abtastung), dabei wird der Ausgangspunkt durch den Ursprung des aktuellen Koordinatensystems bestimmt und die Gerade durch die inverse z-Achse des aktuellen Koordinatensystems. 85 86 6. Resultate Im vorherigen Kapitel 5 Konzept wurde das konzep­ tionierte und realisierte System beschrieben, mit dessen Hilfe Regeln (Grammatiken) für die verschiedenen Gebäudetypen formuliert werden können. Aus diesen Regeln kann das System dann beliebig viele Variationen des Typs erzeugen. In diesem Kapitel werden schließlich ein paar Anwendungsbeispiele erläutert und dargestellt. Die Abbildung 6.1 verdeutlicht zum einen ein Mansarddach und zum anderen ein spitz zulaufendes Regeldach. Die Dachkonstruktionen wurden jeweils durch das Straight-Skeleton erzeugt und die Form nachträglich durch eine Profile-Grammar verändert. Aus den Beispiel ist bereits ersichtlich, dass jede der typischen Dachformen (siehe Kapitel 2.2.2 Dachformen) über das System konstruiert werden kann. Das Bauwerk aus Abb. 6.2 und Abb. 6.3 soll hauptsächlich den Einsatz der Facade- und Border-Grammars zeigen. Wie üblich bei solchen Bauwerken, besteht das unterste Stockwerk aus einer Stein- und die oberen Stockwerke aus einer Holzkonstruktion, bei denen die einzelnen Stockwerke etagenweise herausragen. Über die in Kapitel 5.5 Subdivision und Symmetrie beschriebenen Funktionen wurde die Fassade in einzelne Zellen unterteilt, in denen ein zufälliges und symmetrisches Fachwerkmuster eingebaut wird. Falls ein Fenster erzeugt wird, erhält dieses jeweils einen linken und rechten, zufällig ausgerichteten, Fensterladen. Außerdem erhält das Dach einen Schornstein in der Nähe des Firsts und entlang der Traufe mehrere Holzbalken, die unter dem Dach hervortreten. Der kleine Nebentrakt besteht lediglich aus einer Balkenkonstruktion. Die Balken wurden mit Hilfe einer Border- und Facade-Grammar realisiert. Da dem Primitiv keine Profile-Grammar zugewiesen wird, werden keine Wände für das Primitiv erzeugt, und es sind deshalb ausschließlich die Balken sichtbar. Zusätzlich besteht die Möglichkeit, dass Efeu eine der unteren Wände bedeckt. Realisiert wurde der Efeu mit der Hilfe eines einzigen Moduls, welches drei Regeln und jeweils eine passende Bedingung enthält. Die Bedingungen sorgen dafür, dass der Efeu nicht den Bereich der Wand verlässt und um ein eventuell vorhandenes Fenster herum wächst. Die drei Regeln bestimmen die Fortpflanzung des Efeus. Dabei wächst der Efeu entweder in eine Richtung weiter, erzeugt eine Verzweigung oder hört ganz auf zu wachsen. Abb. 6.1 Dachkonstruktionen Abb. 6.2 Fachwerkhaus Abb. 6.3 Fachwerk-Fassade 87 Der Gebäudetyp aus Abbildung 6.4 und 6.5 verdeutlicht den Entwurf von modernen Hochhäusern, unter verstärkter Berücksichtigung der Umwelt. Beispielsweise passen die einzelnen Häuser ihre Größe entsprechend den umliegenden Gebäuden an. D.h. Gebäude, die in den Außenbezirken liegen, sind kleiner als Gebäude, die sich mitten in einem Ballungsgebiet befinden. Weiterhin werden Balkone niemals an Wänden erzeugt, die direkt neben einem anderen Haus liegen. Das Gleiche gilt für Fenster, allerdings in etwas abgeschwächter Form. Das heißt, Fenster dürfen zwar mit geringer Wahrscheinlichkeit an Wänden platziert werden, die direkt an ein anderes Gebäude grenzen, aber wenn sie dies tun, sind sie schmaler als die üblichen Fenster des Gebäudes. Weiterhin können eine große Anzahl an verschiedenen Masse-Modellen erzeugt werden, u. a. die üblichen I-, H-, U-, T- und L-Formen, bei denen der Haupttrakt und die Nebentrakte eine zufällige Größe erhalten. Auch hier besitzen die untersten Stockwerke eine andere Fassade als die darüber liegenden Stockwerke. Außerdem unterscheiden sich die Fassaden aufgrund der Umgebung, zum Beispiel erhalten Fassaden des untersten Stockwerks Schaufenster, wenn sie an eine Straße grenzen (siehe Abb. 6.5). Solche Fassaden erhalten ebenfalls höchstwahrscheinlich die Eingangstür des Gebäudes, welche wiederum eine Treppe erhalten kann, usw. Obwohl Walmdächer vorhanden sind, stellen diese eine Seltenheit dar und treten häufiger in den Außenbezirken auf. Die meisten Gebäude besitzen Flachdächer, auf denen eventuell ein Treppenhaus angebracht wird. In Abbildung 6.7 wird der implementierte Editor dargestellt. Über den Editor können die unterschiedlichen Gebäudetypen angelegt, die Gruppen verwaltet, die lokale Umgebung verändert und sonstige Einstellungen vorgenommen werden. Außerdem können die verschiedenen Grammatiken in einem Fenster editiert werden, und es werden entsprechende Informationen, Warnungen und Fehlermeldungen über ein Ausgabefenster angezeigt. Abb. 6.4 Virtuelle Stadt 88 Abb. 6.5 Modernes Gebäude Abb. 6.6 Fachwerkhaus Abb. 6.7 Editor 89 90 7. Schlussbemerkungen 7.1 Fazit Im Großen und Ganzen erweist sich der Einsatz von Grammatiken bzw. stark ausgebauten Linden­mayerSystemen als geeignetes Mittel, um Regeln für Bauwerke aufzustellen. Die eingeführten Änderungen an der Definition und der Syntax, die automatische Anpassung der Wahrscheinlichkeiten und weitere Anpassungen (siehe Kapitel 5.1 Regelbasierte System) ermöglichen die Aufstellung von übersichtlichen Grammatiken und sorgen für gezielte Transformationen der einzelnen Gebäudeteile. Durch den Vorteil der Substitution von Grammatiken können mit wenig Regeln rekursive Konstruktionen wie Stockwerke, Treppenstufen und Ähnliches formuliert werden. Ebenfalls als sehr effektiv erweisen sich die implementierten Subdivision-Verfahren (siehe Kapitel 5.5 Subdivision und Symmetrie), mit deren Hilfe sich schnell und verständlich Fassaden anfertigen lassen. Durch eine geringe Anzahl an Regeln kann die Fassade zerlegt werden und innerhalb der einzelnen Teile können zufällige und evtl. symmetrisch angeordnete Strukturen wie Fenster, Türen, Fachwerkmuster und vieles mehr generiert werden. Durch diese Unterteilungen erhalten die Fassaden geordnete Strukturen und es entstehen keine chaotischen Anordnungen. Im Gegensatz zu den vorherigen Arbeiten lassen sich die einzelnen Bereiche des Gebäudes logisch trennen und auf die verschiedenen konzeptionierten Grammatiken (siehe Kapitel 5.2 Grammatiken) aufteilen. Diese Aufspaltung trägt stark zur Übersichtlichkeit und Verringerung der Komplexität bei, woraus wiederum eine geringere Fehleranfälligkeit resultiert. Weiterhin können die einmal aufgestellten Grammatiken so effektiv wiederverwendet werden: z.B. kann eine Facade-Grammar von jeder beliebigen Mass-Grammar aufgerufen werden und damit bei einer großen Anzahl von Gebäudetypen zum Einsatz kommen. Eine Schwachstelle der bisherigen Arbeiten war die Dachkonstruktion. Hier erweist sich das StraightSkeleton als mächtiges Mittel, und in Kombination mit einer Profile-Grammar können sehr komplexe, vielfältige und so gut wie alle Dachformen gebildet werden, auf denen dann optimal Dachgauben und sonstige Modelle platziert werden können. Einer der wesentlichsten Vorteile des Systems liegt in der Möglichkeit, mit einfachen Methoden die Gebäude an ihre lokale und globale Umgebung (siehe Kapitel 5.6 Umwelt) anzupassen. In den meisten Fällen reichen ein oder zwei Aufrufe der vordefinierten Anfrage-Funktionen aus, um einen gezielten Effekt zu erreichen. Solch ein Effekt könnte beispielsweise die Anpassung der Fenstergrößen oder deren Vorkommenswahrscheinlichkeit sein. Genauso effektiv wie simpel ist der Einsatz von Gruppen, über die Modelle oder Texturen schnell und sinnvoll verwaltet und von den Grammatiken selektiert werden können. Sollen Objekte (z.B. Fenster) variieren, müssen keine speziellen Regeln innerhalb der Grammatiken eingebaut werden, sondern ­lediglich die verschiedenen Objekte einer Gruppe zugeordnet werden. Dadurch werden Regeln aus der Grammatiken ausgelagert und die Komplexität der Grammatiken sinkt. Trotz all der Verbesserungen müssen für komplexe Gebäude immer noch komplizierte Grammatiken aufgestellt werden. Diese benötigen Zeit zur Formulierung und vor allem Verständnis über die Funktionsweise von Grammatiken sowie deren Kommunikation. D.h. der Nutzer des Systems sollte bereits Vorkenntnisse in der Programmierung oder besser noch in der theoretischen Informatik besitzen, damit ein Verständnis der Funktionsweise von Grammatiken vorhanden ist. Zusätzlich ist selbst bei entsprechenden Vorkenntnissen eine Einarbeitungszeit unvermeidbar. So wird beispielsweise zur Erstellung der sehr abstrakten Profile-Grammars oder für Optimierungen innerhalb der Grammatiken einiges an Erfahrungen benötigt. Die Berechnungszeit eines Gebäudes ist stark von dessen Komplexität abhängig, d.h. von der Anzahl der Regeln, Verschachtelungen, Struktur der Dachkonstruktion usw. Obwohl hier noch Verbesserungen vorgenommen werden könnten, liegt sie in einem akzeptablen Bereich. Ebenfalls gut, aber durchaus 91 noch verbesserungsbedürftig ist die Performance beim Rendering. Durch einfache Methoden (z.B. Ausblenden von Objekten an einer Fassade, die der Betrachter nicht sehen kann) und modernen Verfahren (Stichwort: Hardware Instancing) können bereits relativ gute Laufzeiten erreicht und mehrere hundert Gebäude in Echtzeit dargestellt werden. Selbstverständlich wächst und sinkt die Performance abhängig von der Komplexität der darzustellenden Gebäude. Der Speicherverbrauch ist im Vergleich zu den bisherigen Arbeiten einer der großen Vorteile. Dadurch das komplexe Objekte (wie Fenster, Türen, usw.) vorgefertigt geladen werden und deshalb nur einmal den Speicher belegen, Texturen wiederverwendet werden können und die Primitive auf Grund der ­CutOut-Modelle (siehe Kapitel 5.4.1.3 CutOut-Modell) keine komplexe Strukturen bilden müssen, wird ein sehr geringer Speicherverbrauch erreicht. Dieser liegt ungefähr zwischen 10 und 200 kByte pro Gebäude. Natürlich soll hier kein falscher Eindruck vermittelt werden, im schlechtesten Fall werden für eintausend Gebäude rund 200 MByte benötigt, dies ist für ein Computerspiel oft nicht akzeptabel. Dafür sind die Variationen sowie die Komplexität bei derartigen Gebäuden sehr hoch und die Gebäude unterscheiden sich in allen Bereichen (Masse-Modell, Dachkonstruktion, Fassaden, etc). Obwohl die Ergebnisse recht ansehnlich sind, kann nicht der Qualitätsstandard erreicht werden, der ­mittlerweile innerhalb der Videospiel-Branche üblich ist. Der größte Kritikpunkt liegt bei der momentan sehr eingeschränkten Texturierung der Gebäude. Solange diese nicht verbessert wird, ohne dabei einen zu hohen Speicherverbrauch zu erhalten, ist es nicht sinnvoll, komplette Gebäude durch das System erstellen zu lassen. Dies gilt zu mindestens für den Einsatz in Computerspielen mit hohen Standards. Da das System sehr flexibel ist, wäre es vorstellbar, die Gebäude vollständig aus vorgefertigten Modellen zusammenzubauen. Dadurch würde das Problem der Texturierung umgangen, es müsste allerdings auf einen Großteil der Vielfalt verzichtet werden. So könnten ohne den Einsatz von sichtbaren Primitiven komplett vorgefertigte Stockwerke, Fenster, Türen und vieles mehr von dem System zufällig zusammengesetzt werden. Trotz allem könnten die Gebäude die Vorteile des Systems nutzen und beispielsweise entsprechend auf ihre Umwelt reagieren. 7.2 Ausblick Zum Schluss folgt eine kleine Beschreibung an Vorschlägen, wie das realisierte System noch verändert, ausgebaut und verbessert werden könnte. An erste Stelle steht hier eine Verbesserung des vorhandenen Editors. In dem Editor könnten weitere Mittel eingesetzt werden, um die Formulierung der Grammatiken, die Verwaltung von Gruppen, die Änderung von Einstellungen und vieles mehr zu erleichtern. Beispielsweise könnten die innerhalb einer Grammatik definierten Konstanten über eine grafische Schnittstelle dargestellt und verändert werden. Da solche Konstanten meistens die Anzahl der Stockwerke, die Größe der einzelnen Fachwerkmuster und ähnliche Eigenschaften definieren, wäre es praktisch, wenn diese über Eingabefenster oder Regler verändert werden könnten und sich damit die parametrische Kontrolle verbessert. Zusätzlich wäre eine fünfte Grammatik vorstellbar, eine so genannte Group-Grammar. Einer Gruppe könnte eine Group-Grammar zugewiesen werden und über deren Regeln die Rückgabe der Objekte steuern. Falls nun eine Grammatik zufällige Objekte aus der Gruppe anfordert, entscheidet die Gruppe anhand ihrer Regeln aus der Group-Grammar (dazu zählen u. a. die Wahrscheinlichkeiten, die Zufallsfunktionen und der Einfluss durch die Umgebung), welches der vorhandenen Objekte zurückgegeben wird. Ein weiterer Ausbau könnte vorsehen, dass neben vorgefertigten Modellen zusätzlich Partikeleffekte geladen und platziert werden können. Dadurch könnte beispielsweise Rauch aus den Schornsteinen oder einer Feuerstelle entspringen. Außerdem könnten die Effekte wie Modelle oder Texturen innerhalb von Gruppen verwaltet werden. Genauso wäre eventuell eine Gruppe für Grammatiken sinnvoll, zum Beispiel eine Gruppe, die mehrere Facade-Grammars verwaltet. Anstatt nun einem Primitiv eine feste Facade-Grammar zuzuweisen, könnte so zufällig eine Grammatik aus der Gruppe entnommen werden. Daraus würde eine erhöhte 92 Wiederverwendbarkeit und Vielfalt resultieren. Oder aber es könnte eine Grammatik ähnlich der ControlGrammar von [WWS+03] implementiert werden. Diese könnte über einfache Regeln den jeweiligen Primitiven passende Grammatiken (z.B. Facade-Grammars) zuweisen. Zur Optimierung der Berechnungszeiten könnten die von [EE98] oder [Obd] beschriebenen Verfahren implementiert werden. Beide Arbeiten stellen Methoden vor, die die Berechnung des Straight-Skeletons beschleunigen, zum Beispiel durch optimierte Listen bei der Split-Event-Berechnung. Weiterhin werden eventuell entstehende Löcher innerhalb des Straight-Skeletons noch nicht berücksichtigt, d.h. bilden mehrere Dach-Primitive einen Kreis, so dass in der Mitte ein oder mehrere Lücken entstehen, können diese von dem bisherigen Algorithmus noch nicht erkannt werden. Die Verbesserung der Performance könnte über eine Verringerung der Zeichenaufrufe (Draw Calls) erreicht werden. So könnten beispielsweise mehrere nahe aneinander stehende Gebäude zusammengefasst und im Speicher optimiert abgelegt werden. Dadurch würden Häuserblöcke entstehen, die dann effizient in einem Draw Call gezeichnet werden. 7.3 Zusammenfassung Innerhalb der Arbeit wurde ein prozeduraler Ansatz zur Erzeugung von Gebäuden vorgestellt. Die Basis des konzeptionierten Systems bilden mehrere Grammatiken, mit deren Hilfe unterschiedliche Gebäudetypen definiert werden können. Über diese Definition können dann beliebig viele Gebäude prozedural erstellt werden. Damit die einzelnen Gebäudetypen möglichst übersichtlich, verständlich und schnell formuliert werden können, wurden vier verschiedene Grammatik-Arten in das System integriert. Jede dieser Arten besitzt eine andere Aufgabe und erstellt andere Bereiche des Gebäudes. Die Mass-Grammar erstellt das grundlegende Modell des Gebäudes, die Facade-Grammar die Objekte an den einzelnen Fassaden und Dachflächen, die Border-Grammar die Objekte entlang der Kanten und die Profile-Grammar die Geometrie des Masse-Modells. Durch diese Aufspaltung werden die einzelnen Bereiche des Gebäudes logisch getrennt und können bei anderen Gebäudetypen besser wiederverwendet werden. Um das Gebäude zu erzeugen, können die Grammatiken entweder Primitive generieren oder vorgefertigte Modelle laden. Primitive sind grundlegende Körper (Würfel, Zylinder, ...), denen weitere Grammatiken zugeteilt werden können. Die zugewiesenen Grammatiken können dann an den einzelnen Flächen und Kanten des Primitivs weitere Primtive oder Modelle platzieren. Damit solche Modelle möglichst einfach verwaltet werden können, wurden Gruppen implementiert. Gruppen können beliebig viele Objekte (Modelle oder Texturen) enthalten und geben ein zufälliges Objekt zurück, falls sie eine Anforderung von einer Grammatik erhalten. Damit die einzelnen Grundflächen und Fassaden einfache gestaltet werden können, wurden spezielle Subdivision-Verfahren in die Grammatiken eingebaut. Mit Hilfe dieser Verfahren können die Primitive in Stockwerke oder sonstige Zellen unterteilt werden und darin symmetrische Strukturen und Muster erzeugen. Damit die Gebäude möglichst glaubwürdig erscheinen, müssen sie ihre lokale sowie globale Umwelt berücksichtigen. Dazu wurden verschiedene Verfahren zur Analyse der Umgebung realisiert, die dann innerhalb der einzelnen Grammatiken eingesetzt werden können, um die Eigenschaften der Gebäude ihrer Umgebung anzupassen. Beendet wird die Arbeit so, wie sie begonnen hat: mit einer Beschreibung der Selbstähnlichkeit und der Fraktale. Diesmal von Lewis F. Richardson (1922) treffend in einem Gedicht verfasst. Bigger swirls have smaller swirls, That feed on their velocity, And smaller swirls have smaller swirls, And so on, to viscosity 93 8. Literaturverzeichnis [Mül99] Pascal Müller. Prozedurales Modellieren einer Stadt. Semesterarbeit ETH Zürich 1999 [Mül01] Pascal Müller. Design und Implementation einer Preprocessing Pipeline zur Visualisierung prozedural erzeugter Stadtmodelle. Diplomarbeit ETH Zürich 2001 [MP01] Pascal Müller und Yoav I. H. Parish. Procedural Modeling of Cities. In Proceedings of ACM SIGGRAPH 2001, S. 301-308 [MWH+06] Pascal Müller, Peter Wonka, Simon Haegler, Andreas Ulmer und Luc Van Gool. Procedural Modeling of Buildings. In Proceedings of ACM SIGGRAPH 2006, S. 614 - 623 [MVU+05] P. Müller, T. Vereenooghe, A. Ulmer und L. Van Gool. Automatic reconstruction of Roman housing architecture. International Workshop on Recording, Modeling and Visualization of Cultural Heritage, Balkema Publishers 2005, S. 287-297 [WWS+03] Peter Wonka, Michael Wimmer, François Sillion und William Ribarsky. Instant Architecture. In Proceedings of ACM SIGGRAPH 2003, S. 669 - 677 [DHM+98] Oliver Deussen, Patrick Hanrahan, Matt Pharr, Bernd Lintermann, Randomír Mĕch und Przemyslaw Prusinkiewicz. Realistic Modeling and Rendering of Plant Ecosystems. In Proceedings of SIGGRAPH 98, S. 275-286 [AS82] Hal Abelsson und Andrea diSessa. Turtle geometry. MIT-Press Cambridge 1982 [Deu03] Oliver Deussen. Computer generierte Pflanzen. Technik und Design digitaler Pflanzenwelten. Springer-Verlag Heidelberg 2003. ISBN 3-540-43606-5 [EBE+03] David S. Ebert, F. Kenton Musgrave, Darwyn Peachy, Ken Perlin, Steven Worley. Texturing & Modeling A Procedural Approach. Morgen Kaufmann Publishers 2003. ISBN 1-55860-848-6 [Man82] Benoît B. Mandelbrot. The Fractal Geometry of Nature. W. H. Freeman and Company 1982. ISBN 0-7167-1186-9 [LP90] P. Prusinkiewicz, A. Lindemayer. The Algorithmic Beauty of Plants. New York: Springer Verlag 1990. ISBN 0-387-94676-4 [Hol81] S. R. Holtzman. Using Generative Grammars for Music Composition. Computer Music Journal 1981, 5(1):51-64 [Pru86] P. Prusinkiewicz. Score Generation with L-Systems. Proc. Intl. Computer Music Conf 1986, S. 455-457 [WS05] Peter Worth und Susan Stepney. Growing Music: musical interpretations of L-Systems. EvoWorkshops 2005, S. 545-550 [Smi84] A. R. Smith. Plants, fractals, and formal languages. Computer Graphics. In Proceedings of ACM SIGGRAPH 84, 18(3):1–10 94 [Lor02] Wolfgang E. Lorenz. Fractal and Fractal Architecture. Technische Universität Wien 2002 [Han] Michael Hansmeyer. Algorithms in Architecture. http://www.mh-portfolio.com/ [Gar85] G. Y. Gardner. Visual Simulation of Clouds. In Proceedings of SIGGRAPH 85, S. 297–304 [FSJ01] Ronald Fedkiw, Jos Stamy und Henrik Wann Jensenz. Visual Simulation of Smoke. Stanford 2001 [FKM+06] Alfred R. Fuller, Hari Krishnan, Karim Mahrous, Bernd Hamann und Kenneth I. Joy. Real-time Procedural Volumetric Fire. 2006 [MP96] R. Měch und P. Prusinkiewicz. Visual Model of Plants Interacting with Their Environment. In: Proceedings of SIGGRAPH 1996, S.397 – 410 [MPJ95] R. Měch, P. Prusinkiewicz und P. James. Synthetic Topiary. In Proceedings of SIGGRAPH 1995 S.351 – 358 [HH74] P. Hogeweg und B.Hesper. A model study on biomorphological description. Pattern Recognition 1974, S. 165-179 [BFH+80] Wolfgang Brennecke, Heiko Folkerts, Friedrich Haferland und Franz Hart. Dachatlas Geneigte Dächer. Institut für internationale Architektur Dokumentation München 1980 [Pre99] Prehl, Hagen. Hölzerne Dachkonstruktionen Berechnung, Konstruktion, Tafeln, Beispiele. Werner Verlag GmbH & Co Düsseldorf 1999 [War00] Otto Warth. Die Konstruktion in Holz. Th. Schäfer Druckerei GmbH 1900 [Bro88] F.A. Brockhaus GmbH Mannheim 1988. Brockhaus Enzyklopädie Fünfter Band COT-DR, 19. Auflage, S.74 – 77 [Bro882] F.A. Brockhaus GmbH Mannheim 1988. Brockhaus Enzyklopädie Vierter Band BRO-COS, 19. Auflage, S.153, 158 [LD03] Robert G. Laycock, A. M. Day. Automatically Generating Roof Models from Building Footprints. In WSCG 2003, S 81-84. ISBN 80-903100-2-8 [LD032] Robert G. Laycock und A. M. Day. Automatically Generating Large Urban Environments based on the Footprint Data of Buildings. Proceedings of ACM Symposium on Solid and Physical Modeling 2003, S. 346-351 [Aic+95] Oswin Aichholzer, Franz Aurenhammer, David Alberts und Bernd Gärtner. A novel type of skeleton for polygons. J. Universal Computer Science 1995, S. 752-761 [DEC03] M. Dikaiakou, A. Efthymiou und Y. Chrysanthou. Modelling the Walled City of Nicosia. In D. Arnold, A. Chalmers, and F. Niccolucci, VAST 2003, S. 57-65 95 [EE98] David Eppstein und Jeff Erickson. Raising Roofs, Crashing Cycles, and Playing Pool: Applications of a Data Structure for Finding Pairwise Interactions. In Proceedings of the 14th Annual ACM Symposium on Computational Geometry 1998, S. 58-67 [AA96] Oswin Aichholzer und Franz Aurenhammer. Straight skeletons for general polygonal figures in the plane, In Proceedings 2nd Annual International Conference. Computing and Combinatorics, S. 117-126. Lecture Notes in Computer Science 1090, Springer, 1996 [Gör04] Robert Görke. Ein schneller Konstruktionsalgorithmus für eine Quickest-Path-Map bezüglich der City-Metrik. Diplomarbeit Universität Karlsruhe 2004 [Bre00] Claus Brenner. Towards fully automatic generation of city models. International Archives of Photogrammetry and Remote Sensing, Volume 33, S. 85-92 [Obd] Štěpán Obdržálek. The Angular bisector network Implementation and the CGAL library [Jag04] Robert Carl Jagnow. Stereological Techniques for Synthesizing Solid Textures from Images of Aggregate Materials. Massachusetts Institute of Technology 2004 [1] http://de.wikipedia.org/wiki/Dach [2] http://de.wikipedia.org/wiki/Dachausmittlung [3] http://en.wikipedia.org/wiki/Fractal [4] http://gumuz.looze.net/wordpress/index.php/archives/2006/03/30/python-conways-game-of-Life [5] http://de.wikipedia.org/wiki/Zellul%C3%A4rer_Automat [6] http://en.wikipedia.org/wiki/Cellular_automaton [7] http://de.wikipedia.org/wiki/Conways_Spiel_des_Lebens 96 Anhang A Die nachfolgenden Funktionen können von allen vier Grammatik-Arten (Mass-, Facade-, Border- und Profile-Grammar) aufgerufen werden. cos(x) Berechnet den Kosinus von x sin(x) tan(x) Berechnet den Sinus von x Berechnet den Tangens von x acos(x) Berechnet den Arkuskosinus von x asin(x) atan(x) Berechnet den Arkussinus von x Berechnet den Arkustangens von x pow(x, y) Liefert xy zurück sqr(x) min(x, y) Berechnet die Quadratwurzel von x Liefert das Minimum von x und y zurück max(x, y) floor(x) ceil(x) abs(x) rand(x, y) Liefert das Maximum von x und y zurück Rundet x auf den nächsten ganzzahligen Wert ab Rundet x auf den nächsten ganzzahligen Wert auf Berechnet den Absolutbetrag von x Liefert eine zufällige reelle Zahl zwischen x und y zurück rani(x, y) Liefert eine zufällige ganze Zahl zwischen x und y zurück ranb() Liefert zufällig eine 0 (false) oder 1 (true) zurück noise(x, y, f) Berechnet einen zweidimensionalen Perlin-Noise an der Stelle (x, y). Vorher werden x und y mit f multipliziert Gibt die einzelnen Argumente im Ausgabefenster aus. Als Argumente dürfen beliebig viele Zahlen oder Zeichenketten übergeben werden Trace(s0, s1, ...) AutoPP(b) Aktiviert oder deaktiviert das automatische Push und Pop vor bzw. nach der Substitution einer Variablen, siehe Kapitel 5.1.8 Verzweigungen String(x) Konvertiert eine Zahl in eine Zeichenkette For("Production", i) Ruft i-mal die Produktion auf If(i, a, b) VCount("Variable") Wenn i ungleich 0 (true) ist, wird a zurückgegeben, andernfalls (false) wird b zurückgegeben Selektiert das i-te Argument und liefert dies zurück. Es dürfen beliebig viele Argumente an die Funktion übergeben werden, egal ob Zahlen oder Zeichenketten Liefert die Anzahl der bisherigen Substitutionen einer Variablen zurück GCount("Group") Liefert die Anzahl der Gruppenaufrufe zurück Switch(i, a, b, ...) Führt ein Anfrage an die globale Umgebung durch, siehe Kapitel 5.6.2.3 AnfrageFunktionen Env("EnvironmentType", Führt ein Anfrage an die lokale Umgebung durch, siehe Kapitel 5.6.2.3 AnfrageScanLevels, Absolute) Funktionen EnvP("EnvironmentType", Führt ein Anfrage an die lokale Umgebung durch, siehe Kapitel 5.6.2.3 AnfrageScanLevels, Absolute) Funktionen EnvPN("EnvironmentType", Führt ein Anfrage an die lokale Umgebung durch, siehe Kapitel 5.6.2.3 AnfrageScanLevels, Absolute) Funktionen R(x, y, z) Führt eine zusätzliche Rotation auf die aktuelle Transformation aus GEnv("EnvironmentType") S(x, y, z) Ersetzt die Skalierung der aktuellen Transformation 97 T(x, y, z) Führt eine zusätzliche Translation auf die aktuelle Transformation aus TR(x ,y, z) R() Führt eine zusätzliche Translation auf die aktuelle Transformation aus, bzgl. der aktuellen Ausrichtung Setzt die aktuelle Rotation zurück S() Setzt die aktuelle Skalierung zurück T() Setzt die aktuelle Translation zurück RX(x) RY(y) Führt eine zusätzliche Rotation um die x-Achse aus Führt eine zusätzliche Rotation um die y-Achse aus RZ(z) Führt eine zusätzliche Rotation um die z-Achse aus SX(x) Ersetzt die aktuelle Skalierung entlang der x-Achse SY(y) Ersetzt die aktuelle Skalierung entlang der y-Achse SZ(z) Ersetzt die aktuelle Skalierung entlang der z-Achse TX(x) Führt eine zusätzliche Translation entlang der x-Achse aus TY(y) Führt eine zusätzliche Translation entlang der y-Achse aus TZ(z) Führt eine zusätzliche Translation entlang der z-Achse aus Anhang B Die nachfolgenden Funktionen dürfen ausschließlich von einer Profile-Grammar aufgerufen werden. In("Register") PointV() PointH() TexCoord(u, v) TextureGroup("GroupIndex") Reset() 98 Liest das Register aus und gibt den übergebenen Wert zurück, siehe Kapitel 5.2.5 Kommunikation Erzeugt einen neuen vertikalen Punkt und verbindet diesen mit dem vorherigen Punkt Erzeugt einen neuen horizontalen Punkt und verbindet diesen mit dem vorherigen Punkt Setzt den Faktor für die Texturkoordinaten, siehe Kapitel 5.3.1.3 Texturierung Lädt ein ausgewählte oder zufällige Textur aus einer Textur-Gruppe und setzt diese als aktuelle Textur, siehe Kapitel 5.2.4.2 Texturierung Löscht die aktuelle Textur, siehe Kapitel 5.2.4.2 Texturierung Anhang C Die nachfolgenden Funktionen dürfen ausschließlich von einer Mass-, Facade- oder Border-Grammar aufgerufen werden. Disp(x, y) ranq() Out(x, "Register") In("Register") ModelGroup("Name:Index", UndoRotate, IntersectSetting) Box("Profile", "Facade", "Border") HalfBox("Profile", "Facade", "Border") Cylinder("Profile", "Facade", "Border") HalfCylinder("Profile", "Facade", "Border") Wall("Profile", "Facade", "Border") RoofBox("Profile", "Facade", "Border", Slope, AboveWall, GableWeighting, ConnectRoof) RoofHalfBox("Profile", "Facade", "Border", Slope, AboveWall, GableWeighting, ConnectRoof) RoofCylinder("Profile", "Facade", "Border", Slope, AboveWall, GableWeighting, ConnectRoof) RoofHalfCylinder("Profile", "Facade", "Border", Slope, AboveWall, GableWeighting, ConnectRoof) Prim("Type", "Profile", "Facade", "Border") Roof("Type", "Profile", "Facade", "Border", Slope, AboveWall, GableWeighting, ConnectRoof) Liefert den Displacement-Wert (erzeugt durch die ProfileGrammar) an der Stelle (x, y) zurück Setzt die aktuelle Verschiebung auf einem zufälligen Punkt innerhalb des Rechtecks, welches durch die aktuelle Transformations-Sequenz definiert wird Legt ein Ausgaberegister an und weist diesem den Wert x zu. Sobald ein Primitiv oder Dach-Primitiv erzeugt wird, werden alle bisherigen Register an die Grammatiken des Primitiv‘s weitergeleitet, siehe Kapitel 5.2.5 Kommunikation Liest das Register aus und gibt den übergebenen Wert zurück, siehe Kapitel 5.2.5 Kommunikation Lädt ein ausgewähltes oder zufälliges Model aus einer ModellGruppe, siehe Kapitel 5.4.1.1 Funktionen Erstellt einen Würfel bzgl. der aktuellen Transformations-Sequenz und führt die zugeteilten Grammatiken aus, siehe Kapitel 5.3.1.4 Funktionen Erstellt einen Halb-Würfel bzgl. der aktuellen TransformationsSequenz und führt die zugeteilten Grammatiken aus, siehe Kapitel 5.3.1.4 Funktionen Erstellt einen Zylinder bzgl. der aktuellen TransformationsSequenz und führt die zugeteilten Grammatiken aus, siehe Kapitel 5.3.1.4 Funktionen Erstellt einen Halb-Zylinder bzgl. der aktuellen TransformationsSequenz und führt die zugeteilten Grammatiken aus, siehe Kapitel 5.3.1.4 Funktionen Erstellt eine Wand bzgl. der aktuellen Transformations-Sequenz und führt die zugeteilten Grammatiken aus, siehe Kapitel 5.3.1.4 Funktionen Erstellt das Dach für einen Würfel bzgl. der aktuellen Transformations-Sequenz und führt die zugeteilten Grammatiken aus, siehe Kapitel 5.3.2.6 Funktionen Erstellt das Dach für einen Halb-Würfel bzgl. der aktuellen Transformations-Sequenz und führt die zugeteilten Grammatiken aus, siehe Kapitel 5.3.2.6 Funktionen Erstellt das Dach für einen Zylinder bzgl. der aktuellen Transformations-Sequenz und führt die zugeteilten Grammatiken aus, siehe Kapitel 5.3.2.6 Funktionen Erstellt das Dach für einen Halb-Zylinder bzgl. der aktuellen Transformations-Sequenz und führt die zugeteilten Grammatiken aus, siehe Kapitel 5.3.2.6 Funktionen Allgemeine Funktion zum Erstellen von Primitiven, siehe Kapitel 5.3.1.4 Funktionen Allgemeine Funktion zum Erstellen von Dach-Primitiven, siehe Kapitel 5.3.2.6 Funktionen 99 Anhang D Die nachfolgenden vordefinierten Konstanten können von allen Grammatik-Arten (Mass-, Facade-, Border- und Profile Grammar) verwendet werden. WIDTH TYPE R.x Enthält entweder die Breite des Grundstücks (Mass-Grammar), die Breite der Fassade (Facade-Grammar, Profile-Grammar) oder die Länge der Kante (Border-Grammar) Enthält die Länge des Grundstücks (Mass-Grammar), die Höhe der Fassade (FacadeGrammar, Profile-Grammar) oder den Wert 0 bei einer Kante (Border-Grammar) Beschreibt den aktuellen Typ der Fassade bzw. Kante Enthält den aktuellen Rotations-Winkel um die x-Achse R.y R.z S.x S.y S.z T.x T.y T.z TRUE FALSE PI Enthält den aktuellen Rotations-Winkel um die y-Achse Enthält den aktuellen Rotations-Winkel um die z-Achse Enthält die aktuelle Skalierung bzgl. der x-Achse Enthält die aktuelle Skalierung bzgl. der y-Achse Enthält die aktuelle Skalierung bzgl. der z-Achse Enthält die aktuelle Verschiebung entlang der x-Achse Enthält die aktuelle Verschiebung entlang der y-Achse Enthält die aktuelle Verschiebung entlang der z-Achse Enthält die Zahl 1 Enthält die Zahl 0 Enthält die Zahl Pi HEIGHT Anhang E Die nachfolgenden vordefinierten Konstanten können nur von einer Mass-, Facade- oder BorderGrammar verwendet werden. SYM SNR Enthält den aktuellen Symmetrie-Status, siehe Kapitel 5.5.2 Gibt die Nummer der aktuellen Fläche bzw. Kante an Anhang F Die nachfolgenden vordefinierten Konstanten können nur von einer Profile-Grammar verwendet werden. PTYPE VERTICAL HORIZONTAL BOTH 100 Enthält den aktuellen Profil-Typ, demzufolge ob ein horizontales oder vertikales Profil erstellt werden soll Kann in Verbindung mit PTYPE eingesetzt werden Kann in Verbindung mit PTYPE eingesetzt werden Kann in Verbindung mit PTYPE eingesetzt werden