Generative visualizations

Generative visualizations for ›neues museum‹

Ausgangspunkt für die Illustrationen des Schwerpunktes der Ausgabe 14-4 der Zeitschrift ›neues museum‹ sind prototypische Museumsobjekte: die Mona Lisa als DAS Kunstwerk, die Venus von Willendorf als DAS historische Artefakt, Herbarbeleg, Schmetterling und das Skelett eines Triceratops stehen für die umfangreichen Sammlungen der Naturkundemuseen, die Glühbirne für technische Sammlungen, die Münze mit dem Abbild Hadrians für Geschichtsmuseen.
Für die Visualisierung wurden zunächst signifikante Punkte der Objektform algorithmisch ermittelt, indem Bildpunkte anhand ihrer Farb- und Kontrastwerte verglichen werden. Diese formgebenden Punkte dienen anschließend als Material für die visuelle Neukonstruktion des Objekts nach formalisierten Regeln. Durch zufallsgesteuerte Modifikation dieser Regeln entstehen Variationen und jede neuerliche  Ausführung erzeugt eine visuelle Instanz des Objekts. Bei Mark Taylor findet sich eine besondere Varianz: Das Abbild einer Münze generiert sich aus den Worten seines Textes. Die so erzeugten abstrakten und zwangsläufig unscharfen Blicke ergänzen jene der Autorinnen und Autoren in eine bestenfalls erahnbare Zukunft.

Visualisation #1: Venus von Willendorf

Die Unschärfe der Gestalt resultiert aus einer nach stochastischen Verfahrensweisen gesteuerten Translation der visuellen Einzelelemente. Diese folgt der Gleichung der Lévy-Verteilung. Das Objekt „Venus von Willendorf“ steht für frühgeschichtliche Sammlungen.

overview-dots-and-lines


// --------------------------------------------------
// Generative Illustration Script
// Language: Processing
// Author: Andreas Pirchner
// --------------------------------------------------

import processing.pdf.*;
import controlP5.*;

float[][] kernel = {{ -1, -1, -1}, 
                    { -1,  9, -1}, 
                    { -1, -1, -1}};

int nodesUsed = 1000; 
float valRed;
float valBlue;
float valGreen;
int nodeCount = 0; 
int maxConnect = 10;
float currentDistance = 0;
PImage img;
int nodeLoop = 1;

PVector[] nodes = new PVector[1000*1000];
int[] connections = new int[1000*1000];
color[] nodeColors = new color[1000*1000];
boolean [] visible = new boolean[1000*1000];

ControlP5 cp5;
CheckBox RecordPDF;

boolean recording = false;
boolean saveNow = false;
boolean justDrawn = false;
int saveCount = 0;

int dotAmount;
int lineLength;
int transp;
int ellipseRadius;

boolean startDraw = true;

void setup() { 
  size(2000, 2000);
  img = loadImage("venusblack2.jpg"); // Load the original image
  image(img,500,0);

  for (int i=0; i< nodes.length; i++) {
      nodes[i] = new PVector(0,0);
  } 
  for(int m=0; m < connections.length; m++){
     connections[m] = 0; 
     nodeColors[m] = color(0,0,0); 
     visible[m] = false;
  }

  cp5 = new ControlP5(this);
  cp5.addSlider("dotAmount")
     .setPosition(20,50)
     .setRange(50,45000)
     .setValue(2000)
     .setLabel("Number of Dots")
     .setColorLabel(color(0))
     ;

  cp5.addSlider("lineLength")
     .setPosition(20,70)
     .setRange(0,1000)
     .setValue(450)
     .setLabel("Length of lines")
     .setColorLabel(color(0))
     ;

  cp5.addSlider("transp")
     .setPosition(20,90)
     .setRange(0,255)
     .setValue(10)
     .setLabel("Transparency")
     .setColorLabel(color(0))
     ;

  cp5.addSlider("ellipseRadius")
     .setPosition(20,110)
     .setRange(1,20)
     .setValue(4)
     .setLabel("Ellipse Radius")
     .setColorLabel(color(0))
     ;

  cp5.addSlider("dotsRandom")
     .setPosition(20,130)
     .setRange(0,100)
     .setLabel("Random deviation of used Dots")
     .setColorLabel(color(0))
     ;

  cp5.addBang("bangSave")
       .setPosition(20, 150)
       .setSize(10, 10)
       .setLabel("Export PDF")
       .setColorLabel(color(0))
       ;

  img.loadPixels();

  for (int y = 1; y < img.height-1; y++) { // Skip top and bottom edges
    for (int x = 1; x < img.width-1; x++) { // Skip left and right edges
      float sum = 0; // Kernel sum for this pixel
      float sumRed = 0; // Kernel sum for this pixel
      float sumGreen = 0; // Kernel sum for this pixel
      float sumBlue = 0; // Kernel sum for this pixel

      for (int ky = -1; ky <= 1; ky++) {
        for (int kx = -1; kx <= 1; kx++) {
          // Calculate the adjacent pixel for this kernel point
          int pos = (y + ky)*img.width + (x + kx);
          // Image is grayscale, red/green/blue are identical
           valRed = red(img.pixels[pos]);
           valGreen = green(img.pixels[pos]);
           valBlue = blue(img.pixels[pos]);

          // Multiply adjacent pixels based on the kernel values
          sumRed += kernel[ky+1][kx+1] * valRed;
          sumGreen += kernel[ky+1][kx+1] * valGreen;
          sumBlue += kernel[ky+1][kx+1] * valBlue;
        }
      }

      // Kantenpunkte in Array ablegen: x-Position, y-Position, Farbe
      if (sumRed > 400 || sumBlue > 90 || sumGreen > 200 ) {

         nodeCount ++;
         nodes[nodeCount].x = x+500;
         nodes[nodeCount].y = y; 
         //if (valRed < 50){ valRed = 50;}
         nodeColors[nodeCount] = color(valRed,valGreen, valBlue,255);      

      }

    }
  }
  print(nodeCount);
  background(255);
}

void draw() {

  if(startDraw == true){
        resetCanvas();
        drawStructure();
        startDraw = false;     
  }
}

