Daten II: Sonnenstunden - Parameter, Form, Farbe, Komposition

Daten im Array, Arrayinhalt zeichnen, Zeichnen mit 2D-Transformationen, Beschriftung, Zuordnungen: map(...), Vom Kleinsten zum Größten: min/max, Zeichnen mit eigenen Methoden, Tabs

Daten im Array

Neben der Erzeugung von Arrays mit new und [] kann man Arrays und die Daten darin auch direkt definieren. Mit { und }. Hier zum Beispiel die Sonnenstunden pro Monat in Deutschland (Jan.-Dez.).

float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };

Das Array sonne hat jetzt 12 Einträge und kann wie gewohnt benutzt werden.

println("Monate im Jahr: "+sonne.length);
println("Sonnenstunden im Januar: "+sonne[0]);

for (int i=0; i<sonne.length; i++) {
  println("Sonnenstunden im Monat Nummer "+(i+1)+": "+sonne[i]);
}

Arrayinhalt zeichnen

Die Daten kann man natürlich auf unterschiedlichste Art darstellen. Einen Überblick verschafft z.B. eine for-Schleife plus line(...). Damit alles auf den Bildschirm passt, sollte size(...) mindestens so groß, wie der größte Wert im Array sein (hier: 209).

size(250,250);
float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };

for (int i=0; i<sonne.length; i++) {
  line(i*10+10,10,i*10+10,sonne[i]);
}

Die Balken umdrehen (von unten nach oben durch Abziehen der y-Koordinate und des 10 Pixel Rands von der Gesamthöhe).

size(250,250);
float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };

for (int i=0; i<sonne.length; i++) {
  line(i*10+10,height-10,i*10+10,height-10-sonne[i]);
}

Das Zeichnen kann natürlich viel komplexer sein. Z.B. Balkendiagramm mit Farbe abhängig von den Sonnenstunden. Abstand nach links, unten und rechts: dicke/2

size(300,250);
background(255);
float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };
float dicke = 25;

for (int i=0; i<sonne.length; i++) {
  stroke(sonne[i]);
  strokeWeight(dicke);
  line(i*dicke+dicke/2,height-10,i*dicke+dicke/2,height-10-sonne[i]);
}

Zeichnen mit 2D-Transformationen

Ein Kreis für jeden Monat. Größe und Farbe abhängig von den Sonnenstunden. Achtung: colorMode(HSB) damit wir den Farbwert (hue: Gelb) festlegen können und nur Sättigung (saturation) mit den Sonnenstunden ändern müssen. Helligkeit (brightness) bleibt konstant.

size(600,400);
background(0,0,128);
float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };

colorMode(HSB);

for (int i=0; i<sonne.length; i++) {
  noStroke();
  fill(43,sonne[i],255,200);
  ellipseMode(CENTER);
  ellipse(i*50+25,height/2,sonne[i]/2,sonne[i]/2);
}

Jetzt kann das Verschieben der Kreise noch mit translate(...) erfolgen...

size(600,400);
background(0,0,128);
float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };

colorMode(HSB);

translate(25,height/2);  // 25 Pixel Abstand nach links, Höhe: halbes Fenster

for (int i=0; i<sonne.length; i++) {
  noStroke();
  pushMatrix();
  translate(i*50,0);
  fill(43,sonne[i],255,200);
  ellipseMode(CENTER);
  ellipse(0,0,sonne[i]/2,sonne[i]/2);
  popMatrix();
}

...oder die Zeichenfläche für jeden Kreis etwas gedreht werden.

Bei 12 Monaten z.B. um 30 Grad (360/12), damit die Monate auf einen vollen Kreis verteilt werden. Damit nicht alle Kreise aufeinander liegen, wird jeder 100 Pixel nach oben gesetzt (durch ein translate(0,-100)).

size(600,400);
background(0,0,128);
float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };

colorMode(HSB);

translate(width/2,height/2);  // Fenstermitte

for (int i=0; i<sonne.length; i++) {
  noStroke();
  pushMatrix();
  rotate(radians(i*30));
  translate(0,-100);
  fill(43,sonne[i],255,200);
  ellipseMode(CENTER);
  ellipse(0,0,sonne[i]/2,sonne[i]/2);
  popMatrix();
}

Beschriftung

String-Variablen für Text...

String monat = "Januar";

...Texte zeichnen mit text(...) (mehr zu Typographie später).

textAlign(CENTER);
text(monat, width/2, height/2);

Ein Array mit den Namen der 12 Monate...

String[] namen = { "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember" };

Und dieses Array verwenden wie das der Sonnenstunden (Textfarbe ist fill(...)).

size(600,400);
background(0,0,128);
float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };
String[] namen = { "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember" };

colorMode(HSB);

translate(width/2,height/2);  // Fenstermitte

