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