void drawStructure() {
  while (nodeLoop < dotAmount){
        int nextPoint = int(random(nodeCount));
        if (visible[nextPoint] != true){
            visible[nextPoint] = true;
            nodeLoop ++;
            strokeWeight(1);
            //stroke(red(nodeColors[nextPoint])*5, green(nodeColors[nextPoint])*5, blue(nodeColors[nextPoint])*5,5);
            stroke(red(nodeColors[nextPoint]), red(nodeColors[nextPoint])/3, red(nodeColors[nextPoint])*2/3,transp);
            //stroke(red(nodeColors[nextPoint]), 0, 0,10);
            line(nodes[nextPoint].x, nodes[nextPoint].y, nodes[nextPoint].x+random(-lineLength,lineLength), nodes[nextPoint].y+random(150,lineLength));
            strokeWeight(0);
            //fill(red(nodeColors[nextPoint])*4, green(nodeColors[nextPoint])*2, blue(nodeColors[nextPoint])*2, 150 );
            fill(red(nodeColors[nextPoint])*2, red(nodeColors[nextPoint])*1, red(nodeColors[nextPoint])*1, 200 );
            //fill(red(nodeColors[nextPoint])*2, 250, 250, 150 );
            ellipse(nodes[nextPoint].x+(levyDistribution(0,0.1)*20),nodes[nextPoint].y,random(2,ellipseRadius),random(2,ellipseRadius));
        }
    }
}

void resetCanvas(){
      background(255);
      nodeLoop = 0;
      print(dotAmount);
}

// Function for Record-Bang
void bangSave() {
  resetCanvas();
  beginRecord(PDF, "DAL_dots_"+dotAmount+".pdf");
  drawStructure(); 
  endRecord();
}

// Function for Amount-Slider
void dotAmount(int dA) {
  //background(255);
  dotAmount = dA;
  startDraw = true; 
}

void lineLength(int lL) {
  lineLength = lL;
  startDraw = true; 
}

void transp(int t) {
  transp = t;
  startDraw = true; 
}

void ellipseRadius(int eR) {
  ellipseRadius = eR;
  startDraw = true; 
}

// Function for Lévy-Distibution
float levyDistribution(float Wert1, float Wert2){
               float u = Wert1; // u = Lageparameter
               float c = Wert2; // s = Stauchung
               float x = 0;
               float y = 1;
               float fx = 0;
             while (y > fx) {
               x = random(10);
               y = random(1);
               fx = sqrt(c/(2*PI))*(exp(-c/(2*(x-u)))/pow((x-u), 3/2));
             }
             //point(x*100+200,500-y*500);
             return x;
        }

Visualisation #2: Rhizom

Die visuelle Gestalt der Objekt-Serie ist Effekt rhizomartiger Verbindungen der formgebenden Punkte. Die Anzahl der Knotenpunkte und der Grad ihrer Konnektivität untereinander werden stochastisch variiert und steuern somit als Parameter die Gestalt des visuellen Artefakts des Rhizoms. Das gewählte Objekt verweist auf technische Sammlungen, welche den Verlauf technischer Innovation und daraus resultierender industrieller Massenproduktion sowie deren Auswirkungen auf den Lebensalltag dokumentieren. Mit jeder Ausführung entsteht eine neue visuelle Instanz des Objekts, die Effekt der Konfiguration der Parameter ist.


// --------------------------------------------------
// Generative Illustration Script
// Language: Processing
// Author: Andreas Pirchner
// --------------------------------------------------

import processing.pdf.*;
import controlP5.*;

float[][] kernel = {{ -1, -1, -1}, 
                    { -1,  9, -1}, 
                    { -1, -1, -1}};

int nodesPossible = 1000*1000; 
float valRed;
float valBlue;
float valGreen;
int nodeCount = 0; 
int maxConnect = 10;
float currentDistance = 0;
PImage img;
int activePoints = 0;
int startPoint = int(random(nodeCount));
int nextPoint = 0;
float bestDistance = 100;

IntList nodesActive = new IntList();
PVector[] nodes = new PVector[nodesPossible];
int[] connections = new int[nodesPossible];
color[] nodeColors = new color[nodesPossible];
int nodeOne = 40;
int nodeTwo;
int newNode;

ControlP5 cp5;
CheckBox RecordPDF;

int dotAmount;
int conDist;
int dotCon;

boolean startDraw = true;

