/** petit bleu et petit jaune by renaud richardet, oct 07 work is licensed under a creative commons attribution-Share alike 3.0 license (http://creativecommons.org/licenses/by-sa/3.0/) */ // parameters int numBalls = 14; float SPRING = 0.1;; float EXPLODE_SPRING = 0.2; float START_SPEED = 1.5; //constants color BLUE = color(25, 64, 133);//cst color YELLOW=color(255, 255, 0); color GREEN=color(22, 129,0); float HIDDEN = -100; // coord for hidden balls; Ball[] balls = new Ball[numBalls]; void setup() { size(600, 600); noStroke(); smooth(); for (int i = 0; i < numBalls; i++) { balls[i] = new Ball(random(width), random(height), random(20, 40), i, balls); } } void draw() { background(255); for (int i = 0; i < numBalls; i++) {// each ball if (!(balls[i].isHidden())){// only non-hidden balls[i].collide(); balls[i].move(); balls[i].display(); balls[i].maybeExplode(); } } } class Ball { float x, y; float diameter; float vx = random(1.5);//random speed float vy = random(1.5); int id; int c; Ball[] others; int stayTogether = 0;//stay together for ... cycles Ball(float xin, float yin, float din, int idin, Ball[] oin) { x = xin; y = yin; diameter = din; id = idin; others = oin; c= (random(1.0)<0.5) ? BLUE: YELLOW; // init with blue or yellow } void collide() { for (int i = id + 1; i < numBalls; i++) { Ball other = others[i]; float dx = other.x - x; float dy = other.y - y; float distance = sqrt(dx*dx + dy*dy); if ((other.c == BLUE && c == YELLOW)||(c == BLUE && other.c == YELLOW )){//merge float minMergeDist = other.diameter/3 + diameter/3;// only merge if close enough if (distance < minMergeDist) { // this ball turns green x = (other.x+x)/2;//mean location y = (other.y+y)/2; vx = (other.vx+vx)/2;// mean speed vy = (other.vy+vy)/2; diameter += other.diameter; //add diameter c=GREEN; stayTogether = (int) random (200,400); // hide the other ball other.x = HIDDEN; other.y = HIDDEN; other.vx = 0; other.vy = 0; other.c = color(0);// so that it does not merge } } else{//bounce float minDist = others[i].diameter/2 + diameter/2; if (distance < minDist) { float angle = atan2(dy, dx); float targetX = x + cos(angle) * minDist; float targetY = y + sin(angle) * minDist; float ax = (targetX - others[i].x) * SPRING; float ay = (targetY - others[i].y) * SPRING; vx -= ax; vy -= ay; others[i].vx += ax; others[i].vy += ay; } } } } void move() { x += vx; y += vy; if (x + diameter/2 > width) {//hitting right border x = width - diameter/2; vx *= -1; } else if (x - diameter/2 < 0) {// hitting left border x = diameter/2; vx *= -1; } if (y + diameter/2 > height) { y = height - diameter/2; vy *= -1; } else if (y - diameter/2 < 0) { y = diameter/2; vy *= -1; } } boolean isHidden(){ if (x==HIDDEN) return true; return false; } void maybeExplode(){ if (c==GREEN){ if (stayTogether-- ==0){ Ball other = balls[getFirstHidden()]; other.c = YELLOW; c = BLUE; float share= random(0.3,0.7); // speed and size is distributed btw the 2 nodes other.diameter = diameter * (1-share); diameter *= share; // simulates split speeds other.vx = vx + vy/4 + ( (vx>0) ? EXPLODE_SPRING : -(EXPLODE_SPRING) ); vx = vx - vy/4 + ( (vx>0) ? EXPLODE_SPRING : -(EXPLODE_SPRING) ); other.vy = vy + vx/4 + ( (vy>0) ? EXPLODE_SPRING : -(EXPLODE_SPRING) ); vy = vy - vx/4 + ( (vy>0) ? EXPLODE_SPRING : -(EXPLODE_SPRING) ); other.x = x +other.diameter/3 +2; other.y = y +other.diameter/3 +2; x = x -diameter /3 -2; y = y -diameter /3 -2; } } } void display() { fill(c); ellipse(x, y, diameter, diameter); } } int getFirstHidden(){ for (int i = 0; i < numBalls; i++) {// each ball if ((balls[i].isHidden())){// first hidden return i; } } return -1;//error }