class Ball {
int camp = 0;
int state = 0;
boolean dead = false;
boolean colliding = false;
color c;
float cAlpha = 20;
float r;
float theta = 0.00;
PVector loc = new PVector(0, 0);
PVector vel = new PVector(0, 0);
Ball(float $R) {
r = $R/2;
loc.x = random(r,width-(r));
loc.y = random(r,height-(r));
vel.x = 0.05*random(-5,5);
vel.y = 0.05*random(-5,5);
}
Ball(float $R, int $camp) {
r = $R/2;
loc.x = random(r,width-(r));
loc.y = random(r,height-(r));
vel.x = 0.05*random(-5,5);
vel.y = 0.05*random(-5,5);
camp = $camp;
}
Ball(float $R, PVector $v, PVector $l) {
r = $R/2;
vel = $v.get();
loc = $l.get();
}
Ball(float $R, PVector $v, PVector $l, int $camp) {
r = $R/2;
vel = $v.get();
loc = $l.get();
camp = $camp;
}
void go(float $i) {
cAlpha = constrain(cAlpha, minAlpha, maxAlpha);
removeDead();
if (!pause) {
move();
}
display();
}
void move(){
if (dead == false) {
border();
loc.add(vel);
if (gravityOn) {
vel.add(gravity);
}
}
}
void moveTo(float $x, float $y){
if (dead == false) {
border();
loc = new PVector($x, $y);
}
}
private void border(){
if (loc.y > height-r) {
vel.y *= -bounce;
loc.y = height-r;
}
else if (loc.y < r) {
vel.y *= -bounce;
loc.y = r;
}
if (loc.x > width-r) {
vel.x *= -bounce;
loc.x = width-r;
}
else if (loc.x < r) {
vel.x *= -bounce;
loc.x = r;
}
}
void display(){
if (dead == false) {
noStroke();
r = constrain(r, 0, width+height/8);
fillAs();
fill(c);
ellipse(loc.x, loc.y, r*2, r*2);
}
}
void fillAs() {
switch (camp) {
case 1:
c = color(255, 100, 100, cAlpha);
break;
case 2:
c = color(255, 240, 100, cAlpha);
break;
case 3:
c = color(50, 144, 255, cAlpha);
break;
case 0:
c = color(0, cAlpha);
break;
}
}
void removeDead() {
if(r < 0.5) {
dead = true;
loc.x = -100;
loc.y = -100;
}
}
boolean isInRangeAndBigger(Ball $b1, Ball $b2, float range){
if ($b1.dead == false && $b2.dead == false) {
float distance = dist($b1.loc.x, $b1.loc.y, $b2.loc.x, $b2.loc.y);
if (distance < $b1.r*range + $b2.r*range) {
if($b1.r > $b2.r) {
return true;
}
else return false;
}
else return false;
}
else return false;
}
boolean isInRange(Ball $b1, Ball $b2, float $range){
if ($b1.dead == false && $b2.dead == false) {
float distance = dist($b1.loc.x, $b1.loc.y, $b2.loc.x, $b2.loc.y);
if (distance < $b1.r*$range + $b2.r*$range) {
return true;
}
else return false;
}
else return false;
}
void collideEqualMass(Ball $t) {
float d = PVector.dist(loc, $t.loc);
float sumR = r + $t.r;
if (!colliding && d < sumR) {
colliding = true;
PVector n = PVector.sub($t.loc, loc);
n.normalize();
PVector u = PVector.sub(vel, $t.vel);
PVector un = componentVector(u,n);
u.sub(un);
vel = PVector.add(u, $t.vel);
$t.vel = PVector.add(un, $t.vel);
}
else if (d > sumR) {
colliding = false;
}
}
}
PVector componentVector (PVector vector, PVector directionVector) {
directionVector.normalize();
directionVector.mult(vector.dot(directionVector));
return directionVector;
}
class Hungry {
float assimilation;
float reduction;
Hungry(float $tempAssimilation, float $tempReduction) {
assimilation = $tempAssimilation;
reduction = $tempReduction;
}
void hunger(int $thisBall, Ball[] $tempBalls) {
if (!pause) {
for (int i=0; i<$tempBalls.length; i++){
if (i != $thisBall) {
Ball tempBallObjective = $tempBalls[i];
if ($tempBalls[$thisBall].camp == tempBallObjective.camp) {
if ($tempBalls[$thisBall].isInRangeAndBigger($tempBalls[$thisBall], tempBallObjective, 1)) {
$tempBalls[$thisBall].cAlpha += tempBallObjective.cAlpha/tempBallObjective.r/5;
tempBallObjective.cAlpha -= tempBallObjective.cAlpha/tempBallObjective.r/5;
$tempBalls[$thisBall].r += assimilation/PI/2;
tempBallObjective.r -= reduction/2;
}
}
else if (PVector.dist($tempBalls[$thisBall].loc, tempBallObjective.loc)+tempBallObjective.r < $tempBalls[$thisBall].r) {
tempBallObjective.r -= $tempBalls[$thisBall].r/$tempBalls[$thisBall].cAlpha;
}
else {
Balls[$thisBall].collideEqualMass(Balls[i]);
}
}
}
}
}
}
/*
World of Colorfulness
by Foo Shun @NCKU, TW on 29 May 2009
fooshun@gmail.com
code modified and combined from:
"CollisionEqualMass" by Daniel Shiffman
http://www.shiffman.net
"BallWord in Grid" by pablo.vazquez
http://www.openprocessing.org/visuals/?visualID=817
click mouse: accelerate
press [SPACE]: pause
press [g]: gravity on/off
press [r]: start a new world
press [v]: show ball's vector on/off
press [1]: background redraw on (white)
press [2]: background redraw on (transparent white)
press [3]: background redraw off
when this apllet starts, balls are positioned in a grid or random place.
there are up to 4 different camps of balls,
balls can be assembled to be bigger and their color would be darker,
and balls of different camps would collide;
but if the ball is inside of the other,
the smaller one would be gradually wiped out.
*/
int maxBalls = 1000;
int camps;
float minSize;
float maxAlpha = 220;
float minAlpha = 5;
float acc = 0.03;
float bounce = 1.0;
float bgAlpha = 255;
boolean showVectors = false;
boolean pause = false;
boolean gravityOn = false;
PVector gravity = new PVector(0.0, 0.125);
Ball[] Balls = new Ball[maxBalls];
Hungry hungry = new Hungry(1.0, 1.0);
void setup() {
background(255);
size(612, 315, P2D);
smooth();
setBalls();
frameRate(30);
}
void draw() {
if (bgAlpha != 0) {
fill(255, bgAlpha);
rect(0, 0, width, height);
}
for (int i=0;i < Balls.length;i++) {
if (Balls[i].dead == false) {
hungry.hunger(i, Balls);
Balls[i].go(i);
}
}
if (showVectors) {
for (int i = 0; i < Balls.length; i++) {
drawVector(Balls[i].vel, Balls[i].loc, 10);
}
}
if (mousePressed) {
for (int i=0;i < Balls.length;i++) {
if (Balls[i].dead == false) {
PVector tempVector = new PVector(acc*random(-5, 5), acc*random(-5,5));
Balls[i].vel.add(tempVector);
}
}
}
if (pause) {
println();
println();
println("paused");
}
else {
println();
println("minSize = " + minSize);
println("camps = " + camps);
}
}
void setBalls() {
camps = (int)random(4)+1;
minSize = (screen.width+screen.height)/400*(0.75+camps/4);
for (int i=0;i < Balls.length;i++) {
Balls[i] = new Ball(random(minSize, minSize+1), (int)random(camps));
}
int ballIndex = 0;
int gridX = 12;
int gridY = 15;
if (random(2) < 1) {
for (int y=1; y < height/gridY; y++){
for (int x=1;x < width/gridX; x++){
Balls[ballIndex].moveTo(x*gridX, y*gridY);
if(ballIndex < maxBalls-1) {
ballIndex++;
}
}
}
}
}
void keyPressed() {
switch(key) {
case ' ':
pause = !pause;
break;
case 'r':
setBalls();
break;
case 'v':
showVectors = !showVectors;
break;
case 'g':
gravityOn = !gravityOn;
break;
case '1':
bgAlpha = 255;
break;
case '2':
bgAlpha = 50;
break;
case '3':
bgAlpha = 0;
break;
}
}
void drawVector(PVector $v, PVector $loc, float $scayl) {
pushMatrix();
float arrowsize = 6;
translate($loc.x, $loc.y);
stroke(150);
rotate($v.heading2D());
float len = $v.mag()*$scayl;
line(0, 0, len, 0);
line(len, 0, len-arrowsize, +arrowsize/2);
line(len, 0, len-arrowsize, -arrowsize/2);
popMatrix();
}
code modified and combined from:
"CollisionEqualMass" by Daniel Shiffman
http://www.shiffman.net
"BallWord in Grid" by pablo.vazquez
http://www.openprocessing.org/visuals/?visualID=817
press mouse: accelerate
press [SPACE]: pause
press [g]: gravity on/off
press [r]: start a new world
press [v]: show ball's vector on/off
press [1]: background redraw on (white)
press [2]: background redraw on (transparent white)
press [3]: background redraw off
up to 4 colors of balls