Return to menu
/* 
   
   A surface filled with one hundred medium to small sized circles. 
   Each circle has a different size and direction, but moves at the same slow rate. 
   Display: 
   >>> A. The instantaneous intersections of the circles 
   B. The aggregate intersections of the circles 
 
   Implemented by Robert Hodgin <http://flight404.com> 
   6 April 2004 
   Processing v.68 <http://processing.org> 
 
*/ 
 
// ******************************************************************************** 
// INITIALIZE VARIABLES 
// ******************************************************************************** 
 
int xStage         = 600;            // x dimension of applet 
int yStage         = 600;             // y dimension of applet 
 
int xMid           = xStage/2;        // x midpoint of applet 
int yMid           = yStage/2;        // y midpoint of applet 
 
int totalCircles   = 100;             // total number of circles 
Circle[] circle;                      // Circle object array 
 
float gravity;                        // Strength of gravitational pull 
float xGrav;                          // x point of center of gravity 
float yGrav;                          // y point of center of gravity 
float xGravOffset; 
 
float angleOffset; 
float initRadius; 
float maxDistance; 
 
color bgColor; 
 
// ******************************************************************************** 
// SETUP FUNCTION 
// ******************************************************************************** 
 
void setup() 
{ 
  size(600, 600); 
  bgColor = color(255,255,255); 
  background(bgColor); 
  smooth(); 
  colorMode(RGB, 255); 
  ellipseMode(CENTER_RADIUS); 
  noStroke(); 
  framerate(30); 
  createCircles(); 
} 
 
// ******************************************************************************** 
// MAIN LOOP FUNCTION 
// ******************************************************************************** 
 
void loop(){ 
  if (mousePressed){ 
    createCircles(); 
  } 
  background(bgColor); 
  tellCirclesToBehave(); 
} 
 
void createCircles(){ 
  gravity                        = .075; 
  maxDistance                    = 150; 
  angleOffset                    = random(360); 
  circle                         = new Circle[totalCircles]; 
  initRadius                     = 150; 
  for (int i=0; i<totalCircles; i++){ 
    float initAngle              = i * 3.6 + angleOffset + random(10); 
    float initTheta              = (-((initAngle) * PI))/180; 
 
    float initxv                 = cos(initTheta) * initRadius; 
    float inityv                 = sin(initTheta) * initRadius; 
    float xPos = xMid + initxv; 
    float yPos = yMid + inityv; 
    circle[i] = new Circle(xPos, yPos,0,0, i); 
  } 
} 
 
void tellCirclesToBehave(){ 
  for (int i=0; i<totalCircles; i++){ 
    circle[i].behave(); 
  } 
} 
 
class Circle 
{ 
  int index;                    // Circle global ID 
 
  float x;                      // Circle x position 
  float y;                      // Circle y position 
  float r;                      // Circle radius 
  float rBase;                  // Circle original radius 
  
  float angle;                  // Angle of movement in degrees 
  float theta;                  // Angle of movement in radians 
  float speed;                  // Speed of movement 
  
  float xv;                     // Current velocity along x axis 
  float yv;                     // Current velocity along y axis 
 
  boolean[] mightCollide;       // Collision might happen 
  boolean[] hasCollided;        // Collision is happening 
  
  float[] distances;            // Storage for the distance between circles 
  float[] angles;               // Storage for the angle between two connected circles 
  float[] thetas;               // Storage for the radians between two connected circles 
  int numCollisions;            // Number of collisions in one frame 
  int numConnections;           // Total number of collisions 
 
  float xd;                     // Distance to target along x axis 
  float yd;                     // Distance to target along y axis 
  float d;                      // Distance to target 
  
  float alphaVar;               // Late addition variable for alpha modification 
 
  float cAngle;                 // Angle of collision in degrees 
  float cTheta;                 // Angle of collision in radians 
  float cxv;                    // Collision velocity along x axis 
  float cyv;                    // Collision velocity along y axis 
  
  float gAngle;                 // Angle to gravity center in degrees 
  float gTheta;                 // Angle to gravity center in radians 
  float gxv;                    // Gravity velocity along x axis 
  float gyv;                    // Gravity velocity along y axis 
 
  Circle (float xSent, float ySent, float xvSent, float yvSent, int indexSent){ 
    x               = xSent; 
    y               = ySent; 
    r               = 2; 
    
    index           = indexSent; 
 
    xv              = xvSent; 
    yv              = yvSent; 
    
    alphaVar        = random(35); 
    
    mightCollide    = new boolean[totalCircles]; 
    hasCollided     = new boolean[totalCircles]; 
    distances       = new float[totalCircles]; 
    angles          = new float[totalCircles]; 
    thetas          = new float[totalCircles]; 
  } 
  
  void behave(){ 
    move(); 
    areWeClose(); 
    areWeColliding(); 
    areWeConnected(); 
    applyGravity(); 
    //move(); 
    render(); 
    reset(); 
  } 
  
