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;
}