Funktionen & Vektoren

In den heutigen Beispielen kombinieren wir bereits erlerntes Wissen ( z.B. Arrays, whileLoop) mit neuen Werkzeugen, um die Grundlagen für die Themengebiete Animation und OOP zu erlernen.

Wiederholung Funktionen

Bereits in der ersten Stunden haben wir die internen Funktionen setup() & draw() kennengelernt. Im weiteren Verlauf haben wir noch mousePressed und keyPressed() kennengelernt.

Neben der Verwendung der internen Funktionen können wir auch eigene Funktionen schreiben, wobei der Grundaufbau immer der gleiche ist.

Eine Funktion besteht aus Name, Rückgabewert und optional aus einem oder mehreren Parametern.

Der Einsatz von Funktionen verfolgt mehre Ziele:

1. Gliederung eines Programmes

2. bessere Extraktion und Wiederverwendbarkeit

3. Parametrisierung, um Funktionen vielschichtig zu verwenden

4. Reduzierung des Codes und Erhöhung der Übersichtlichkeit

Diese Tatsachen erhöhen die Modularität der Werkzeuge und wir haben mehr Zeit die Werkzeuge in unserem iterativen und kreativen Prozess zu verwenden.

Funktionsarten

Beispiel:

 
// Funktion ohne Parameter und ohne Rückgabewert
 
void functionName ()
{
// Anweisungen
}
 
// Funktion mit einem Parameter und mit Rückgabewert
 
int functionName ( int argument1 )
{
// Anweisungen
}
 
// Funktion mit zwei Parametern und mit Rückgabewert
 
String functionName ( String argument1, String argument2 )
{
// Anweisungen
}

Viele Funktionen nutzen als Rückgabewert void, was leer bedeutet, sprich sie geben keinen Wert zurück. Beispiele hierfür sind unter anderem setup() & draw().

Ein Beispiel für eine Funktion mit einem Rückgabewert ist dist(), mit der man den Abstand zwischen zwei Punkten messen kann. Die Funktion erwartet minimal x1, y1 und x2, y2. Der Rückgabewert ist ein float und ist der Abstand zwischen den beiden Punkten.

Die Funktion map() besitzt ebenfalls einen Rückgabewert und erwartet fünf Parameter. Mit map() kann man einen Wert von einem Zahlenbereich in einen anderen Zahlenbereich umrechnen. Das erste Parameter ist der vorhandene Wert, gefolgt von der oberen und unteren Grenzen des aktuellen Zahlenbereiches. Die letzten beiden Parameter grenzen den neuen Zahlenbereich ein.

distance

Beispiel:

void draw()
{
  drawCircles( 1000 );  
}
 
void drawCircles( int amount )
{
  int count = 0;
 
  // maximale Distance von oben links zum Mittelpunkt berechnen
  float maxDistance = dist( 0, 0, width / 2, height / 2 );
 
  while( count < amount )
  {
    float cirlceSize = random( 10 );
    float nextX = random( width );
    float nextY = random( height ); 
 
    // Distance vom aktuellen Kreis zum Mittelpunkt
    float distance = dist( nextX, nextY, width / 2, height / 2 );
 
    // Alphawert aufgrund der Distance bestimmen
    float nextAlpha = map( distance, 0, maxDistance, 0, 255 );
 
    // außen -> alpha 0, innen -> alpha 255
    fill( 255, 255 - nextAlpha );
 
    ellipse( nextX, nextY, cirlceSize, cirlceSize );  
    count++;
  }
}

Gültigkeitsbereich von Variablen

Da es in den vergangen Wochen immer wieder Probleme beim Einsatz und der Verwendung von Variablen gab, gehen wir noch einmal ins Detail.

Je nach Definitionsort besitzen Variablen einen unterschiedlichen Gültigkeitsbereich. Definiert man Variablen ganz am Anfang eines Sketches, also vor dem setup()-Bereich, dann ist es eine globale Variable, auf die man von überall aus zugreifen kann.

Erstellt man hingegen eine Variable innerhalb einer Funktion, dann ist sie nur in diesem Funktionsbereiches verfügbar und wird als lokale Variablen bezeichnet.

Empfehlung: So oft es geht mit lokalen Variablen arbeiten. Die Verwendung von globalen Variablen in unterschiedlichen Funktionen kann zu Überschneidung führen und somit zu Fehlern. Beim Einsatz von lokalen Variablen können sich die Namen der Variablen in den einzelnen Funktionen auch wiederholen, da sie jeweils ihren eigenen Gültigkeitsbereich haben.

Beispiel:

int globalVar = 1;
 
void setup()
{
  int localSetupVar = 2;
}
 
void draw()
{
  int localDrawVar = 3;
 
  // works
  println( globalVar );
  // error
  println( localSetupVar );
  // works
  println( localDrawVar );
}

PVector

Das erste neue Werkzeug ist PVector, ein Datencontainer, der zwei bis drei (x,y,z) Werte speichern kann. In der Regel verwendet man diese Klasse um Postionen, Geschwindigkeiten oder Beschleunigungen festzuhalten.

