class Walker { ArrayList particles; int dimension; int boundXLow; int boundXHigh; int boundYLow; int boundYHigh; int centerX; int centerY; ArrayList centerArchive; Walker(int _dimension) { particles = new ArrayList(); dimension = _dimension; boundXLow = width/2 - dimension/2; boundXHigh = width/2 + dimension/2; boundYLow = height/2 - dimension/2; boundYHigh = height/2 + dimension/2; centerX = width/2; centerY = height/2; centerArchive = new ArrayList(); } void addParticle(Particle p) { // Add new particle if it is contained by walker if(particleInBounds(p)) { particles.add(p); } } boolean particleInBounds(Particle p) { // Check if particle is contained by walker if((p.loc.x - p.rad > boundXLow) && (p.loc.x + p.rad < boundXHigh) && (p.loc.y - p.rad > boundYLow) && (p.loc.y + p.rad < boundYHigh)) return true; return false; } void go() { // Run walker solveCollisions(); render(); } void solveCollisions() { // Iterate through primary particles for(int i = 0; i < particles.size(); i++) { Particle currA = (Particle)particles.get(i); // Perform bounds collision procedure on primary particle boundsCollision(currA); // Iterate through comparison particles for(int j = 0; j < particles.size(); j++) { Particle currB = (Particle)particles.get(j); // Check if comparison should be made if(i != j && currA.visited == false && currB.visited == false) { // Perform particle collision procedure particleCollision(currA, currB); } } // Update primary particle's position currA.loc.add(currA.vel); // Record that primary particle has been visited currA.visited = true; } } void boundsCollision(Particle p) { // Check if particle collides with bottom side of walker if(p.loc.x - p.rad < boundXLow) { // Push in from walker wall p.loc.x = boundXLow + p.rad; // Reverse particle direction p.vel.x *= -1; // Set collision status p.justCollided = JUST_COLLIDED_FRAMECOUNT; // Move walker boundXLow -= 1; boundXHigh -= 1; } // Check if particle collides with top side of walker else if(p.loc.x + p.rad > boundXHigh) { // Push in from walker wall p.loc.x = boundXHigh - p.rad; // Reverse particle direction p.vel.x *= -1; // Set collision status p.justCollided = JUST_COLLIDED_FRAMECOUNT; // Move walker boundXLow += 1; boundXHigh += 1; } // Check if particle collides with left side of walker if(p.loc.y - p.rad < boundYLow) { // Push in from walker wall p.loc.y = boundYLow + p.rad; // Reverse particle direction p.vel.y *= -1; // Set collision status p.justCollided = JUST_COLLIDED_FRAMECOUNT; // Move walker boundYLow -= 1; boundYHigh -= 1; } // Check if particle collides with right side of walker else if(p.loc.y + p.rad > boundYHigh) { // Push in from walker wall p.loc.y = boundYHigh - p.rad; // Reverse particle direction p.vel.y *= -1; // Set collision status p.justCollided = JUST_COLLIDED_FRAMECOUNT; // Move walker boundYLow += 1; boundYHigh += 1; } // Keep walker on screen if(constrainWalkerToScreen) { if(boundXLow < 0) { boundXLow = 0; boundXHigh = boundXLow + dimension; } else if(boundXHigh > width) { boundXHigh = width; boundXLow = boundXHigh - dimension; } if(boundYLow < 0) { boundYLow = 0; boundYHigh = boundYLow + dimension; } else if(boundYHigh > height) { boundYHigh = height; boundYLow = boundYHigh - dimension; } } } void particleCollision(Particle a, Particle b){ // Procedure interpreted from: http://processing.org/learning/topics/circlecollision.html // Get distance vector between particles PVector distVect = new PVector(); distVect.x = b.loc.x - a.loc.x; distVect.y = b.loc.y - a.loc.y; // Calculate magnitude of distance vector float distVectMag = sqrt(sq(distVect.x) + sq(distVect.y)); if (distVectMag < a.rad + b.rad) { // Set each particle's collision status a.justCollided = JUST_COLLIDED_FRAMECOUNT; b.justCollided = JUST_COLLIDED_FRAMECOUNT; // Get angle of distance vector float theta = atan2(distVect.y, distVect.x); // Precalculate trig values float sine = sin(theta); float cosine = cos(theta); // Store temporary positions float aTempLX = 0.0; float aTempLY = 0.0; float bTempLX = cosine * distVect.x + sine * distVect.y; float bTempLY = cosine * distVect.y - sine * distVect.x; // Store and rotate temporary velocities float aTempVX = cosine * a.vel.x + sine * a.vel.y; float bTempVX = cosine * b.vel.x + sine * b.vel.y; // Calculate final velocities with 1D conservation of momentum float aFinalVX = (((a.rad*0.1) - (b.rad*0.1)) * aTempVX + 2 * (b.rad*0.1) * bTempVX) / ((a.rad*0.1) + (b.rad*0.1)); float aFinalVY = cosine * a.vel.y - sine * a.vel.x; float bFinalVX = (((b.rad*0.1) - (a.rad*0.1)) * bTempVX + 2 * (a.rad*0.1) * aTempVX) / ((a.rad*0.1) + (b.rad*0.1)); float bFinalVY = cosine * b.vel.y - sine * b.vel.x; // Avoid clumping aTempLX += aFinalVX; bTempLX += bFinalVX; // Unrotate particle locations and velocities float aFinalX = cosine * aTempLX - sine * aTempLY; float aFinalY = cosine * aTempLY + sine * aTempLX; float bFinalX = cosine * bTempLX - sine * bTempLY; float bFinalY = cosine * bTempLY + sine * bTempLX; // Set particle locations b.loc.x = a.loc.x + bFinalX; b.loc.y = a.loc.y + bFinalY; a.loc.x = a.loc.x + aFinalX; a.loc.y = a.loc.y + aFinalY; // Set particle velocities a.vel.x = cosine * aFinalVX - sine * aFinalVY; a.vel.y = cosine * aFinalVY + sine * aFinalVX; b.vel.x = cosine * bFinalVX - sine * bFinalVY; b.vel.y = cosine * bFinalVY + sine * bFinalVX; } } void render() { stroke(0); fill(175); // Handle walker if(drawWalker) { rect(boundXLow,boundYLow,boundXHigh-boundXLow,boundYHigh-boundYLow); } // Update walker center centerX = (boundXLow+boundXHigh)/2; centerY = (boundYLow+boundYHigh)/2; Pt newPt = new Pt(centerX,centerY); centerArchive.add(newPt); // Handle particles for(int i = 0; i < particles.size(); i++) { Particle curr = (Particle)particles.get(i); // Draw Particle (if feature is turned on) if(drawParticles) curr.render(); // Reset visited parameter for next iteration curr.visited = false; // Decrement collision status for next iteration curr.justCollided--; } // Handle walker center path if(drawCenterPath) { for(int i = 0; i < centerArchive.size()-1; i++) { // Get current and next points in path Pt currPt = (Pt)centerArchive.get(i); Pt nextPt = (Pt)centerArchive.get(i+1); // Set color int colorVal = int(map(i,0,centerArchive.size()-1,0,255)); stroke(0,colorVal,0); // Draw current line segment line(currPt.x,currPt.y,nextPt.x,nextPt.y); } } } void setParticleRadius(int r) { // Iterate through particles and change their radii for(int i = 0; i < particles.size(); i++) { Particle curr = (Particle)particles.get(i); curr.rad = r; } } }