/* ------------ Mehrzeilige Kommentare werden so gemacht */ // Einzeilige Kommentare funktionieren so // /* Da Processing von Java abstammt, ist die Syntax für Kommentare gleich wie bei Java (wie du vielleicht oben bemerkt hast)! Mehrzeilige Kommentare werden wie hier umschlossen. */ /* ------------------------------------------------- Schreiben und Ausführen von Processing Programmen ------------------------------------------------- */ // In Processing ist der Startpunkt eines Programms die Funktion `setup()` // mit dem Rückgabetyp `void`. // Beachte: Die Syntax ist derjenigen von C++ ziemlich ähnlich. void setup() { // Dies gibt beim Ausführen "Hallo Welt!" auf der Konsole aus. println("Hallo Welt!"); // eine weitere Sprache mit einem Semikolon am Ende. } // Normalerweise wird der Code für statische Elemente innerhalb der Methode // `setup()` geschrieben, da diese lediglich einmal ausgeführt wird. // Dies kann zum Beispiel das Setzen der Hintergrundfarbe oder das Bestimmen // der Canvas-Größe sein. background(color); // Setze die Hintergrundfarbe size(width, height, [renderer]); // bestimme die Canvasgröße mit dem optionalen // Parameter `renderer`. // Du wirst innerhalb dieses Dokuments noch weitere Parameter sehen. // Wenn du möchtest, dass der Code unendlich oft ausgeführt wird, so muss dieser // Code innerhalb der `draw()`-Methode stehen. // `draw()` muss existieren, wenn du möchtest, dass das Programm durchgehend // läuft. Die `draw()`-Methode darf nur einmal vorkommen. int i = 0; void draw() { // Dieser Codeblock wird ausgeführt bis er gestoppt wird. print(i); i++; // Inkrement-Operator } // Da wir nun wissen, wie man ein funktionierendes Skript erstellen kann und wie // dieses ausgeführt wird, fahren wir mit den unterschiedlichen Datentypen und // Collections weiter, welche in Processing unterstützt werden. /* ------------------------------------------------- Datentypen und Collections ------------------------------------------------- */ // Gemäß den Angaben in der Processingreferenz, unterstützt Processing die // folgenden acht primitiven Datentypen: boolean booleanValue = true; // Boolean byte byteValueOfA = 23; // Byte char charValueOfA = 'A'; // Char (einzelnes Zeichen) color colorValueOfWhiteM = color(255, 255, 255); // Farben (angegeben durch die // `color()`-Methode) color colorValueOfWhiteH = #FFFFFF; // Farbe (angegeben mit der Hexadezimal- // schreibweise.) int intValue = 5; // Integer (ganze Zahl) long longValue = 2147483648L; // "L" wird hinzugefügt, um es als `long` zu // markieren. float floatValue = 1.12345; // Float (32-Bit Gleitkommazahl) double doubleValue = 1.12345D // Double (64-Bit Gleitkommazahl) //BEACHTE! // Auch wenn es die Datentypen "long" und "double" gibt und auch funktionieren, // verwenden Processing-Funktionen diese Datentypen nicht. Das bedeutet, dass // diese zu "int" resp. "float" konvertiert werden müssen. // Dies geschieht, indem man `(int)` oder `(float)` vor die Variable schreibt, // bevor diese einer Funktion übergeben werden. // Es gibt eine ganze Reiher zusammengesetzter Datentypen, welche in Processing // gebraucht werden können. Um Zeit zu sparen, gehen wir in diesem Tutorial // lediglich die wichtigsten durch. // String // Während der Datentyp `char` einfache Anführungszeichen (' ') braucht, haben // Strings doppelte Anführungszeichen (" "). String sampleString = "Hallo, Processing!"; // Strings können auch durch ein Array von `char`s erstellt werden. // Wir werden Arrays gleich anschauen. char source = {'H', 'A', 'L', 'L', 'O'}; String stringFromSource = new String(source); // HALLO // Wie auch in Java können in Processing Strings auch zusammengefügt werden // mit dem +-Operator. print("Hallo " + "Welt!"); // => Hallo Welt! // Arrays // In Processing können Arrays jeden Datentypen beinhalten, sogar Objekte. // Da Arrays ähnlich wie Objekte sind, müssen diese mit dem Schlüsselwort `new` // erstellt werden. int[] intArray = new int[5]; int[] intArrayWithValues = {1, 2, 3} // Arrays können auch mit Daten gefüllt // werden. // ArrayList // Die Funktionen einer ArrayList sind ähnlich wie die eines Arrays und können // auch jegliche Datentypen beinhalten. Der einzige Unterschied zwischen Arrays // und `ArrayList`s ist, dass eine `ArrayList` die Größe dynamisch anpassen kann, // da es eine Implementierung des "List" Interface in Java ist. ArrayList intArrayList = new ArrayList(); // Objekte // Da Processing auf Java basiert, unterstützt Processing die Objektorientierte // Programmierung. Dies bedeutet, dass du grundsätzlich jegliche Datentypen // selber erstellen kannst und diese nach deinen Bedürfnissen manipulieren kannst. // Selbstverständlich muss eine Klasse definiert werden, bevor du ein Objekt // davon instanziieren kannst. // Format: ClassName InstanceName SomeRandomClass myObject // hier musst du das Objekt später instanziieren // Hier wird das Objekt direkt instanziiert: SomeRandomClass myObjectInstantiated = new SomeRandomClass(); // Processing hat noch weitere Collections (wie zum Beispiel Dictionaries und // Listen). Aus Einfachheitsgründen wird dies in diesem Tutorial weggelassen. /* ------------------------------------------------- Mathematik ------------------------------------------------- */ // Arithmetik 1 + 1 // => 2 2 -1 // => 1 2 * 3 // => 6 3 / 2 // => 1 3.0 / 2 // => 1.5 3.0 % 2 // => 1.0 (Modulo) // Processing beinhaltet auch einige Funktionen, welche mathematische // Operationen vereinfachen float f = sq(3); // Quadrat => f = 9.0 float p = pow(3, 3); // Potenz => p = 27.0 int a = abs(-13); // Absolute Zahl => a = 13 int r1 = round(3.1); // Runden => r1 = 3 int r2 = round(3.7); // Runden => r2 = 4 int sr = sqrt(25); // Quadratwurzel => sr = 5.0 // Vektoren // Processing bietet eine einfache Möglichkeit an, mit Vektoren zu arbeiten mit // der Klasse PVector. Die Klasse kann zwei- und dreidimensionale Vektoren // darstellen und bietet Methoden an, welche nützlich sein können für Matrizen- // Operationen. Weitere Informationen findest du hier: // (https://processing.org/reference/PVector.html) // Trigonometrie // Processing unterstützt auch trigonometrische Operationen mit Hilfe dieser // Funktionen: `sin()`, `cos()`, `tan()`, `asin()`, `atan()`. Für die einfache // Konvertierung gibt es außerdem noch die Funktionen `degrees()` und `radians()`. // Die trigonometrischen Funktionen rechnen mit dem Winkelmaß Radian, wodurch // die Gradzahlen zuerst konvertiert werden müssen. float one = sin(PI/2); // => one = 1.0 // Wie du vielleicht bemerkt hast, existieren einige Konstanten für trigo- // metrische Operationen; `PI`, `HALF_PI`, `QUARTER_PI` und so weiter ... /* ------------------------------------------------- Kontrollstrukturen ------------------------------------------------- */ // Bedingte Anweisungen // Bedinge Anweisungen werden gleich wie in Java geschrieben. if (author.getAppearence().equals("hot")) { print("Narzissmus vom Feinsten!") } else { // Du kannst hier weitere Bedingungen prüfen. print("Irgendetwas ist falsch hier!"); } // Für die `if`-Anweisungen gibt es auch eine Kurzschreibweise // Dies sind sogenannte ternäre Operatoren. int i = 3; String value = (i > 5) ? "Groß" : "Klein"; // => "Klein" // Die Switch-Case-Anweisung kann verwendet werden, um mehrere Bedingungen // zu prüfen. // Wichtig ist, dass nach jeder Bedingung ein `break`-Statement verwendet wird, // sonst werden alle folgenden ausgeführt und es wird nicht mehr überprüft, ob // die Bedingung wahr ist. int value = 2; switch(value) { case 0: print("Auf keinen Fall!"); // Dies wird nicht ausgeführt. break; // Geht zum nächsten Statement und prüft dieses case 1: print("Wir kommen näher..."); // Auch dies wird nicht ausgeführt break; case 2: print("Bravo!"); // Dies wird ausgeführt. break; default: print("Nicht gefunden."); // Diese Zeile wird ausgeführt, wenn keine // der anderen Operatoren wahr sind. break; } // Wiederholungen // For-Schleifen - Auch hier ist die Syntax wieder gleich wie in Java for(int i = 0; i < 5; i++) { print(i); // Gibt die Zahlen 0 bis 4 aus. } // While-Statements int j = 3; while(j > 0) { print(j); j--; // Dies ist wichtig, dass der Code nicht unendlich lange läuft. } // `loop()` | `noloop()` | `redraw()` | `exit()` // Dies sind spezifische Funktionen, welche in Processing verwendet werden // können, um den Programmablauf zu steuern. loop(); // erlaubt es der `draw()`-Methode immer zu laufen, während noloop(); // dies nur für einmal erlaubt. redraw(); // führt die `draw()`-Methode noch einmal aus. exit(); // Diese Methode stoppt das Programm. Dies kann nützlich sein, wenn die // Methode `draw()` immer läuft. /* ------------------------------------------------- Figuren ------------------------------------------------- */ // 2D-Figuren // Punkte point(x,y); // im zweidimensionalen Raum point(x, y, z); // im dreidimensionalen Raum // Diese Befehle zeichnen einen Punkt an der Koordinate. // Linien line(x1, y1, x2, y2); // im zweidimensionalen Raum // Dies zeichnet eine Linie, welche durch die zwei Punkte (x1, y1) und (x2, y2) // definiert wird. line(x1, y1, z1, x2, y2, z2); // im dreidimensionalen Raum // Analog wird hier eine Linie gezeichnet mit drei Punkten // Dreieck triangle(x1, y1, x2, y2, x3, y3); // Zeichnet ein Dreieck, welches als Eckpunkte die drei Koordinaten hat. // Rechteck rect(a, b, c, d, [r]); // Mit dem optionalen Parameter kann der Winkel aller // vier Ecken definiert werden rect(a, b, c, d, [tl, tr, br, bl]); // Mit weiteren optionalen Parametern kann // jeder Winkel des Rechtecks definiert werden. // Dies zeichnet ein Quadrat mit der Koordinate {a, b} als linke obere Ecke // die Parameter c und d sind für die Breite und Höhe. // Vierecke quad(x, y, x2, y2, x3, y3, x4, y4); // Dies zeichnet ein Viereck, welches die einzelnen Koordinaten als Eckpunkte hat. // Ellipse ellipse(x, y, width, height); // Zeichnet eine Ellipse beim Punkt {x. y}. Die Breite und die Höhe werden durch // die Parameter width und height definiert. // Arc arc(x, y, width, height, start, stop, [mode]); // Die ersten vier Parameter sollten selbsterklärend sein. // start und end definieren die Winkel, bei welchen `arc` starten resp. enden // (in Radians) // Der optionale Parameter `mode` definiert, ob der Kreisbogen gefüllt wird // oder nicht. // Die möglichen Optionen für `mode` sind: PIE, CHORD und OPEN. // Kurven // Processing bietet zwei mögliche Kurven an, welche verwendet werden können. // Da es hier darum geht, dass es möglichst simpel ist, werden hier keine // weiteren Details genannt. Wenn du Kurven in deinem Programm verwenden möchtest, // sind die folgenden Links empfehlenswert: // https://processing.org/reference/curve_.html // https://processing.org/reference/bezier_.html // 3D-Figuren // Der dreidimensionale Raum kann aktiviert werden, indem man den Renderer- // Parameter in der Methode `size()` zu "P3D" setzt. size(width, height, P3D); // Im dreidimensionalen Raum müssen die Koordinaten übersetzt werden, damit // diese korrekt gerendert werden. // Box box(size); // Würfel mit der Seitenlänge `size` box(w, h, d); // Quader definiert durch Breite, Höhe und Tiefe // Kugel sphere(radius); // Die Größe wird definiert durch den Parameter `radius` // Der Mechanismus hinter dem Rendern von Kugeln wurde durch mosaikartige // Dreiecke implementiert. // Mit der folgenden Funktion ist es möglich, zu bestimmen wie detailliert die // Kugel gerendert wird. // spereDetail(res); // Weitere Informationen sind hier zu finden: (https://processing.org/reference/sphereDetail_.html) // Unregelmäßige Figuren // Was ist, wenn du etwas zeichnen möchtest, was nicht durch Processing-Funktionen // abgedeckt ist? // Es ist möglich, die Funktionen `beginShape()`, `endShape()` und `vertex(x,y) // zu verwenden. // Weitere Informationen findest du hier: (https://processing.org/reference/beginShape_.html) // Du kannst selber gemachte Formen auch verwenden mit der PShape-Klasse. // Informationen zu PShape gibt es hier: (https://processing.org/reference/PShape.html) /* ------------------------------------------------- Transformationen ------------------------------------------------- */ // Transformationen sind nützlich, um ständig zu wissen, wo die Koordinaten und // die Ecken einer Form sind, welche du gezeichnet hast. Grundsätzlich sind dies // Matrizenoperationen. `pushMatrix()`, `popMatrix()` und `translate()`. pushMatrix(); // Speichert das aktuelle Koordinatensystem auf dem Stack // alle Transformationen werden hier angewendet. popMatrix(); // Stellt das gespeicherte Koordinatensystem wieder her. // Wenn du diese Funktionen verwendest, kann das Koordinatensystem gespeichert // und visualisiert werden, ohne dass es Konflikte gibt. // Translate translate(x,y); // Setzt den Ursprung zu diesem Punkt. translate(x, y, z); // Pendant zu der oberen Funktion im dreidimensionalen Raum // Rotationen rotate(angle); // Rotiere, um den Betrag, welcher spezifiziert wurde. // Es gibt drei Pendants im dreidimensionalen Raum. // Namentlich sind dies: `rotateX(angle)`, `rotateY(angle)` und `rotateZ(angle)` // Skalierung scale(s); // Skaliert das Koordinatensystem (entweder erweitern oder verkleinern) /* ------------------------------------------------- Styling und Texturen ------------------------------------------------- */ // Farben // Wie ich zuvor schon erklärt habe, kann die Hintergrundfarbe mit der Funktion // `background()` definiert werden. Außerdem ist es möglich, dass man zuerst // eine Farbe definiert und diese erst danach der Funktion übergeben wird. color c = color(255, 255, 255); // WEISS! // Standardmäßig verwendet Processing das RGB-Farbschema, aber dies kann // zu HSB konfiguriert werden, indem die Funktion `colorMode()` verwendet wird. // Weitere Informationen findest du hier: (https://processing.org/reference/colorMode_.html) background(c); // Ab jetzt ist der Hintergrund in weiß. // Du kannst die Funktion `fill()` verwenden, um die Farbe auszuwählen, mit // welcher die Formen ausgefüllt werden. // Dies muss konfiguriert werden bevor Formen und Figuren gezeichnet werden. fill(color(0, 0, 0)); // Wenn du nur die Farbe der Umrandungen definieren möchtest, kannst du die // Funktion `stroke()` verwenden. stroke(255, 255, 0, 200); // Linienfarbe wird zu gelb mit einer höheren // Transparenz geändert. // Bilder // Processing kann Bilder rendern und diese unterschiedlich verwenden. Die // meisten Bilder sind im Datentyp `PImage` gespeichert. filter(shader); // Processing unterstützt mehrere Filter-Funktionen, damit // Bilder verändert werden können. texture(image); // PImage kann als Argument, weiteren Funktionen übergeben // werden, um die Figuren zu "Text" zu machen. // Bevor wir weiterfahren, werde ich einige Aspekte zum Importieren von // Bibliotheken und Paketen sagen, damit du Processing erweitern kannst.. /* ------------------------------------------------- Import ------------------------------------------------- */ // Die Macht von Processing kann besser veranschaulicht werden, wenn wir // Bibliotheken und Pakete importieren. // Die Import-Anweisung kann wie unten geschrieben zu Beginn des Quelltextes // geschrieben werden. import processing.something.*; // Disclaimer: Ich habe das Programm nicht selbst geschrieben. Diese Skizze // stammt aus openprocessing, allerdings soll dieses Programm zeigen, wie wenig // Zeilen Code notwendig sind, um etwas Cooles zu machen. // Abgerufen von: (https://www.openprocessing.org/sketch/559769) float theta; float a; float col; float num; void setup() { size(600,600); } void draw() { background(#F2F2F2); translate(width/2, height/2); theta = map(sin(millis()/1000.0), -1, 1, 0, PI/6); float num=6; for (int i=0; i30) { pushMatrix(); translate(0, -30); rotate(theta); branch(len); popMatrix(); pushMatrix(); translate(0, -30); rotate(-theta); branch(len); popMatrix(); } }