  void areWeClose(){ 
    for(int i=0; i<totalCircles; i++){ 
      if (i != index){ 
        if (abs(x - circle[i].x) < 50 && abs(y - circle[i].y) < 50){ 
          mightCollide[i] = true; 
        } else { 
          mightCollide[i] = false; 
        } 
      } 
    } 
  } 
  
  void areWeColliding(){ 
    for(int i=0; i<totalCircles; i++){ 
      if (mightCollide[i] && i != index){ 
        distances[i] = findDistance(x, y, circle[i].x, circle[i].y); 
        if (distances[i] < (r + circle[i].r) * 1.1){ 
          hasCollided[i]               = true; 
          circle[i].hasCollided[index] = true; 
 
          angles[i]                    = findAngle(x,y,circle[i].x,circle[i].y); 
          thetas[i]                    = (-(angles[i] * PI))/180.0; 
          cxv                         += cos(thetas[i]) * ((circle[i].r + r)/2.0); 
          cyv                         += sin(thetas[i]) * ((circle[i].r + r)/2.0); 
          numCollisions               += 1; 
        } 
      } 
    } 
    
    if (numCollisions > 0){ 
      xv = -cxv/numCollisions; 
      yv = -cyv/numCollisions; 
    } 
 
    cxv = 0.0; 
    cyv = 0.0; 
    
  } 
  
  void areWeConnected(){ 
    for(int i=0; i<totalCircles; i++){ 
      if (hasCollided[i] && i != index){ 
        distances[i] = findDistance(x, y, circle[i].x, circle[i].y); 
        if (distances[i] < maxDistance){ 
          angles[i]                    = findAngle(x,y,circle[i].x,circle[i].y); 
          thetas[i]                    = (-(angles[i] * PI))/180.0; 
          cxv                         += cos(thetas[i]) * (circle[i].r/8.0); 
          cyv                         += sin(thetas[i]) * (circle[i].r/8.0); 
          numConnections              += 1; 
        } else { 
          hasCollided[i]               = false; 
          circle[i].hasCollided[index] = false; 
        } 
      } 
    } 
    if (numConnections > 0){ 
      xv += (cxv/numConnections)/4.0; 
      yv += (cyv/numConnections)/4.0; 
    } 
    
    cxv = 0.0; 
    cyv = 0.0; 
    
    r = numConnections*.85 + 2; 
  } 
 
  
  void applyGravity(){ 
    gAngle        = findAngle(x,y,xMid,yMid); 
    gTheta        = (-(gAngle * PI))/180; 
    gxv           = cos(gTheta) * gravity; 
    gyv           = sin(gTheta) * gravity; 
    xv += gxv; 
    yv += gyv; 
  } 
  
  void move(){ 
    x += xv; 
    y += yv; 
  } 
 
  void render(){ 
 
    noStroke(); 
    fill(0,25); 
    ellipse(x, y, r, r); 
    fill(0 + r*10,50); 
    ellipse(x, y, r*.5, r*.5); 
    fill(0 + r*10); 
    ellipse(x, y, r*.3, r*.3); 
    
    if (numCollisions > 0){ 
      noStroke(); 
      fill(0,25); 
      ellipse(x,y, r, r); 
      
      fill(0,55); 
      ellipse(x,y,r*.85,r*.85); 
      fill(0); 
      ellipse(x,y,r*.7,r*.7); 
    } 
    
    for(int i=0; i<totalCircles; i++){ 
      if (hasCollided[i] && i < index){ 
        beginShape(LINE_LOOP); 
        xd = x - circle[i].x; 
        yd = y - circle[i].y; 
        stroke(0,150 - distances[i]*2.0); 
        vertex(x,y); 
        vertex(x - xd*.25 + random(-1.0,1.0), y - yd*.25 + random(-1.0,1.0)); 
        vertex(x - xd*.5 + random(-3.0,3.0), y - yd*.5 + random(-3.0,3.0)); 
        vertex(x - xd*.75 + random(-1.0,1.0), y - yd*.75 + random(-1.0,1.0)); 
        vertex(circle[i].x, circle[i].y); 
        endShape(); 
        //line (x, y, circle[i].x, circle[i].y); 
      } 
    } 
    noStroke(); 
  } 
  
  void reset(){ 
    numCollisions = 0; 
    numConnections = 0; 
  } 
} 
 
float findDistance(float x1, float y1, float x2, float y2){ 
  float xd = x1 - x2; 
  float yd = y1 - y2; 
  float td = sqrt(xd * xd + yd * yd); 
  return td; 
} 
  
float findAngle(float x1, float y1, float x2, float y2){ 
  float xd = x1 - x2; 
  float yd = y1 - y2; 
 
  float t = atan2(yd,xd); 
  float a = (180 + (-(180 * t) / PI)); 
  return a; 
}