We have been messing around with the idea of making a physical structure interactive. In this case it will be through lighting. The physical part of the installation will most likely be made of parts coming out of rhino + grasshopper, but the UI for how it will behave will most likely be in processing. During the design phase it is important for us to not only see various iterations of the structure, but also how it will behave. We came up with a workflow that allows us to quickly export the three dimensional points from the cells created in grasshopper so they can easily be imported into the processing sketch.

Here are the source files for the grasshopper definition and the above processing sketch: cell

In the rest of this post you can find a simplified version of the processing code above that simply regenerates the structure from rhino in processing using a *.txt file.

As you stream the vector coordinates for different iterations of the model in grasshopper the model will update when you rerun the processing sketch.

Here is the base code for importing the start and endpoints of each line in the grasshopper model into processing using *.txt files.

ArrayList<PVector> stpoints = new ArrayList<PVector>();
ArrayList<PVector> endpoints = new ArrayList<PVector>();

void setup(){
  size(600,600,P3D);
  String[] stpts = loadStrings("startpoints.txt");
  String[] endpts = loadStrings("endpoints.txt");
  
  for(int i=0; i < (stpts.length/3); i++){
    
    float xx1 = float(stpts[i*3])*20;
    float yy1 = float(stpts[i*3 + 1])*20;
    float zz1 = float(stpts[i*3 + 2])*20;
    stpoints.add(new PVector(xx1,yy1,zz1));
    
    float xx2 = float(endpts[i*3])*20;
    float yy2 = float(endpts[i*3 + 1])*20;
    float zz2 = float(endpts[i*3 + 2])*20;
    endpoints.add(new PVector(xx2,yy2,zz2));
  }  
}

void draw(){
  
  background(0);
  pushMatrix();
  ///simulated camera//////////////////
  translate(width/2,height/2);
  rotateY(PI*2*mouseX/width);
  rotateX(PI*2*mouseY/height);
 
  ///end camera//////////////////
  stroke(255);
  for(int i = 0; i < stpoints.size(); i++){
    
    PVector spt = (PVector) stpoints.get(i);
    PVector ept = (PVector) endpoints.get(i);
    
    line(spt.x, spt.y, spt.z, ept.x, ept.y, ept.z);

  }
  popMatrix();
  
}

The code below is an expanded version that is shown in the video at the top of this post. It is importing the same geometry from grasshopper but using it to create a structure that responds to sound.

import ddf.minim.*;
import ddf.minim.analysis.*;

import controlP5.*;

ControlP5 cp5;
RadioButton r;

Minim minim;
AudioInput in;

BeatDetect beater;

int lifespan = 25;
int rannum = 20;
float rate = 1;
float volume = 1.0;
float threshold = 0.5;
float time = 100;

boolean Sound = false;
boolean Beat = false;
boolean Random = false;
boolean trail = true;
boolean Pulse = false;
boolean multiple = false;

float trailcount = 20000;

ArrayList<PVector> pStart = new ArrayList<PVector>();
ArrayList<PVector> pEnd = new ArrayList<PVector>();
ArrayList<PVector> light = new ArrayList<PVector>();

int[] trailseed = new int[20];
float[] trailtime = new float[20];

int tcount = 0;

FloatList chpts;  

FloatList on;  

float x,y,z;

float scroll;

float radius;

int spannum;

float chx = 0;
float chy = 0;
float chz = 0;

float xs;
float ys;
float zs;
float xe;
float ye;
float ze;
float rh = 0;
int lastmy;
float pdist = 1000;

int pcenter;

