Funktionen & Vektoren
Nach den erfolgreichen Zwischenpräsentationen wird im zweiten Teil der Vorlesungsreihe das bereits erlernte Wissen noch einmal vertieft und mit neuen Komponenten erweitert.
In der heutigen Vorlesung wird zuerst noch einmal der Themenkomplex Funktionen bearbeitet, bevor am Ende das Laden und Nutzen von Vektoren besprochen wird.
Wiederholung Funktionen
Bereits in der ersten Stunden haben wir die internen Funktionen setup() & draw() kennengelernt. Im weiteren Verlauf haben wir noch mousePressed und keyPressed() kennengelernt.
Neben der Verwendung der internen Funktionen können wir auch eigene Funktionen schreiben, wobei der Grundaufbau immer der gleiche ist.
Eine Funktion besteht aus Name, Rückgabewert und optional aus einem oder mehreren Parametern.
Der Einsatz von Funktionen verfolgt mehre Ziele:
1. Gliederung eines Programmes
2. bessere Extraktion und Wiederverwendbarkeit
3. Parametrisierung, um Funktionen vielschichtig zu verwenden
4. Reduzierung des Codes und Erhöhung der Übersichtlichkeit
Diese Tatsachen erhöhen die Modularität der Werkzeuge und wir haben mehr Zeit die Werkzeuge in unserem iterativen und kreativen Prozess zu verwenden.
Funktionsarten
Beispiel:
// Funktion ohne Parameter und ohne Rückgabewert void functionName () { // Anweisungen } // Funktion mit einem Parameter und mit Rückgabewert int functionName ( int argument1 ) { // Anweisungen } // Funktion mit zwei Parametern und mit Rückgabewert String functionName ( String argument1, String argument2 ) { // Anweisungen }
Viele Funktionen nutzen als Rückgabewert void, was leer bedeutet, sprich sie geben keinen Wert zurück. Beispiele hierfür sind unter anderem setup() & draw().
Ein Beispiel für eine Funktion mit einem Rückgabewert ist dist(), mit der man den Abstand zwischen zwei Punkten messen kann. Die Funktion erwartet minimal x1, y1 und x2, y2. Der Rückgabewert ist ein float und ist der Abstand zwischen den beiden Punkten.
Die Funktion map() besitzt ebenfalls einen Rückgabewert und erwartet fünf Parameter. Mit map() kann man einen Wert von einem Zahlenbereich in einen anderen Zahlenbereich umrechnen. Das erste Parameter ist der vorhandene Wert, gefolgt von der oberen und unteren Grenzen des aktuellen Zahlenbereiches. Die letzten beiden Parameter grenzen den neuen Zahlenbereich ein.