void setup() { 
  size(1000, 1000);
  img = loadImage("bulb.jpg"); // Load the original image

  for (int i=0; i< nodes.length; i++) {
      nodes[i] = new PVector(0,0);
      connections[i] = 0; 
     nodeColors[i] = color(0,0,0); 
  } 

  cp5 = new ControlP5(this);
  cp5.addSlider("dotAmount")
     .setPosition(20,50)
     .setRange(50,100000)
     .setSize(600,10)
     .setValue(1000)
     .setLabel("Number of Dots")
     .setColorLabel(color(0))
     ;

  cp5.addSlider("conDist")
     .setPosition(20,70)
     .setRange(0,500)
     .setSize(600,10)
     .setValue(50)
     .setLabel("Range of Connectivity")
     .setColorLabel(color(0))
     ;

  cp5.addSlider("dotCon")
     .setPosition(20,90)
     .setRange(0,30)
     .setSize(600,10)
     .setValue(12)
     .setLabel("Connections per Dot")
     .setColorLabel(color(0))
     ;

  cp5.addBang("bangSave")
       .setPosition(20, 110)
       .setSize(10, 10)
       .setLabel("Export PDF")
       .setColorLabel(color(0))
       ;

  img.loadPixels();

  for (int y = 1; y < img.height-1; y++) { // Skip top and bottom edges
    for (int x = 1; x < img.width-1; x++) { // Skip left and right edges
      float sum = 0; // Kernel sum for this pixel
      float sumRed = 0; // Kernel sum for this pixel
      float sumGreen = 0; // Kernel sum for this pixel
      float sumBlue = 0; // Kernel sum for this pixel

      for (int ky = -1; ky <= 1; ky++) {
        for (int kx = -1; kx <= 1; kx++) {           int pos = (y + ky)*img.width + (x + kx);           // Image is grayscale, red/green/blue are identical            valRed = red(img.pixels[pos]);            valGreen = green(img.pixels[pos]);            valBlue = blue(img.pixels[pos]);                       sumRed += kernel[ky+1][kx+1] * valRed;           sumGreen += kernel[ky+1][kx+1] * valGreen;           sumBlue += kernel[ky+1][kx+1] * valBlue;         }       }              // Kantenpunkte in Array ablegen: x-Position, y-Position, Farbe       if (sumRed > 400 || sumBlue > 90 || sumGreen > 200 ) {       
         nodeCount ++;
         nodes[nodeCount].x = x;
         nodes[nodeCount].y = y; 
         nodeColors[nodeCount] = color(valRed,valGreen, valBlue,255);              
      }      
    }
  }
  background(255);
}

void draw() {
  if(startDraw == true){
        resetCanvas();
        drawStructure();
        startDraw = false;     
  }
}

void drawStructure() {
  for (int h=10; h < nodesActive.size(); h++){
            nodeOne = nodesActive.get(h);
            for (int j=h+1; j < nodesActive.size(); j++){
              newNode = nodesActive.get(j);
              float abstand = nodes[nodeOne].dist(nodes[newNode]);
              if (abstand < conDist && connections[h] < dotCon && connections[j] < dotCon && nodes[nodeOne].x != 0 && nodes[newNode].x != 0){     
                connections[h] ++;
                connections[j] ++;
                strokeWeight(1);
                stroke(red(nodeColors[nodeOne])*2, red(nodeColors[nodeOne])*1, red(nodeColors[nodeOne])*1, 100 );
                line(nodes[nodeOne].x, nodes[nodeOne].y, nodes[newNode].x, nodes[newNode].y);
              }
            }         
        }
        print("done painting");  
}

void resetCanvas(){
  background(255);
  // Clear List of Connections between Nodes
      for (int h=0; h< nodesActive.size(); h++){  
        connections[h] = 0;
      }
  // Clear List of active Nodes and populate it again
      nodesActive.clear();
      for (int l=0; l< dotAmount; l++){           nodesActive.append(int(random(nodes.length)));       }       print(dotAmount); } // Function for Record-Bang void bangSave() {   resetCanvas();   beginRecord(PDF, "RHIZOM_dots_"+dotAmount+"_range_"+conDist+"_links_"+dotCon+".pdf");   drawStructure();    endRecord(); } // Functions for Sliders void dotAmount(int dA) {   dotAmount = dA;   startDraw = true;  } void conDist(int cD) {   conDist = cD;   startDraw = true; } void dotCon(int dC) {   dotCon = dC;   startDraw = true; } // Function for Lévy-Distibution float levyDistribution(float Wert1, float Wert2){                float u = Wert1; // u = Lageparameter                float c = Wert2; // s = Stauchung                float x = 0;                float y = 1;                float fx = 0;              while (y > fx) {
               x = random(10);
               y = random(1);
               fx = sqrt(c/(2*PI))*(exp(-c/(2*(x-u)))/pow((x-u), 3/2));
               //fx = 2 * exp(-2*x); Exponential
             }
             return x;
        }

Visualisation #3: Celular

Die Visualisierung verwendet die Metapher der Zelle als Grundelement um akstrakte Versionen eines Herbarbelegs hervorzubringen. Indem die Parameter Zellgröße und Zellaufbau durch stochastische Prozesse variiert werden entsteht eine rhythmisierte Struktur. Das Prinzip der zufallsgesteuerten Variation wird bei dieser Illustration insbesondere auf die Gestalt der einzelnen Zellen angewandt, die sich in der Anzahl ihrer Bestandteile und deren Anordnung alle voneinander unterscheiden. Das gewählte Objekt steht für naturhistorische Sammlungen, es handelt sich um den Herbarbeleg des Zucker-Ahorns.



// --------------------------------------------------
// Generative Illustration Script
// Language: Processing
// Author: Andreas Pirchner
//
// You`ll need an image called "Herbar3.jpg"
// --------------------------------------------------

import processing.pdf.*;
import controlP5.*;

float[][] kernel = {{ -1, -1, -1}, 
                    { -1,  9, -1}, 
                    { -1, -1, -1}};

int nodesUsed = 1000; 
float valRed;
float valBlue;
float valGreen;
int nodeCount = 0; 
int maxConnect = 10;
float currentDistance = 0;
PImage img;
int nodeLoop = 1;
int activePoints = 0;
int startPoint = int(random(nodeCount));
int nextPoint = 0;
float bestDistance = 100;

PVector[] nodes = new PVector[1000*1000];
IntList nodesActive = new IntList();

int[] connections = new int[1000*1000];
int[] distance2current = new int[1000*1000];
color[] nodeColors = new color[1000*1000];
boolean [] visible = new boolean[1000*1000];
boolean [] used = new boolean[1000*1000];
int nodeOne = 40;
int nodeTwo;
int newNode;

ControlP5 cp5;
CheckBox RecordPDF;

boolean recording = false;
boolean saveNow = false;
boolean justDrawn = false;
boolean pointfound = false;
int saveCount = 0;

boolean startDraw = true;

int dotAmount;
int cellSize;
int cellTrans;

ArrayList  cells = new ArrayList  ();