void setup() {
  size(1200, 900,P3D);

  frameRate(30);
  
  minim = new Minim(this);
 
  // use the getLineIn method of the Minim object to get an AudioInput
  in = minim.getLineIn();
  
  beater = new BeatDetect();
  
  radius = 0;
  
  x = width/2;
  y = height/2;
  z = 0;
  
  on = new FloatList(); 
   
  // Load text file as a string
  String[] spts = loadStrings("startpoints.txt");
  // Convert string into an array of integers using ',' as a delimiter
 
  for(int i = 0; i < (spts.length/3); ++i){
    float xx = float(spts[i*3])*40;
    float yy = float(spts[i*3+1])*40;
    float zz = float(spts[i*3+2])*40;
    pStart.add(new PVector(xx,yy,zz));
    on.append(0.0);
  }

  String[] spts2 = loadStrings("endpoints.txt");
  for(int i = 0; i < (spts2.length/3); ++i){
    float xx = float(spts2[i*3])*40;
    float yy = float(spts2[i*3+1])*40;
    float zz = float(spts2[i*3+2])*40;
    pEnd.add(new PVector(xx,yy,zz));
  }
  
  for(int j = 0; j < (trailseed.length); ++j){
    trailseed[j] = 0;
    trailtime[j] = 0;
    light.add(pEnd.get(j));
  }

  trailtime[0] = 2000;
  
  cp5 = new ControlP5(this);
  
  cp5.addSlider("lifespan")
     .setPosition(80,40)
     .setRange(0,100)
     .setSize(240,20)
     .setColorForeground(color(20,200,200))
     .setColorLabel(color(255))
     .setColorBackground(color(70,70,70))
     .setColorValue(color(0,0,0))
     .setColorActive(color(0,255,255))
     ;
     //cp5.getController("X_pulse").getCaptionLabel().align(ControlP5.LEFT, ControlP5.RIGHT_OUTSIDE).setPadding(23,2);
 
  cp5.addSlider("rate")
     .setPosition(80,70)
     .setRange(0.0,10.0)
     .setSize(240,20)
     .setColorForeground(color(20,200,200))
     .setColorLabel(color(255))
     .setColorBackground(color(70,70,70))
     .setColorValue(color(0,0,0))
     .setColorActive(color(0,255,255))
     ;
     
     cp5.addSlider("time")
     .setPosition(160,140)
     .setRange(0.0,100.0)
     .setSize(160,20)
     .setColorForeground(color(20,200,200))
     .setColorLabel(color(255))
     .setColorBackground(color(70,70,70))
     .setColorValue(color(0,0,0))
     .setColorActive(color(0,255,255))
     ;
     
     cp5.addSlider("rannum")
     .setPosition(120,180)
     .setRange(0.0,100.0)
     .setSize(200,20)
     .setColorForeground(color(20,200,200))
     .setColorLabel(color(255))
     .setColorBackground(color(70,70,70))
     .setColorValue(color(0,0,0))
     .setColorActive(color(0,255,255))
     ;

     cp5.addSlider("threshold")
     .setPosition(80,850)
     .setRange(0.0,1.0)
     .setSize(200,20)
     .setColorForeground(color(20,200,200))
     .setColorLabel(color(255))
     .setColorBackground(color(70,70,70))
     .setColorValue(color(0,0,0))
     .setColorActive(color(0,255,255))
     ;
     
     cp5.addToggle("trail")
     .setPosition(80,140)
     .setSize(20,20)
     .setColorForeground(color(20,20,20))
     .setColorLabel(color(255))
     .setColorBackground(color(70,70,70))
     .setColorValue(0xffff88ff)
     .setColorActive(color(0,200,200))
     ;
     
      cp5.addToggle("multiple")
     .setPosition(120,140)
     .setSize(20,20)
     .setColorForeground(color(20,20,20))
     .setColorLabel(color(255))
     .setColorBackground(color(70,70,70))
     .setColorValue(0xffff88ff)
     .setColorActive(color(0,200,200))
     ;
     
     
     cp5.addToggle("Random")
     .setPosition(80,180)
     .setSize(20,20)
     .setColorForeground(color(20,20,20))
     .setColorLabel(color(255))
     .setColorBackground(color(70,70,70))
     .setColorValue(0xffff88ff)
     .setColorActive(color(0,200,200))
     ;
     
     
      cp5.addToggle("Pulse")
     .setPosition(80,220)
     .setSize(20,20)
     .setColorForeground(color(20,20,20))
     .setColorLabel(color(255))
     .setColorBackground(color(70,70,70))
     .setColorValue(0xffff88ff)
     .setColorActive(color(0,200,200))
     ;
     
     cp5.addToggle("Sound")
     .setPosition(20,40)
     .setSize(20,20)
     .setColorForeground(color(20,20,20))
     .setColorLabel(color(255))
     .setColorBackground(color(70,70,70))
     .setColorValue(0xffff88ff)
     .setColorActive(color(0,200,200))
     ;
     
     cp5.addToggle("Beat")
     .setPosition(20,80)
     .setSize(20,20)
     .setColorForeground(color(20,20,20))
     .setColorLabel(color(255))
     .setColorBackground(color(70,70,70))
     .setColorValue(0xffff88ff)
     .setColorActive(color(0,200,200))
     ;
}

