Processing‎ > ‎CreativeCoding‎ > ‎

Einstieg in Processing

http://sites.prenninger.com/arduino-uno-r3/processing/creativecoding/einstieg-in-processing

http://www.linksammlung.info/

http://www.schaltungen.at/

                                                                                            Wels, am 2015-01-10

BITTE nützen Sie doch rechts OBEN das Suchfeld  [                                                              ] [ Diese Site durchsuchen]

DIN A3 oder DIN A4 quer ausdrucken
**********************************************************************************
DIN A4  ausdrucken
*********************************************************


01. Einstieg in Processing

Geschichte, Kontext und die ersten Zeilen Code


Diese Lesson führt Processing als Programmiersprache und Programm-Umgebung ein.

Nach einem kurzen Überblick über die Entstehung und Wurzeln des Projekts am MIT Media Lab wird die Processing Software mit ihren grundlegenden Funktionen vorgestellt.

Danach folgt der unmittelbare Einstieg in die Programmierung, indem das Koordinatensystem, die visuellen Grundelemente und die Verwendung von Farben eingeführt werden.








In dieser Lesson dreht sich alles um das Gestalten von generativen und interaktiven Systemen mittels der Open Source Umgebung Processing.
Sie ermöglicht Interessierten ohne Programmierhintergrund einen schnellen und simplen Einstieg in das kreative Arbeiten mit Code.
Ohne aufwendige Vorbereitung erlaubt Processing das Zeichnen von grafischen Elementen und die Verwendung von Input, z.B. durch Maus und Tastatur.

Das Projekt wurde von Ben Fry und Casey Reas 2001 am MIT ins Leben gerufen. Inspiriert von John Maeda's 1999 entstandenem Projekt Design by Numbers sollte es als visuelles Werkzeug für Designer dienen, das einen flexibleren Umgang mit Software und Technologie ermöglicht.
Schnell wurde die Umgebung von vielen Künstlern nicht nur im Entwurfsprozess, sondern auch für Installationen und Ausstellungen genutzt.

Processing basiert auf der 'höheren' Programmiersprache Java, vereinfacht deren Schreibweise und minimiert so den Schreibaufwand bei Programmieren beachtlich.
Es handelt sich also streng genommen bei Processing eher um einen Dialekt von Java, als um eine eigenständige Programmiersprache.
Der Vorteil dieses Umstands liegt dabei auf der Hand.
Möchte (oder muss) man später einmal über den Tellerrand von Processing hinausschauen, so fällt der Einstieg in Java sehr leicht.
Auch das spätere Erlernen von Java-ähnlichen Programmiersprachen wie ActionScript oder JavaScript fällt durch Processing leichter.
Der Schlüssel für das Verständnis von Programmierung liegt im Erlernen der Formulierung von Programmanweisungen - der sog. Syntax - und der logischen Strukturierung von Programmabläufen, also dem Gestalten von Algorithmen.
Alle aktuellen Sprachen ähneln sich in diesen Punkten stark und können deshalb mit Grundkenntnissen in Processing leicht erlernt werden.
Deshalb - und natürlich wegen der starken visuellen Ausrichtung - ist Processing unser Werkzeug der Wahl wenn es um die Einführung in die Programmierung für Künstler und Designer geht.

1. Das Programm  (Processing-IDE)

Als Applikation ist Processing ein Texteditor mit kleinen Zusatzfunktionen die grundlegende, programmierspezifische Aufgaben erfüllen.
Im Vergleich zu Entwicklungsumgebungen wie eclipse und netbeans bedarf es keiner aufwendigen Projekteinrichtung und Konfiguration bevor die ersten Schritte gemacht werden können.
Kostenlos verfügbar für Mac, Linux und Windows kann es im Downloadbereich der Processingseite runtergeladen werden.
Vorraussetzungen sind eine aktuelle Java- und QuickTime-Version.
Teilweise benötigen Plugins (sog. Libraries) systemspezifische Zusatzdateien, die jedoch immer mitgeliefert werden.

Der Kern des Programmfenster stellt der Texteditor dar.
Alle Bestandteile der hier geschriebenen Anweisungen werden zur besseren Lesbarkeit eingefärbt.
Über dem Editor befindet sich die Menüleiste mit den gewohnten Optionen 'Öffnen', 'Speichern' bis hin zur Pluginverwaltung und Ausgabe des Sketches.
Einige der essentiellen Funktionen, wie z.B. das Ausführen des Programms, sind zwischen Menüleiste und Texteditor als Icons angesiedelt.
Beim Starten der Applikation springt ein zweites Fenster auf in dem der Sketch abgebildet wird.
Im späteren Verlauf kann es zu mehreren Textdokumenten für ein Programm kommen.
Direkt über dem Texteditor befindet sich zu diesem Zweck eine Tab-Leiste um diese aufzulisten.

