This code was written with the _ALPHA_ version of Processing
and it may not run correctly with the current vesion.


// Tissue
// Casey Reas


int NOO = 900;  // Number of objects
int LOO = 150;  // Length of objects
int mx = 0;     // Mouse X position  
int my = 0;     // Mouse Y position
int pw = 600;   // Project width
int ph = 300;   // Project height
int lightCount;  
BShape[] all = new BShape[NOO];
float[][] lights = new float[3][2];
float[][] lightsNew = new float[3][2];
int rr, gg, bb;
int NUMBER_OF_LIGHTS = 3;
int CLICK = 0;
int barW = 21;
float markerY = 150;
float markerN = 150;
float markerYnew = 150.0;
float markerNnew = 150.0;
boolean markerN_on = false;
boolean markerY_on = false;
int whichLight = 100;
int lightOn = 100;
boolean a_on = false;


void setup() {
  size(600, 300);
  colorMode(RGB, 255);
  background(255, 255, 255);
  noStroke();
  ellipseMode(CENTER_DIAMETER);
  
  for(int i=0; i<NUMBER_OF_LIGHTS; i++) {
    lights[i][0] = lightsNew[i][0] = random(pw-40) + 20;
    lights[i][1] = lightsNew[i][1] = random(ph-40) + 20;
  }

  for(int i=0; i<NOO; i++) {
    int tm = i%4;
    if (tm == 0) {
      rr = 153; gg = 0; bb = 0;
    } else if (tm == 1) {
      rr = 102; gg = 102; bb = 102;
    } else if (tm == 2) {
      rr = 204; gg = 102; bb = 0;
    } else if (tm == 3) {
      rr = 102; gg = 51; bb = 0;
    }
          
    int temp = (int)random(0, 4);
    if (temp == 0) {
      all[i] = new BShape(random(pw-barW), random(ph), random(360), 
                          rr, gg, bb, tm, 0, 150, 20);
    } else if (temp == 1) {
      all[i] = new BShape(random(pw-barW), random(ph), random(360), 
                          rr, gg, bb, tm, 50, 150, 20);
    } else if (temp == 2) {
      all[i] = new BShape(random(pw-barW), random(ph), random(360), 
                          rr, gg, bb, tm, 100, 150, 20);
    } else if (temp == 3) {
      all[i] = new BShape(random(pw-barW), random(ph), random(360), 
                          rr, gg, bb, tm, 150, 150, 20);
    }
  }
}


void loop() {
  bIdle();
  bDraw();
  bDrawUI();
}

boolean insideC(float x, float y) {
  float dx = sqrt((mouseX-x) * (mouseX-x) + (mouseY-y) * (mouseY-y));
  if (dx < 8.0) {
    return true;
  } else {
    return false;
  }
}


int clipX(int x) {
  if(x > pw-barW ) { x = pw-barW; }
  if (x < 0 ) { x = 0; }
  return x;  
}

int clipY(int y) {
  if (y > ph) { y = ph; }
  if (y < 0) { y = 0; }
  return y;
}


void bIdle() {
  mx = mouseX;
  my = mouseY;
  
  if(mousePressed) {
    if(!markerY_on && !markerN_on) {
      lightOn = whichLight; 
    }
  } else {
    a_on = false;
    whichLight = 100;
    lightOn = 100;
    markerY_on = false;
    markerN_on = false;
  }
  
  for (int i=0; i<NUMBER_OF_LIGHTS; i++) {
    if (lightOn == i) {
      a_on = true;
      lightsNew[i][0] = clipX(mouseX);
      lightsNew[i][1] = clipY(mouseY);
    } 
    if(abs(lightsNew[i][0] - lights[i][0]) > 1.0) {
      lights[i][0] = lights[i][0] + (lightsNew[i][0] - lights[i][0])/2.0;
    }
    if(abs(lightsNew[i][1] - lights[i][1]) > 1.0) {
      lights[i][1] = lights[i][1] + (lightsNew[i][1] - lights[i][1])/2.0;
    }
  }

  
  for(int i=0; i<NOO; i++) {
    all[i].idle();
  }
  
  // Markers
  idleMarkers();
}

void bDraw() {
  int numm = NOO-((int)markerY*3);
  if (numm < 4) { numm = 4; } 
  for(int i=0; i<numm; i++) {
    all[i].draw();
  }
  
  noStroke();

  for(int i=0; i<3; i++) {
    if((insideC(lights[i][0], lights[i][1]) && !a_on) || lightOn == i) {
      fill(76, 26, 0);   
      whichLight = i;
    } else {
      fill(204, 153, 0); 
    }
    ellipse(lights[i][0], lights[i][1], 14, 14);
  }
  

}