for (int i=0; i<sonne.length; i++) {
  noStroke();
  pushMatrix();
  rotate(radians(i*30));
  translate(0,-100);
  fill(43,sonne[i],255,200);
  ellipseMode(CENTER);
  ellipse(0,0,sonne[i]/2,sonne[i]/2);
  fill(255);
  text(namen[i],0,0);
  popMatrix();
}

Damit die Texte gerade stehen, die Zeichenfläche wieder zurück drehen. Also statt...

rotate(radians(i*30));

...jetzt:

rotate(radians(-i*30));

Insgesamt:

size(600,400);
background(0,0,128);
float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };
String[] namen = { "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember" };

colorMode(HSB);

translate(width/2,height/2);  // Fenstermitte

for (int i=0; i<sonne.length; i++) {
  noStroke();
  pushMatrix();
  rotate(radians(i*30));
  translate(0,-100);
  fill(43,sonne[i],255,200);
  ellipseMode(CENTER);
  ellipse(0,0,sonne[i]/2,sonne[i]/2);
  rotate(radians(-i*30));
  fill(255);
  text(namen[i],0,0);
  popMatrix();
}

Oder die Texte über/unter die Kreise setzen (deren Größe ist sonne[i]/2, der Radius also sonne[i]/4], zusätzlich z.B. 4 Pixel Abstand: -6):

  translate(0,-sonne[i]/4-4);
  rotate(radians(-i*30));
  fill(255);
  text(namen[i],0,0);

Die Texte sind so alle linksbündig. Die linke Hälfte des Kreises (Winkel größer 180 Grad) rechtsbündig setzen mit textAlign (dabei auch die Höhe der Texte zentrieren)...

  if (i*30 < 180) {
    textAlign(LEFT, CENTER);
  } else {
    textAlign(RIGHT, CENTER);
  }

Insgesamt:

size(600,400);
background(0,0,128);
float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };
String[] namen = { "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember" };

colorMode(HSB);

translate(width/2,height/2);  // Fenstermitte

for (int i=0; i<sonne.length; i++) {
  noStroke();
  pushMatrix();
  rotate(radians(i*30));
  translate(0,-100);
  fill(43,sonne[i],255,200);
  ellipseMode(CENTER);
  ellipse(0,0,sonne[i]/2,sonne[i]/2);
  translate(0,-sonne[i]/4-4);
  rotate(radians(-i*30));
  if (i*30 < 180) {
    textAlign(LEFT, CENTER);
  } else {
    textAlign(RIGHT, CENTER);
  }
  text(namen[i],0,0);
  popMatrix();
}

Zuordnungen: map(...)

Visualisierung nimmt immer eine Zuordnung vor: z.B. von den Sonnenstunden (44, 73, 111, 152, 196...) auf die Position oder Größe einer Form (ellipse(0,0,sonne[i]/2,sonne[i]/2)) oder ihre Farbe (fill(43,sonne[i],255,200);). Meist sind die Daten um die es geht aber nicht im passenden Bereich (wie hier zwischen 38 und 209, so dass immer eine passende Farbe entsteht). Dann müssen die Daten von einem Bereich in den Anderen abgebildet ("map") werden. Dazu muss man rechnen (wie oben: sonne[i]/2 damit die Kreise nicht so groß werden).

Zum Beispiel: Von der Mausposition zum Grauwert, kein Problem, wenn die Maus immer im Bereich 0-255 ist:

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

void draw() {
  background(mouseX);
}

Bei doppelt so breitem Fenster allerdings...

void setup() {
  size(500,255);
}

void draw() {
  background(mouseX);
}

...passt die Mausposition nicht mehr in den Grauwert und es passieren unvorhersehbare Dinge.

Jetzt zum Beispiel durch 2 teilen.

void setup() {
  size(500,255);
}

void draw() {
  background(mouseX/2);
}

Was aber ist bei anderen Bildschirmgrößen?

void setup() {
  size(732,255);
}

void draw() {
  background(mouseX);
}

mouseX kann jetzt zwischen 0 und 732 (das ist width) liegen. Der Grauwert des Hintergrunds zwischen 0 und 255. Die Übertragung von einem Bereich in den Anderen macht map(...): Map überträgt eine Zahl, die zwischen zwei Grenzen liegt zwischen zwei neue Grenzen:

map(Zahl, alte Untergrenze, alte Obergrenze, neue Untergrenze, neue Obergrenze);

Also mouseX vom Bereich zwischen 0 und width in den Bereich zwischen 0 und 255:

void setup() {
  size(732,255);
}

void draw() {
  background(map(mouseX, 0, width, 0, 255));
}

Oder die Helligkeit eines gesättigten Rots im HSB:

void setup() {
  size(732,255);
  colorMode(HSB);
}