Text-Editor-Fenster
Nachrichten-Fenster (Programm-Konsole)

2. Zeichenfläche

Als Basis zum Zeichnen in Processing ist das Sketchfenster in ein kartesisches Koordinatensystem unterteil.
Horizontal befindet sich die x-Achse am oberen Fensterrand und die y-Achse vertikal an der linken Außenseite.
In der oberen-linken Ecke ist der Punkt x=0, y=0 (Koordinatenursprung).
Von dort aus breitet sich das Koordinatensystem auf der x-Achse nach rechts und auf der y-Achse nach unten aus.
Positions- und Größenangaben werden in der Einheit Pixel (ein Bildschirmpunkt) vorgenommen - demnach ist der xWert für den Punkt in der oberen-rechten Ecke die Breite des Fensters in Pixel.
Beide Achsen dehnen sich ebenfalls in den negativen Wertebereich noch links bzw. rechts aus.
Auch diese Koordinaten können beim Positionieren angegeben werden, je nach Größe der grafischen Objekte rutschen diese aber aus der sichtbaren Fläche und werden nicht dargestellt.

Neben dem zweidimensionalem Raum können unter Hinzunahme der z-Achse dreidimensionale Abbildungen erzeugt werden.
Die dritte Achse verläuft ebenfalls durch die obere-linke Ecke.
Bildlich gesehen geht sie durch den Bildschirm, wobei sich der positive Wertebereich hinter dem Bildschirm befindet.
In den folgenden Lessons konzentrieren wir uns auf das Gestalten mit 2D-Codes.


2.1 Größe des Sketchfensters

Die Größe der Zeichenfläche lässt sich mit dem Aufruf des Befehls size() festlegen.
Processing passt die Ausgabefläche anderenfalls auf 100x100 Pixel an.
Damit diese Einstellung funktioniert, braucht das Programm zwei Informationen von uns.
Wie breit und wie hoch die gewünschte Zeichenfläche sein soll.
Bei Positionen und Größen im 2D-Raum wird immer erst der Wert auf der x-Achse und danach der y Wert genannt (demnach erst die Breite, dann die Höhe).
Beide Werte kommen als Parameter zwischen die beiden runden Klammern, die dem Begriff size folgen.

size(width, height) legt die Größe der sichtbaren Zeichenfläche (im Sketchfenster) fest.
Das unten aufgeführte Beispiel legt einen Bereich von 800x600 Pixeln im Ausgabefenster an. size()

//Processing-Sketch: NAME_1a.pde
size (800, 600); // Breite Höhe
background (200);  // hell-grau

3. Visuelle Grundelemente

3.1 Punkt

Ein Punkt ist das simpelste Element der primitiven Grundformen.
Nur mit einer x und einer y-Koordinate nimmt er, ohne spezielle Angaben, die Fläche eines Pixels auf dem Bildschirm ein.
Die für die Füllung verwendete Farbe kann mit stroke() festgelegt werden.
point(x, y) zeichnet einen Punkt auf die Zeichenfläche.
Bestimmt wird dieser durch x & y-Koordinate. point()

point (0, 0);     // obere-linke Ecke
stroke(255);      // Punkt-Farbe weis
strokeWeight(10); // Punkt-Durchmesser
point (40, 90);   // Punkt-Position X, Y



3.2 Linie

Als zweites Element lernen wir die Linie kennen.
Als eine Reihe von Punkten erstreckt sie sich zwischen einem Anfangs- und einem Endpunkt.
Um eine Linie zu zeichnen verwendet man den line() Befehl.
Dieser hat genau vier Parameter, zwei Werte für den Anfangs- und zwei für den Endpunkt.
line(x1, y1, x2, y2) zeichnet eine Linie zwischen zwei Punkten.
Punkt1 und Punkt2 werden jeweils durch eine x & y-Koordinate angegeben. line()

stroke(0);              // Linien-Farbe schwarz
strokeWeight(2);        // Linien-Breite
line (10, 20, 80, 60);  // Linie X, Y, x, y
noStroke();             // Linienfarbe deaktivieren



3.3 Rechteck / Quadrat

Der Befehl rect() ermöglicht das Füllen bzw. Umranden einer rechteckigen Fläche.
Bestimmt durch den oberen-linken Punkt dehnt dich diese nach rechts-unten aus.
Dafür benötigt Processing neben der x und y-Koordinate zur Punktbestimmung die Breite und Höhe des Elements.
rect(x, y, width, height) zeichnet ein Rechteck mit einer definierten Breite und Höhe an eine bestimmte Position auf die Zeichenfläche des Sketchfensters. rect()

