http://sites.schaltungen.at/arduino-uno-r3/processing/creativecoding/einstieg-in-processingWels, am 2015-01-10BITTE nützen Sie doch rechts OBEN das Suchfeld [ ] [ Diese Site durchsuchen]DIN A3 oder DIN A4 quer ausdrucken ********************************************************************************** DIN A4 ausdrucken
*********************************************************
|
quad (12, 19, 31, 85, 67, 74, 80, 19);
|
4. Graustufen
Im RGB Farbraum mischen sich Graustufen aus gleichen Anteilen der drei Kanäle Rot, Grün und Blau.Durch die additive Zusammensetzung erhält man bei drei vollen Teilen die Farbe weiß, bei keinen Angaben für R,G,B schwarz.
Processing bietet für diesen Fall eine Kurzschreibweise für die Festlegung von Füll- und Strichfarben.
Statt drei gleicher Werte wird nur eine Wert zwischen den runden Klammern genannt.
Die Zeile fill(38, 38, 38); hat die gleiche Aufgabe wie fill(38);
//Processing-Sketch: NAME_1a.pde
background (110); // grau
fill (0); // schwarz
stroke (255); // weiß
|
background (110); // grau
fill (0, 90); // schwarz, transparent
stroke (255, 200); // weiß, transparent
|
- 1 Wert Grauton, deckend
- 2 Werte Grauton, mit Transparenzangabe
- 3 Werte RGB Farbtonangabe, deckend
- 4 Werte RGB Farbtonangabe, mit Transparenzangabe
5. Farben
Standardmäßig arbeitet Processing mit dem RGBA-Farbraum (Rot, Grün, Blau, Alpha).Für jeden der sogenannten Kanäle steht ein Wertebereich von 0..255 zur Verfügung um das Mischverhältnis zu bestimmen (additive Farbmischung).
Ein Anteil von 0 bedeutet, daß kein Teil dieses Kanals in die Farbmischung eingeht - mit 255 der komplette Kanal.
Drei Befehlen bietet Processing um Farben für unterschiedliche Eigenschaften/Bereiche festzulegen:
background(200); // für das einfärben der gesamten Zeichenfläche,
fill(240); // für die Füllfarbe
stroke(0); // für die Umrandung von visuellen Elemente.
5.1 Hintergrund einfärben
Processing färbt ohne unser Zutun den Hintergrund des Sketchfensters hell-grau (200); ein.Wenn wir die Fläche leeren wollten, sie mit einem Farbton füllen, müssten wir ein Rechteck im Fensterformat zeichnen.
Diese Funktionalität bietet uns ebenfalls der Befehl background().
Ausgestattet mit Informationen über die Anteiligkeit der Farbkanäle überschreibt er den momentanen Inhalt der Zeichfläche
(ohne das wie bei rect(); Position oder Größe angegeben werden muss).
-
background(r, g, b) löscht den Inhalt der Zeichenfläche durch homogenes Füllen mit einer Farbe.
- Diese wird durch die drei Kanäle r Rot, g Grün und b Blau bestimmt. background()
background
(231, 0, 89);
5.2 Flächen füllen & Ränder einfärben
Um Flächen/Einschlüsse farblich zu füllen kann man dem grafischen Element keine konkreten Informationen mit auf den Weg geben.Innerhalb von Processing existiert für Füll- und Strichfarbe jeweils ein Farbeimer, bildlich gesprochen.
Der Inhalt dieser beiden Eimer kann mit zwei Funktionen bestimmt werden:
fill() für die Füllfarbe
stroke() für die Farbgebung der Umrandung.
Wenn keine Farbe angewendet werden soll kommen
noFill() keine Füllfarbe
noStroke() keine Strichfarbe
Diese Einstellungen bleiben solange aktiv,
bis sie durch einen Aufruf von fill() bzw. noFill() für die Füllfarbe
und stroke() bzw. noStroke() für die Umrandung geändert werden.
-
fill(r, g, b) setzt die Füllfarbe für alle im Anschluss gezeichneten Elemente.
- Die drei Werte stehen jeweils für einen Farbkanal: r Rot, g Grün und b Blau.
- Optional kann durch einen vierten Wert a die Transparenz bestimmt werden.
- Der Wertebereich liegt für alle vier Angaben zwischen 0 und 255. fill()
fill
(255, 0, 0);
//volles Rot
fill
(0, 255, 0);
//volles Grün
fill
(0, 0, 255);
//volles Blau
-
noFill() deaktiviert die Füllfarbe für alle im Anschluss gezeichneten Elemente. noFill()
noFill
();
-
stroke(r, g, b) setzt die Farbe für alle im Anschluss gezeichneten Elemente.
- Die drei Werte stehen jeweils für einen Farbkanal: r Rot, g Grün und b Blau.
- Optional kann durch einen vierten Wert a die Transparenz bestimmt werden.
- Der Wertebereich liegt für alle vier Angaben zwischen 0 und 255. stroke()
stroke
(255, 0, 0);
//volles Rot
stroke
(0, 255, 0);
//volles Grün
stroke
(0, 0, 255);
//volles Blau
-
noStroke() deaktiviert die Umrandungsfarbe für alle im Folgenden gezeichneten Elemente. noStroke()
noStroke
();
*********************************************************
02. Mausinteraktion
Grundlagen für die Gestaltung interaktiver Programme
Nach einer kurzen Einführung in die Erstellung unendlich fortlaufender Programme werden die Grundlagen für die Interaktion mit der Maus in Processing vorgestellt.
Dabei wird auf die Verwendung der Mausposition sowie auf die Abfrage der Maustasten eingegangen.
1. Vorbereitung:
Fortlaufende Programme
Grundsätzlich werden alle im Vorherigen geschriebenen Programme nach einmaligem Durchlaufen beendet.Das Resultat wird im definierten Fensterbereich abgebildet - weitere Modifikationen am Dargestellten durchzuführen ist jedoch nicht möglich.
Um dies zu ermöglichen müssen im Programm spezielle Bereiche angelegt werden.
Block eins setup() und zwei draw().
- setup() wird direkt nach dem Programmstart einmalig ausgeführt.
- draw() wird nach dem Ablauf von setup() ins Leben gerufen und läuft in einer Schleife bis das Fenster geschlossen wird.
Nach Beendigung steigt die Applikation in den draw() Teil ein und führt diesen in einer Endlos-Schleife aus.
D.h. sobald die letzte Anweisung im draw() ausgeführt wurde springt sie zur ersten Zeile im draw() Block
- solange bis das Sketchfenster eben geschlossen wird.
*******************************
z.B.: Dauernd laufende Programmschleife
//Processing-Sketch: NAME_1a.pde
// Aufruf in einer Schleife bis zum Schließen des Fensters
void draw () {
fill (255,0,0, 1); // transparent 1 ist rot fast unsichtbar duch die dauernden Überlagerung schaut es aber ähnlich (255,0,0, 100) aus
rect (18,18, 80,80);
}
|
Durch die Überlagerungen der einzelnen Objekte addieren sich die Transparenzen und das Quadrat erhält eine rote Farbe (transparent ca. 100).
*******************************
z.B.: Initialisierung & Fortlaufende
//Processing-Sketch: NAME_1a.pde
// Aufruf einmal direkt nach dem Programmstart
void setup () {
fill (0,125,0, 50); // grünes Rechteck, transparent 50
rect (40,40, 20,20);
}
// Aufruf in einer Schleife nach Beendigung des "setup" Blocks bis zum Schließen des Fensters
void draw () {
fill (255, 0, 0, 2); // rot transparent 1 (od. 4) rot ist fast unsichtbar
rect (10, 10, 80, 80);
}
|
Nach Beendigung dieser Anweisungen wird der draw() Block bis zum Schließen des Fensters in einer Schleife ausgeführt.
D.h. es wird permanent ein fast nich sichtbares rotes, stark transparentes Rechteck in die Arbeitsfläche gezeichnet.
Nach 2 Sekunden wurden hunderte Rechtecke platziert um den grünen Farbton zu verdecken.
Diese obige Prozedur müsste ohne die Unterteilung in setup() und draw() nach dem folgenden Schema ablaufen:
// setup() direkt nach dem Programmstart
fill (0,125,0, 50);
rect (40,40, 20,20);
// draw() in einer unendlichen Schleife
// Durchlauf 1
fill (255,0,0, 4);
rect (10,10, 80,80);
// Durchlauf 2
fill (255,0,0, 4);
rect (10,10, 80,80);
bis
// Durchlauf 1000
// ... usw. usw. usw.
*******************************
Einen Durchlauf des draw() Abschnitts bezeichnet man als Einzelbild (ursprünglich aus dem Film).
Processing versucht pro Sekunde 60 dieser Einzelbilder zu erzeugen und im Sketchfenster abzubilden.
Diese Bildfrequenz wird im Bereich der Computergrafik als frameRate bezeichnet.
// Durchlauf 1000
// ... usw. usw. usw.
*******************************
Einen Durchlauf des draw() Abschnitts bezeichnet man als Einzelbild (ursprünglich aus dem Film).
Processing versucht pro Sekunde 60 dieser Einzelbilder zu erzeugen und im Sketchfenster abzubilden.
Diese Bildfrequenz wird im Bereich der Computergrafik als frameRate bezeichnet.
- frameRate beinhaltet die aktuell angestrebte Anzahl von Einzelbildern (frames) der Processing Sketches.
-
frameRate() erlaubt das Steuern der Bildfrequnz (wie oft der draw() Block pro Sekunde abgearbeitet wird) durch Angabe einer Ganzzahl.
- Für Processing stellt es jedoch nur einen Richtwert dar - größere Abweichungen sind möglich.
z.B.: Bildfrequenz 0,5Hz
// Processing-Sketch: Bildfrequenz_1a.pdevoid setup () { frameRate (0.5); // festlegung der Bildrate auf ein Bild pro 2 Sekunde}void draw () { fill (255,0,0, 10); // Füll-Farbe rot, transparent 10 rect (15,15, 70,70);}
******************************* |
2.1 Mausposition
Die Kommunikation zwischen Benutzer und Programm kann auf vielfältige Weise geschehen.Im Folgenden wollen wir uns auf den Bereich der Maus - deren Position in der Zeichenfläche - beschäftigen.
Die Processingumgebung bietet für diesen Zweck vier Ausdrücke:
mouseX, mouseY für die aktuelle Position der Maus
pmouseX, pmouseY für die Position im vorherigen Bild.
Alle vier Angaben geben dabei die Distanz zum Koordinatenursprung (oben, links) in Pixeln auf der jeweiligen Achse an.
z.B.: aktuelle Mausposition 1
//Processing-Sketch: aktuelle Mausposition 1_1a.pdevoid setup() { size(400, 400); // Breite Höhe background (200); // hell-grau rect(120, 70, 150, 80);}void draw () { ellipse (mouseX, mouseY, 20, 20); //print("mouseX = " + mouseX); //println(" mouseY = " + mouseX); //delay(75); // Wartezeit}
|
In der Vergangenheit gezeichnete Kreise befinden sich im Hintergrund und werden bei Überlagerungen verdeckt.
*******************************
z.B.: aktuelle Mausposition 2
//Processing-Sketch: aktuelle Mausposition 2_1a.pdevoid setup() { size(400, 400); // Breite Höhe}void draw () { background (255); // weiß ellipse (mouseX, mouseY, 20, 20); print("mouseX = " + mouseX); println(" mouseY = " + mouseX); //delay(75); // Wartezeit}
Durch das Aufrufen des background() Befehls wird vor jedem Zeichnen eines Kreises die vorherige Arbeitsfläche vollflächig weiß gelöscht.
Es scheint als würde sich ein und der selbe Kreis nach der Position der Mauszeigers bewegen.
Bsp.: aktuelle und vorherige Mausposition
//Processing-Sketch: Strich von der vorherige zur aktuellen Mausposition_1a.pdevoid setup() { size(400, 400); // Breite Höhe frameRate (5);}void draw () { background (255); // weiß line (pmouseX, pmouseY, mouseX, mouseY);}
Bei schneller Bewegung der Maus wird eine schwarze Linie sichtbar.
Diese wird zwischen der vorherigen und der aktuellen Mausposition gezeichnet
und stellt die während eines Zeichenvorgangs zurückgelegte Distanz dar.
Das "p" in pmouseX und pmouseY steht dabei für den englischen Begriff previous (dt.: früher, vorig).
*******************************
2.2 Maustasten
Um in einem Processing Programm herauszufinden, ob die Maustaste gedrückt ist müssen wir lediglich den Zustand der Maustaste "abfragen". Genau eine solche Abfrage können wir mit einem neuen Befehl stellen:- if (Bedingung ) also "falls" eine von uns genannte Bedingung "wahr" ist(true), wird der darauf folgende Abschnitt in geschweiften Klammern ausgeführt ( {…} )
- mousePressed ist die Maustaste gedrückt, oder nicht.
z.B.: Drücken der Maustaste 1
// Processing-Sketch: Drücken der Maustaste 1_1a.pde// beim zeichnen kommt nun nichts weiter hinzu als die Abfrage,// ob die Maus gedrückt ist mit Hilfe der "if" Abfrage (Statement)void setup() { size(300, 300); // Breite Höhe}void draw() { background (200); // lösche den Hintergrund und fülle ihn mit hell-grau if (mousePressed) { // wenn die Maus gedrückt ist ellipse (mouseX, mouseY, 40, 40); // zeichne eine Ellipse an der Mausposition } rect (90, 90, 20, 20); // Rechteck (unabhängig vom Mauszustand)}
Bei einer if-Abfrage wird immer zwischen Ja und Nein entschieden.
Im oberen Beispiel haben wir den Status der Maustaste als Bedingung festgelegt -
und zeichnen bei "Wahrheit" einen Kreis.
Als Gegenstück zum Ja-Abschnitt können wir einen Bereich definieren der nur ausgeführt wird wenn die Maustaste nicht gedrückt wird.
-
else leitet einen Programmblock ein der bei Nichtzutreffen der if-Bedingung abgearbeitet wird.
- Dieser Block wird ebenfalls von zwei geschweiften Klammern umfasst ( {...} ).
Bsp.: Drücken der Maustaste II
// Processing-Sketch: Drücken der Maustaste 2_1a.pde// beim zeichnen kommt nun nichts weiter hinzu als die Abfrage,// ob die Maus gedrückt ist mit Hilfe der "if" Abfrage (Statement)void setup() { size(300, 300); // Breite Höhe}void draw() { background (200); // lösche den Hintergrund und fülle ihn mit hell-grau if (mousePressed) { // wenn die Maus gedrückt ist ellipse (mouseX, mouseY, 40, 40); // zeichne eine Ellipse an der Mausposition} else { // zeichne ein Rechteck an der Mausposition // wenn die Maustaste nicht gedrückt ist rectMode (CENTER); rect (mouseX, mouseY, 25, 25); // Rechteck (unabhängig vom Mauszustand) }}
|
3. Anwendungsbeispiele:
Mausinteraktion Kreis
Elementar für die Interaktion mit der Maus sind Abfragen ob sich ein Punk in einem bestimmten Bereich befindet.
Angefangen bei simplen Elementen wie beispielsweise rechteckigen Buttons bis hin zu komplexen Ansammlungen die einen Charakter eines Spiels darstellen funktioniert dies immer nach dem selben Prinzip.
Punkt in Kreis in Processing
Beim Test ob sich ein Punkt, in diesem Fall die Mausposition, in der Fläche eines Kreises befindet bedarf es wenig Aufwand.
Indem der Abstand zwischen dem Punkt und dem Zentrum des Kreises mit dem Radius dessen verglichen wird,
erhält man eine konkrete Aussage ob der Fall zutrifft oder nicht.
Der dist() Befehl erleichtert uns das Arbeiten und liefert die Entfernung zwischen zwei Punkten
ohne das mit Quadrat oder »Wurzel aus...« gearbeitet werden muss.
// Processing-Sketch: Mausinteraktion_1a.pdefloat x; // x-Positionfloat y; // y-Positionfloat rad = 70; // Radiusvoid setup () { size(260, 260); // Größe des Grafik-Ausgabebereiches noStroke (); smooth (); x = width/2; // Kreis im Mittelpunkt y = height/2; // Kreis im Mittelpunkt}void draw () { background (225); // Hintergrund hell-grau // wenn der Abstand zwischen Maus und Kreiszentrum kleiner als der Radius des Kreises ist d.h. Cursor ist im Kreis! if (dist (mouseX, mouseY, x, y) < rad) { fill(255,0,0); // Rot wenn Mauskursor im Kreis ist } else { fill(0); // Schwarz wenn Mauskursor außerhalb des Kreises ist } ellipse(x,y, rad*2,rad*2); // Zeichne einen Kreis}
Es wird überprüft, ob sich die Koordinaten des Punktes in beiden Achsenabschnitten befinden (2D).
D.h. auf der x-Achse muss sich die x-Koordinate des Punktes zwischen der linken und rechten Außenseite des Rechtecks befinden.
Trifft dies auch für die y-Achse zu, ist der Punkt innerhalb der durch das Rechteck bestimmten Fläche.
Im Beispiel sind beide if-Abfragen ineinander verschachtelt.
Wenn die Erste (für die x-Achse) nicht zutreffen sollte, brauch die Zweite nicht ausgeführte werden - beide müssen erfüllt sein.
// Processing-Sketch: Mausinteraktion Rechteck_1a.pdefloat x = 120; // x-Positionfloat y = 80; // y-Positionfloat w = 100; // Breitefloat h = 50; // Höhevoid setup () { size(320, 240); noStroke ();}void draw () { background (255); boolean hit = false; // Wenn sich die Maus auf der x-Achse zwischen Flächenanfang und Ende befindet. if (mouseX > x && mouseX < x + w) { // Wenn sich die Maus auf der y-Achse zwischen Flächenanfang und Ende befindet. if (mouseY > y && mouseY < y + h) { hit = true; // setze "hit" auf true! } } if (hit == true) { // Bestimme Füllfarbe am Zustand von "hit" fill (255, 0, 0); } else { fill (0); } rect (x, y, w, h); // Zeichne Rechteck}
*********************************************************
a
Vorgestellt werden die wichtigsten Datentypen für erste kleine Programme in Processing:
boolean, int, float, String.
Anhand dieser Datentypen wird die Verwendung von Variablen - deren Deklaration, Initialisierung, Abfrage und Zuweisung - eingeführt.
Danach folgt ein Überblick über den Umgang mit Daten mit Hilfe von Operatoren.
Dabei spielen Rechenoperationen ebenso eine Rolle, wie auch Vergleiche und logische Verknüpfungen.
1. Datentypen
Kommunikation basiert auf dem Austausch von Daten.Entscheidungen werden mit ja - nein gefällt.
Die Kühlschranktemperatur ist meist ein Wert zwischen 3 und 7 Grad Celsius.
Einem Menschen werden Reihungen von Zeichen zur Identifikation zugewiesen.
Diese Art der Beschreibung von Gegenständen und Individuen mit Informationen kann auch in Programmiersprachen verwendet werden.
Hierbei sprechen wir immer von Daten.
Neben den Algorithmen sind sie der Hauptbestandteil jeglicher Computer Software.
Software besteht also aus Informationen (Daten) und Anweisungen zu ihrer Verarbeitung (Algorithmen).
Im Bereich der Programmierung reden wir beim Umgang mit Daten oftmals von Variablen - einen Namen, den man sich gut darüber herleiten kann, dass Daten in Programmen prinzipiell variabel - also veränderbar - sind.
Eine bewährte Metapher die beim Verständnis zum Umgang mit Variablen hilft ist die einer Schublade.
Variablen können wie Schubladen einen Wert haben (Inhalt) - müssen dies aber nicht (die Schublade existiert, ist jedoch leer).
Jede Schublade hat jedoch unabhängig von ihrem Inhalt einen Namen (eine Beschriftung).
Über diesen Namen können wir Werte von Variablen abfragen oder verändern.
Die einzige Besonderheit von Variablen ist, dass sie jeweils beschränkt sind auf eine bestimmte Art von Inhalten.
So gibt es Schubladen in denen man nur Ganzzahlen oder Kommazahlen speichern kann,
sowie Schubladen die nur ein einfaches »Ja« oder »Nein« beinhalten können.
Wir werden im Verlauf der weiteren Lessons noch weiteren speziellen Schubladen über den Weg laufen und auch lernen wie wir Schubladen speziell nach unseren Wünschen anlegen[1].
In der Lesson zur Mausinteraktion haben wir bereits vier solcher Variablen/Schubladen kennengelernt.
Die Processing-Umgebung stellt uns
z.B. die Koordinaten der Maus über die Ausdrücke mouseX und mouseY zur Verfügung.
Da sich hinter diesen Begriffen keine einfachen Klammern befinden handelt es sich bei ihnen um Variablen.
Processing ändert für uns vor jedem draw() Durchlauf den Wert dieser Variablen, sodass wir immer auf die aktuelle Lage der Maus zugreifen können.
Zum Einsteigen gebrauchen wir vier elementare Typen.
Sie unterscheiden - wie oben beschrieben - sich in Ihrem Fassungsvermögen, also darin welche Art von Inhalten in ihnen gespeichert/abgelegt werden kann:
- boolean speicher nur »Ja« oder »Nein« (true oder false, z.B. ist die Maustaste gedrückt, oder nicht?) boolean
- int für ganzzahlige Werte (integer) 0, 1, 2, -3, ... bis zu zwei Billionen int
- float für Gleitkommazahlen (floating point number). Die Notation wird wie im Englischen durch einen Punkt durchgeführt (falsch: 1,42 richtig: 1.42) float
- String Zeichenkette die in doppelten Anführungszeichen anzugeben ist, z.B. "Creative Coding" String
Processing mitteilen das wir vorhaben mit ihr im späteren Ablauf des Programms zu arbeiten.
Dies geschieht immer mit der Angabe ihres Typs (z.B. int, float, etc. ) und einem Namen zur Identifikation.
z.B.: Variablen definieren
boolean jaNein;int ganzzahl;float gleitzahl;String textabschnitt;
|
z.B.: Werte zuweisen
boolean jaNein = true; // boolean Variable mit dem Wert 'ja' ('wahr')int ganzzahl = 40; // ganzzahlige Variable mit dem Wert 40float gleitzahl = 36.9; // Variable mit dem gleitkomma Inhalt 36.9String textabschnitt = "Creative Coding"; // Variable die den Text "Creative Coding" beinhaltet
|
Das Sketchfenster öffnet sich und stellt uns den üblichen grauen Hintergrund dar.
Das Einsehen des Wertes/Status der Variable könnte über eine textuelle Ausgabe in unserer Zeichenfäche geschehen, wäre aber vom Aufwand hoch einzustufen.
In Processingprogramm existiert unter dem Texteditor, in welchem wir unseren Quellcode schreiben, eine schwarze Box, (Konsole) das Nachrichtenfenster.
Bisher wurden uns hier Fehlermeldungen abgebildet - zwei Befehlen erlauben uns aber auch sie selbst zu beschreiben:
z.B.: Werte modifizieren
// erstellt eine ganzzahlige Variable names "zahl" und gib ihr den Wert 39int zahl = 39;println (zahl); // Ausgabe in der Konsolezahl = zahl + 1; // erhöhe den Wert von "zahl" um einsprintln (zahl); // Ausgabe im Nachrichtenfensterzahl = zahl + 1; // erhöhe den Wert von "zahl" um einsprintln (zahl); // Ausgabe im Nachrichtenfenster
|
z.B.: Verwendung von Variablen
//Processing-Sketch: NAME_1a.pde
void draw() { background (255, 255, 255); // lösche die Zeichenfläche und fülle sie mit weiss int grenze = 50; // erstelle eine Variable mit dem Namen "grenze" und gib ihr den ganzzahligen Wert 50 if (mouseX < grenze) { // wenn die Mausposition auf der x-Achse kleiner ist als unsere definierte Grenze... ellipse (mouseX, mouseY, 10, 10); // zeichne eine kleine Kreis an der Mausposition }
else { ellipse (mouseX, mouseY, 50, 50); // zeichne eine große Kreis an der Mausposition } rect (80, 80, 10, 10); // dieser Teil wird immer ausgeführt unabhängig vom der Mausposition}
|
2. Operatoren
Operatoren kommen stets in Verbindung mit Variablen zum Einsatz und führen Operationen an ihnen durch.Die bekanntesten Operatoren sind die aus dem unbeliebten Mathekurs: + - * /
Mit ihnen haben wir vorher schon Operationen an Zahlen durchgeführt, also mit ihnen gerechnet.
Andere Operatoren werden z.B. zur Formulierung von Fragen verwendet.
Beispielsweise ob die Frucht ein Apfel ist und dieser die Farbe Grün besitzt.
2.1 Rechenoperationen
Der Titel dieses Abschnittes lässt Erinnerungen an vergangene Zeiten im unbeliebten Mathekurs aufkommen.Visuelle Eigenschaften wie Positionen und Farbwerte werden von uns durch Zahlen angegeben - nun lernen wir diese Werte zu kontrollieren.
Processing gibt uns dabei die vier grundlegenden Rechenoperationen, plus zwei Weitere die uns das Tippen ersparen.
- + addiert zwei numerische Werte oder fügt zwei Zeichenketten (String) zusammen.
- - subtrahiert zwei numerische Werte oder negiert einen Wert.
- * multipliziert zwei numerische Werte.
- / dividiert zwei numerische Werte.
- ++ erhöht den Wert einer Variable um 1.
- -- verringert den Wert einer Variable um 1.
2.1.1 Strichrechnung
float a = 4.2;float b = 1.3;println (a + b); // Ausgabe im Nachrichtenfenster 5.5println (b - a); // Ausgabe im Nachrichtenfenster -2.8999999println ("Creative " + "Coding " + 1); // Ausgabe: "Creative Coding 1"
|
2.1.2 Punktrechnung 1
int c = 5 ;int d=2;println (c * d); // Ausgabe: 10println (c/d); // Ausgabe: 2
|
2.1.3 Punktrechnung 2
float e=5;float f = 2;println (e / f); // Ausgabe: 2.5
|
2.1.4 Erhöhen
int zahl_1 = 94;println (zahl_1) ; // Ausgabe: 94zahl_1 ++; // erhöht den Wert um 1println (zahl_1); // Ausgabe: 95
|
2.1.5 Verringern
int zahl_2 = 94;println (zahl_2); // Ausgabe: 94zahl_2 --; // verringert den Wert um 1println (zahl_2) ; // Ausgabe: 93
|
2.2 Vergleichsoperationen
- == gleich
- != ungleich
- > größer als
- >= größer gleich als
- < kleiner als
- <= kleiner gleich als
Die Abfrage könnte folgende Struktur haben:
String fruchtSorte = "apfel";String fruchtFarbe = "gruen";if (fruchtSorte == "apfel") { if (fruchtFarbe == "gruen") { println ("greif zu!"); // Ausgabe im Nachrichtenfenster "greif zu!" }}
|
2.3 Logische Operationen
Alle im Weiteren aufgeführten Elemente dienen zur Formulierung und Verknüpfung von Abfragen. Grundsätzlich muss das Resultat immer wahr oder falsch sein.- && UND
- ! NICHT
- || ODER
String fruchtSorte = "apfel";String fruchtFarbe = "gruen";if (fruchtSorte == "apfel" && fruchtFarbe == "gruen") { // UND println ("greif zu!"); // Ausgabe im Nachrichtenfenster "greif zu!"}
|
Beide müssen zutreffen damit "greif zu!" in der Konsole erscheint.
Im nächsten Schritt lassen wir neben grünen Äpfeln alle roten Früchte zu:
String fruchtSorte = "apfel";String fruchtFarbe_1 = "gruen";
String fruchtFarbe_2 = "rot";
if (fruchtSorte == "apfel" && fruchtFarbe_1 == "gruen" || fruchtFarbe_2 == "rot") { // UND ODER println ("greif zu!"); // Ausgabe im Nachrichtenfenster "greif zu!"}
Im Fokus dieser Lesson steht die Auseinandersetzung mit der while und for-Schleife, die durch Wiederholungen komplexere Prozesse effizient im Code abbilden können.
Am Schluß folgt ein Beispiel zu verschachtelten for-Schleifen, die die Grundlage für die Anordnung visueller Elemente im Raster bzw. in einer Matrix bilden können.
1. Schleifen
Um visuelle Komplexität zu generieren muss eine Vielzahl von Zeichenbefehlen aufgerufen werden.Viele Formen oder Formzusammenschlüsse weisen Ähnlichkeiten auf und unterscheiden sich im Quelltext nur durch unterschiedliche Angaben von Parametern
(z.B. x1, y1, x2, y2) die den Zeichenbefehlen folgen.
Im unteren Beispiel werden fünf Linien gleichmäßig mit einem Abstand von jeweils 20px auf der y-Achse verteilt.
//Processing-Sketch: NAME_1a.pde
void setup () { size(256, 101); background (255, 255, 0);}void draw () { line (0, 0, 200, 0); line (0, 20, 200, 20); line (0, 40, 200, 40); line (0, 60, 200, 60); line (0, 80, 200, 80); line (0, 100, 200, 100);}
Die x-Position der Anfangs- und Endpunkte bleibt bei allen fünf Aufrufen bei 0px bzw. 200px.
Nur die y-Position erhöht sich von line() zu line() Aufruf.
Im Folgenden lernen wir nun eine Schreibform die uns Arbeit erspart und uns die Möglichkeit gibt die Anzahl der Linien zu steuern.
Bisher haben wir in zwei Fällen von sogenannten "Blöcken" gesprochen. Blöcke die mittels geschweifter Klammern ( {.........} ) einen besonderen Bereich markieren.
Im Fall von draw() wird ein Einzelbild erstellt - bei if und else entscheidet eine Bedingung über die Ausführung des Programmabschnittes.
Nun lernen wir eine neue Art von Block kennen, die einen Bereich beschreibt der mehrfach ausgeführt werden kann.
Das resultierende Konstrukt (Kontrollstruktur) wird als "Schleife" bezeichnet, von denen es mehrere Formen gibt.
1.1 while-Schleife
Die einfachste Form einer Schleife ist die sogenannte while-Schleife.Formuliert wird sie durch die Angabe einer Laufbedinung und den geschweiften Klammern { }.
Eine Laufbedinung muss eine Frage stellen, die klar mit Ja (true) oder Nein (false) beantwortbar ist.
Hat eine Variable einen bestimmten Wert; liegt ein Wert unter/über einer Grenze;
z.B. Ist die Maustaste gedrückt?.
Wenn aus dieser Bedingung ein true resultiert wird der Code innerhalb des Schleifenkörpers (zwischen den geschweiften Klammern) ausgeführt.
Kommt es jedoch zu einem false - wird die gesamte Schleife übersprungen und das Programm läuft weiter.
Im Falle einer Bedingung die immer mit Ja (true) beantwortet wird erhält man eine Endlosschleife.
In dieser speziellen Version hängt das Programm in der while-Schleife und es werden beispielsweise keine weiteren Einzelbilder gezeichnet, wenn die while-Schleife im draw() Block platziert ist.
while (Bedingung) {
// repetiver Prozess
}
|
Nachdem die Klammernkombination geschlossen wird beginnt die Defintion des Schleifenkörpers.
Wie jeder weitere Block, z.B. setup() oder draw() wird dieser mit geschweiften Klammern umfasst.
Wenn die Bedingung ein false als Resultat besitzt, läuft das Programm unterhalb des Schleifenkörpers, nach der zweiten geschweiften Klammer, weiter.
z.B.: while-Schleife 1
//Processing-Sketch: NAME_1a.pde
void setup () { size(256, 101); background (255, 255, 0);}void draw () { background (255, 255, 0); float y=0; while (y <= 100) { line (0, y, 220, y); y = y + 20; }}
|
Bevor die Schleife mit dem Begriff while ins Leben gerufen wird, legen wir die Variable y mit dem Wert 0 an.
In ihr soll die y-Position der Linien gespeichert werden.
Bevor der Schleifenkörper abgearbeitet wird, stellt Processing die in der Bedinung formulierte
Frage:"Ist y kleiner-gleich 100?".
Da y zu Beginn den Wert 0 besitzt springt Processing in den Schleifenkörper und zeichnet eine Linie an der Position x1=0, y1=0, x2=320 und y2=0.
Damit liegt diese direkt an der oberen Bildschirmkante.
Die zweite Zeile im Schleifenkörper erhöht den Wert von y um 20.
Da die Bedingung der while-Schleife positiv war versucht Processing nun ein zweites Mal die Schleife auszuführen.
Der aktuelle Wert von y ist nun 20 - eine weitere Linie wird im Sketchfenster 20 Pixel unter der Ersten gezeichnet.
Processing schafft es genau 6 Linien zu setzen.
Dann hat y einen Wert von 120.
Die Frage ob es "unter, bzw. gleich 100 ist" gibt false zurück.
Dieses Beispiel zeigt die Struktur und Funktion von while-Schleifen.
Da die Anzahl der Durchläufe relativ gering ist und innerhalb des Schleifenkörpers simple Prozesse ablaufen, ist der große Vorteil von Schleifen nicht unmittelbar kenntlich.
Trifft jedoch einer der beiden Punkte zu, wird dieses Konstrukt unverzichtbar.
z.B.: while-Schleife 2
//Processing-Sketch: NAME_1a.pde
void setup () { size(200, 101); background (255, 255, 0);}void draw () { background (255, 255, 0); float y=0; while (y <= 100) { line (0, y, 150, y); y = y + 2; }}
|
In der ersten Version erhöhen wir den Wert von y um 20, im zweiten Fall um 2.
Das Resultat von Nummer zwei ist ein viel dichteres Linien-Muster da wir 52 statt 6 Durchläufe der Schleife zählen.
1.2 for-Schleife
Als zweite Form einer Schleife lernen wir nun die for-Schleife kennen.Ebenso wie die while Version dient sie dazu einen Codeblock als repitiven Prozess zu formulieren.
Der Kontrollmechanismus der while-Schleife bestand aus einer Frage die mit true zum Ausführen der Schleife führte.
Bei false wurde die Schleife abgebrochen bzw. gar nicht erst ausgeführt.
Im Beispiel nutzten wir die Variable y um die Anzahl der Durchgänge zu regulieren und einen Abbruch nach 6 gezeichneten Linien zu erzwingen.
Die for-Schleife ist neben der Abbruchbedingung mit zwei weiteren Bestandteilen ausgestattet, welche das Anlegen einer Variable zur Kontrolle (y im while Beispiel) überflüssig macht.
Diesen Typ von Variable, der sich auf die Anzahl der Durchläufe auswirkt, nennt man "Zählvariable".
Beim Schleifenaufruf, der Initialisierung, wird diese angelegt und nach jedem Durchgang aktualisiert.
Neben der englischen Bezeichnung ist die for-Schleife unter dem Namen "Zählschleife" bekannt.
Ausschlaggebend dafür ist die Zählvariable, welche diese Schleifenform von der while-Schleife unterscheidet.
Aufgerufen wird die for-Schleife in Processing über die folgende Struktur:
for (Initialisirung; Test; Aktualisierung) {
// repetiver Prozess
}
|
Zur Formulierung des Schleifenblocks folgen nach der geschlossenen runden Klammer ein Paar geschweifte Klammern { }.
Alles innerhalb dieses Blocks wird als Schleifenkörper bezeichnet und bei jedem Durchlauf von Processing abgearbeitet.
- Initialisierung beschreibt die Festlegung der Zählvariable zur Steuerung der Zählschleife. Als Variable hat sich die Verwendung von einer Ganzzahl mit dem einfachen Namen i, für Iterator, durchgesetzt.
- Test, Bedingung beinhaltet eine logische Abfrage die vor jedem Beginn eines Schleifendurchlaufs getestet wird. Bei Nichtzutreffen wird das Programm unterhalb des for-Blocks abgearbeitet.
- Aktualisierung der Zählvariable nach Ablauf eines Schleifendurchgangs.
Grundlage dafür sind Fehler beim Verfassen der "Initialisierung-Test-Aktualisierung"-Kombination.
Beispielsweise wenn die Zählvariable bei 0 startet und nach jedem Durchlauf um 1 erhöht wird, wobei der Test die Schleife nur abbricht, wenn der Wert der Zählvariable unter 0 kommt (was nie der Fall ist).
1.2.1 for-Schleife 1
//Processing-Sketch: NAME_1a.pde
void setup () { size(200, 101); background (255, 255, 180); // Hintergrund hell-gelb}void draw () { for (int i=0; i < 5; i = i + 1) { line (0, i * 20, 180, i * 20); }}
|
es steht uns die Option offen den Teil i < 5 durch i < 50 zu ersetzen.
Unsere Linie würde damit 50 mal abgebildet werden. Vorher aber auf size(200, 1001); ändern.
1.2.2 for-Schleife 2
//Processing-Sketch: NAME_1a.pde
void setup () { size(255, 255); background (255); // Hintergrund weiß}void draw () { for (int i=0; i < mouseY; i++) { stroke (i * 2, i, 0); // weißt der Linienfarbe einen Farbwert mittels der Zählvariable "i" zu line (0, i, width, i); // zeichne eine horizontale Linie verwende den Wert der Zählvariable als y-Position }}
|
Die Schleife beginnt bei 0px auf der y-Achse und zeichnet solange eine Horizontale bis sie unsere Maus erreicht.
Dabei wird von Linie zu Linie der Farbwert leicht variiert - ein Verlauf entsteht.
1.2.3 rückläufige for-Schleife
//Processing-Sketch: NAME_1a.pde
void setup () { size(260, 260);}void draw () { background (200); // fülle die gesamte Zeichenfläche mit hell-grau smooth (); // aktiviere Kantenglättung noStroke (); // deaktiviere outlines // führe die Schleife aus bis die Zählvariable "diameter" einen Wert erreicht der kleiner als 0 ist. // Verringere dabei ihren Wert nach jedem Durchgang um 12. for (int diameter=255; diameter > 0; diameter = diameter - 12) { // setze die Füllfarbe neu fill (diameter); ellipse (width / 2, height / 2, diameter, diameter); // zeichne den Kreis mit variablem Durchmesser }}
|
1.2.4 verschachtelte for-Schleife
//Processing-Sketch: NAME_1a.pde
void setup () { size(200, 200);}void draw () { background (255); // fülle die gesamte Zeichenfläche mit weiß noStroke (); // deaktiviere Umrandungen float x; // erstelle Variable zur späteren Ablage der X-Position float y; // erstelle Variablen zur späteren Ablage der Y-Position for (int i=0; i < 10; i = i + 1) { // Schleife für die Matrix-Zeilen for (int j=0; j < 10; j = j + 1) { // Schleife für die Matrix-Spalten // DIESER TEIL WIRD FÜR JEDEN KREIS AUSGEFÜHRT! x = i * 20; // Berechnung der X-Position y = j * 20; // Berechnung der Y-Position fill (i * 25, 0, j * 25); // bestimme die Füllfarbe und zeichne anschließend den Kreis ellipse (x, y, 25, 25); // Kreis } }}
|
Doppelbelegungen sind hierbei nicht zulässig.
Deshalb verwenden wir in der zweiten for-Schleife den Variablennamen j (i, j, k haben sich als Standard etabliert).
*********************************************************
05. Zufall und Rauschen
random & noise
Stellt die Befehle random() und noise() vor und erläutert anhand einfacher Beispiele ihre Unterschiede.
1. random
Für Anzahl, Farbe, Form und Position die Würfel fliegen lassen.-
random(a) erzeugt eine Zufallszahl zwischen 0 und a, wobei a eine rationale oder gebrochen rationale Zahl sein kann.
-
Bei dieser Parameteranzahl legen wir demnach nur die Obergrenze des möglichen Wertebereiches fest.
- Die Untergrenze ist durch Processing auf 0 gesetzt.
println (random (1)); // z.B. 0.8751683
-
random(a, b) erzeugt eine Zufallszahl zwischen a und b, wobei a und b rationale oder gebrochenrationale Zahlen seien können.
- Beide, Unter- und Obergrenze, werden von uns festgelegt.
println (random (1, 10); // z.B.: 3.9300842
Als Ergebnis liefert uns die random() Funktion immer einen gebrochen rationalen Wert.
Das Befüllen eines int mit dem Resultat ist uns demnach nicht gestattet - wir müssen den Datentyp float verwenden.
05.1.1 random 1
//Processing-Sketch: NAME_1a.pde
void setup () { size(200, 200);}void draw () { ellipse (100, 100, random (200), random (200));} |
Breite und Höhe werden jedes Mal zufällig bestimmt - zwischen 0 und 200 Pixel.
Eine Variable zur Zwischenablage der Zufallszahlen wird dabei nicht benötigt.
Das Resultat der random() Funktion wird direkt als Parameter an die ellipse() Funktion weitergereicht.
05.1.2 random 2
//Processing-Sketch: NAME_1a.pde
void setup () { size(256, 256);}void draw () { // erstelle eine Variable "durchmesser" und lege in ihr einen zufälligen Wert zwischen 0 und 200 ab float durchmesser = random (200); // zeichne einen Kreis in die Mitte der Zeichenfläche und verwende für Breite und Höhe den Wert der Variable "durchmesser" ellipse (width/2, height/2, durchmesser, durchmesser/2); ellipse (width/2, height/2, durchmesser/2, durchmesser);} |
Dies geht nur mittels Zuhilfenahme von einer Variable deren Wert bei der Breite und Höhe der Ellipse verwendet wird.
Beachte das sie vom Datentyp float zu sein hat.
Da das Resultat des random() Aufrufs eine gebrochen rationale Zahl zurück gibt - int kann nur mit Ganzzahlen gefüllt werden.
05.1.3 random 3
//Processing-Sketch: Smarties_1a.pdevoid setup () { size(300, 250); // Grafik-Fenstergröße smooth(); // aktiviert Kantenglättung frameRate (2); // begrenzung der Einzelbilder pro Sekunde auf 2 (besser für die Augen)}void draw () { background (235); // Hintergrund hell-grau float anzahl = random (40); // bestimmung der Kreisanzahl für dieses Einzelbild (Maxima ist 40) for (int i=0; i < anzahl; i = i + 1) { // für jeden Kreis werden position und Farbe neu bestimmt float posX=random (width); // random Position auf der x-Achse float posY = random(height); // random Position auf der y-Achse fill (random (255), random(255), random (255)); // random Werte für alle drei Farbkanäle RGB ellipse (posX, posY, 35, 30); // zeichnen der Ellipse an den vorher ermittelten Koordinaten }}
|
Die Verwendung von random() ist also nicht automatisch ein Rezept für die Erstellung von hochwertigen Grafiken.
Setze es mit Bedacht ein um geringe Abweichungen in deinem Arrangement zu generieren oder die Verteilung im Aufbau deines Programmes zu organisieren
- für abwechslungsreiche Resultate.
2. noise
Im Gegensatz zu random() ist noise() eine kontrollierbarere Methode um Zufall zu erzeugen.Sie basiert auf der Theorie des Perlin Noise, 1982 von Ken Perlin für den legendären Film Tron entwickelt.
random() führt die Bestimmung des Zufallswertest jedes Mal auf neue aus - unabhänig von vorherigen Ergebnissen.
Dieser Aspekt unterscheidet beide Funktionen. noise() bezieht sich auf das Resultat des letzten Aufrufes und fügt ihm eine leichte Varianz hinzu.
Auf dieser Grundlage werden in der Computergrafik natürlich wirkende Texturen, Gelände und Wolken generiert.
Die Varianz verteilt sich auf eine bestimmte Anzahl von Funktionen die unterschiedliche Frequenzen und Amplituden besitzen.
Durch Addition all dieser Wellen erhählt man ein harmonisches/gleichmäßiges Rauschen. Weiterführend
- noise(x) Zufallszahl in eindimensionaler Abfolge.
- noise(x, y) Zufallszahl in zweidimensionaler Abfolge, z.B. für eine 2d Textur.
- noise(x, y, z) Zufallszahl in dreidimensionaler Abfolge, z.B. für eine räumlichen Wolke.
-
noiseDetail(octaves) definiert die Anzahl an Wellen die addiert das Rauschen ergeben. Je größer die Anzahl - umso feiner die Varianz.
- Processing arbeitet standardmäßig mit vier Oktaven.
05.2.1 random() versus noise()
//Processing-Sketch: random versus noise_1a.pdesize(600, 180);background (255);noStroke ();fill (0);float v = 0;for (int i=0; i < width; i += 3) { float n = 90 + noise (v) * 40; rect (i, n, 2, 20); v = v + 0.15;}for (int i=0; i < width; i += 3) { float r = random (10, 30); rect (i, r, 2, 20);}
http://www.creativecoding.org/lesson/basics/processing/zufall-events
*********************************************************
Impressum: Fritz Prenninger, Haidestr. 11A, A-4600 Wels, Ober-Österreich, mailto:[email protected]
ENDE
Impressum: Fritz Prenninger, Haidestr. 11A, A-4600 Wels, Ober-Österreich, mailto:[email protected]
ENDE