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 William Ngan <http://metaphorical.net>
   4 April 2004
   Processing v.68 <http://processing.org>

*/


float[][] field;
int[][] fieldShade;
int gaph, gapv;
int marginh, marginv; // margin
float cnt = 0;

float[] sintable = new float[628];
float[] costable = new float[628];
final float PI2 = 2*PI;
float tiltAngle = 0;

Circle[] circles;

float counter = 0;

int cCounter = 0;
int cTimer = 0;

void setup()
{
  size( 500, 500 );
  framerate( 30 );

  gaph = 3;
  gapv = 3;
  marginh = 20;
  marginv = 20;

  // lookup table
  for (int i=0; i<sintable.length; i++) {
    sintable[i] = sin( i/100.0f );
  }

  for (int i=0; i<costable.length; i++) {
    costable[i] = cos( i/100.0f );
  }

  // field
  field = new float[(width-marginh*2)/gaph][(height-marginv*2)/gapv];
  fieldShade = new int[field.length][field[0].length];

  for (int i=0; i<field.length; i++) {
    for (int k=0; k<field[0].length; k++) {
      field[i][k] = PI2-PI/3;
      fieldShade[i][k] = 1;
    }
  }

  circles = new Circle[100];

  ellipseMode( CENTER_DIAMETER );
  noFill();
}

void loop()
{
  background( 50 );
  stroke(255,255,255,50);

  int ax, ay;
  cTimer++;
  if (cTimer>5 && cCounter<circles.length-1) {
    circles[cCounter] = new Circle( 250, 250, 40, cCounter );
    cCounter++;
    cTimer = 0;
  }

  float len = 10;

  for (int i=0; i<field.length; i++) {
    for (int k=0; k<field[0].length; k++) {
      ax = marginh + i*gaph;
      ay = marginv + k*gapv;
      line( ax, ay, ax+len*getCos(field[i][k]), ay+len*getSin(field[i][k]) );
    }
  }

  noStroke();
  noFill();

  for (int i =0; i<cCounter; i++) {
    circles[i].draw();
    circles[i].getGrid();
  }

}

int[] getLocation( int i, int k )
{
return new int[]{ marginh+i*gaph, marginv+k*gapv };
}

float getSin( float val )
{
  if (val<0) val = 6.28 + val;
  if (val>=6.28) val -= 6.28;
  val = min( 6.27, max( 0, val ) );
  return sintable[ (int)floor(val*100) ];
}

float getCos( float val )
{
  if (val<0) val = 6.28 + val;
  if (val>=6.28) val -= 6.28;
  val = min( 6.27, max( 0, val ) );

  return costable[ (int)floor(val*100) ];
}

class Circle
{
  float x, y, r, d, rr;
  float ac1, ac2, ac3;
  float sp1, sp2, sp3;
  int id;

  float inx = 0;
  float iny = 0;
  boolean over = true;

  Circle( float px, float py, float pr, int id ) {
    x = px;
    y = py;
    r = pr;
    d = r*2;
    rr = r*r;

    this.id = id;

    sp1 = random(2);
    sp2 = random(2);
    sp3 = random(2);

    ac1 = random(0.5)-random(0.5);
    ac2 = random(0.5)-random(0.5);
    ac3 = random(0.5)-random(0.5);

  }

  void draw() {
    move();
    ellipse( x, y, d, d );
  }

  void move() {
    float angle = sin( sp1 ) - cos(sp2);

    sp1+=ac1;
    sp2+=ac2;
    sp3+=ac3;

    angle = (angle<0) ? angle+PI2 : ( (angle>=PI2) ? angle-PI2 : angle );

    x = x + getSin(angle);
    y = y + getCos(angle);

    checkBounds();
    checkOverlap();

  }

  void checkBounds() {
    if ( x > width ) x = 0;
    if ( x < 0 ) x = width;
    if ( y > height ) y = 0;
    if ( y < 0 ) y = height;
  }

  void repel( float angle ) {
    x = x + getCos(angle)/10;
    y = y + getSin(angle)/10;
  }

  void setState( float px, float py ) {
    inx = px;
    iny = py;
    over = true;
  }

  void checkOverlap() {

    for ( int i=id+1; i<cCounter; i++ ) {
      if ( i!=id ) {
        float dx = circles[i].x - x;
        float dy = circles[i].y - y;
        float drr = dx*dx + dy*dy;
        float brr = circles[i].rr + 2*circles[i].r*r + rr;
        float d = sqrt( drr );

        if ( d>r+circles[i].r || d<abs(r-circles[i].r) ) continue; // no solution

        float ang = atan2( dy, dx );
        repel( ang+PI );
        circles[i].repel( ang);

        float a = (rr - circles[i].rr + drr) / (2*d);
        float h = sqrt( rr - a*a );
        float x2 = x + a*(circles[i].x - x)/d;
        float y2 = y + a*(circles[i].y - y)/d;

        setState( x2, y2 );
        circles[i].setState( x2, y2 );
      }
    }
  }

  void getGrid() {

    int sx = (int)ceil((x-r-marginh)/gaph);
    int sy = (int)ceil((y-r-marginv)/gapv);

    int numx = (int)floor(d/gaph);
    int numy = (int)floor(d/gapv);

    int[] pos;
    float dx, dy, ang;

    for (int i=sx; i<sx+numx; i++) {
      if (i>=0 && i<field.length) {
        for (int k=sy; k<sy+numy; k++) {
          if (k>=0 && k<field[0].length) {
            if (over) {
              pos = getLocation(i, k);
              dx = pos[0]-x;
              dy = pos[1]-y;

              if (dist( x, y, pos[0], pos[1]) < r) {
                float da = atan2( pos[1]-iny, pos[0]-inx );
                if (field[i][k] < da) field[i][k] += PI/20;
                else if (field[i][k] > da) field[i][k] -= PI/20;
                fieldShade[i][k] = 2;
              }
            }
          }
        }
      }
    }
    over = false;
  }
}