void draw() {
  
  ///GET SOUND/////////////
  beater.detect(in.mix);
  float sd = in.mix.get(0);
  float s = 200 * abs(sd);
  float tnum = threshold * 200;
  sd = 0;
  for(int i = 0; i < in.bufferSize() - 1; i++)
  {
    if(abs(in.mix.get(i)) > sd){
      sd = abs(in.mix.get(i));
    }
  }
  s = sd*200;
  if(Beat == true){
    if ( beater.isOnset() ){
      s = 200;
    }else{
      s = 0;
    }
  }
  
  ///////////////////////////
  
  if((Sound == true) && (s > tnum)){
    if(tcount == 19){
      tcount = 0;
    }else{
      tcount++;
    }
    trailtime[tcount] = 2000;
    trailseed[tcount] = int(random(pStart.size()));
  }
  if(multiple == false){
    tcount = 0;
  }
  
  if(Pulse == true){
      if((s > tnum)){
       pcenter = int(random(pStart.size()));
       pdist = 0;
      }
    }
  
  pdist = pdist + 10;
  ///SOUND BAR VISUALISER////
  background(000);

  if(s > 200){
    s = 200;
  }
  if(s < 0){
    s = 0;
  }
  noStroke();
  fill(color(70,70,70));
  rect(80, 800, 60, -200);
  fill(color(20,200,200));
  
  if(Beat == true){
    println(beater.isOnset());
    if ( beater.isOnset() ){
      
      rh = -200;
    }
    rh *=.9;
  }else{
    rh = -1*s;
  }
  
  rect(80, 800, 60, rh);
  stroke(255);
  strokeWeight(1); 
  line(150,800-tnum,170,800-tnum);
  fill(255);
  textSize(9);
  text("AMPLITUDE",80,820);
  ////////////////////////////
  
  ///CAMERA//////////////////
  pushMatrix();
 
  translate(width/2, height/2,radius);

  if(mouseX > 220){
    rotateY(PI*2*(mouseX-220)/(width-220));
    lastmy = mouseY;
  }
  rotateX(PI*2*lastmy/height);
  ///END CAMERA//////////////////
 
  for(int i = 0; i < (pStart.size()); ++i){
    float r = random(50);
    
   
    PVector vs =  pStart.get(i);
    PVector ve =  pEnd.get(i);
    
    ///02. RANDOM//////////////
    if(Random == true){
      if(s > tnum){
        float ro = random(100);
        if(ro < rannum){
          on.set(i,lifespan); 
        }
      }
    }
    
    ///END RANDOM//////////////

/////03. PULSE//////////////////
    
  
    if((Pulse == true)&&(Sound == true)){
      PVector vc =  pStart.get(pcenter);
      float vdist = dist(vs.x,vs.y,vs.z,vc.x,vc.y,vc.z);
      float dif = vdist - pdist;
      if((dif < 10) && (dif > -10)){
        on.set(i,lifespan); 
      }
    }
    
/////PULSE END//////////////

    for(int c = 0; c < (trailseed.length); ++c){
      if((trail == true) && (trailtime[c]> 0)){
        if( i == trailseed[c]){
            on.set(i,lifespan); 
          float stn = random(50);
          if(stn > 25){
              light.set(c,vs);
          }else{
              light.set(c,ve);
           }
        }
      }
    }

    float onoff = on.get(i);
    
    if( onoff > 0){
      stroke(0,255,255);
      if((onoff/6 < 1) && (onoff/6 > 0)){
        strokeWeight(1); 
        }else{
          strokeWeight(int(onoff/6));
      }
    }else{
      stroke(100);
      strokeWeight(1); 
    }
    
    onoff = on.get(i);
    on.set(i,(onoff-rate)); 
    
    noFill();
    
    line(vs.x, vs.y, vs.z, ve.x, ve.y, ve.z);
    
  }

  for(int n = 0; n < (trailseed.length); ++n){
    
    if((time == 100) && (n == 0)){
        trailtime[n] = 2000;
      }else{
        if((trailtime[n]-(100-time)) < 0){
          trailtime[n] = 0;
        }else{
          trailtime[n] = trailtime[n] - (100-time);
        }
      }
    
    chpts = new FloatList(); 
    int pcount = 0;
    for(int i = 0; i < (pStart.size()); ++i){
      PVector vs =  pStart.get(i);
      PVector ve =  pEnd.get(i);
      PVector lc = light.get(n);
      
      PVector ivs = new PVector(int(vs.x), int(vs.y), int(vs.z));
      PVector ive = new PVector(int(ve.x), int(ve.y), int(ve.z));
      PVector itest = new PVector(int(lc.x), int(lc.y), int(lc.z));
  
      int ch = 0;
      if( (ivs.x == itest.x) || (ive.x == itest.x) ){
        ch++;
       
      }
      if( (ivs.y == itest.y) || (ive.y == itest.y) ){
        ch++;
      }
      if( (ivs.z == itest.z) || (ive.z == itest.z) ){
        ch++;
      }
      float onoff = on.get(i);
      if((ch == 3) && (i != spannum)){
          chpts.append(i); 
          pcount++;
        }  
    }
    int ci = int(random(chpts.size()));
    trailseed[n] = int(chpts.get(ci));
  }
  popMatrix();
}

void mouseWheel(MouseEvent event) {
  float e = event.getCount();
  radius = radius - e*5;
}