void setup() { 
  size(1000, 1000);
  img = loadImage("Herbar3.jpg"); // Load the original image

  for (int i=0; i< nodes.length; i++) {
      nodes[i] = new PVector(0,0);
  } 
  for(int m=0; m < connections.length; m++){
     connections[m] = 0; 
     distance2current[m] = 0;
     nodeColors[m] = color(0,0,0); 
     visible[m] = false;
     used[m] = false;
  }

  cp5 = new ControlP5(this);
  cp5.addSlider("dotAmount")
     .setPosition(20,50)
     .setRange(50,1000000)
     .setSize(600,10)
     .setValue(500)
     .setLabel("Number of Dots")
     .setColorLabel(color(0))
     ;

  cp5.addSlider("cellSize")
     .setPosition(20,70)
     .setRange(0,500)
     .setSize(600,10)
     .setValue(50)
     .setLabel("Cell Size")
     .setColorLabel(color(0))
     ;

  cp5.addSlider("cellTrans")
     .setPosition(20,90)
     .setRange(0,255)
     .setSize(600,10)
     .setValue(10)
     .setLabel("Transparency")
     .setColorLabel(color(0))
     ;

  cp5.addBang("bangSave")
       .setPosition(20, 110)
       .setSize(10, 10)
       .setLabel("Export PDF")
       .setColorLabel(color(0))
       ;

  img.loadPixels();

  for (int y = 1; y < img.height-1; y++) { // Skip top and bottom edges
    for (int x = 1; x < img.width-1; x++) { // Skip left and right edges
      float sum = 0; // Kernel sum for this pixel
      float sumRed = 0; // Kernel sum for this pixel
      float sumGreen = 0; // Kernel sum for this pixel
      float sumBlue = 0; // Kernel sum for this pixel

      for (int ky = -1; ky <= 1; ky++) {
        for (int kx = -1; kx <= 1; kx++) {           // Calculate the adjacent pixel for this kernel point           int pos = (y + ky)*img.width + (x + kx);           // Image is grayscale, red/green/blue are identical            valRed = red(img.pixels[pos]);            valGreen = green(img.pixels[pos]);            valBlue = blue(img.pixels[pos]);                       // Multiply adjacent pixels based on the kernel values           sumRed += kernel[ky+1][kx+1] * valRed;           sumGreen += kernel[ky+1][kx+1] * valGreen;           sumBlue += kernel[ky+1][kx+1] * valBlue;         }       }              // Kantenpunkte in Array ablegen: x-Position, y-Position, Farbe       if (sumRed > 400 || sumBlue > 90 || sumGreen > 200 ) {       
         nodeCount ++;
         nodes[nodeCount].x = x;
         nodes[nodeCount].y = y; 
         nodeColors[nodeCount] = color(valRed,valGreen, valBlue,255);              
      }      
    }
  }
  print(nodeCount);
  background(255);

}

void draw() {

  // Start recording PDF
  if (saveNow == true && recording ==true) beginRecord(PDF, "mona"+saveCount+".pdf");
  //image(img, 0, 0); // Displays the image from point (0,0) 
  // Ellipsen nach Daten aus dem Array zeichnen
      if(startDraw == true){
          resetCanvas();
          drawStructure();
          startDraw = false;    
      }
  // Stop recording PDF
  if (saveNow == true && justDrawn == true) {
    saveNow = false;
    recording = false;
    endRecord();
    saveCount ++;
  }
}

void drawStructure() {
      while (activePoints < nodesActive.size()){
                    while (pointfound != true){
                       nextPoint = int(random(0, nodesActive.size()));   
                       if (used[nextPoint] != true){
                           pointfound = true;
                       }
                    }
                    float x = nodes[nodesActive.get(nextPoint)].x;
                    float y = nodes[nodesActive.get(nextPoint)].y;               
                    boolean overlap = false;
                    for (Cell c : cells) {
                      if (dist(x, y, c.x, c.y) <= c.radius - 2) {
                        overlap = true;
                        break;
                      }
                    }
                    if (!overlap) {
                      cells.add(new Cell(x, y, nodeColors[nodesActive.get(nextPoint)])); 
                      used[nextPoint] = true;

                    }      
                    pointfound = false;
                    activePoints ++;               
       }

       for (Cell c : cells) {
                //c.update();
                c.drawCell();
       }
       print("done painting");  
}

void resetCanvas(){
    background(100);
    nodeLoop = 1;
    activePoints=0;
    for (int n=0; n< nodeCount; n++){
        visible[n] = false;
    }
    saveNow = true;
    nodesUsed = dotAmount;
    startDraw = true;    
    nodesActive.clear();
    cells.clear();
    for (int l=0; l<nodesUsed; l++){         nodesActive.append(int(random(nodes.length)));     }     print(nodesActive.size()+",       "); } // Function for Record-Bang void bangSave() {   resetCanvas();   beginRecord(PDF, "2CELLS_dots_"+dotAmount+"_range_"+cellSize+"_links_"+cellTrans+".pdf");   drawStructure();    endRecord(); } // Function for Amount-Slider void dotAmount(int dA) {   dotAmount = dA;   startDraw = true;  } void cellSize(int cS) {   cellSize = cS;   startDraw = true; } void cellTrans(int cT) {   cellTrans = cT;   startDraw = true; } // Function for Lévy-Distibution float levyDistribution(float Wert1, float Wert2){                float u = Wert1; // u = Lageparameter                float c = Wert2; // s = Stauchung                float x = 0;                float y = 1;                float fx = 0;              while (y > fx) {
               x = random(5);
               y = random(1);
               fx = sqrt(c/(2*PI))*(exp(-c/(2*(x-u)))/pow((x-u), 3/2));
               //fx = 2 * exp(-2*x); Exponential
             }
             //point(x*100+200,500-y*500);
             return x;
        }

Visualisation #4: Ancient Coin

Die Illustrationstechnik thematisiert den (sprachlichen) Diskurs, welcher das Bild formt, das wir von Museumsobjekten haben. Die Worte des Artikels von Mark Taylor liefern die Bausteine für die generative Visualisierung. Sie werden in ihrer ursprünglich linearen semantischen Folge gestört, zufallsgesteuert ausgewählt und durch den veränderten Kontext mit neuer Bedeutung belegt um die visuelle Struktur der Münze zu bilden.  Die Unschärfe entsteht durch stochastisch gesteuerte Abweichungen bei der Positionierung der Wortbausteine. Bei dem Objekt handelt sich um eine Silbermünze mit dem Abbild Hadrians aus dem Jahr 119 n.C.


// --------------------------------------------------
// Generative Illustration Script
// Language: Processing
// Author: Andreas Pirchner
//
// You`ll need an image called "Muenze6.jpg"
// --------------------------------------------------

import processing.pdf.*;
import controlP5.*;

float[][] kernel = {{ -1, -1, -1}, 
                    { -1,  9, -1}, 
                    { -1, -1, -1}};

