Hi,
heute zu einem sehr spannenden Thema, der Rotation, wie bereits kurz angesprochen ist dieses Thema die Basis für solche Spielereien. Bisher konnten wir beliebige Formen (ellipse(), line(), point(), rect(), etc...) von links nach rechts, rechts nach links, oben nach unten usw.. bewegen, linear eben, immer in eine Richtung. Viel cooler ist es aber, Formen im Kreis zu bewegen. Hier haben wir die Möglichkeit, die Objekte kontinuierlich zu rotieren, beispielsweise im Kreis immer nach links oder immer nach rechts. Mit Sinus ( sin() ) und Kosinus ( cos() ) gibt es darüber hinaus die Möglichkeit, Objekte pendeln zu lassen, beispielsweise ein Pendel wie bei älteren Standuhren, die in einem ca. 45° Winkel von links nach rechts pendeln. Rotation eben, und das ist das Thema, mit dem wir uns heute und in den kommenden Artikeln beschäftigen werden - ein sehr umfangreiches Thema also. Ich gehe davon aus dass deine Mathematikkenntnisse -sagen wir mal- recht passabel sind und du das nötige Wissen für Kreisberechnungen, Sinus, Kosinus, PI etc.. bereits mitbringst. Im Zusammenhang mit Processing sehen wir uns jetzt zuerst einmal die Funktion rotate() an. In der Referenz ist recht schön beschrieben, was diese Funktion tut:
<blockquote> Rotates a shape the amount specified by the angle parameter. Angles should be specified in radians (values from 0 to TWO_PI) or converted to radians with the radians() function. </blockquote>
Übersetzen wir das einmal ins Deutsche:
<blockquote> Rotiert eine Form zu dem Wert eines Winkels. Winkel werden über radians (Radiant) angegeben (gültig sind Werte von 0 is TWO_PI, also 2xPI) bzw. über die radians() Funktion in einen Radianten konvertiert. </blockquote>
radians() konvertiert eine Zahl die in Grad (°) angegeben ist in eben diesen sogenannten Radiant. 2xPI entspricht 360°, aus der Schule wissen wir, 360° bedeutet immer genau einen vollständigen Kreis. PI entspricht somit 360° / 2, also 180°. Stell dir eine Uhr vor mit Sekunden-Zeiger. Der Zeiger startet bei 0 (oder bei 12, wie auch immer) und fängt an zu wandern, wenn der Zeiger auf 6 Uhr steht, haben wir 180° geschafft. 180° sind 3,14159..., also PI. Der Zeiger wandert weiter, wenn er wieder auf 12 Uhr steht hat er genau eine Runde gemacht, 360° also, und somit 2xPI (6.2831855...). Du wirst feststellen, dass 0° und 360° sich überschneiden, der Zeiger steht in beiden Fällen an derselben Stelle. Soweit eigentlich klar. Und was hat das nun mit dem Radianten zu tun? radians() konvertiert einen in Grad angegebenen Wert in eine natürliche Zahl, und diese Zahl ist der Radiant.
Im Zusammenhang mit der rotate()-Funktion kann so eine Grad-Angabe (mit der wir Menschen in der Regel besser zurecht kommen) übergeben werden, um eine Rotation darzustellen. Wir brauchen nicht überlegen um zu wissen 180° ist ein halber Kreis, 90° ist ein Viertel und 360° ist ein Ganzes. Somit bleibt es uns erspart, diese ganzen Zahlen als Radiant zu berechnen, das macht die radians() Funktion für uns.
Ich möchte, dass ein Objekt (eine Form, z.B. ein Rechteck) um 90° rotiert wird. Kein Problem, radians() macht das für uns. Durch folgende Angabe übergeben wir einfach unsere Anzahl in Grad (90°) an die radians()-Funktion, die rotate()-Funktion wiederum arbeitet dann mit dem an die radians()-Funktion übergebenen Wert, intern ist es für die Rotate-Funktion ja dann kein Grad-Wert mehr sondern der Radiant. Die Schreibweise sieht wie folgt aus:
<pre> <span style="color: #CC6600;">rotate</span>(<span style="color: #CC6600;">radians</span>(90)); </pre>
BAM! Ganz simpel und saumäßig praktisch. Wir übergeben 90 und radians macht daraus 1.5707964, das ist der Wert mit dem die rotate()-Funktion dann arbeitet, aber das braucht uns dann nicht mehr interessieren, wir bleiben entspannt bei unserer Grad-Angabe. Jetzt wo du das weißt, sehen wir uns an wie die rotate()-Funktion arbeitet. Die Rotate-Funktion kann an beliebiger Stelle im Code eingefügt werden. Alle nachfolgenden Formen (alles was nach dem rotate()-Befehl geschrieben wird), wird rotiert. Du musst dann für z.B. eine Ellipse keinen Code hinterlegen, der diese Bewegt, denn das macht die rotate()-Funktion. So könntest du beispielsweise ein Rechteck nach links rotieren lassen, und ein darüberliegendes nach rechts. Aber Ich würde sagen genug von dem ganzen theoretischen Kram, schaun wir uns lieber an wie das mit der Rotation in der Praxis funktioniert. Darstellen möchte Ich ein Rechteck, welches sich kontinuierlich dreht.
<pre> <span style="color: #CC6600;">void</span> <span style="color: #CC6600;"><b>setup</b></span>() { <span style="color: #CC6600;">size</span>(300, 300); <span style="color: #CC6600;">smooth</span>(); <span style="color: #CC6600;">background</span>(0); <span style="color: #006699;">frameRate</span>(60); } <span style="color: #CC6600;">int</span> grad = 0; <span style="color: #CC6600;">void</span> <span style="color: #CC6600;"><b>draw</b></span>() { <span style="color: #CC6600;">background</span>(0); <span style="color: #CC6600;">rotate</span>(<span style="color: #CC6600;">radians</span>(grad)); grad++; <span style="color: #CC6600;">println</span>(grad + <span style="color: #006699;">"°"</span>); <span style="color: #CC6600;">fill</span>(255); <span style="color: #CC6600;">noStroke</span>(); <span style="color: #CC6600;">rect</span>(0, 0, 50, 50); <span style="color: #CC6600;">if</span> (grad >360) { grad = 0; } } </pre>
So, schaut doch schon ganz gut aus. Nur das Rechteck, dass das in der linken oberen Ecke ist, schaut hässlich aus. Verlegen wir den Nullpunkt in die Mitte, dann macht das schon mehr her:
<pre> <span style="color: #CC6600;">void</span> <span style="color: #CC6600;"><b>setup</b></span>() { <span style="color: #CC6600;">size</span>(300, 300); <span style="color: #CC6600;">smooth</span>(); <span style="color: #CC6600;">background</span>(0); <span style="color: #006699;">frameRate</span>(60); } <span style="color: #CC6600;">int</span> grad = 0; <span style="color: #CC6600;">void</span> <span style="color: #CC6600;"><b>draw</b></span>() { <span style="color: #CC6600;">background</span>(0); <span style="color: #CC6600;">translate</span>(<span style="color: #006699;">width</span> / 2, <span style="color: #006699;">height</span> / 2); <span style="color: #CC6600;">rotate</span>(<span style="color: #CC6600;">radians</span>(grad)); grad++; <span style="color: #CC6600;">println</span>(grad + <span style="color: #006699;">"°"</span>); <span style="color: #CC6600;">fill</span>(255); <span style="color: #CC6600;">noStroke</span>(); <span style="color: #CC6600;">rect</span>(0, 0, 50, 50); <span style="color: #CC6600;">if</span> (grad >360) { grad = 0; } } </pre>
Ich deklariere eine Variable "grad" mit dem Wert 0. Dieser Wert wird je um 1 erhöht. Diese Variable übergebe ich an radians, diese macht aus dem Grad-Wert den Radiant, die rotate()-Funktion arbeitet mit diesem konvertierten Wert und rotiert entsprechend. Wie du siehst ist es ganz einfach. Interessant wäre nun auch, wenn sich das Rechteck anstatt von der oberen linken Ecke aus um den Mittelpunkt des Rechtecks rotiert. Hierfür stehen dir zwei Möglichkeiten zur Verfügung: Entweder du passt den rectMode() entsprechend an mit rectMode(CENTER); (dafür musst du bevor das Rechteck gezeichnet wird eine Zeile mit eben genanntem Code einfügen) oder aber du zeichnest das Rechteck in der Positionsangabe jeweils minus die Hälfte der Breite / Höhe.
Beide Möglichkeiten einmal zum testen:
<pre> <span style="color: #CC6600;">rectMode</span>(<span style="color: #006699;">CENTER</span>); <span style="color: #CC6600;">rect</span>(0, 0, 50, 50); </pre>
oder so:
<pre> <span style="color: #CC6600;">rect</span>(-25, -25, 50, 50); </pre>
Versuche, die Stelle mit dem Rechteck im Code jeweils mit der einen und der anderen Möglichkeit zu zeichnen, das Ergebnis ist dasselbe. Falls Ich in meinen Artikeln noch gar nicht erklärt habe, was der rectMode() überhaupt ist, will Ich es dir noch ganz kurz erklären: Der rectMode() gibt an, wo der quasi-Ausgangspunkt des Rechtecks ist. Die ersten beiden Parameter für rect() geben ja an, ab welcher X- / Y-Position angefangen soll zu zeichnen, die letzten beiden Parameter bestimmen Breite und Höhe des Rechtecks. Der Punkt von dem ab das Rechteck dann gezeichnet wird, ist immer der obere linke (das wäre dann standardmäßig rectMode(CORNER);). Wenn du nun aber rectMode(CENTER) angibst bevor du das Rechteck zeichnest, ist dieser Punkt nicht mehr die obere linke Ecke der Form sondern genau die Mitte. Es gibt noch weitere Modi, insgesamt stehen CORNER, CORNERS, CENTER und RADIUS zur Verfügung, dasselbe gibt es noch für ellipseMode(). Bevor ich aber jetzt zu weit abschweife, komme Ich lieber auf das wesentliche zurück. Rotation. Wie gesagt, du siehst es ist ziemlich einfach. Die Abfrage ob grad > 360 ist könntest du auch weglassen, das ganze würde trotzdem ganz normal weiterlaufen, allerdings wollte Ich das mit den Grad im Ausgabefenster nochmal verdeutlichen.
[caption id="attachment_2151" align="alignleft" width="150" caption="Transparente Rechtecke mit Rotation"][/caption] Ich denke es ist an der Zeit, mit dieser tollen Funktion endlich etwas sinnvolles anzufangen. Ich schlage vor wir fügen in einer Schleife einfach mehrere Rechtecke (natürlich transparant ;-) ) mit unterschiedlicher Rotation hinzu. Den Wert der Rotation müssen wir nicht selbst anpassen, da jede rotate()-Anweisung von der zuvor geschriebenen abhängig ist. Außerdem bleibt so nichts komplett dem Zufall überlassen, das heißt unsere Bewegungen lassen im Laufe der Zeit ein Muster wiedererkennen. Wo war ich ... achja genau, und rectMode() stellen wir wieder auf CENTER. Du kannst übrigens in deinen Tests das rectMode(CENTER); einmal rausnehmen und gucken wie es dann aussieht. Ich zeig' dir mal was Ich mir da ausgedacht hab', auf der linken Seite ein Screenshot, unten der Code:
<pre> <span style="color: #CC6600;">void</span> <span style="color: #CC6600;"><b>setup</b></span>() { <span style="color: #CC6600;">size</span>(300, 300); <span style="color: #CC6600;">background</span>(0); <span style="color: #CC6600;">smooth</span>(); <span style="color: #006699;">frameRate</span>(60); } <span style="color: #CC6600;">float</span> angle; <span style="color: #CC6600;">void</span> <span style="color: #CC6600;"><b>draw</b></span>() { <span style="color: #CC6600;">background</span>(0); <span style="color: #CC6600;">translate</span>(<span style="color: #006699;">width</span>/2, <span style="color: #006699;">height</span>/2); <span style="color: #CC6600;">rectMode</span>(<span style="color: #006699;">CENTER</span>); <span style="color: #CC6600;">fill</span>(255); <span style="color: #CC6600;">for</span> (<span style="color: #CC6600;">int</span> i = 255; i >10; i-=10) { <span style="color: #CC6600;">rotate</span>(<span style="color: #CC6600;">radians</span>(angle)/<span style="color: #006699;">PI</span>); <span style="color: #CC6600;">strokeWeight</span>(0.1); <span style="color: #CC6600;">stroke</span>(0); <span style="color: #CC6600;">fill</span>(i, 25); <span style="color: #CC6600;">rect</span>(0, 0, i, i); } angle += 1; } </pre>
Ergebnis:
<script type="application/processing"> //Info: http://processingjs.org/reference void setup() { size(300, 300); background(0); smooth(); frameRate(60); } float angle; void draw() { background(0); translate(width/2, height/2); rectMode(CENTER); fill(255); for (int i = 255; i >10; i-=10) { rotate(radians(angle)/PI); strokeWeight(0.1); stroke(0); fill(i, 25); rect(0, 0, i, i); } angle += 1; } void keyPressed() { if(key == 's'){save("file.png");} } </script>
Ja, ich würde sagen ich bin recht zufrieden mit dem Ergebnis, es schaut ziemlich gut aus. Auf OpenProcessing kannst du dir den Sketch auch nochmal live ansehen oder hier die rotierenden Rechtecke als Download direkt herunterladen. Ich denke das Prinzip hast du verstanden, und wie Ich dir schon vorausprophezeit habe, ist es nicht wirklich schwierig. Fassen wir dennoch die wichtigsten Punkte erneut zusammen:
- Jede rotate()-Anweisung ist abhängig von der zuvor geschriebenen, wenn wir zwei Rechtecke rotieren wird das zweite (darüberliegende) sich in jedem Fall schneller bewegen.
- Tipp: Um eine Form in die andere Richtung zu rotieren kannst du vor die Variable (ich nenne sie meist "angle") ein Minuszeichen setzen, den Wert also negativ machen. Beispiel: rotate(radians(-angle));. Doch aufgepasst, wenn du zuvor bereits eine rotate-Anweisung hast ohne das Minuszeichen aber mit demselben Wert ("angle" eben dann) hört die Rotation auf, damit hebst du für alle weiteren Objekte oder Formen die du hinzufügst die Rotation auf. Ich weiß, etwas schwierig vorzustellen, unten folgt noch ein Beispiel.
- Für 90° kannst du statt rotate(radians(90)); auch rotate(HALF_PI); schreiben
Ich erkläre dir noch was mit Punkt 2 und dem Aufheben der Rotation gemeint ist. Wenn du eine rotation() von sagen wir 45° hast, ein Rechteck zeichnest, dann noch eine rotation() hinzufügst wieder mit 45° und erneut ein Rechteck darüberlegst, dreht sich das erste Rechteck um 45°, das zweite um 90°, weil es durch die erste rotate()-Anweisung 45° rotiert und durch die zweite Anweisung nochmal. Ich zeige es dir anhand eines Screenshots, zuerst wurde das weiße, dann das schwarze Rechteck hinzugefügt:
[caption id="attachment_2163" align="aligncenter" width="300" caption="Zwei um 45° rotierende Rechtecke"][/caption]
Um also eine Rotation für alle nachfolgenden Objekte aufzuheben, schreibst du denselben Code wie den in der vorangehenden Rotate-Anweisung, nur mit Minus-Zeichen vor der Winkel-Variable:
[caption id="attachment_2162" align="aligncenter" width="300" caption="Rotation aufheben"][/caption]
Mit dieser rotate()-Funktion lässt sich, wie du nun siehst, so allerhand anstellen. Nun möchte Ich einmal etwas neues versuchen, und zwar einen Punkt (oder eine Ellipse) die sich zwar im Kreis dreht, dessen Radius dabei jedoch immer größer wird. Dafür musst du den X-/Y-Koordinaten der Form lediglich einen dynamischen, sich inkrementierenden Wert zuweisen, also eine Variable deren Wert immer größer wird. In der einfachsten Form könnte dies so aussehen:
<pre> <span style="color: #CC6600;">void</span> <span style="color: #CC6600;"><b>setup</b></span>() { <span style="color: #CC6600;">size</span>(300, 300); <span style="color: #CC6600;">background</span>(0); <span style="color: #CC6600;">smooth</span>(); } <span style="color: #CC6600;">float</span> angle; <span style="color: #CC6600;">float</span> speed = 5; <span style="color: #CC6600;">float</span> ball_radius = 0; <span style="color: #CC6600;">float</span> ball_size = 5; <span style="color: #CC6600;">void</span> <span style="color: #CC6600;"><b>draw</b></span>() { <span style="color: #CC6600;">translate</span>(<span style="color: #006699;">width</span> / 2, <span style="color: #006699;">height</span> / 2); <span style="color: #CC6600;">rotate</span>(<span style="color: #CC6600;">radians</span>(angle)); <span style="color: #CC6600;">noStroke</span>(); <span style="color: #CC6600;">fill</span>(255); <span style="color: #CC6600;">ellipse</span>(ball_radius, ball_radius, ball_size, ball_size); ball_radius += 0.25; angle += speed; } </pre>
Und wenn man nun noch mit Füllfarbe, Strichfarbe und -dicke, Schnelligkeit, Größe und so weiter rumspielt und hier und da anpasst, könnte das nach wenigen Änderungen so aussehen:
<pre> <span style="color: #CC6600;">void</span> <span style="color: #CC6600;"><b>setup</b></span>() { <span style="color: #CC6600;">size</span>(300, 300); <span style="color: #CC6600;">background</span>(0); <span style="color: #CC6600;">smooth</span>(); } <span style="color: #CC6600;">float</span> angle; <span style="color: #CC6600;">float</span> speed = 77; <span style="color: #CC6600;">float</span> ball_radius = 1; <span style="color: #CC6600;">float</span> ball_size = 15; <span style="color: #CC6600;">void</span> <span style="color: #CC6600;"><b>draw</b></span>() { <span style="color: #CC6600;">translate</span>(<span style="color: #006699;">width</span> / 2, <span style="color: #006699;">height</span> / 2); <span style="color: #CC6600;">rotate</span>(<span style="color: #CC6600;">radians</span>(angle)); <span style="color: #CC6600;">fill</span>(255, 10); <span style="color: #CC6600;">stroke</span>(0); <span style="color: #CC6600;">strokeWeight</span>(0.1); <span style="color: #CC6600;">ellipse</span>(ball_radius, ball_radius, ball_size, ball_size); ball_radius += 0.25; ball_size += 0.1; angle += speed; } </pre>
[caption id="attachment_2167" align="alignleft" width="150" caption="Rotierende Linien"][/caption] Damit kannst du vielerlei Zeugs anstellen, es gibt beinahe endlos viele Möglichkeiten, die du einbauen könntest. Strichdicke, Strichfarbe, Füllfarbe, Transparenz (Alpha), Größe, Geschwindigkeit, Rechtecke, Linien, Ellipsen und dann noch Zufallswerte für eine der genannten Funktionen, Radius ändern blablablablabla ... So einiges also =) In dem Screenshot den du links siehst habe Ich denselben Code wie eben, nur statt einer ellipse Linien, eine andere Strichfarbe, andere Geschwindigkeit und ... ja, völlig anderes Ergebnis:
<pre> <span style="color: #CC6600;">void</span> <span style="color: #CC6600;"><b>setup</b></span>() { <span style="color: #CC6600;">size</span>(300, 300); <span style="color: #CC6600;">background</span>(0); <span style="color: #CC6600;">smooth</span>(); } <span style="color: #CC6600;">float</span> angle; <span style="color: #CC6600;">float</span> speed = 20; <span style="color: #CC6600;">float</span> ball_radius = 0; <span style="color: #CC6600;">float</span> ball_size = 5; <span style="color: #CC6600;">void</span> <span style="color: #CC6600;"><b>draw</b></span>() { <span style="color: #CC6600;">translate</span>(<span style="color: #006699;">width</span> / 2, <span style="color: #006699;">height</span> / 2); <span style="color: #CC6600;">rotate</span>(<span style="color: #CC6600;">radians</span>(angle)); <span style="color: #CC6600;">fill</span>(255, 10); <span style="color: #CC6600;">strokeWeight</span>(0.1); <span style="color: #CC6600;">stroke</span>(0, <span style="color: #CC6600;">random</span>(200), <span style="color: #CC6600;">random</span>(255), <span style="color: #CC6600;">random</span>(255)); <span style="color: #CC6600;">line</span>(<span style="color: #CC6600;">random</span>(ball_radius), <span style="color: #CC6600;">random</span>(ball_radius), <span style="color: #CC6600;">random</span>(ball_size), <span style="color: #CC6600;">random</span>(ball_size)); ball_radius += 0.25; ball_size += 0.1; angle += speed; } </pre>
Damit bist du nun eine Weile beschäftigt, es gibt viel auszuprobieren ;-) Auch in den kommenden Artikeln wird die Rotation immer wieder mal das Thema sein, spätestens wenn wir dann sin() und cos() hinzufügen. Außerdem gibt's ja noch rotateX, rotateY und rotateZ für 3-Dimensionale Spielereien ... wir haben viel vor :-)
Bis dahin Marius
Kommentare
Kommentar schreiben