//Processing-Sketch: NAME_1a.pde
stroke(0);               // Linien-Farbe schwarz
strokeWeight(2);         // Linien-Breite
fill(255,255,0, 200);    // Flächen-Farbe gelb, Farbdichte 200
rect (20, 20, 60, 40);   // Rechteck
  X, Y, width, height
noFill();                // Füll-Farbe deaktivieren
rect (40, 40, 80, 80);   // Quadrat

Alle Außenkante des Vierecks bleiben beim Aufruf von rect() immer parallel zur x bzw. y-Achse.
Um dies zu umgehen kann für das freie Zeichnen von viereckigen Flächen der quad() Befehl verwendet werden.



3.4 Ellipse

ellipse(x, y, width, height) zeichnet eine Ellipse/Kreis in die Zeichenfläche.
Grundsätzlich befindet sich der mit x und y angegebene Punkt im Zentrum der Ellipse.
Die beiden weiteren Werte geben die Breite (width) auf der x-Achse und Höhe (height) auf der y-Achse des Elements an.
Bei den Größenangaben handelt es sich demnach um den Durchmesser auf der jeweiligen Achse, nicht um den Radius. ellipse()

fill(255,255,0, 200);       // Flächen-Farbe gelb, Farbdichte 200
ellipse (50, 50, 40, 60);   // Ellipse
X, Y, width, height
noFill();                   // Füll-Farbe deaktivieren
stroke(0);                  // Strich-Farbe schwarz
strokeWeight(2);            // Linienbreite
ellipse (50, 50, 80, 80);   // Kreis





3.5 Viereck

Neben rect() erlaubt die erweiterte Möglichkeit quad() ebenfalls das Darstellen von Vierecken.
Mittels vier ausformulierten Punkten können Formen gezeichnet werden, die keine Innenwinkel von 90° besitzen und parallele Seitenpaare haben.
  • quad(x1, y1, x2, y2, x3, y3, x4, y4) zeichnet ein Viereck in Zeichenfläche.
  • Bestimmt durch 4 Punkte, jeweils eine x und y Koordinate, beschreiben die Lage der Fläche. quad()
    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ß

Neben dem Grauwert kann als zweiter Wert die Transparenz für den Grauton angageben werden, ebenfalls von 0 bis 255.

background (110);  // grau
fill (0, 90);      // schwarz, transparent
stroke (255, 200); // weiß, transparent

Aufrufe von fill() und stroke() lassen sich wie folgt lesen:
  • 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.
Wenn beide Blöcke vorhanden sind und wir das Programm starten wird zuerst der Inhalt ( { .......... } ) des setup() Blocks abgearbeitet.
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);
}

Das Programm zeichnet über 60 mal in der Sekunde ein fast nicht sichtbares rotes Quadrat in die Zeichenfläche.
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);
}

Im setup() Block wird nach dem Festlegen der Füllfarbe auf ein Grün ein Rechteck einmal gezeichnet.
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.

  • 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.pde void 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. Mausinteraktion

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.pde

void 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 jedem draw() Durchlauf wird ein Kreis an der aktuellen Mauskoordinaten positioniert.
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.pde

void 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.pde

void 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 ( {…} )
Für unsere Abfrage, ob die Taste der Mausgedrückt ist, haben wir nun eine Eigenschaft in unserem Processing Programm ähnlich der width oder height Eigenschaft zur Verfügung:
  • mousePressed ist die Maustaste gedrückt, oder nicht.
Folgendes Beispiel demonstriert, wie man diese Abfrage richtig stellt.
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.pde
float x;        // x-Position
float y;        // y-Position
float rad = 70; // Radius

void 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
}

 
Mausinteraktion Rechteck


Punkt in Rechteck in Processing

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.pde
float x = 120;   // x-Position
float y = 80;    // y-Position
float w = 100;   // Breite
float h = 50;    // Höhe

void 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
}




*********************************************************
03. Variablen 1

Einführung in Variablen und Datentypen



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
Bevor wir eine Variable nutzen können müssen wir sie ins Leben rufen;
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;

Namen von Variablen dürfen nur Buchstaben, Zahlen, $ und _ enthalten und sollten immer mit einem Buchstaben beginnen. Die Vergaben von Leerzeichen ist nicht erlaubt. Um zusammengesetzte Name trotzdem lesbar abzubilden wird der erste Buchstabe eines folgenden Wortes großgeschrieben (z.B. roteaepfel wird zu roteAepfel). Genau wie beim Aufruf von Befehlen ist Groß- und Kleinschreibung unbedingt zu beachten! Im oberen Beispiel haben wir zwar drei Variablen unterschiedliche Typs erstellt - jedoch können wir ohne Inhalt nicht mit ihnen arbeiten. Wir benötigen eine sogenannte Zuweisung eines Wertes. Dies geschieht mit einem = welches dem Variablennamen folgt. Rechts neben dem Istgleich erwartet Processing den Wert den es in hinter unserem Begriff (Variable) verstecken soll - gefolgt von einem Semikolon, um die Anweisung zu Beenden.