int nodesPossible = 1000*1000; 
float valRed;
float valBlue;
float valGreen;
int nodeCount = 0; 
int maxConnect = 10;
float currentDistance = 0;
PImage img;
//int nodeLoop = 1;
int activePoints = 0;
int startPoint = int(random(nodeCount));
int nextPoint = 0;
float bestDistance = 100;

IntList nodesActive = new IntList();
PVector[] nodes = new PVector[nodesPossible];
int[] connections = new int[nodesPossible];
color[] nodeColors = new color[nodesPossible];
int nodeOne = 40;
int nodeTwo;
int newNode;

ControlP5 cp5;
CheckBox RecordPDF;

int dotAmount;
int conDist;
int dotCon;

boolean startDraw = true;

String[] wordList = new String[nodesPossible];
//String[] essay = new String[nodesPossible];
//String[] essay = new String[nodesPossible];

PFont genFont;
void setup() { 
  size(3000, 1600);
  img = loadImage("Muenze6.jpg"); // Load the original image
   genFont= loadFont ("GalaxiePolaris-Book-48.vlw");
   textFont (genFont);

   String[] essay = loadStrings("textSource.txt");
   String essayJoined = join(essay, " ");
   wordList = split(essayJoined, " ");
   print(wordList[int(random(wordList.length))]);
   print(wordList.length);
   //for (int i=0; i< essey.length; i++) {

   //}

   //String[] splitWords = split(essay[1], " ");

  for (int i=0; i< nodes.length; i++) {
      nodes[i] = new PVector(0,0);
      connections[i] = 0; 
     nodeColors[i] = color(0,0,0); 
  } 

  cp5 = new ControlP5(this);
  cp5.addSlider("dotAmount")
     .setPosition(20,50)
     .setRange(50,1000000)
     .setSize(600,10)
     .setValue(10000)
     .setLabel("Number of Dots")
     .setColorLabel(color(0))
     ;

  cp5.addSlider("conDist")
     .setPosition(20,70)
     .setRange(1,100)
     .setSize(600,10)
     .setValue(20)
     .setLabel("Size")
     .setColorLabel(color(0))
     ;

  cp5.addSlider("dotCon")
     .setPosition(20,90)
     .setRange(0,30)
     .setSize(600,10)
     .setValue(12)
     .setLabel("Connections per Dot")
     .setColorLabel(color(0))
     ;

  cp5.addBang("bangSave")
       .setPosition(20, 110)
       .setSize(10, 10)
       .setLabel("Export PDF")
       .setColorLabel(color(0))
       ;

  img.loadPixels();

  for (int y = 1; y < img.height-1; y++) { // Skip top and bottom edges
    for (int x = 1; x < img.width-1; x++) { // Skip left and right edges
      float sum = 0; // Kernel sum for this pixel
      float sumRed = 0; // Kernel sum for this pixel
      float sumGreen = 0; // Kernel sum for this pixel
      float sumBlue = 0; // Kernel sum for this pixel

      for (int ky = -1; ky <= 1; ky++) {
        for (int kx = -1; kx <= 1; kx++) {           // Calculate the adjacent pixel for this kernel point           int pos = (y + ky)*img.width + (x + kx);           // Image is grayscale, red/green/blue are identical            valRed = red(img.pixels[pos]);            valGreen = green(img.pixels[pos]);            valBlue = blue(img.pixels[pos]);                       // Multiply adjacent pixels based on the kernel values           sumRed += kernel[ky+1][kx+1] * valRed;           sumGreen += kernel[ky+1][kx+1] * valGreen;           sumBlue += kernel[ky+1][kx+1] * valBlue;         }       }              // Kantenpunkte in Array ablegen: x-Position, y-Position, Farbe       if (sumRed > 200 || sumBlue > 60 || sumGreen > 150 ) {       
         nodeCount ++;
         nodes[nodeCount].x = x;
         nodes[nodeCount].y = y; 
         nodeColors[nodeCount] = color(valRed,valGreen, valBlue,255);              
      }      
    }
  }
  background(255);
}

void draw() {
  if(startDraw == true){
        resetCanvas();
        drawStructure();
        startDraw = false;     
  }
}

void drawStructure() {
  for (int h=10; h < nodesActive.size(); h++){                 nodeOne = nodesActive.get(h);                 if (nodes[nodeOne].x != 0 && nodes[nodeOne].y != 0){                     //strokeWeight(30/abstand);                     strokeWeight(1);                     //stroke(red(nodeColors[nodeOne])*2, red(nodeColors[nodeOne])*1, red(nodeColors[nodeOne])*1, 3000/abstand );                     stroke(red(nodeColors[nodeOne])*2, red(nodeColors[nodeOne])*1, red(nodeColors[nodeOne])*1, 100 );                     //line(nodes[nodeOne].x, nodes[nodeOne].y, nodes[newNode].x, nodes[newNode].y);                     textSize (random(1,conDist));                     //textSize(levyDistribution(0,0.01)*2);                     fill (red(nodeColors[nodeOne])*2, red(nodeColors[nodeOne])*1, red(nodeColors[nodeOne])*1, 200);                     pushMatrix();                     //translate(nodes[nodeOne].x+300+random(-60,60), nodes[nodeOne].y+300+random(-60,60));                     float distX = (levyDistribution(0,0.001)*20);                     float distY = (levyDistribution(0,0.001)*20);                     float indi = random(-1,1);                     if (indi>= 0){
                      translate(nodes[nodeOne].x+300+distX, nodes[nodeOne].y+300+distY);
                    }else{
                      translate(nodes[nodeOne].x+300-distX, nodes[nodeOne].y+300-distY);
                    }
                    rotate(radians(random(-90,90)));  
                    int nextIndex = int(random(wordList.length-2));
                    text(wordList[nextIndex]/* + " " + wordList[nextIndex+1] + " " + wordList[nextIndex+2]*/,0,0);      
                    //text(wordList[nextIndex],0,0);   
                    //text ("" + char( int( random(33,126) ) ), 0, 0);
                    popMatrix();
                }
        }
        print("done painting");  
}