Beispiel:
void draw() { drawCircles( 1000 ); } void drawCircles( int amount ) { int count = 0; // maximale Distance von oben links zum Mittelpunkt berechnen float maxDistance = dist( 0, 0, width / 2, height / 2 ); while( count < amount ) { float cirlceSize = random( 10 ); float nextX = random( width ); float nextY = random( height ); // Distance vom aktuellen Kreis zum Mittelpunkt float distance = dist( nextX, nextY, width / 2, height / 2 ); // Alphawert aufgrund der Distance bestimmen float nextAlpha = map( distance, 0, maxDistance, 0, 255 ); // außen -> alpha 0, innen -> alpha 255 fill( 255, 255 - nextAlpha ); ellipse( nextX, nextY, cirlceSize, cirlceSize ); count++; } }
Gültigkeitsbereich von Variablen
Da es in den vergangen Wochen immer wieder Probleme beim Einsatz und der Verwendung von Variablen gab, gehen wir noch einmal ins Detail.
Je nach Definitionsort besitzen Variablen einen unterschiedlichen Gültigkeitsbereich. Definiert man Variablen ganz am Anfang eines Sketches, also vor dem setup()-Bereich, dann ist es eine globale Variable, auf die man von überall aus zugreifen kann.
Erstellt man hingegen eine Variable innerhalb einer Funktion, dann ist sie nur in diesem Funktionsbereiches verfügbar und wird als lokale Variablen bezeichnet.
Empfehlung: So oft es geht mit lokalen Variablen arbeiten. Die Verwendung von globalen Variablen in unterschiedlichen Funktionen kann zu Überschneidung führen und somit zu Fehlern. Beim Einsatz von lokalen Variablen können sich die Namen der Variablen in den einzelnen Funktionen auch wiederholen, da sie jeweils ihren eigenen Gültigkeitsbereich haben.
Beispiel:
int globalVar = 1; void setup() { int localSetupVar = 2; } void draw() { int localDrawVar = 3; // works println( globalVar ); // error println( localSetupVar ); // works println( localDrawVar ); }
Vektoren laden
Bis dato haben einige von Euch mit viel Aufwand Charakters in Processing erstellt, was sicherlich in Programmen wie Illustrator oder Freehand leichter und schneller vonstatten gegangen wäre. Da es immer sinnvoll ist das richtige Werkzeug für die entsprechende Aufgabe zu verwenden, werden wir jetzt Illustrator & Processing zusammen nutzen.
Als Ideengeber sollen uns die Arbeiten Lovebytes 2007 und Nokia Friends von Karsten Schmidt dienen.
Wir werden dabei aber nicht alles in Processing erstellen, sondern wir werden uns die Grundvektoren in Illustrator anlegen. Um die Vektoren aus Illustrator in Processing nutzen zu können, speichert man aus Illustrator eine SVG-Datei mit den Standardeinstellungen.
Scalable Vector Graphics (SVG) ist ein Standard zur Beschreibung zweidimensionaler Vektorgrafiken auf Basis der XML-Syntax. Die Verwendung der skalierbaren Vektordateien ist die einzige Möglichkeit extern erstellte Vektoren in Processing zu laden.
Die Nutzung der Vektoren besteht aus dem Ladeprozess und dem Zeichnen bzw. Anzeigen des Vektors. Zum Laden verwendet man die Funktion loadShape("shapeFile.svg") und zum Anzeigen die Funktion shape( pshapeInstance, x, y, width, height ).
Spannend wird es, wenn man mit der Funktion pshapeInstance.getChild( "childName" ) auf einzelne Bestandteile der Vektorgrafik zugreift, um diese zu modifizieren. Für den Zugriff ist es essentiell, dass man in Illustrator jeder Ebene, auf die man zugreifen will, einen beschreibenden Name gibt.
So kann man sich vorstellen, dass man in Illustrator verschiedene Vektoren zeichnet, für die man dann ein Programm in Processing schreibt, um Größe, Position, Farbe etc. zu verändern.
Nützlich ist dabei die Funktion pshapeInstance.setVisible(), um die Sichtbarkeit eines Vektorelements zu verändern.
Um den Style zu ändern, muss man zu erst die Informationen aus dem SVG-Format pshapeInstance.disableStyle() deaktivieren, damit mit den definierten Einstellung aus Processing der Vektor gezeichnet wird.
Als Beispiel erstellen wir uns einen ganz simplen Charakter mit Kopf, zwei Armen und zwei Beinen.
Beispiel:
// Shapes definieren PShape character; PShape head; void setup() { size( 1000, 800 ); smooth(); background( 0 ); noStroke(); noLoop(); // SVG-Datei laden character = loadShape("character.svg"); // Element aus der SVG-Datei ansprechen head = character.getChild("headShape"); } void draw() { // Style ausschalten head.disableStyle(); // Shape mit eigener Füllung zeichnen fill(0, 51, 102); shape( head, 0, 0 ); }
Hausaufgabe
Die heutige Hausaufgabe besteht nicht aus drei kleine Aufgaben, sondern wieder aus einer großen. Erstellt in Illustrator verschiedene Charakter-Elemente, die in Processing eingesetzt werden, um verschiedene Charakters zu erstellen. Wer sich mit dem Thema Charakters schwer tut, kann auch andere Grundelemente in Illustrator erstellen, um sie dann in Processing zu verwenden.
Das Endprodukt sollte ein Plakat sein, bei dem der Variantenreichtum dargestellt wird.
Wichtig ist dabei, dass ihr sowohl die Processing-Sketches als auch die Illustrator-Dateien abgebt.
Informationen zum Dateiformat, der Bezeichnung und zum Upload der Dateien sind unter dem Punkt Prüfungsleistung zu finden.
Die Beispiele der Woche können hier herunter geladen werden.