Schlagwort-Archive: Processing

Rekursionen


Processing 2.0

Rekursionen sind Funktionen, die sich selbst aufrufen. D.h. auch, das eine Funktion durch sich selbst definiert wird. GNU (Gnu’s not Unix) der freien Softwarebewegung von Richard Stallman ist eine Beispiel. Man kann sich eine Rekursion auch so vorstellen: Man steht zwischen zwei Spiegeln und sieht dann unendlich viele Wiederholungen der Bilder von Vorne und von Hinten. Was man damit alles machen kann ist nicht ganz einfach zu verstehen. Dazu vielleicht ein einfaches Beispiel.

Beispiel: starte Applet

/** Copyright 2012 Thomas Koberger
*/

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

void setup() {
size(300,300);
int x=width;
half_size(x);
}

void half_size (int xpos) {
if (xpos>1) {
int x= xpos*3/4;
line(x,0,x,height);
half_size(x);
}
}

In diesem Beispiel teilt die Funktion half_size eine Strecke in 3/4 und zeichnet dort eine Linie. danach ruft sich die Funktion selbst neu auf und teilt die verbleibende Strecke wieder in 2 Teile  (bei 3/4 der Länge und zeichnet eine Linie. Und so weiter, bis nur noch 1 Pixel übrig ist. Dann wird das ganze beendet.

An dem Beispiel kann man schon ganz gut erkennen, dass Rekursionen die Gefahr bergen, dass sie unendlich weiterlaufen. Das muss durch irgendeinen geeigneten Mechanismus (in diesem Beispiel die Abfrage, ob die verbleibende Strecke noch größer ist als 1) verhindert werden.

Beispiel: starte Applet

/** Copyright 2012 Thomas Koberger
*/

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

void setup() {
size(300, 300);
teileBox(0, 0, 600, 400, 20); //Funktion wird einmal aufgerufen
}

void teileBox(float x, float y, float breite, float hoehe, int grau) {
fill(grau,100);
rect(x, y, breite, hoehe);
int minSeite = 100; //definiert ab welcher minimalen Breite/Höhe die Funktion stoppen soll.
if ((breite < minSeite) || (hoehe < minSeite)) {
return;
}
if (breite > hoehe) { //Wenn die Box breiter ist als hoch, dann wird sie horizontal geteilt!
float leftbreite = breite * random(0.2, 0.8);  // Breite der linken Box
teileBox(x, y, leftbreite, hoehe, grau);
teileBox(x+leftbreite, y, breite-leftbreite, hoehe, grau+40);
} else { //Wenn die Box höher ist als breit, dann wird sie vertikal geteilt!
float tophoehe = hoehe * random(0.2, 0.8);  // Höhe der oberen Box
teileBox(x, y, breite, tophoehe, grau);
teileBox(x, y+tophoehe, breite, hoehe-tophoehe, grau+40);
}
}

Dieses Beispielprogramm teilt Rechtecke je nachdem, ob sie höher oder breiter sind, horizontal und vertikal, bis eine minimale Breite bzw. Höhe erreicht ist.

Objekte und Klassen


Processing 2.0

Das Programmieren mit Objekten und Klassen nennt man objektorientiertes Programmieren. Es bietet den Vorteil einer viel besseren Übersicht bei längeren Programmen und ist hier auch dringend anzuraten.

Grundlegendes:

Eine Klasse ist eine allgemeine Vorlage für ein Objekt. Nehmen wir als Beispiel ein Auto. Die Vorstellung von einem Auto wäre dann die Klasse. Die Eigenschaften eines Autos sind in der Klasse definiert (Bsp.: Auto hat 4 Räder, 4 Lichter, ein Lenkrad usw.). Manche Eigenschaften können aber auch variieren, so dass z.B. manche Autos unterschiedlich viele Sitze oder Türen aufweisen (oder auch eine aktuelle Geschwindigkeit und Richtung). Diese Eigenschaften übermittelt man der Klasse, wenn nach ihrer Vorlage ein Objekt erstellt werden soll. Ein Auto hat aber nicht nur Eigenschaften, die in Form von Variablen gespeichert werden, sondern auch Funktionen. Man nennt diese Methoden. Mit Hilfe dieser Methoden kann man ein Objekt manipulieren. Das Auto könnte beispielsweise beschleunigen und somit ändert sich die aktuelle Geschwindigkeit. Es könnte auch nach links lenken und somit die Fahrtrichtung ändern.

Beispiel: Auto starte Applet

Wir schreiben eine Klasse Auto. Diese Autos sollen beschleunigen, bremsen und lenken können.

Klasse: Auto

class Auto {
}

Die Richtung der Autos wird über einen Winkel gesteuert, der dann in einen Richtungsvektor umgerechnet wird. Er kann durch die Tasten LINKS, RECHTS verändert werden. Der Richtungsvektor wird dann jeden draw() Durchlauf zum aktuellen Positionsvektor addiert. Und schon bewegt sich das Auto. Durch die Tasten AUF, AB kann das Auto dann noch beschleunigen und bremsen.

Eigenschaften: Farbe, aktuelle Richtung,  aktuelle Position usw.

class Auto {
// 2 Vektoren für die Position und Richtung des Fahrzeugs
PVector position;
PVector direction;
// Richtung und Geschwindigkeit
float angle, speed;
//Farbe
color clr;
}

Methoden: Lenken, Beschleunigen, Bremsen, Auto zeichnen,  usw.

class Auto {
  //Methode des Objekts Auto
void render() {
fill(clr);
//der Code ab hier zeichnet des Autos
PVector normVector = new PVector(1, 0);
float angle = direction.heading2D();
pushMatrix();
translate(position.x, position.y);
rotate(angle-PI);
strokeWeight(2);
rect(0, 0, 100, 50);
fill(0);
rect(-35, -30, 15, 3);
rect(-35, 30, 15, 3);
rect(30, -30, 15, 3);
rect(30, 30, 15, 3);
popMatrix();
}

//Methode des Objekts Auto
void move() {
// aufgrund des Winkels wird die Bewegungsrichtung angepasst
direction.x = sin(angle)*speed;
direction.y = cos(angle)*speed;
position.add (direction);
}

void steerLeft() {
angle+=0.1;
}

void steerRight() {
angle-=0.1;
}

void accelerate() {
speed+=0.1;
}

void slowDown() {
speed-=0.1;
}
}

Der Konstruktor

Damit das Ganze dann auch wirklich funktioniert, brauchen wir noch einen sog. Konstruktor. Das ist eine Methode, die bei der Erzeugung des Objekts ein mal aufgerufen wird und die initialen Werte für das Objekt festlegt. Diese können beim Aufruf als Parameter mitgeliefert, oder einfach in der Klasse selbst festgelegt werden.

class Auto {
// der sog. Konstruktor wird einmal ausgeführt, wenn das Objekt erstellt wird
Auto (float aangle, color aclr) {
position    = new PVector (width/2, height/2);
angle = aangle;
speed = 0.1;
direction   = new PVector (0.1, 0);
clr    = aclr;
}
}

Die gesamte Klasse Auto sieht dann so aus:

class Auto {
// 2 Vektoren für die Position und Richtung des Fahrzeugs
PVector position;
PVector direction;
// Richtung und Geschwindigkeit
float angle, speed;
//Farbe
color clr;

// der sog. Konstruktor wird einmal ausgeführt, wenn das Objekt erstellt wird
Auto (float aangle, color aclr) {
position    = new PVector (width/2, height/2);
angle = aangle;
speed = 0.1;
direction   = new PVector (0.1, 0);
clr    = aclr;
}

//Methode des Objekts Auto
void render() {
fill(clr);
//der Code ab hier zeichnet des Autos
PVector normVector = new PVector(1, 0);
float angle = direction.heading2D();
pushMatrix();
translate(position.x, position.y);
rotate(angle-PI);
strokeWeight(2);
rect(0, 0, 100, 50);
fill(0);
rect(-35, -30, 15, 3);
rect(-35, 30, 15, 3);
rect(30, -30, 15, 3);
rect(30, 30, 15, 3);
popMatrix();
}

//Methode des Objekts Auto
void move() {
// aufgrund des Winkels wird die Bewegungsrichtung angepasst
direction.x = sin(angle)*speed;
direction.y = cos(angle)*speed;
position.add (direction);
}

void steerLeft() {
angle+=0.1;
}

void steerRight() {
angle-=0.1;
}

void accelerate() {
speed+=0.1;
}

void slowDown() {
speed-=0.1;
}
}

So, jetzt stellt sich noch die Frage, wie man Objekte erzeugt und manipuliert (also auf die Eigenschaften und Methoden zugreifen kann). Objekte verhalten sich diesbezüglich wie Variablen. Diese müssen, wie wir in Variablen gelernt haben, initialisiert und deklariert werden. Das gilt auch für Objekte.

Hier das Hauptprogramm des fertigen Beispiels:

/** Copyright 2012 Thomas Koberger
*/

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* KEYS
* UP               : Beschleunigen
* DOWN             : Bremsen
* LINKS            : Lenke nach links
* RECHTS           : Lenke nach rechts
*/

// Erzeugt ein Objekt der Klasse Auto
Auto Kaefer;

void setup() {
size(800, 800);
// Aufruf des Konstruktors
Kaefer = new Auto(0, color(100, 50, 255));
rectMode(CENTER);
}

void draw() {
background(200);
//Aufruf der Methoden des Objekts
Kaefer.move();
Kaefer.render();
}

// Zum Steuern des Autos werden
void keyPressed() {
if (key == CODED) {
if (keyCode == UP) {
Kaefer.accelerate();
}
else if (keyCode == DOWN) {
Kaefer.slowDown();
}
else if (keyCode == LEFT) {
Kaefer.steerLeft();
}
else if (keyCode == RIGHT) {
Kaefer.steerRight();
}
}
}

Und noch ein Beispiel: Wir wollen ein Programm schreiben, das Ameisen erzeugt, die sich auf zufälligen Bahnen bewegen und Futter sammeln. Dabei sollten ganz einfache Regeln gelten: Findet eine Ameise Futter und trägt gerade kein Futter, soll sie das gefunden Futter aufnehmen. Das trägt sie dann, bis sie wieder auf Futter stößt. Hier soll sie das Futter, das sie gerade trägt fallen lassen.

Wir werden also diesmal objektorientiert vorgehen und brauchen für unser Projekt 2 Objekte: das Futter und die Ameisen. Wie müssen diese Objekte nun beschrieben werden? Fangen wir der Einfachheit halber mit der Beschreibung des Futters an. Dieses hat in unserem Beispiel eine sehr passive Rolle und sollte somit leicht zu beschreiben sein.

Wie wir wissen nennt man die Beschreibung eines Objektes Klasse (class). Die sieht nun bei unserem Futter so aus:

class Food {
 float xPos;
 float yPos;
 float radius;
 int traeger;
 int frame=0;
 color clr;

 Food (float axPos, float ayPos, float aradius, int atraeger, color aclr) {
 xPos   = axPos;
 yPos   = ayPos;
 radius = aradius;
 traeger = atraeger;
 clr    = aclr;
 }

Dabei werden im oberen Teil die benötigten Variablen definiert. Unten haben wir den sog. Konstruktor. Dieser weist den Variablen xPos, yPos usw. von uns definierte Werte zu. Der Konstruktor gibt im Gegensatz zu einer Funktion nichts zurück, nicht einmal void und wird immer, wenn ein neues Objekt erstellt wird automatisch aufgerufen. Somit ist die Definition der Klasse abgeschlossen.

Um ein Objekt der Klasse Food zu generieren müssen wir noch folgendes machen:

1. Wir erzeugen eine Variable der Klasse Food mit dem Namen Futter.

Food Futter; </em>

2. Wir rufen eine Funktion mit dem Namen der Klasse auf und befüllen die Variable Futter mit einem Objekt der Klasse Food.

Futter=new Food();

3. Jetzt enthält unsere Variable Futter lauter Nullen für alle int, float und color – Variablen, die in ihr enthalten sind. Indem wir jetzt unseren Konstruktor aufrufen, befüllen wir unser Objekt mit den von uns gewünschten Werten. Dafür schreiben wir der besseren Übersichtlichkeit halber eine eigene Funktion.

void gibFutter() {
 Futter = new Food[50];
 int border = 50;
 for (int f=0; f<Futter.length; f++) {
 float xPos = random(border, width-border);
 float yPos = random(border, height-border);
 float radius = random(5, 5);
 int traeger = 500;
 color clr = color(50,200,0,120);
 Futter[f] = new Food(xPos, yPos, radius, traeger, clr);
 }
}

Diese Funktion nennen wir gibFutter. Sie erstellt in diesem Fall 20 Futterobjekte, die mit zufälligen Koordinaten versehen werden (und 50px Abstand zum Rand, damit sie auch von den Ameisen aufgenommen werden können).

Das wärs dann eigentlich. Fast das gleiche, aber mit ein wenig mehr Eigenschaften machen wir mit den Ameisen. Die Klasse inkl. dem Konstruktor sieht bei den Ameisen dann so aus:

class Animals {

 PVector position;
 PVector direction;
 float spin = 0.10;
 float radius;
 boolean loaded;
 int blocked;
 int traegt;
 color clr;

 Animals (float theX, float theY,
 float aradius, boolean aloaded, int ablocked, int atraegt, color aclr) {
 position    = new PVector (theX, theY);
 direction   = new PVector (10,10);
 direction.x = random (-1, 1);
 direction.y = random (-1, 1);
 radius = aradius;
 loaded = aloaded;
 blocked = ablocked;
 traegt = atraegt;
 clr    = aclr;
 }
}

Methoden:

Um unseren Objekten jetzt noch sagen zu können, was sie machen sollen, müssen wir in den Klassendefinitionen noch etwas ergänzen. Und zwar sog. Methoden. Das sind Funktionen in der Klassendefinition, die  Eigenschaften der Objekte ändern können. Z.B. sollten sich die Ameisen fortbewegen und am Bildschirm dargestellt werden können. Dafür haben wir 2 Methoden, nämlich die Methode render und die Methode move.

//Methode des Objekts Ameise
 void render() {
 fill(clr);
 //der Code ab hier dient dem Zeichnen der Ameise
 PVector normVector = new PVector(1,0);
 float angle = direction.heading2D();
 pushMatrix();
 translate(position.x,position.y);
 rotate(angle-PI);
 strokeWeight(2);
 line(0-radius/2,0-radius, 0+radius/2, 0+radius);
 line(0+radius/2,0-radius, 0-radius/2, 0+radius);
 line(0,0-radius, 0, 0+radius);
 ellipse(0,0,radius*0.5,radius*0.5);
 ellipse(0+radius-1,0,radius*1.5,radius);
 ellipse(0-radius*0.7,0,radius,radius);
 //ellipse(position.position.x, position.position.y, radius*2, radius*2);
 popMatrix();
 }

 //Methode des Objekts Ameise
 void move() {
 direction.x += random (-spin, spin);
 direction.y += random (-spin, spin);
 direction.normalize ();
 position.add (direction);

 if (position.x < radius || position.x > (width-radius)) {
 direction.x *= -1;
 }
 if (position.y < radius || position.y > (height-radius)) {
 direction.y *= -1;
 }
 }

Der Aufruf einer solchen Methode sieht dann so aus:

Ameise[i].move();
Ameise[i].render();

Außerdem können zu jedem Zeitpunkt alle Variablen eines Objekts abgerufen werden. Ein Beispiel für eine Abfrage kommt im gesamten Quellcode unten vor.

Unser Beispiel ist bewusst eher einfach gehalten und könnte noch um viele nette Features erweitert werden.

Beispiel Ameisen: starte Applet


Animals [] Ameise;
Food [] Futter;
//Der Aufruf einer solchen
//Methode sieht dann so aus:
void setup() {
 size(400, 400,P2D);
 gibFutter();
 buildAmeise();
 frameRate(120);
}

void draw() {
 background(180);
 //die erste Schleife geht in jedem Frame alle Ameisen durch
 for (int i=0; i<Ameise.length; i++) {
 //damit weichen die Ameisen einader aus
 for (int u=0; u<Ameise.length; u++) {
 if ((Ameise[i].position.x < Ameise[u].position.x+20 &&  Ameise[i].position.x > Ameise[u].position.x-20
 && Ameise[i].position.y < Ameise[u].position.y+20 && Ameise[i].position.y > Ameise[u].position.y-20) && u!=i) {
 Ameise[i].direction.x *= -1;
 Ameise[i].direction.y *= -1;
 }
 }

 Ameise[i].move();
 Ameise[i].render();
 Ameise[i].blocked-=1;

 //die zweite Schleife alle Futterpakete
 for (int f=0; f<Futter.length; f++) {
 if (Ameise[i].position.x < Futter[f].xPos+6 &&  Ameise[i].position.x > Futter[f].xPos-6
 && Ameise[i].position.y < Futter[f].yPos+6 && Ameise[i].position.y > Futter[f].yPos-6) {
 //hierhin kommt man nur, wenn eine Ameise auf Futter gestoßen ist
 if(Ameise[i].loaded && Ameise[i].traegt!=f) {
 //hier kommt hin, was passiert, wenn eine schon beladene Ameise auf Futter trifft
 Ameise[i].blocked=30; //Anzahl der Frames, bevor die Ameise wieder Futter aufnehmen kann
 Ameise[i].loaded = false;
 Futter[f].traeger=500;
 }
 else {
 //das passiert, wenn die Ameise auf Futter gestoßen ist, nichts trägt und nicht gesperrt ist.
 if (Ameise[i].blocked<1) {
 Ameise[i].traegt= f;
 Futter[f].traeger= i;

 Futter[f].dragged();
 Ameise[i].loaded = true;
 }
 }
 }
 Futter[f].render();
 println(Futter[f].traeger);
 }
 }
}

//diese Funktion erzeugt die Ameisen
void buildAmeise() {
 Ameise = new Animals[5];
 int border = 50;
 for( int i =0;i<Ameise.length; i++) {
 float X = random(border, width-border);
 float Y = random(border, height-border);

 float theX = random(border, width-border);
 float theY = random(border, height-border);

 float radius = random(15, 15);
 boolean loaded = false;
 int blocked = 0;
 int traegt = 50;
 color clr = color(120,100);
 Ameise[i] = new Animals(theX, theY, radius, loaded, blocked, traegt, clr);
 }
}

//hier beginnt die Definition des Objekts Ameise

void gibFutter() {
 Futter = new Food[50];
 int border = 50;
 for (int f=0; f<Futter.length; f++) {
 float xPos = random(border, width-border);
 float yPos = random(border, height-border);
 float radius = random(5, 5);
 int traeger = 500;
 color clr = color(50,200,0,120);
 Futter[f] = new Food(xPos, yPos, radius, traeger, clr);
 }
}

class Animals {

 PVector position;
 PVector direction;
 float spin = 0.10;
 float radius;
 boolean loaded;
 int blocked;
 int traegt;
 color clr;
 Animals (float theX, float theY,
 float aradius, boolean aloaded, int ablocked, int atraegt, color aclr) {
 position    = new PVector (theX, theY);
 direction   = new PVector (10,10);
 direction.x = random (-1, 1);
 direction.y = random (-1, 1);
 radius = aradius;
 loaded = aloaded;
 blocked = ablocked;
 traegt = atraegt;
 clr    = aclr;
 }

 //Methode des Objekts Ameise
 void render() {
 fill(clr);
 //der Code ab hier dient dem Zeichnen der Ameise
 PVector normVector = new PVector(1,0);
 float angle = direction.heading2D();
 pushMatrix();
 translate(position.x,position.y);
 rotate(angle-PI);
 strokeWeight(2);
 line(0-radius/2,0-radius, 0+radius/2, 0+radius);
 line(0+radius/2,0-radius, 0-radius/2, 0+radius);
 line(0,0-radius, 0, 0+radius);
 ellipse(0,0,radius*0.5,radius*0.5);
 ellipse(0+radius-1,0,radius*1.5,radius);
 ellipse(0-radius*0.7,0,radius,radius);
 //ellipse(position.position.x, position.position.y, radius*2, radius*2);
 popMatrix();
 }

 //Methode des Objekts Ameise
 void move() {
 direction.x += random (-spin, spin);
 direction.y += random (-spin, spin);
 direction.normalize ();
 position.add (direction);

 if (position.x < radius || position.x > (width-radius)) {
 direction.x *= -1;
 }
 if (position.y < radius || position.y > (height-radius)) {
 direction.y *= -1;
 }
 }
}
//hier beginnt die Definition des Objekts Futter
class Food {
 float xPos;
 float yPos;
 float radius;
 int traeger;
 int frame=0;
 color clr;

 Food (float axPos, float ayPos, float aradius, int atraeger, color aclr) {
 xPos   = axPos;
 yPos   = ayPos;
 radius = aradius;
 traeger = atraeger;
 clr    = aclr;
 }
 //Methode des Objekts Futter
 void render() {
 fill(clr);
 ellipse(xPos, yPos, radius*2, radius*2);
 }

//Methode des Objekts Futter
void dragged() {
if (Ameise[traeger].blocked<1) {

xPos=Ameise[traeger].position.x;
yPos=Ameise[traeger].position.y;
}

}
}

Aufgabe: Programmiere basierend auf dem Ameisen-Beispiel ein kleines Autorennspiel.

Vektoren


 

Processing 2.0

Ein PVector ist ein Objekt, das 2- oder 3-dimensionale Vektoren beschreibt. Ein Vektor hat eine bestimmte Richtung und eine bestimmte Länge. Man kann auch sagen, er erstreckt sich vom Punkt A zum Punkt B. In der Programmierung verwenden wir Vektoren, um Bewegung zu generieren.

Der Vorteil bei der Verwendung von Vektoren liegt darin, dass man schon mit 2 Vektor- Variablen die aktuelle Geschwindigkeit und Richtung eines Objektes beschreiben kann und mit einem zweiten die Änderung derselben. Diese beiden müssen dann nur noch in jedem Frame addiert werden.

Beispiel: starte Applet

/** Copyright 2012 Thomas Koberger
*/

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//hier werden die Vektoren erstellt
PVector direction = new PVector(1,1);
PVector position = new PVector(20,20);
int radius = 15;

void setup() {
size(300,300);
}

void  draw() {
//für den Schweif
fill(255,20);
rect(0,0,width,height);
//damit sich überhaupt was bewegt
position.add (direction);
//die nächsten Zeile lassen die Ellipse zurückprallen
if (position.x < radius || position.x > (width-radius)) {
direction.x *= -1;
}
if (position.y < radius || position.y > (height-radius)) {
direction.y *= -1;
}
//zeichnet die Ellipse
fill(50);
ellipse(position.x,position.y,radius*2,radius*2);
}

Das Ganze kann dann noch gut mit Zufallszahlen kombiniert werden.

Methoden:

  • vector.set(x,y,z) … legt die x-, y- und z-Werte des Vektors fest
  • vector.get() … gibt die x-, y- und z-Werte des Vektors zurück
  • vector.mag() … gibt die Länge des Vektors zurück
  • vector1.add(vector2) … Addiert 2 Vektoren
  • vector1.sub(vector2) … Subtrahiert 2 Vektoren
  • vector1.mult(float) … Multipliziert  einen Vektor mit einer Zahl
  • vector1.div(float) … Dividiert  einen Vektor durch eine Zahl
  • vector.nomalize() … Ändert die Länge auf 1
  • vector1.anglebetween(vector2) … gibt den Winkel zwischen 2 Vektoren zurück
  • vector.heading2D() … gibt die Richtung des Vektors zurück

Aufgabe: Verändere das Programm so, dass der Kreis zufällig seine Richtung leicht variiert.

Vektoren im 3 dimensonalen Raum

Richtig interessant wird die Verwendung von Vektoren in 3D. Auch hier können alle oben genannten Methoden verwendet werden. D.h., um beispielsweise ein Objekt an die Position eines Vektors zu verschieben geht man wie folgt vor:

PVector loc=new PVector(x,y,z);

translate(loc.x, loc.y, loc.z);
Dann wird das Objekt auf die Position (0,0,0) gezeichnet. Wir haben also das Koordinatensystem verschoben. Um das Koordinatensystem wieder an zurückzusetzten verwendet man pushMatrix() und popMatrix().

Da Objekte im Raum, aber nicht nur eine Position, sondern auch eine bestimmte Orientierung aufweisen, stellt sich schnell die Frage, wie man die Orientierung im Raum definiert.

In einer Fläche (2 Dimensionen) ist das raltiv einfach. Man braucht dafür nur einen Winkel. Dieser steht normal zur Fläche (siehe Bsp.).

Beispiel: Rotate 2D starte Applet

/** Copyright 2012 Thomas Koberger
*/

// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

PVector zero, actual;

void setup() {
size(300,300, P2D);
zero= new PVector(100,0);
rectMode(CENTER);
}

void draw() {
background(200);
stroke(0);

// Verschiebe das Koordinatensystem in den Mittelpunkt
translate(width/2, height/2);
line(0,0,zero.x,zero.y);

// Berechne die Koordinaten des Vektors actual aus der Mausposition
actual=new PVector(cos(map(mouseX,0,width,0,2*PI))*100,sin(map(mouseX,0,width,0,2*PI))*100);

stroke(200,0,50);
line(0,0,actual.x,actual.y);
text("Winkel:"+degrees(actual.heading2D()),height/2-130,width/2-30);

//Verschiebe das Koordinatensystem an die Position von actual
translate(actual.x,actual.y);

//Rotiere das Koordinatensystem
rotate(actual.heading2D());
rect(0,0,20,20);
}

Die Axis-Angle Methode

Im 3 dimensionalen Raum muss neben dem Winkel noch eine Achse angegeben werden, um welche gedreht wird. Wenn ich  sage, meine beiden Unterarme liegen  im Winkel von 90 zueinander, so würde sofort gefragt, in welcher Richtung. Ich könnte nämlich meine Arme in viele verschiedene Positionen drehen, und trotzdem könnten sie immer 90 zueinander liegen. Was wir brauchen ist die Definition einer Achse, um die gedreht wird.

Eine Methode, Axis-Angle Methode genannt, bietet die Möglichkeit beides, nämlich die Achse und den Rotationswinkel zu berechnen. Dabei kommen zwei mathematische Methoden zum Einsatz. Nämlich das sog. Punktprodukt od. Skalarprodukt (gibt den Rotationswinkel an) und das Kreuzprodukt, welches über einen neuen Vektor die Rotationsachse repräsentiert.

Beispiel: Axis-Angle Methode für Processing 2.0

Achtung: läuft mit Processing 2.0b6 nicht im Android Mode!!!

// von Thomas Koberger
/**
* MOUSE               : Rotation um die Achse zwischen Objekten
*
* KEYS
* Space               : neue Zufallswerte für Vektoren
*/
// https://lernprocessing.wordpress.com/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Damit die Szene rotiert und gezoomt werden kann
// Im Javascript auszukommentieren
import peasy.*;

// Im Javascript Mode auszukommentieren
PeasyCam cam;

PVector zero, left, right;

void setup() {
size(800, 800, P3D);

// Im Javascript auszukommentieren
cam = new PeasyCam(this, 0, 0, 0, 1000);

// Vektoren werden definiert
zero= new PVector(0, 0, 0);
left=new PVector(-width/4+random(-100, 100), random(-100, 100), random(-100, 100));
right=new PVector(width/4+random(-100, 100), random(-100, 100), random(-100, 100));
}

void draw() {
background(50);

// Im Javascript und Android Mode einkommentieren
//translate (width/2,height/2,0);
drawAxes(300);
stroke(255);
fill(200);

// Zeichne Würfel und Linien
line(left.x, left.y, left.z, right.x, right.y, right.z);
pushMatrix();
translate(left.x, left.y, left.z);
box(20);
popMatrix();
pushMatrix();
translate(right.x, right.y, right.z);
box(20);
popMatrix();
pushMatrix();
stroke(200, 0, 50);

// Verschiebe das Koordinatensystem an die Position right
translate(right.x, right.y, right.z);

// Kopie von left erstellen
PVector diff=new PVector(left.x, left.y, left.z);

// Differenzvektor berechnen
diff.sub(right);

// Koordinatensystem verschieben
translate(diff.x/2, diff.y/2, diff.z/2);
diff.normalize();

// Orientierung des mittleren Quaders festlegen
zero=new PVector(0, 0, 1);

// Achse und Winkel berechnen
float angle = acos(zero.dot(diff));
PVector axis = zero.cross(diff);

//Rotiere das Koordinatensystem
rotate(angle, axis.x, axis.y, axis.z);

// Rotiere um die Achse
rotateZ(map(mouseX, 0, width, -PI, PI));
drawAxes(50);
fill(50, 0, 200);
box(20);
popMatrix();
}

// Koordinatensystem zeichnen
void drawAxes(int scl) {
stroke(255, 0, 0);
line(-scl, 0, 0, scl, 0, 0);
text("+x", scl, 0, 0);
text("-x", -scl-30, 0, 0);
stroke(0, 255, 0);
line(0, -scl, 0, 0, scl, 0);
text("+y", 0, scl+30, 0);
text("-y", 0, -scl, 0);
stroke(0, 0, 255);
line(0, 0, -scl, 0, 0, scl);
text("+z", 0, 0, scl+30);
text("-z", 0, 0, -scl+30);
}

// Achse neu ausrichten
// für Javascript Mode auskommentieren
void keyPressed() {
if (key == ' ') {
left=new PVector(-width/4+random(-100, 100), random(-100, 100), random(-100, 100));
right=new PVector(width/4+random(-100, 100), random(-100, 100), random(-100, 100));
}
}

PVector – Einführung von Daniel Shiffman