void resetCanvas(){
  background(255);
  // Clear List of Connections between Nodes
      for (int h=0; h< nodesActive.size(); h++){  
        connections[h] = 0;
      }
  // Clear List of active Nodes and populate it again
      nodesActive.clear();
      for (int l=0; l< dotAmount; l++){           nodesActive.append(int(random(nodes.length)));       }       print(dotAmount); } // Function for Record-Bang void bangSave() {   resetCanvas();   beginRecord(PDF, "WORDS_dots_"+dotAmount+"_range_"+conDist+".pdf");   drawStructure();    endRecord(); } // Functions for Sliders void dotAmount(int dA) {   //background(255);   dotAmount = dA;   startDraw = true;  } void conDist(int cD) {   conDist = cD;   startDraw = true; } void dotCon(int dC) {   dotCon = dC;   startDraw = true; } // Function for Lévy-Distibution float levyDistribution(float Wert1, float Wert2){                float u = Wert1; // u = Lageparameter                float c = Wert2; // s = Stauchung                float x = 0;                float y = 1;                float fx = 0;              while (y > fx) {
               x = random(10);
               y = random(1);
               fx = sqrt(c/(2*PI))*(exp(-c/(2*(x-u)))/pow((x-u), 3/2));
               //fx = 2 * exp(-2*x); Exponential
             }
             //point(x*100+200,500-y*500);
             return x;
        }

Visualisation #5: Random Walks, Triceratops

Das Konzept der Random Walks entstammt Einsteins Beiträgen zur kinetischen Gastheorie, wurde von Iannis Xenakis für die Avantgarde Musik adaptiert und dient nun als Modell für die Regeln zur Generierung obenstehender Illustration. Konkret fungieren die formgebende Punkte des Objekts als Zielpunkte eines Random Walk Prozesses, bei dem sich eine virtuelle Linie zufallsgesteuert schrittweise von Punkt zu Punkt bewegt, bis alle Punkte erfasst sind. Je näher zwei aufeinanderfolgend angelaufene Punkte beieinander liegen, desto dicker und gesättigter erscheint die verbindende Linie. Bei jeder Ausführung des Zeichenvorgangs entsteht eine neue, visuell eigenständige Instanz des Objekts. Das Skelett des Triceratops verweist auf naturhistorische Sammlungen.



// --------------------------------------------------
// Generative Illustration Script
// Language: Processing
// Author: Andreas Pirchner
//
// You`ll need an image called "Triceratops2.jpg"
// --------------------------------------------------

import processing.pdf.*;
import controlP5.*;

float[][] kernel = {{ -1, -1, -1}, 
                    { -1,  9, -1}, 
                    { -1, -1, -1}};

int nodesUsed = 1000; 
float valRed;
float valBlue;
float valGreen;
int nodeCount = 0; 
int maxConnect = 10;
float currentDistance = 0;
PImage img;
int nodeLoop = 1;
int activePoints = 0;
int startPoint = int(random(nodeCount));
int nextPoint = 0;
float bestDistance = 100;

PVector[] nodes = new PVector[1000*1000];
IntList nodesActive = new IntList();

int[] connections = new int[1000*1000];
int[] distance2current = new int[1000*1000];
color[] nodeColors = new color[1000*1000];
boolean [] visible = new boolean[1000*1000];
boolean [] used = new boolean[1000*1000];
boolean startDraw = true;
int nodeOne = 40;
int nodeTwo;
int newNode;

ControlP5 cp5;
CheckBox RecordPDF;

boolean recording = false;
boolean saveNow = false;
boolean justDrawn = false;
int saveCount = 0;

int dotAmount;
int conDist;
int dotCon;