/////////////////////////////////////////

void idleMarkers() {
  if(markerY_on) {
    markerYnew = my;
    if (markerYnew > 291) { markerYnew = 291; }
    if (markerYnew < 0) { markerYnew = 0; }
  }
  if(markerN_on) {
    markerNnew = my;
    if (markerNnew > 291) { markerNnew = 291; }
    if (markerNnew < 0) { markerNnew = 0; }
  }
  
  if(abs(markerNnew - markerN) > 1.0) {
    markerN = markerN + (markerNnew - markerN)/2.0;
  }
    
  if(abs(markerYnew - markerY) > 1.0) {
    markerY = markerY + (markerYnew - markerY)/2.0;
  }
  
  noStroke();
  // Marker for number
  if(((mx > pw-barW && mx < pw-10 && !markerN_on && !a_on) || markerY_on)) {
    fill(66, 16, 0);
    if (mousePressed) {
      markerY_on = true;
      a_on = true;
    }
  } else {
    fill(204, 153, 0);
  }
  rect(pw-21, markerY, 9, 9);
    
  // Marker for length
  if((mx > pw-10 && mx < pw && !markerY_on && !a_on) || markerN_on) {
    fill(66, 16, 0);
    if(mousePressed) {
      markerN_on = true;
      a_on = true;
    }
  } else {
    fill(204, 153, 0);
  }
  rect(pw-10, markerN, 9, 9); 
}

void bDrawUI() {

  fill(153, 51, 0);
  beginShape(QUADS);
  vertex(pw-21, 0);
  vertex(pw, 0);
  fill(102, 26, 0);
  vertex(pw, ph);
  vertex(pw-21, ph);
  endShape();
  
  stroke(153, 51, 0);
  line(pw-11, 0, pw-11, ph);
  
  noStroke();
  // marker for number
  if((mx > pw-barW && mx < pw-10 && !markerN_on && !a_on) || markerY_on) {
    fill(76, 26, 0);
    if (mousePressed) {
      markerY_on = true;
    }
  } else {
    fill(204, 153, 0);
  }
  rect(pw-21, markerY, 9, 9);
    
  // marker for length
  if((mx > pw-10 && mx < pw && !markerY_on && !a_on) || markerN_on) {
    fill(76, 26, 0);
    if(mousePressed) {
      markerN_on = true;
    }
  } else {
    fill(204, 153, 0);
  }
  rect(pw-10, markerN, 9, 9); 

}


/////////////////////////////////////////
/////////////////////////////////////////
  

class BShape {
  int NNN = LOO;
  float pos_x = 0;
  float pos_y = 0;
  float light_x = 0;
  float light_y = 0;
  int direction = 1;
  float lineRotate;
  float lineRotateNew = 0;
  float lineWidth = 50.0;
  int r, g, b;
  float ld = 0.0;
  float rd = 0.0;
  float left_sensor_x = 0.0;
  float left_sensor_y = 0.0;
  float right_sensor_x = 0.0;
  float right_sensor_y = 0.0;
  float dx, dy;
  float yd, xd;
  float max_distance = sqrt(pw*pw + ph*ph);
  float lineSpeed = 2.0;
  float MAX_SPEED = 6.0;
  float tempX = 0;
  float tempY = 0;
  int type = 0;
  int xW = 0;
  int h = 0;
  float size = 45.0 * random(1.0) + 5.0;
  float[] past_x = new float[NNN];
  float[] past_y = new float[NNN];
    
  BShape (float xIn, float yIn, float theta, 
          int rIn, int gIn, int bIn, int typeIn,
          int xBin, int xWin, int Height) {
    pos_x = xIn;
    pos_y = yIn;
    lineRotate = theta;
    r = rIn; 
    g = gIn; 
    b = bIn;
    type = typeIn;
    xW = xWin;
    h = Height;
    for( int i=0; i<NNN; i++) {
      past_x[i] = xIn;
      past_y[i] = yIn;
    }
    
  }
  
  float hump(float sa) {
    sa = (sa - 0.5) * 2.0; //scale from -1 to 1
    sa = 1 - sa*sa;
    return sa;
  }
    
