output_8IPr1y.gif

Jellyfish

I wrote this sketch in Processing for a study with object oriented programming. 

 
int population = 3;
Force field;
Jelly[] jellyList; 

void setup(){
  size(600, 600);
  background(255);
  
  field = new Force(20);
  jellyList = new Jelly[population];
  
  for(int initialJelly = 0; initialJelly < jellyList.length; initialJelly++){
    jellyList[initialJelly] = new Jelly(initialJelly);
  }
}

void draw(){
  background(255);
  
  field.run();
  
  for(Jelly currentJelly : jellyList){
    currentJelly.run();
  }
  // FRAME RECORDER
  /*
  if(frameCount % 3 == 0 && frameCount < 1600 && frameCount > 700){
    println("RECORDING FRAME: " + frameCount);
    saveFrame("/Users/YOUR_NAME_HERE/Desktop/jellyfish/frame-####.png");
  }
  */
}

class Force{
  PVector[][] field;
  PVector angleOffset;
  int cols, rows;
  int resolution;
  float forceRabbit;
  
 Force(int temp_resolution){
  resolution = temp_resolution;
  cols = width / resolution;
  rows = height / resolution;
  field = new PVector[cols][rows];
  angleOffset = new PVector();
  
  initiate();
 } 

 void run(){
   update();
   forceStyle();
   
   if(mousePressed){
     display();
   }
 }
 
 void update(){
   for (int x = 0; x < cols; x++) {
     float seedY = 100;
     
      for (int y = 0; y < rows; y++) {
        float theta = map(noise(forceRabbit + seedY), 0, -1, 0, -PI);
       
        field[x][y] = new PVector(cos(theta), sin(theta));
        forceRabbit += .00001;
        seedY += .1;
      }
      seedX += .1;
    }
 }
 
 void display(){
   for(int currentCol = 0; currentCol < cols; currentCol++) {
      for(int currentRow = 0; currentRow < rows; currentRow++) {
        drawVector(field[currentCol][currentRow], currentCol * resolution, currentRow * resolution, resolution-2);
      }
    }
 }
 
 void drawVector(PVector v, float x, float y, float scayl) {
    pushMatrix();
      float len = v.mag() * scayl;
      
      translate(x, y);
      rotate(v.heading2D());
      
      line(0, 0, len, 0);
    popMatrix();
  }
  
  PVector lookup(PVector lookup) {
    int column = int(constrain(lookup.x / resolution, 0, cols-1));
    int row = int(constrain(lookup.y / resolution, 0, rows-1));
    
    return field[column][row].get();
  }
  
  void forceStyle(){
   noFill();
  //noStroke();
  stroke(0, 100);
  strokeWeight(1); 
  }
  
  void initiate() {
    for (int x = 0; x < cols; x++) {
      for (int y = 0; y < rows; y++) {
        float theta = map(-noise(forceRabbit + x + y), 0, 1, 0, -PI);
       
        field[x][y] = new PVector(cos(theta), sin(theta));
        forceRabbit += 0.1;
      }
    } 
  }
  
}

class Jelly{
  PVector[] anchors = new PVector[50];
  Tenticle[] tenticles = new Tenticle[anchors.length];
  
  PVector position;
  int seed;
  float bellContract, rabbit, size, anchorStep;
  
  Jelly(int temp_seed){
    seed = temp_seed;
    position = new PVector(random(100, width-100), random(100, 300));
    size = random(40, 150);
    
    for(int initTent = 0; initTent < anchors.length; initTent++){
      anchors[initTent] = new PVector(0, 0);
      tenticles[initTent] = new Tenticle(new PVector(anchors[initTent].x, anchors[initTent].y), int(size));
    }
  }  
  
  void run(){
    
    for(Tenticle current : tenticles){
      current.run();
    }
    
    update();
    display();
  }
  
  void display(){
   bellContract = map(cos(radians(rabbit*3+seed*100)), -1, 1, .5, 1);
   
   styleBell();
   
   beginShape();
     for(float angle = 0; angle > -180; angle -= 10){
       vertex( cos(radians(angle)) * size/2 * bellContract + position.x, sin(radians(angle)) * size/2 + position.y );
     }
     for(PVector currentAnchor : anchors){
       vertex(currentAnchor.x, currentAnchor.y);
     }
   endShape();
 }
 
