/*
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>
*/
Circle[] cc;
void setup()
{
size( 600, 600 );
framerate( 30 );
cc = new Circle[100];
for (int i=0; i<cc.length; i++) {
cc[i] = new Circle( random(width), random(height), 15+random(20), i );
}
ellipseMode(CENTER_DIAMETER);
rectMode(CENTER_DIAMETER);
noFill();
}
void loop()
{
background(255);
noStroke();
fill(0);
for (int i=0; i<cc.length; i++) {
cc[i].draw();
}
noFill();
stroke(255);
for (int i=0; i<cc.length; i++) {
cc[i].drawHair();
}
}
class Circle {
float x, y, r, r2, d, d2;
float lastx, lasty;
float ac1, ac2, ac3;
float sp1, sp2, sp3;
float inx = 0;
float iny = 0;
boolean hasIntersect = false;
Hair[] hairs;
int gray;
int id;
Circle( float px, float py, float pr, int id ) {
x = px;
y = py;
r = pr;
r2 = r*r;
d = r*2;
d2 = d*d;
this.id = id;
gray = 0;
hairs = new Hair[30];
float stepA = 2*PI/hairs.length;;
for (int i=0; i<hairs.length; i++) {
hairs[i] = new Hair( cos(stepA*i)*r, sin(stepA*i)*r, 5, stepA*i+PI, this );
}
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() {
ellipse(x, y, d, d);
move();
}
void drawHair() {
for (int i=0; i<hairs.length; i++) {
hairs[i].updatePos();
hairs[i].draw();
}
}
void move() {
float angle = sin( sp1 ) - cos(sp2);
sp1+=ac1;
sp2+=ac2;
sp3+=ac3;
angle = (angle<0) ? angle+TWO_PI : ( (angle>=TWO_PI) ? angle-TWO_PI : angle );
x += sin(angle);
y -= cos(angle);
checkBounds();
checkIntersect();
}
void checkIntersect() {
boolean flag = false;
boolean flag2 = false;
for (int i=0; i<cc.length; i++) {
if (i!=id) {
flag = intersect( cc[i]);
if (!flag2) flag2 = flag;
}
}
if (flag2) {
hairFocus(inx, iny);
} else if ( hasIntersect ) {
hairFocus();
}
hasIntersect = flag2;
}
boolean intersect( Circle cB ) {
float dx = x - cB.x;
float dy = y - cB.y;
float d2 = dx*dx + dy*dy;
float d = sqrt( d2 );
if ( d>r+cB.r || d<abs(r-cB.r) ) return false;
float a = (r2 - cB.r2 + d2) / (2*d);
float h = sqrt( r2 - a*a );
float x2 = x + a*(cB.x - x)/d;
float y2 = y + a*(cB.y - y)/d;
float paX = x2 + h*(cB.y - y)/d;
float paY = y2 - h*(cB.x - x)/d;
//float pbX = x2 - h*(cB.y - y)/d;
//float pbY = y2 + h*(cB.x - x)/d;
repel( atan2( dy, dx) );
ellipse( paX, paY, 15, 15 );
//ellipse( pbX, pbY, 20, 20 );
//if ( cB.id > id ) {
inx = x2;
iny = y2;
//} else {
//inx = pbX;
//iny = pbY;
//}
return true;
}
void repel( float angle ) {
x = x + cos(angle)/4;
y = y + sin(angle)/4;
}
void hairFocus( float px, float py) {
for (int i=0; i<hairs.length; i++) {
hairs[i].focus( px, py );
}
}
void hairFocus() {
for (int i=0; i<hairs.length; i++) {
hairs[i].revertFocus();
}
}
void checkBounds() {
//x = Math.min( width, Math.max( 0, x ) );
//y = Math.min( height, Math.max( 0, y ) );
if ( x > width ) x = 0;
if ( x < 0 ) x = width;
if ( y > height ) y = 0;
if ( y < 0 ) y = height;
}
}
class Hair {
float repel_speed;
float radius, origRadius;
float angle, origAngle;
float x, y, regX, regY, nextX, nextY, lastx, lasty;
float speedFactor = 5;
Circle parent;
Hair( float rx, float ry, float r, float a, Circle parent ) {
radius = r;
origRadius = r;
angle = a;
origAngle = a;
this.parent = parent;
regX = rx;
regY = ry;
nextX = parent.x+regX+cos(angle)*radius;
nextY = parent.y+regY+sin(angle)*radius;
x = nextX;
y = nextY;
}
void updatePos() {
nextX = parent.x+regX+cos(angle)*radius;
nextY = parent.y+regY+sin(angle)*radius;
float dx = nextX - x;
float dy = nextY - y;
if (abs(dx)>1) {
x += dx/speedFactor;
y += dy/speedFactor;
}
if (abs(dx)>200 || abs(dy)>200) {
x = nextX;
y = nextY;
}
}
void draw() {
line( parent.x+regX, parent.y+regY, x, y );
}
void focus( float px, float py ) {
float dx = px-(parent.x+regX);
float dy = py-(parent.y+regY);
angle = atan2( dy, dx );
radius = dist( px, py, parent.x+regX, parent.y+regY );
}
void revertFocus() {
angle = origAngle;
radius = origRadius;
}
}