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 J. Tarbell <http://levitated.net>
   8 April 2004
   Processing v.68 <http://processing.org>

*/

// widthensions
int num = 100;
int time = 0;

// display flag
boolean showStructure = false;

// object array
Disc[] discs;


// methods
void setup() {
  size(500, 500);
  colorMode(RGB,255);
  ellipseMode(CENTER_RADIUS);
  background(0);
  framerate(30);

  // make 100 discs
  discs = new Disc[num];

  // arrange linearly
  for (int i=0;i<num;i++) {
    float x = random(width);
    float y = random(width);
    float fy = 0;
    float fx = random(-1.0,1.0);
    float r = 20+random(20);

    discs[i] = new Disc(i,x,y,fx,fy,r);
  }
}

// main
void loop() {
  if (showStructure) {
    background(0);
    // render circles and intersections
    for (int c=0;c<num;c++) {
      discs[c].draw();
    }
  }
  // move discs
  for (int c=0;c<num;c++) {
    discs[c].move();
    discs[c].render();
  }
  time++;
}

void keyReleased () {
  if (key==' ') {
    background(0);
    if (showStructure) {
      showStructure = false;
    } else {
      showStructure = true;
    }
  }
}

// translucent point
void tpoint(int x1, int y1, int gc, float a) {
  noStroke();
  fill(gc,a*255);
  rect(x1,y1,1,1);
}


// disc object
class Disc {
  // index identifier
  int id;
  // position
  float x,y;
  // radius
  float r, dr;
  // velocity
  float vx,vy;

  // sand painters
  int numsands = 1;
  SandPainter[] sands = new SandPainter[numsands];

  Disc(int Id, float X, float Y, float Vx, float Vy, float R) {
    // construct
    id=Id;
    x=X;
    y=Y;
    vx=Vx;
    vy=Vy;
    dr=R;
    r=1.0;

    // create sand painters
    for (int n=0;n<numsands;n++) {
      sands[n] = new SandPainter();
    }
  }

  void draw() {
    stroke(64);
    noFill();
    ellipse(x,y,r,r);
  }

  void render() {
    // find intersecting points with all ascending discs
    for (int n=id+1;n<num;n++) {
      // find distance to other disc
      float dx = discs[n].x-x;
      float dy = discs[n].y-y;
      float d = sqrt(dx*dx+dy*dy);
      // intersection test
      if (d<(discs[n].r+r)) {
        // complete containment test
        if (d>abs(discs[n].r-r)) {
          // find circle intersection solutions
          float a = (r*r - discs[n].r*discs[n].r + d*d ) / (2*d);

          float p2x = x + a*(discs[n].x - x)/d;
          float p2y = y + a*(discs[n].y - y)/d;

          float h = sqrt(r*r - a*a);

          float p3ax = p2x + h*(discs[n].y - y)/d;
          float p3ay = p2y - h*(discs[n].x - x)/d;

          float p3bx = p2x - h*(discs[n].y - y)/d;
          float p3by = p2y + h*(discs[n].x - x)/d;
          // P3a and P3B may be identical - ignore this case (for now)

          if (showStructure) {
            stroke(255);
            point(p3ax,p3ay);
            point(p3bx,p3by);
          } else {
            tpoint(int(p3ax-1),int(p3ay),255,0.21);
            tpoint(int(p3ax+1),int(p3ay),0,0.21);
            tpoint(int(p3bx-1),int(p3by),255,0.21);
            tpoint(int(p3ax+1),int(p3ay),0,0.21);
          }
          // draw sand painters
          for (int s=0;s<numsands;s++) {
            sands[s].render(p3ax,p3ay,p3bx,p3by);
          }
        }
      }
    }
  }

  void move() {
    // move radius towards destination radius
    if (r<dr) r+=0.02;
    // add velocity to position
    x+=vx;
    y+=vy;
    // bound check
    if (x+r<0) {
      x+=width+r+r;
      y=random(width);
    }
    if (x-r>width) {
      x-=width+r+r;
      y=random(width);
     }
    if (y+r<0) {
      y+=width+r+r;
      x=random(width);
    }
    if (y-r>width) {
      y-=width+r+r;
      x=random(width);
    }
  }

}

// sandpainter object
class SandPainter {

  float p;
  float g;
  int c;

  SandPainter() {
    p = random(1.0);
    c = int(random(256));
    g = random(0.01,0.1);
  }

  void render(float x, float y, float ox, float oy) {
    // draw painting sweeps
    tpoint(int(ox+(x-ox)*sin(p)),int(oy+(y-oy)*sin(p)),c,0.11);

    g+=random(-0.050,0.050);
    float maxg = 0.5;
    if (g<-maxg) g=-maxg;
    if (g>maxg) g=maxg;
    p+=random(-0.050,0.050);
    if (p<0) p=0;
    if (p>1.0) p=1.0;

    float w = g/10.0;
    for (int i=0;i<11;i++) {
      tpoint(int(ox+(x-ox)*sin(p + sin(i*w))),int(oy+(y-oy)*sin(p + sin(i*w))),c,0.1-i/110);
      tpoint(int(ox+(x-ox)*sin(p - sin(i*w))),int(oy+(y-oy)*sin(p - sin(i*w))),c,0.1-i/110);
    }
  }
}