/* 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 Robert Hodgin <http://flight404.com> 7 April 2004 Processing v.68 <http://processing.org> */ // ******************************************************************************** // INITIALIZE VARIABLES // ******************************************************************************** int xStage = 900; // x dimension of applet int yStage = 500; // y dimension of applet int xMid = xStage/2; // x midpoint of applet int yMid = yStage/2; // y midpoint of applet int totalCircles = 100; // total number of circles Circle[] circle; // Circle object array float gravity; // Strength of gravitational pull float xGrav; // x point of center of gravity float yGrav; // y point of center of gravity float xGravOffset; // offset var to warp gravitational field float angleOffset; // sets the initial point of rotation for the creation of the circles float initRadius; // the starting radius of the creation of the circles float maxDistance; // maximum distance allowed for connections color bgColor; // BOOLEANS FOR TESTING PURPOSES boolean clear = false; int timer = 0; int timerPause = 750; int timerMax = 775; // ******************************************************************************** // SETUP FUNCTION // ******************************************************************************** void setup() { size(900, 500); bgColor = color(20,20,20); background(bgColor); smooth(); colorMode(RGB, 255); ellipseMode(CENTER_RADIUS); noStroke(); framerate(30); createCircles(); } // ******************************************************************************** // MAIN LOOP FUNCTION // ******************************************************************************** void loop() { if (clear){ background(bgColor); } dealWithTimer(); if (timer < timerPause){ tellCirclesToBehave(); } } void createCircles(){ gravity = random(0.005,0.1); xGrav = xMid + random(-50,50); yGrav = yMid + random(-50,50); xGravOffset = random(1.0, 1.24); maxDistance = random(75,150); angleOffset = random(360); circle = new Circle[totalCircles]; initRadius = random(100,140); for (int i=0; i<totalCircles; i++){ float initAngle = i * 3.6 + angleOffset; float initTheta = (-((initAngle) * PI))/180; float initxv = cos(initTheta) * initRadius; float inityv = sin(initTheta) * initRadius; float xPos = xMid + initxv; float yPos = yMid + inityv; circle[i] = new Circle(xPos, yPos,0,0, i); } } void tellCirclesToBehave(){ for (int i=0; i<totalCircles; i++){ circle[i].behave(); } } void dealWithTimer(){ if (timer > timerMax){ timer = 0; background(bgColor); createCircles(); } timer ++; } class Circle { int index; // Circle global ID float x; // Circle x position float y; // Circle y position float r; // Circle radius float rBase; // Circle original radius float angle; // Angle of movement in degrees float theta; // Angle of movement in radians float speed; // Speed of movement float xv; // Current velocity along x axis float yv; // Current velocity along y axis boolean[] mightCollide; // Collision might happen boolean[] hasCollided; // Collision is happening float[] distances; // Storage for the distance between circles float[] angles; // Storage for the angle between two connected circles float[] thetas; // Storage for the radians between two connected circles int numCollisions; // Number of collisions in one frame int numConnections; // Total number of collisions float xd; // Distance to target along x axis float yd; // Distance to target along y axis float d; // Distance to target float alphaVar; // Late addition variable for alpha modification float cAngle; // Angle of collision in degrees float cTheta; // Angle of collision in radians float cxv; // Collision velocity along x axis float cyv; // Collision velocity along y axis float gAngle; // Angle to gravity center in degrees float gTheta; // Angle to gravity center in radians float gxv; // Gravity velocity along x axis float gyv; // Gravity velocity along y axis Circle (float xSent, float ySent, float xvSent, float yvSent, int indexSent){ x = xSent; y = ySent; r = 4; index = indexSent; xv = xvSent; yv = yvSent; alphaVar = random(35); mightCollide = new boolean[totalCircles]; hasCollided = new boolean[totalCircles]; distances = new float[totalCircles]; angles = new float[totalCircles]; thetas = new float[totalCircles]; } void behave(){ move(); areWeClose(); areWeColliding(); areWeConnected(); applyGravity(); //move(); // this is where the move function should go, but for some reason having it come first makes for more interesting renders. render(); reset(); } void areWeClose(){ for(int i=0; i<totalCircles; i++){ if (i != index){ if (abs(x - circle[i].x) < 50 && abs(y - circle[i].y) < 50){ mightCollide[i] = true; } else { mightCollide[i] = false; } } } } void areWeColliding(){ for(int i=0; i<totalCircles; i++){ if (mightCollide[i] && i != index){ distances[i] = findDistance(x, y, circle[i].x, circle[i].y); if (distances[i] < (r + circle[i].r) * 1.1){ hasCollided[i] = true; circle[i].hasCollided[index] = true; angles[i] = findAngle(x,y,circle[i].x,circle[i].y); thetas[i] = (-(angles[i] * PI))/180.0; cxv += cos(thetas[i]) * ((circle[i].r + r)/2.0); cyv += sin(thetas[i]) * ((circle[i].r + r)/2.0); numCollisions += 1; } } } if (numCollisions > 0){ xv = -cxv/numCollisions; yv = -cyv/numCollisions; } cxv = 0.0; cyv = 0.0; } void areWeConnected(){ for(int i=0; i<totalCircles; i++){ if (hasCollided[i] && i != index){ distances[i] = findDistance(x, y, circle[i].x, circle[i].y); if (distances[i] < maxDistance){ angles[i] = findAngle(x,y,circle[i].x,circle[i].y); thetas[i] = (-(angles[i] * PI))/180.0; cxv += cos(thetas[i]) * (circle[i].r/10.0); cyv += sin(thetas[i]) * (circle[i].r/10.0); numConnections += 1; } else { hasCollided[i] = false; circle[i].hasCollided[index] = false; } } } if (numConnections > 0){ xv += (cxv/numConnections)/4.0; yv += (cyv/numConnections)/4.0; } cxv = 0.0; cyv = 0.0; r = numConnections + 1; } void applyGravity(){ gAngle = findAngle(x,y,xGrav,yGrav); gTheta = (-(gAngle * PI))/180; gxv = cos(gTheta) * gravity * xGravOffset; gyv = sin(gTheta) * gravity; xv += gxv; yv += gyv; } void move(){ x += xv; y += yv; } void render(){ noFill(); d = findDistance(x, y, xMid, yMid); if (d > initRadius){ stroke(255 - d, d - alphaVar); } else { stroke(d, d - alphaVar); } point(x,y); noStroke(); //fill(255,255); //ellipse(x, y, r, r); if (numCollisions > 0){ fill(255,4); ellipse(x, y, r * 5.0, r * 5.0 + 5); ellipse(x, y, r * 3.0, r * 3.0 + 5); if (d > initRadius){ fill(255,255); } else { fill(0,255); } ellipse(x, y, r * 0.5, r * 0.5); } for(int i=0; i<totalCircles; i++){ if (hasCollided[i] && i < index){ stroke(abs(angles[i] - 180)*1.3, 5); //stroke(abs(255 - d*.75),6); line (x, y, circle[i].x, circle[i].y); } } noStroke(); } void reset(){ numCollisions = 0; numConnections = 0; } } float findDistance(float x1, float y1, float x2, float y2){ float xd = x1 - x2; float yd = y1 - y2; float td = sqrt(xd * xd + yd * yd); return td; } float findAngle(float x1, float y1, float x2, float y2){ float xd = x1 - x2; float yd = y1 - y2; float t = atan2(yd,xd); float a = (180 + (-(180 * t) / PI)); return a; }