void setup() { 
  size(1000, 1000);
  img = loadImage("Triceratops2.jpg"); // Load the original image

  for (int i=0; i< nodes.length; i++) {
      nodes[i] = new PVector(0,0);
  } 
  for(int m=0; m < connections.length; m++){
     connections[m] = 0; 
     distance2current[m] = 0;
     nodeColors[m] = color(0,0,0); 
     visible[m] = false;
     used[m] = false;
  }

  cp5 = new ControlP5(this);
  cp5.addSlider("dotAmount")
     .setPosition(20,50)
     .setRange(50,100000)
     .setLabel("Number of Dots")
     .setColorLabel(color(0))
     ;

  cp5.addSlider("lineLength")
     .setPosition(20,70)
     .setRange(0,1000)
     .setLabel("Length of lines")
     .setColorLabel(color(0))
     ;

  cp5.addSlider("dotsRandom")
     .setPosition(20,90)
     .setRange(0,100)
     .setLabel("Random deviation of used Dots")
     .setColorLabel(color(0))
     ;

  cp5.addBang("bangSave")
       .setPosition(20, 110)
       .setSize(10, 10)
       .setLabel("Export PDF")
       .setColorLabel(color(0))
       ;

  img.loadPixels();

  for (int y = 1; y < img.height-1; y++) { // Skip top and bottom edges
    for (int x = 1; x < img.width-1; x++) { // Skip left and right edges
      float sum = 0; // Kernel sum for this pixel
      float sumRed = 0; // Kernel sum for this pixel
      float sumGreen = 0; // Kernel sum for this pixel
      float sumBlue = 0; // Kernel sum for this pixel

      for (int ky = -1; ky <= 1; ky++) {
        for (int kx = -1; kx <= 1; kx++) {           // Calculate the adjacent pixel for this kernel point           int pos = (y + ky)*img.width + (x + kx);           // Image is grayscale, red/green/blue are identical            valRed = red(img.pixels[pos]);            valGreen = green(img.pixels[pos]);            valBlue = blue(img.pixels[pos]);                       // Multiply adjacent pixels based on the kernel values           sumRed += kernel[ky+1][kx+1] * valRed;           sumGreen += kernel[ky+1][kx+1] * valGreen;           sumBlue += kernel[ky+1][kx+1] * valBlue;         }       }              // Kantenpunkte in Array ablegen: x-Position, y-Position, Farbe       if (sumRed > 400 || sumBlue > 90 || sumGreen > 200 ) {       
         nodeCount ++;
         nodes[nodeCount].x = x;
         nodes[nodeCount].y = y; 
         nodeColors[nodeCount] = color(valRed,valGreen, valBlue,255);              
      }      
    }
  }
  print(nodeCount);
  background(255);

}

void draw() {

  // Start recording PDF
  if (saveNow == true && recording ==true) beginRecord(PDF, "mona"+saveCount+".pdf");
  //image(img, 0, 0); // Displays the image from point (0,0) 
  // Linien und Ellipsen nach Daten aus dem Array zeichnen

    if(startDraw == true){
        resetCanvas();
        drawStructure();
        startDraw = false;     
    }

  // Stop recording PDF
  if (saveNow == true && justDrawn == true) {
    saveNow = false;
    recording = false;
    endRecord();
    saveCount ++;
  }

}

void drawStructure() {
  while (activePoints < nodesUsed){

        for (int h=0; h < nodesActive.size(); h++){
            nodeTwo = nodesActive.get(h);
            float currentDistance = nodes[nodeOne].dist(nodes[nodeTwo]);            
            if (currentDistance < bestDistance && currentDistance > 100 && used[nodeTwo] != true){
                bestDistance = currentDistance;
                newNode = nodeTwo;                 
            }            
        }

        used[newNode] = true;
        strokeWeight(100/bestDistance);
        stroke(red(nodeColors[nodeOne])*2, red(nodeColors[nodeOne])*1, red(nodeColors[nodeOne])*1, 100 );
        line(nodes[nodeOne].x, nodes[nodeOne].y, nodes[newNode].x, nodes[newNode].y);

        nodeOne = newNode;
        bestDistance = 1000;
        activePoints ++;    
    }
}

void resetCanvas(){
  background(255);
  nodeLoop = 1;
  activePoints=0;
  for (int n=0; n< nodeCount; n++){
      visible[n] = false;
  }
  //saveNow = true;
  //nodesUsed = dotAmount;
  //print(dotAmount);
  nodesActive.clear();
  for (int l=0; l<nodesUsed; l++){       nodesActive.append(int(random(nodes.length)));   }    } // Function for Record-Bang void bangSave() {   resetCanvas();   beginRecord(PDF, "RANDOMWALK_dots_"+dotAmount+".pdf");   drawStructure();    endRecord(); } // Function for Amount-Slider void dotAmount(int dA) {   dotAmount = dA;   nodesUsed = dotAmount;   startDraw = true; } // Function for Lévy-Distibution float levyDistribution(float Wert1, float Wert2){                float u = Wert1; // u = Lageparameter                float c = Wert2; // s = Stauchung                float x = 0;                float y = 1;                float fx = 0;              while (y > fx) {
               x = random(10);
               y = random(1);
               fx = sqrt(c/(2*PI))*(exp(-c/(2*(x-u)))/pow((x-u), 3/2));
               //fx = 2 * exp(-2*x); Exponential
             }
             //point(x*100+200,500-y*500);
             return x;
        }

Visualisation #6: Butterflies

Die Visualisierung der Schmetterlinge nimmt verschiedene Aspekte des Präparierens und Sammelns von Insekten auf. Einerseits wurden Schmetterlingssammlungen nicht nur nach wissenschaftlichem, sondern häufig auch nach ästhetischen Kriterien angelegt. In dieser Hinsicht stellen sie einen ähnlichen Hybrid dar, wie es die ästhetisierte Formelhaftigkeit der generativen Visualisierung tut.
Andererseits ist die Systematik der Schmetterlinge eine, die sich stark in visueller Diversität manifestiert. Dies stellt einen inspirierenden Ausgangspunkt für die Übertragung von genetischem in elektronischen Code dar.



// --------------------------------------------------
// Generative Illustration Script
// Language: Processing
// Author: Andreas Pirchner
//
// You`ll need an image called "Herbar3.jpg"
// --------------------------------------------------

import processing.pdf.*;
import controlP5.*;

float[][] kernel = {{ -1, -1, -1}, 
                    { -1,  9, -1}, 
                    { -1, -1, -1}};

int nodesUsed = 1000; 
float valRed;
float valBlue;
float valGreen;
int nodeCount = 0; 
int maxConnect = 10;
float currentDistance = 0;
PImage img;
int nodeLoop = 1;

PVector[] nodes = new PVector[1000*1000];
int[] connections = new int[1000*1000];
color[] nodeColors = new color[1000*1000];
boolean [] visible = new boolean[1000*1000];

ControlP5 cp5;
CheckBox RecordPDF;

boolean recording = false;
boolean saveNow = false;
boolean justDrawn = false;
int saveCount = 0;

int dotAmount;
int lineLength;
int dotsRandom;
int lineWeight;

boolean startDraw = true;