void draw() {
  background(0, 255, map(mouseX, 0, width, 0, 255));
}

Die Grenzen müssen nicht bei 0 beginnen. Sie können auch einfach vertauscht werden (z.B. von hell nach dunkel, statt dunkel nach hell)...

void setup() {
  size(732,255);
  colorMode(HSB);
}

void draw() {
  background(0, 255, map(mouseX, 0, width, 255, 0));
}

...oder ein Rechteck, das zwischen einem Drittel und zwei Dritteln der Bildschirmgröße füllt...

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

void draw() {
  background(255);
  translate(width/2, height/2);
  noStroke();
  fill(128,128,0);
  rectMode(CENTER);
  float groesse = map(mouseX,0,width,width/3,2*width/3);
  rect(0,0,groesse,groesse);
}

...oder mit Variablen, oben im Programm festlegen, wie groß das Rechteck werden kann:

float untergrenze = 100;
float obergrenze = 400;

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

void draw() {
  background(255);
  translate(width/2, height/2);
  noStroke();
  fill(128,128,0);
  rectMode(CENTER);
  float groesse = map(mouseX,0,width,untergrenze,obergrenze);
  rect(0,0,groesse,groesse);
}

Vom Kleinsten zum Größten: min/max

Die Sonnenstunden liegen im Bereich zwischen 38 und 209. Festlegen der Größe der Kreise z.B. mit...

float kleinsterKreis = 25;
float groessterKreis = 100;

...und:

float groesse = map(sonne[i],38,209,kleinsterKreis,groessterKreis);

Insgesamt:

size(600,400);
background(0,0,128);
float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };
float kleinsterKreis = 25;
float groessterKreis = 100;

colorMode(HSB);

translate(width/2,height/2);  // Fenstermitte

for (int i=0; i<sonne.length; i++) {
  noStroke();
  pushMatrix();
  rotate(radians(i*30));
  translate(0,-100);
  fill(43,sonne[i],255,200);
  ellipseMode(CENTER);
  float groesse = map(sonne[i],38,209,kleinsterKreis,groessterKreis);
  ellipse(0,0,groesse,groesse);
  translate(0,-groesse/2-4);
  rotate(radians(-i*30));
  popMatrix();
}

Statt selbst nachzuschauen, wie groß oder klein die Werte in einem Array sind, kann Processing das übernehmen: min(...) und max(...).

float groesse = map(sonne[i],min(sonne),max(sonne),kleinsterKreis,groessterKreis);

Also:

size(600,400);
background(0,0,128);
float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };
float kleinsterKreis = 25;
float groessterKreis = 100;

colorMode(HSB);

translate(width/2,height/2);  // Fenstermitte

for (int i=0; i<sonne.length; i++) {
  noStroke();
  pushMatrix();
  rotate(radians(i*30));
  translate(0,-100);
  fill(43,sonne[i],255,200);
  ellipseMode(CENTER);
  float groesse = map(sonne[i],min(sonne),max(sonne),kleinsterKreis,groessterKreis);
  ellipse(0,0,groesse,groesse);
  translate(0,-groesse/2-4);
  rotate(radians(-i*30));
  popMatrix();
}

Interaktive Visualisierung

Jetzt kann die Visualisierung interaktiv werden: Setup/draw verwenden...

float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };
float kleinsterKreis = 25;
float groessterKreis = 100;

void setup() {
  size(600, 400);
}

void draw() {
  colorMode(RGB);
  background(0, 0, 128);
  translate(width/2, height/2);  // Fenstermitte

  colorMode(HSB);
  for (int i=0; i<sonne.length; i++) {
    noStroke();
    pushMatrix();
    rotate(radians(i*30));
    translate(0, -100);
    fill(43, sonne[i], 255, 200);
    ellipseMode(CENTER);
    float groesse = map(sonne[i], min(sonne), max(sonne), kleinsterKreis, groessterKreis);
    ellipse(0, 0, groesse, groesse);
    translate(0, -groesse/2-4);
    rotate(radians(-i*30));
    popMatrix();
  }
}

...und dann z.B. die Größe der Kreise an die Mausposition koppeln:

  kleinsterKreis = map(mouseX,0,width,10,80);
  groessterKreis = map(mouseX,0,width,80,160);

Also:

float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };
float kleinsterKreis = 25;
float groessterKreis = 100;

void setup() {
  size(600, 400);
}