z.B.: Werte zuweisen

boolean jaNein = true; // boolean Variable mit dem Wert 'ja' ('wahr')
int ganzzahl = 40; // ganzzahlige Variable mit dem Wert 40
float gleitzahl = 36.9; // Variable mit dem gleitkomma Inhalt 36.9
String textabschnitt = "Creative Coding"; // Variable die den Text "Creative Coding" beinhaltet

Beim Ausführen der beiden oberen Beispiele erhalten wir momentan keinerlei Feedback vom Programm.
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:
  • print() gibt einen Inhalt bzw. Wert einer Variable in der Konsole aus. print()
  • println() gleiche Funktion wie print(), jedoch wird bei jedem Aufruf eine neue Zeile zur Ausgabe genutzt. println()

z.B.: Werte modifizieren

// erstellt eine ganzzahlige Variable names "zahl" und gib ihr den Wert 39
int zahl = 39;
println (zahl); // Ausgabe in der Konsole
zahl = zahl + 1; // erhöhe den Wert von "zahl" um eins
println (zahl); // Ausgabe im Nachrichtenfenster
zahl = zahl + 1; // erhöhe den Wert von "zahl" um eins
println (zahl); // Ausgabe im Nachrichtenfenster


Die Angabe des Datentypen ist nur bei der Initialisierung nötig. Im weiteren Ablauf geschehen Abfragen und Zuweisungen ohne Angaben wie int, float oder String.

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.5
println (b - a); // Ausgabe im Nachrichtenfenster -2.8999999
println ("Creative " + "Coding " + 1);  // Ausgabe: "Creative Coding 1"


2.1.2 Punktrechnung 1
int c = 5 ;
int d=2;
println (c * d); // Ausgabe: 10
println (c/d);   // Ausgabe: 2



Das Ergebniss der Multiplikation von 5 x 2 = 10 ist zu wie erwartet eingetreten. Aber bei der Division beider Werte fehlt einhalb. Dies liegt an dem von uns vergebenen Datentyp (bei beiden Variablen int, Ganzzahl). Wenn zwei Ganzzahlen durcheinander geteilt werden wird das Ergebniss von Processing immer auf die nächste Ganzzahl abgerundet - in unserem Fall 2. Um dieses Problem zu umgehen muss mindestens eine der beiden Variablen eine Gleitkommazahl (float) sein:

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: 94
zahl_1 ++;         // erhöht den Wert um 1
println (zahl_1);  // Ausgabe: 95


2.1.5  Verringern
int zahl_2 = 94;
println (zahl_2);  // Ausgabe: 94
zahl_2 --;         // verringert den Wert um 1
println (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!"
}


Nun wurden beide Fragen innerhalb einer einzigen if-Abfrage gestellt.
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!" }



*********************************************************

04. Kontrollstrukturen

Abfragen und Schleifen

Einführung in Kontrollstrukturen zur Steuerung des Ablaufs eines Programms.
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
}

Formuliert wird die while-Schleife durch den Begiff while, gefolgt von der Bedingung in runden Klammern.
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;
  }
}

Unsere erste while-Schleife liefert das oberhalb eingeführte Muster aus 6 horizontalen Linien.
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;
  }
}

Beide Beispiele unterscheide sich nur in der letzte Zeile des Blocks der while-Schleife.
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
}

Der Begriff for leitet die Schleife ein, gefolgt von dem in runden Klammern umfassten "Initialisierung-Test-Aktualisierung" Block.
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.
Wie bei der while-Schleife kann es zu einer Endlosschleifen kommen.
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);
  }
}


Dies ist die mit einer for-Schleife vereinfachte Version des oberen Beispiels.
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
  }
}

Wir haben mit dem oberen Beispiel unseren ersten dynamischen Farbverlauf erstellt.
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
    }
  }
}

Bei der Verschachtlung von Zählschleifen muss nur auf die Namen der Zählvariablen geachtet werden.
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));
}
In jedem Einzelbild (einem draw() Durchlauf) wird eine Ellipse gezeichnet.
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);
}
Im zweiten Beispiel erzeugen wir gleichförmige Elypsen deren Durchmesser ebenfalls zufällig ist.
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.pde

void 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
  }
}

Bis auf die Abmaße haben wir im dritten Beispiel mit Anzahl, Position und Farbe fast alle möglichen Attribute in unserer simplen Darstellung vom Zufall bestimmt.
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.pde

size(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


DIN A4  ausdrucken
*********************************************************
Impressum: Fritz Prenninger, Haidestr. 11A, A-4600 Wels, Ober-Österreich, mailto:schaltungen@schaltungen.at
ENDE




Comments