  void idle() {
    lineRotateNew = lineRotate/180.0 * PI;
    
    for (int i=0; i<NUMBER_OF_LIGHTS; i++) {
      if(i==0) { lineSpeed = 0.0; }
      
      // Position of light
      light_x = lights[i][0];
      light_y = lights[i][1];
      
      // Position of left sensor
      float lrn = lineRotateNew+PI/5.0;
      left_sensor_x = pos_x + cos(lrn)*lineWidth;
      left_sensor_y = pos_y + sin(lrn)*lineWidth;
      
      // Position of right sensor  
      float lrl = lineRotateNew-PI/5.0;
      right_sensor_x = pos_x + cos(lrl)*lineWidth;
      right_sensor_y = pos_y + sin(lrl)*lineWidth;
      
      // Distance from left sensor        
      dx = light_x - left_sensor_x;
      dy = light_y - left_sensor_y;
      ld = sqrt(dx*dx + dy*dy);
                           
      // Distance from right sensor                     
      dx = light_x - right_sensor_x;
      dy = light_y - right_sensor_y;
      rd = sqrt(dx*dx + dy*dy);
        
      float norm_sensor_left = 0.0;
      float norm_sensor_right = 0.0;
      float turnSpeed = 8.0; //8.0;
      float norm_sensor_average = 0.0;
        
      norm_sensor_left = ld/max_distance;
      norm_sensor_right = rd/max_distance;
      norm_sensor_right =  hump(norm_sensor_right);
      norm_sensor_left =  hump(norm_sensor_left);
      norm_sensor_average = (norm_sensor_left + norm_sensor_right) / 2.0;
      lineSpeed += MAX_SPEED - MAX_SPEED * norm_sensor_average;
      lineSpeed /= 2 + (size/25.0);
              
      if (type == 0) {
        lineRotate += ld/turnSpeed * (1-norm_sensor_left);
        lineRotate -= rd/turnSpeed * (1-norm_sensor_right);
      } else if (type == 1) {
        lineRotate += rd/turnSpeed * (1-norm_sensor_left);
        lineRotate -= ld/turnSpeed * (1-norm_sensor_right);
      } else if (type == 2) {
        lineRotate -= (ld/turnSpeed * (1-norm_sensor_left));
        lineRotate += (rd/turnSpeed * (1-norm_sensor_right));
      } else if (type == 3) {
        lineRotate -= (rd/turnSpeed * (1-norm_sensor_left));
        lineRotate += (ld/turnSpeed * (1-norm_sensor_right)); 
      }
      
      lineRotate += random(-7, 7);
      if(lineRotate >= 360) { lineRotate = 0.0; }
      
      // Convert to radians
      lineRotateNew = lineRotate/180.0 * PI;
           
      if (lineRotateNew < PI/2.0) {
        yd = sin(lineRotateNew) * lineSpeed;
        xd = cos(lineRotateNew) * lineSpeed;
        pos_x += xd * direction;
        pos_y += yd * direction;
      } else if (lineRotateNew < PI) {
        lineRotateNew -= PI/2.0;
        yd = cos(lineRotateNew) * lineSpeed;
        xd = sin(lineRotateNew) * lineSpeed;
        pos_x -= xd * direction;
        pos_y += yd * direction;
      } else if (lineRotateNew < PI + PI/2.0) {
        lineRotateNew -= PI;
        yd = sin(lineRotateNew) * lineSpeed;
        xd = cos(lineRotateNew) * lineSpeed;
        pos_x -= xd * direction;
        pos_y -= yd * direction;
      } else {
        lineRotateNew -= PI+PI/2.0;
        yd = cos(lineRotateNew) * lineSpeed;
        xd = sin(lineRotateNew) * lineSpeed;
        pos_x += xd * direction;
        pos_y -= yd * direction;
      }
      
      if (pos_x > pw-barW || pos_x < 0 || pos_y > ph || pos_y < 0) {
        if(pos_x > pw-barW) { pos_x = past_x[0]; pos_y = past_y[0]; } 
        if(pos_x < 0) { pos_x = past_x[0]; pos_y = past_y[0]; }
        if(pos_y > ph) { pos_x = past_x[0]; pos_y = past_y[0]; }
        if(pos_y < 0) { pos_x = past_x[0]; pos_y = past_y[0]; }
        reset();
      }
      
      for (int j=NNN-1; j>0; j--) {
  	past_x[j] = past_x[j-1];
  	past_y[j] = past_y[j-1];
      }
      past_x[0] = pos_x;
      past_y[0] = pos_y;
    }
  }
  
  void reset() {
    direction *= -1;
    lineRotate += 1;
  }
    
  void draw() {
    push();
    stroke(r, g, b);
    beginShape(LINE_STRIP);
    for( int j=1; j<NNN-markerN/2; j++) {
      vertex(past_x[j], past_y[j]);
    }
    endShape();
    pop();
  }
}