Die Idee des heutigen Beispieles ist, dass man einmalig eine Liste mit zufälligen Koordinaten erzeugt, die man in der Folge immer wieder verwenden kann.

Jede Koordinate wird als PVector festgehalten und besteht aus x und y. Gespeichert werden die Werte in einer Liste (Array). Die Erstellung erfolgt mit Hilfe eines while-Loops.

Beispiel: In der Methode setupDotList() wird die Liste erstellt.

void setupDotList()
{
  dotList = new PVector[ dotAmount ];
}

Beispiel:In der Methode createDotList() wird mit Hilfe eines while-Loops die Liste mit PVector-Instanzen befüllt.

void createDotList()
{  
  int count = 0;  
  int amount = dotList.length;
 
  while( count < amount )
  {
    dotList[ count ] = new PVector(random(width), random(height)); 
 
    count++; 
  }
}

Bis dato fehlt für die erstellten Punkte noch eine visuelle Repräsentanz. Eine einfache Testmethode, um Kreise an den entsprechenden Stellen zu erzeugen, könnte wie folgt aussehen.

void drawDotList()
{
  int count = 0;
  int amount = dotList.length;
 
  PVector currentDot;
 
  while( count < amount )
  {
    currentDot = dotList[ count ];
    ellipse( currentDot.x, currentDot.y, 10, 10 );
 
    count++;
  }
}

distance

Das Ziel dieser Übung ist es aber, dass wir die Distanz zwischen jeder Koordinate aus der Liste mit den anderen Koordinaten aus der Liste abgleichen.

Der Grundaufbau besteht dabei wieder aus einem while-Loop der über die Liste iteriert. Der aktuelle Punkt (currentDot) wird dann zur Überprüfung an die Funktion checkDistance übergeben.

Beispiel:

void drawLinesBetweendotList()
{
  int count = 0;
  int amount = dotList.length;
 
  PVector currentDot;
 
  while( count < amount )
  {
    currentDot = dotList[ count ];
    checkDistance( currentDot );
 
    count++;
  }
}

Die Funktion checkDistance überprüft die Distanz des übergebenden Punkt gegen die gesamte Liste an Koordinaten. Bewegt sich die Distanz zwischen dem definierten minimalen (minDistance) und maximalen (maxDistance) Wert, dann wird auf Basis des Abstandes eine Linie zwischen den Punkte gezeichnet. Die Linienstärke wird dabei mit strokeWeight gesetzt.

Beispiel:

void checkDistance( PVector currentDot )
{
 
  int count = 0;
  int amount = dotList.length;
 
  PVector checkDot;
 
  while( count < amount )
  {
    // Check-Punkt
    checkDot = dotList[ count ]; 
 
    // Distanz berechnen
    float distance = dist( currentDot.x, currentDot.y, 
checkDot.x, checkDot.y ); 
 
    // Distanz überprüfen
    if( distance < maxDistance && distance > minDistance )
    {
      strokeWeight( 15 / distance );
      stroke( 255, 255, 255, 20 );
 
      line( checkDot.x, checkDot.y,  currentDot.x, currentDot.y );
 
      addRect( distance, currentDot );
    }
 
    count++;
  }
}

noise() – mehr Kontrolle als mit random()

Tabs

Der Nachteil an dem Einsatz von random() ist, dass die Werte sehr unterschiedlich sind und untereinander in keinem Zusammenhang stehen. Um weniger zufällige Werte zu erhalten und mehr organische Ergebnisse zu erhalten empfiehlt sich der Einsatz von noise().

Heute verwenden wir noise() erst einmal nur eindimensional für die Anordnung auf der Y-Achse. Man initialisiert eine Zeitvariable und zählt diese in einem Loop konstant hoch. Die Variable übergibt man dann jeweils für die Berechnung des Y-Wertes der noise()-Funktion, die immer Werte zwischen 0 und 1 liefert, wobei der Wert immer in der Nähe des Vorgänger-Wertes ist und somit ein organischere Eindruck entsteht.

Beispiel mit random():

float time = 0.0;
 
void draw()
{
  float randomValue = random( 100 );
  println( randomValue); 
  time++;
}

Beispiel mit noise():

float time = 0.0;
 
void draw()
{
  float noiseValue = noise( time );
  println( noiseValue); 
  time++;
}

Mehr zum Thema noise() und der Helfer-Methode noiseDetail() bitte in der Hilfe oder im Buch „Learning Processing“ nachlesen.

Hausaufgabe

Programmiert ein Beispiel, dass noise() verwendet und erstellt zwei Arbeiten, bei denen ihr Euch mit der Distanz-Berechnung befasst.

Informationen zum Dateiformat, der Bezeichnung und zum Upload der Dateien sind unter dem Punkt Prüfungsleistung zu finden.

Die Beispiele der Woche können hier herunter geladen werden.