 void update(){
   float anchorRabbit = rabbit/20;
    
   anchorStep = size/(anchors.length-1) * bellContract;
   
   anchors[0] = new PVector(position.x - (size/2 * bellContract), position.y);
   anchors[anchors.length-1] = new PVector(position.x + (size/2 * bellContract), position.y);
    
   for(int current = 1; current < anchors.length-1; current++){
      anchors[current] = new PVector((position.x - (size/2 * bellContract)) + (current * anchorStep), position.y + cos(anchorRabbit+current/2+seed*100)*5);
   }
   
   for(int index = 0; index < tenticles.length; index++){
      tenticles[index].anchor = anchors[index];
   }
   position.y += cos(seed+rabbit/30)*.8;
   rabbit ++;
 }

 void styleBell(){
   //noStroke();
   noFill();
   stroke(0, 100);
   strokeWeight(1);
   fill(255, 150);
 }

}

class Node{
  PVector position, velocity, acceleration;
  float speed, topSpeed, topForce;
  
 Node(PVector temp_position){
  position = temp_position;
  velocity = new PVector(0, 0);
  acceleration = new PVector(0, 0);
  speed = .8;
  topSpeed = 2;
  topForce = .3;
 }
 
 void run(){
   update();
   
   if(mousePressed){
     display();
   }
 }
 
 void follow(Force field){
   PVector desired = field.lookup(position);
   desired.mult(speed);
   
   PVector steer = PVector.sub(desired, velocity);
   steer.limit(topForce);
   acceleration.add(steer);
 }
 
 void update(){
   follow(field);
   
   velocity.add(acceleration);
   velocity.limit(topSpeed);
   position.add(velocity);
   
   acceleration.mult(0);
 }
 
 void display(){
   nodeStyle();
   ellipse(position.x, position.y, 5, 5);
 }
 
 void nodeStyle(){
  fill(255);
  stroke(0, 100);
  strokeWeight(1); 
 }
 
}

class Tenticle{
  Node[] nodes;
  PVector anchor;
  float nodeDist, forceMax, size;
  
  Tenticle(PVector temp_anchor, int temp_size){
    anchor = temp_anchor;
    nodes = new Node[40];
    nodeDist = 50;
    forceMax = .8;
    
    for(int initNode = 0; initNode < nodes.length; initNode++){
      nodes[initNode] = new Node(new PVector(anchor.x, anchor.y));
    }
    nodes[0].topSpeed = 10;
    nodes[0].topForce = 1;
  }

  void run(){
   update();
   display();
  } 
  
  void update(){
    for(int current = 1; current < nodes.length; current++){
      int neighbor = current - 1;
      float currentDistance = dist(nodes[neighbor].position.x, nodes[neighbor].position.y, nodes[current].position.x, nodes[current].position.y);
      
      if(currentDistance > nodeDist){
        seek();
      }
      
      nodes[current].run();
    }
    
    nodes[0].position = anchor;
    nodes[1].position = anchor;
  }
  
  void seek(){
    for(int current = 1; current < nodes.length; current++){
      
      int neighbor = current - 1;
      PVector neighborPos = nodes[neighbor].position;
      PVector currentPos = nodes[current].position;
      
      PVector seek = PVector.sub(currentPos, neighborPos);
      seek.normalize();
      seek.mult(forceMax);
      
      nodes[current].acceleration.sub(seek);
    }
  }
  
  void seperate(){
    for(int current = 1; current < nodes.length; current++){
      int neighbor = current - 1;
      
      PVector neighborPos = nodes[neighbor].position;
      PVector currentPos = nodes[current].position;
      
      PVector seek = PVector.sub(currentPos, neighborPos);
      
      seek.normalize();
      seek.limit(nodes[current].topForce);
      
      nodes[current].acceleration.add(seek);
    }
  }
  
  void display(){
    tenticleStyle();
    
    beginShape();
      for(int current = 0; current < nodes.length; current++){
        curveVertex(nodes[current].position.x, nodes[current].position.y);
      }
    endShape();
  }
  
  void tenticleStyle(){
   noFill();
   stroke(0, 80); 
   strokeWeight(1);
  }
 
}