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