void draw() {
  colorMode(RGB);
  background(0, 0, 128);
  translate(width/2, height/2);  // Fenstermitte

  colorMode(HSB);
  for (int i=0; i<sonne.length; i++) {
    noStroke();
    pushMatrix();
    rotate(radians(i*30));
    translate(0, -100);
    fill(43, sonne[i], 255, 200);
    ellipseMode(CENTER);
    float groesse = map(sonne[i], min(sonne), max(sonne), kleinsterKreis, groessterKreis);
    ellipse(0, 0, groesse, groesse);
    translate(0, -groesse/2-4);
    rotate(radians(-i*30));
    popMatrix();
  }
  
  kleinsterKreis = map(mouseX,0,width,10,80);
  groessterKreis = map(mouseX,0,width,80,160);

Zeichnen mit eigenen Methoden

Zur Übersichtlichkeit, lässt sich das Zeichnen der einzelnen Kreise in eine eigene Methode auslagern.

Denn..

  noStroke();
  pushMatrix();
  rotate(radians(i*30));
  translate(0, -100);
  fill(43, sonne[i], 255, 200);
  ellipseMode(CENTER);
  float groesse = map(sonne[i], min(sonne), max(sonne), kleinsterKreis, groessterKreis);
  ellipse(0, 0, groesse, groesse);
  translate(0, -groesse/2-4);
  rotate(radians(-i*30));
  popMatrix();

...ist nichts Anderes als die Definition aller Kreise. Sie hängt im Grund nur von i (also 0-11) ab. Alles andere ergibt sich aus i, z.B. sonne[i] oder rotate(radians(i*30)). Dabei ist i natürlich der Monat. Wir machen daher eine eigene Methode, die den Monat bekommt, und ihn dann zeichnet.

void drawMonat(int monat) {
  // ...hier jetzt zeichnen
}

Zwischen den { und } der Methode drawMonat steht jetzt, wie das eigentliche Zeichnen ablaufen soll. Also eigentlich:

void drawMonat(int monat) {
  noStroke();
  pushMatrix();
  rotate(radians(i*30));
  translate(0, -100);
  fill(43, sonne[i], 255, 200);
  ellipseMode(CENTER);
  float groesse = map(sonne[i], min(sonne), max(sonne), kleinsterKreis, groessterKreis);
  ellipse(0, 0, groesse, groesse);
  translate(0, -groesse/2-4);
  rotate(radians(-i*30));
  popMatrix();
}

Allerdings kennt drawMonat das i aus draw jetzt nicht mehr. Dafür kennt sie die Varieble int monat, die sie übergeben bekommt. Damit wird i zu monat.

void drawMonat(int monat) {
  noStroke();
  pushMatrix();
  rotate(radians(monat*30));
  translate(0, -100);
  fill(43, sonne[monat], 255, 200);
  ellipseMode(CENTER);
  float groesse = map(sonne[monat], min(sonne), max(sonne), kleinsterKreis, groessterKreis);
  ellipse(0, 0, groesse, groesse);
  translate(0, -groesse/2-4);
  rotate(radians(-monat*30));
  popMatrix();
}

drawMonat kann jetzt wie alle anderen Methoden (random(...), min(...), usw.) benutzt werden. Hier immer dann, wenn ein Monat gezeichnet werden soll.

void draw() {
  colorMode(RGB);
  background(0, 0, 128);
  translate(width/2, height/2);  // Fenstermitte
  colorMode(HSB);
  for (int i=0; i<sonne.length; i++) {
    drawMonat(i);
  }
  kleinsterKreis = map(mouseX, 0, width, 10, 80);
  groessterKreis = map(mouseX, 0, width, 80, 160);
}

Tabs

Für noch mehr Übersichtlichkeit die Zeichenmethode in einem eigenen Tab unterbringen. Dazu New Tab:

Unten einen sinnvollen Namen eingeben...

...und den Code der Zeichenmethode drawMonat hineinkopieren.

Insgesamt haben wir jetzt zwei Tabs. Einen mit setup und draw:

float[] sonne = { 44, 73, 111, 152, 196, 198, 209, 197, 149, 108, 53, 38 };
float kleinsterKreis = 25;
float groessterKreis = 100;

void setup() {
  size(600, 400);
}

void draw() {
  colorMode(RGB);
  background(0, 0, 128);
  translate(width/2, height/2);  // Fenstermitte
  colorMode(HSB);
  for (int i=0; i<sonne.length; i++) {
    drawMonat(i);
  }
  kleinsterKreis = map(mouseX, 0, width, 10, 80);
  groessterKreis = map(mouseX, 0, width, 80, 160);
}

Und einen, in dem steht, wie die Monate gezeichnet werden sollen.

void drawMonat(int monat) {
  noStroke();
  pushMatrix();
  rotate(radians(monat*30));
  translate(0, -100);
  fill(43, sonne[monat], 255, 200);
  ellipseMode(CENTER);
  float groesse = map(sonne[monat], min(sonne), max(sonne), kleinsterKreis, groessterKreis);
  ellipse(0, 0, groesse, groesse);
  translate(0, -groesse/2-4);
  rotate(radians(-monat*30));
  popMatrix();  
}