void setup() { 
  size(2000, 2000);
  img = loadImage("butter1.jpg"); // Load the original image

  for (int i=0; i< nodes.length; i++) {
      nodes[i] = new PVector(0,0);
      //theta[i] = random(PI);
      //phi[i] = random(2*PI);
     // velocities[i] = new PVector(random(-speed,speed), random(-speed,speed));
  } 
  for(int m=0; m < connections.length; m++){
     connections[m] = 0; 
     nodeColors[m] = color(0,0,0); 
     visible[m] = false;
  }

  cp5 = new ControlP5(this);
  cp5.addSlider("dotAmount")
     .setPosition(20,50)
     .setRange(50,100000)
     .setValue(20000)
     .setLabel("Number of Dots")
     .setColorLabel(color(0))
     ;

  cp5.addSlider("lineLength")
     .setPosition(20,70)
     .setRange(1,200)
     .setValue(450)
     .setLabel("Length of lines")
     .setColorLabel(color(0))
     ;

  cp5.addSlider("dotsRandom")
     .setPosition(20,90)
     .setRange(0,100)
     .setLabel("Random deviation of used Dots")
     .setColorLabel(color(0))
     ;

   cp5.addSlider("lineWeight")
     .setPosition(20,110)
     .setRange(1,10)
     .setValue(4)
     .setLabel("Weight of lines")
     .setColorLabel(color(0))
     ;

  cp5.addBang("bangSave")
       .setPosition(20, 150)
       .setSize(10, 10)
       .setLabel("Export PDF")
       .setColorLabel(color(0))
       ;

  img.loadPixels();

  for (int y = 1; y < img.height-1; y++) { // Skip top and bottom edges
    for (int x = 1; x < img.width-1; x++) { // Skip left and right edges
      float sum = 0; // Kernel sum for this pixel
      float sumRed = 0; // Kernel sum for this pixel
      float sumGreen = 0; // Kernel sum for this pixel
      float sumBlue = 0; // Kernel sum for this pixel

      for (int ky = -1; ky <= 1; ky++) {
        for (int kx = -1; kx <= 1; kx++) {           // Calculate the adjacent pixel for this kernel point           int pos = (y + ky)*img.width + (x + kx);           // Image is grayscale, red/green/blue are identical            valRed = red(img.pixels[pos]);            valGreen = green(img.pixels[pos]);            valBlue = blue(img.pixels[pos]);                       // Multiply adjacent pixels based on the kernel values           sumRed += kernel[ky+1][kx+1] * valRed;           sumGreen += kernel[ky+1][kx+1] * valGreen;           sumBlue += kernel[ky+1][kx+1] * valBlue;         }       }              // Kantenpunkte in Array ablegen: x-Position, y-Position, Farbe       if (sumRed > 400 || sumBlue > 90 || sumGreen > 200 ) {

         nodeCount ++;
         nodes[nodeCount].x = x+500;
         nodes[nodeCount].y = y+500; 
         //if (valRed < 50){ valRed = 50;}
         nodeColors[nodeCount] = color(valRed,valGreen, valBlue,255);      

      }

    }
  }
  print(nodeCount);
  background(255);
}

void draw() {
  if(startDraw == true){
        resetCanvas();
        drawStructure();
        startDraw = false;     
  }

  //image(img, 0, 0); // Displays the image from point (0,0) 

  // Linien und Ellipsen nach Daten aus dem Array zeichnen

}

void drawStructure() {
    while (nodeLoop < dotAmount){         int nextPoint = int(random(nodeCount));         if (visible[nextPoint] != true){             visible[nextPoint] = true;             nodeLoop ++;                          //stroke(red(nodeColors[nextPoint])*5, green(nodeColors[nextPoint])*5, blue(nodeColors[nextPoint])*5,5);             //stroke(red(nodeColors[nextPoint]), red(nodeColors[nextPoint])/3, red(nodeColors[nextPoint])*2/3,50);             stroke(red(nodeColors[nextPoint])*2, red(nodeColors[nextPoint])*1, red(nodeColors[nextPoint])*1, 100 );             //stroke(red(nodeColors[nextPoint]), 0, 0,10);             //float heightLine = random (1,lineLength);             float heightLine = (levyDistribution(0,0.0001)*lineLength);             //float abweichung = (levyDistribution(0,0.1)*20);             float abweichung = 0;                         strokeWeight(random(1,lineWeight));             line(nodes[nextPoint].x+abweichung, nodes[nextPoint].y, nodes[nextPoint].x+abweichung, nodes[nextPoint].y+heightLine);             line(nodes[nextPoint].x+abweichung, nodes[nextPoint].y, nodes[nextPoint].x+abweichung, nodes[nextPoint].y-heightLine);             strokeWeight(random(2,lineWeight));             line(nodes[nextPoint].x+abweichung, nodes[nextPoint].y, nodes[nextPoint].x+abweichung+heightLine, nodes[nextPoint].y);             line(nodes[nextPoint].x+abweichung, nodes[nextPoint].y, nodes[nextPoint].x+abweichung-heightLine, nodes[nextPoint].y);                          //stroke(255,255,255,255);                          strokeWeight(random(2,lineWeight));             line(nodes[nextPoint].x+abweichung, nodes[nextPoint].y, nodes[nextPoint].x+abweichung, nodes[nextPoint].y+heightLine/20);             line(nodes[nextPoint].x+abweichung, nodes[nextPoint].y, nodes[nextPoint].x+abweichung, nodes[nextPoint].y-heightLine/20);             strokeWeight(random(2,lineWeight));             line(nodes[nextPoint].x+abweichung, nodes[nextPoint].y, nodes[nextPoint].x+abweichung+heightLine/20, nodes[nextPoint].y);             line(nodes[nextPoint].x+abweichung, nodes[nextPoint].y, nodes[nextPoint].x+abweichung-heightLine/20, nodes[nextPoint].y);             strokeWeight(0);             //fill(red(nodeColors[nextPoint])*4, green(nodeColors[nextPoint])*2, blue(nodeColors[nextPoint])*2, 150 );             //fill(red(nodeColors[nextPoint])*2, red(nodeColors[nextPoint])*1, red(nodeColors[nextPoint])*1, 200 );             //fill(red(nodeColors[nextPoint])*2, 250, 250, 150 );             fill(red(nodeColors[nextPoint])*2, red(nodeColors[nextPoint])*1, red(nodeColors[nextPoint])*1, 100 );             ellipse(nodes[nextPoint].x,nodes[nextPoint].y,random(4,18),random(4,18) );         }         //justDrawn = true;     } } void resetCanvas(){       background(255);       nodeLoop = 0;       print(dotAmount); } // Function for Record-Bang void bangSave() {   resetCanvas();   beginRecord(PDF, "VERTICALS_dots_"+dotAmount+".pdf");   drawStructure();    endRecord(); } // Function for Amount-Slider void dotAmount(int dA) {   dotAmount = dA;   startDraw = true;  } void lineLength(int lL) {   lineLength = lL;   startDraw = true;  } void lineWeight(int lW) {   lineWeight = lW;   startDraw = true;  } // Function for Lévy-Distibution float levyDistribution(float Wert1, float Wert2){                float u = Wert1; // u = Lageparameter                float c = Wert2; // s = Stauchung                float x = 0;                float y = 1;                float fx = 0;              while (y > fx) {
               x = random(10);
               y = random(1);
               fx = sqrt(c/(2*PI))*(exp(-c/(2*(x-u)))/pow((x-u), 3/2));
             }
             return x;
        }