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> 
   7 April 2004 
   Processing v.68 <http://processing.org> 
 
*/ 
 
// ******************************************************************************** 
// INITIALIZE VARIABLES 
// ******************************************************************************** 
 
int xStage         = 900;             // x dimension of applet 
int yStage         = 500;             // 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;                    // offset var to warp gravitational field 
 
float angleOffset;                    // sets the initial point of rotation for the creation of the circles 
float initRadius;                     // the starting radius of the creation of the circles 
float maxDistance;                    // maximum distance allowed for connections 
 
color bgColor; 
 
// BOOLEANS FOR TESTING PURPOSES 
boolean clear      = false; 
 
int timer          = 0; 
int timerPause     = 750; 
int timerMax       = 775; 
 
// ******************************************************************************** 
// SETUP FUNCTION 
// ******************************************************************************** 
 
void setup() 
{ 
  size(900, 500); 
  bgColor = color(20,20,20); 
  background(bgColor); 
  smooth(); 
  colorMode(RGB, 255); 
  ellipseMode(CENTER_RADIUS); 
  noStroke(); 
  framerate(30); 
  createCircles(); 
} 
 
// ******************************************************************************** 
// MAIN LOOP FUNCTION 
// ******************************************************************************** 
 
void loop() 
{ 
  if (clear){ 
    background(bgColor); 
  } 
  dealWithTimer(); 
  if (timer < timerPause){ 
    tellCirclesToBehave(); 
  } 
 
} 
 
void createCircles(){ 
  gravity                        = random(0.005,0.1); 
  xGrav                          = xMid + random(-50,50); 
  yGrav                          = yMid + random(-50,50); 
  xGravOffset                    = random(1.0, 1.24); 
  maxDistance                    = random(75,150); 
  angleOffset                    = random(360); 
  circle                         = new Circle[totalCircles]; 
  initRadius                     = random(100,140); 
  for (int i=0; i<totalCircles; i++){ 
    float initAngle              = i * 3.6 + angleOffset; 
    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(); 
  } 
} 
 
void dealWithTimer(){ 
  if (timer > timerMax){ 
    timer = 0; 
    background(bgColor); 
    createCircles(); 
  } 
  timer ++; 
} 
 
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               = 4; 
    
    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();               // this is where the move function should go, but for some reason having it come first makes for more interesting renders. 
    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/10.0); 
          cyv                         += sin(thetas[i]) * (circle[i].r/10.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 + 1; 
  } 
 
  
  void applyGravity(){ 
    gAngle        = findAngle(x,y,xGrav,yGrav); 
    gTheta        = (-(gAngle * PI))/180; 
    gxv           = cos(gTheta) * gravity * xGravOffset; 
    gyv           = sin(gTheta) * gravity; 
    xv += gxv; 
    yv += gyv; 
  } 
  
  void move(){ 
    x += xv; 
    y += yv; 
  } 
 
  void render(){ 
    noFill(); 
    d = findDistance(x, y, xMid, yMid); 
    if (d > initRadius){ 
      stroke(255 - d, d - alphaVar); 
    } else { 
      stroke(d, d - alphaVar); 
    } 
    point(x,y); 
    noStroke(); 
    //fill(255,255); 
    //ellipse(x, y, r, r); 
    if (numCollisions > 0){ 
      fill(255,4); 
      ellipse(x, y, r * 5.0, r * 5.0 + 5); 
      ellipse(x, y, r * 3.0, r * 3.0 + 5); 
      if (d > initRadius){ 
        fill(255,255); 
      } else { 
        fill(0,255); 
      } 
      ellipse(x, y, r * 0.5, r * 0.5); 
    } 
    
    for(int i=0; i<totalCircles; i++){ 
      if (hasCollided[i] && i < index){ 
        stroke(abs(angles[i] - 180)*1.3, 5); 
        //stroke(abs(255 - d*.75),6); 
        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; 
}