/*
Code by Diana Lange
www.diana-lange.de
This is an exercise to teach myself how to write a class.
You can grab a ball and shoot with it.
Controls:
1 / 2 / 3 = toggle between color sets
Space = change background color
M = show / hide connections
N = show / hide crash location
B = create new set of balls
Right = add new ball
Left = remove a ball
Any key = pause
http://www.vimeo.com/19564417
*/
import processing.video.*;
MovieMaker mm;
boolean saveMov = false, showConnections = false, showCrashPoint = false, colorSetOne = true, colorSetTwo = false, colorSetThree = false, whiteBG = true, newColors = false;
int frameCounter = 0, movieLength = 1200;
float minS = 0.4, maxS = 1.5;
int amount = 30, numOfRecursition = 0;
ArrayList ballsList;
void setup ()
{
size (800, 600);
smooth();
frameRate (25);
ballsList = new ArrayList (amount);
createBalls();
checkOverLay();
}
void draw ()
{
if (whiteBG) background (247);
else background (5);
moveBalls ();
if (saveMov == true)
{
mm.addFrame();
frameCounter++;
}
if (frameCounter >= movieLength)
{
saveMov = false;
mm.finish();
exit();
}
}
void moveBalls ()
{
for (int i = 0; i < amount; i++)
{
Ball checkBall = (Ball) ballsList.get(i);
PVector mp = new PVector (mouseX, mouseY);
boolean mouseisOverEllipse = checkBall.mouseDistance(mp.x, mp.y);
boolean mouseIsPressed = checkBall.checkMousePress (mouseisOverEllipse, mousePressed);
boolean mouseIsReleased_ = checkBall.checkMouseRelease (mouseisOverEllipse, mouseIsReleased);
if (newColors) checkBall.createColorList();
checkBall.shootWithMouse (mouseisOverEllipse, mouseIsPressed, mouseIsReleased_);
checkBall.checkDistance (i, mouseIsPressed);
if(keyPressed == false) checkBall.move(mouseIsPressed, mp);
checkBall.display(mouseisOverEllipse, mouseIsPressed);
}
mouseIsReleased = false;
newColors = false;
}
void createBalls()
{
for (int i = 0; i < amount; i++)
{
float maxEllipseSize = (width+height) / 20;
int ellipseSize = (int) random (50, maxEllipseSize);
PVector startPoint = new PVector (random (ellipseSize/2, width-ellipseSize/2), random (ellipseSize/2, height-ellipseSize/2));
PVector startSpeed = new PVector (random (-maxS, maxS), random (-maxS, maxS));
float s = minS;
if (startSpeed.x <= s && startSpeed.x >= 0) startSpeed.x = s+0.01;
if (startSpeed.x < 0 && startSpeed.x >= -s) startSpeed.x = -s-0.01;
if (startSpeed.y <= s && startSpeed.y >= 0) startSpeed.y = s+0.01;
if (startSpeed.y < 0 && startSpeed.y >= -s) startSpeed.y = -s-0.01;
int ellipseNum = (int) random (4, 10);
ballsList. add ( new Ball (startPoint, startSpeed, ellipseNum, ellipseSize));
}
}
void checkOverLay ()
{
boolean overLayFound = false;
for (int i = 0; i < amount; i++)
{
Ball checkBall = (Ball) ballsList.get(i);
PVector cb = checkBall.location;
for (int j = 0; j < amount; j++)
{
Ball nextBall = (Ball) ballsList.get(j);
PVector nb = nextBall.location;
float distance = PVector.dist(cb, nb);
float overLaydistance = checkBall.ellipseSize/2 + nextBall.ellipseSize/2;
if (distance < overLaydistance && i != j)
{
float maxEllipseSize = (width+height) / 20;
int ellipseSize = (int) random (50, maxEllipseSize);
PVector startPoint = new PVector (random (ellipseSize/2, width-ellipseSize/2), random (ellipseSize/2, height-ellipseSize/2));
PVector startSpeed = new PVector (random (-maxS, maxS), random (-maxS, maxS));
float s = minS;
if (startSpeed.x <= s && startSpeed.x >= 0) startSpeed.x = s+0.01;
if (startSpeed.x < 0 && startSpeed.x >= -s) startSpeed.x = -s-0.01;
if (startSpeed.y <= s && startSpeed.y >= 0) startSpeed.y = s+0.01;
if (startSpeed.y < 0 && startSpeed.y >= -s) startSpeed.y = -s-0.01;
int ellipseNum = (int) random (4, 10);
ballsList. add ( new Ball (startPoint, startSpeed, ellipseNum, ellipseSize));
numOfRecursition ++;
overLayFound = true;
}
}
}
if (overLayFound == true && numOfRecursition < 40) checkOverLay ();
}
boolean mouseIsReleased = false;
void keyPressed ()
{
if (keyCode == KeyEvent.VK_SPACE) whiteBG = !whiteBG;
if (keyCode == KeyEvent.VK_1) {
colorSetOne = !colorSetOne;
colorSetTwo = false;
colorSetThree = false;
newColors = true;
}
if (keyCode == KeyEvent.VK_2) {
colorSetTwo = !colorSetTwo;
colorSetOne = false;
colorSetThree = false;
newColors = true;
}
if (keyCode == KeyEvent.VK_3) {
colorSetThree = !colorSetThree;
colorSetOne = false;
colorSetTwo = false;
newColors = true;
}
if (keyCode == KeyEvent.VK_CONTROL)
{
if (saveMov == false)
{
frameCounter = 0;
saveMov = true;
mm = new MovieMaker (this, width, height, "export/" + timeStamp () + ".mov", 25, MovieMaker.ANIMATION, MovieMaker.HIGH);
}
else
{
saveMov = false;
mm.finish();
}
}
if (keyCode == KeyEvent.VK_ALT) saveFrame ("export/" + timeStamp () + ".png)");
if (keyCode == KeyEvent.VK_M) showConnections = !showConnections;
if (keyCode == KeyEvent.VK_N) showCrashPoint = !showCrashPoint;
if (keyCode == KeyEvent.VK_B)
{
ballsList = new ArrayList (amount);
numOfRecursition = 0;
createBalls();
checkOverLay();
}
if (keyCode == KeyEvent.VK_RIGHT)
{
amount++;
float maxEllipseSize = (width+height) / 20;
int ellipseSize = (int) random (50, maxEllipseSize);
PVector startPoint = new PVector (random (ellipseSize/2, width-ellipseSize/2), random (ellipseSize/2, height-ellipseSize/2));
PVector startSpeed = new PVector (random (-maxS, maxS), random (-maxS, maxS));
float s = minS;
if (startSpeed.x <= s && startSpeed.x >= 0) startSpeed.x = s+0.01;
if (startSpeed.x < 0 && startSpeed.x >= -s) startSpeed.x = -s-0.01;
if (startSpeed.y <= s && startSpeed.y >= 0) startSpeed.y = s+0.01;
if (startSpeed.y < 0 && startSpeed.y >= -s) startSpeed.y = -s-0.01;
int ellipseNum = (int) random (4, 10);
ballsList. add (amount-1, new Ball (startPoint, startSpeed, ellipseNum, ellipseSize));
Ball checkBall = (Ball) ballsList.get (amount-1);
checkBall.createColorList();
}
if (keyCode == KeyEvent.VK_LEFT)
{
amount -= 1;
amount = constrain (amount, 1,200);
float maxEllipseSize = (width+height) / 20;
int ellipseSize = (int) random (50, maxEllipseSize);
PVector startPoint = new PVector (random (ellipseSize/2, width-ellipseSize/2), random (ellipseSize/2, height-ellipseSize/2));
PVector startSpeed = new PVector (random (-maxS, maxS), random (-maxS, maxS));
float s = minS;
if (startSpeed.x <= s && startSpeed.x >= 0) startSpeed.x = s+0.01;
if (startSpeed.x < 0 && startSpeed.x >= -s) startSpeed.x = -s-0.01;
if (startSpeed.y <= s && startSpeed.y >= 0) startSpeed.y = s+0.01;
if (startSpeed.y < 0 && startSpeed.y >= -s) startSpeed.y = -s-0.01;
int ellipseNum = (int) random (4, 10);
ballsList. add ( new Ball (startPoint, startSpeed, ellipseNum, ellipseSize));
}
}
void mouseReleased ()
{
mouseIsReleased = true;
}
class Ball
{
PVector location, lastLocation, speed, speedUp, cm;
int numberOfEllipse, ellipseSize;
color [] colorList;
float [] ellipseSizeList;
color strokeColor, shootColor, dotColor, hoverColor;
float connectionWeight, shootWeight, dotWeight, hoverWeight;
Ball (PVector location_, PVector speed_, int numberOfEllipse_, int ellipseSize_)
{
location = location_;
lastLocation = location;
speed = speed_;
numberOfEllipse = numberOfEllipse_;
ellipseSize = ellipseSize_;
colorList = new color [numberOfEllipse+1];
ellipseSizeList = new float [numberOfEllipse];
createColorList ();
createEllipseSize ();
speedUp = new PVector (0, 0);
cm = speedUp;
strokeColor = color (0,50);
dotColor = strokeColor;
shootColor = strokeColor;
hoverColor = color (0,120);
connectionWeight = 0.5;
shootWeight = 0.75;
dotWeight = 10;
hoverWeight = 3.0;
}
void createEllipseSize ()
{
float startEllipseSize = (float) ellipseSize;
for (int i = 0; i < numberOfEllipse; i++)
{
float ce = random (ellipseSize*0.2, ellipseSize*0.9);
ellipseSizeList [i] = ce;
}
}
void createColorList ()
{
for (int i = 0; i < numberOfEllipse+1; i++)
{
float maxA = 10+i*numberOfEllipse;
float minA = 8+i*2;
if (colorSetOne) colorList [i] = color (random (10,80), random (10,80), random (10,80), random (minA,maxA));
if (colorSetTwo)
{
maxA = 255;
minA = 120;
colorList [i] = color ( random (5, 247+i*numberOfEllipse*5), random (minA,maxA));
}
if (colorSetThree)
{
minA = 240-i*numberOfEllipse*5;
maxA = 247-i*3;
colorList [i] = color (random (50,230), random (50,230), random (50,120), random (minA,maxA));
}
}
}
void createStrokeColorsAndWeights ()
{
if (colorSetOne)
{
if (whiteBG)
{
strokeColor = color (0,50);
dotColor = strokeColor;
shootColor = color (0,150);
hoverColor = color (0,120);
connectionWeight = 0.5;
shootWeight = 1.0;
dotWeight = 10;
hoverWeight = 3.0;
}
else
{
strokeColor = color (255,80);
dotColor = color (255,120);
hoverColor = color (255,150);
shootColor = color (255, 180);
connectionWeight = 0.75;
dotWeight = 10;
shootWeight = 1.25;
hoverWeight = 3.0;
}
}
if (colorSetTwo)
{
if (whiteBG)
{
strokeColor = color (0,20);
dotColor = color (180,0,0,180);
shootColor = color (180,0,0,180);
hoverColor = color (180,0,0,180);
connectionWeight = 3.0;
shootWeight = 3;
dotWeight = 10;
hoverWeight = 4.0;
}
else
{
strokeColor = color (255,80);
dotColor = color (180,0,0,150);
shootColor = color (180,0,0,150);
hoverColor = color (180,0,0,150);
connectionWeight = 3.0;
dotWeight = 10;
shootWeight = 3.0;
hoverWeight = 4.0;
}
}
if (colorSetThree)
{
if (whiteBG)
{
strokeColor = color (colorList[0]);
dotColor = color (0,200);
shootColor = color (0,200);
hoverColor = color (0,200);
connectionWeight = 1.0;
shootWeight = 2.0;
dotWeight = 10;
hoverWeight = 4.0;
}
else
{
strokeColor = color (colorList[0]);
dotColor = color (255,200);
shootColor = color (255,180);
hoverColor = color (255,180);
connectionWeight = 1.5;
dotWeight = 10;
shootWeight = 2.0;
hoverWeight = 4.0;
}
}
}
void display(boolean mouseIsOverEllipse, boolean mouseIsPressed)
{
createStrokeColorsAndWeights ();
if (mouseIsPressed)
{
strokeWeight(shootWeight);
stroke(shootColor);
line (location.x, location.y, lastLocation.x, lastLocation.y);
}
if (mouseIsOverEllipse == false) noStroke ();
else {
strokeWeight (hoverWeight);
stroke (hoverColor);
}
color cc = colorList [0];
if (!whiteBG && colorSetOne) cc = color (red (cc)+60, green (cc)+60, blue (cc)+60, alpha (cc) + 40);
if (!whiteBG && colorSetThree) cc = color (red (cc), green (cc), blue (cc), alpha (cc) - 60);
fill (cc);
ellipse (location.x, location.y, ellipseSize, ellipseSize);
noStroke();
for (int i = 0; i < numberOfEllipse; i++)
{
cc = colorList [i+1];
if (!whiteBG && colorSetOne) cc = color (red (cc)+60, green (cc)+60, blue (cc)+60, alpha (cc) + 20);
if (!whiteBG && colorSetThree) cc = color (red (cc), green (cc), blue (cc), alpha (cc) - 40);
fill (cc);
float currentEllipseSize = ellipseSizeList [i];
ellipse (location.x, location.y, currentEllipseSize, currentEllipseSize);
}
}
void move(boolean mouseIsPressed, PVector mp)
{
if (mouseIsPressed == false)
{
location.x += speed.x+speedUp.x;
location.y += speed.y+speedUp.y;
speedUp.x *= 0.97;
speedUp.y *= 0.97;
}
else
{
location = mp;
}
keepInWinodow();
}
void keepInWinodow()
{
if (location.x >= (width-ellipseSize/2) || location.x <= ellipseSize/2) {
speed.x *= -1;
speedUp.x *= -1;
if (location.x >= (width-ellipseSize/2)) location.x = width-ellipseSize/2;
if (location.x <= ellipseSize/2) location.x = ellipseSize/2;
}
if (location.y >= (height-ellipseSize/2)|| location.y <= ellipseSize/2)
{
speed.y *= -1;
speedUp.y *= -1;
if (location.y >= (height-ellipseSize/2)) location.y = height-ellipseSize/2;
if (location.y <= ellipseSize/2) location.y = ellipseSize/2;
}
}
// colliding balls ---------------------------------------
void checkDistance (int i, boolean mouseIsPressed_)
{
for (int j = 0; j < amount; j++)
{
Ball nextBall = (Ball) ballsList.get (j);
float distance = PVector.dist (location, nextBall.location);
float crashDistance = ellipseSize / 2.0 + nextBall.ellipseSize / 2.0;
if (showConnections && distance <= width/8)
{
strokeWeight (connectionWeight);
stroke (strokeColor);
line(location.x, location.y, nextBall.location.x, nextBall.location.y);
}
if (distance <= crashDistance && i != j) createCrashReaction ( nextBall, j);
}
}
void createCrashReaction (Ball nextBall, int j)
{
float crashAngle = createCrashAngle (nextBall.location);
PVector crashLocation = createCrashLocation (nextBall.location, crashAngle);
float einfallsWinkel = createEinfallsWinkelCurrentBall ();
float einfallsWinkelNB = createEinfallsWinkelNextBall (nextBall);
float ausfallsWinkel = PI-(crashAngle+einfallsWinkel);
float ausfallsWinkelNB = PI-(crashAngle+einfallsWinkelNB);
float distance = dist (location.x, location.y, location.x+speed.x, location.y+speed.y);
float distanceNB = dist (nextBall.location.x, nextBall.location.y, nextBall.location.x+nextBall.speed.x, nextBall.location.y+nextBall.speed.y);
float mx = 1, my = 1, mxNB = 1, myNB = 1;
if (location.x < crashLocation.x) mx = -1;
if (location.y < crashLocation.y) my = -1;
if (nextBall.location.x < crashLocation.x) mxNB = -1;
if (nextBall.location.y < crashLocation.y) myNB = -1;
PVector newSpeed = new PVector (mx* cos (ausfallsWinkel) * distance, my* sin (ausfallsWinkel) * distance );
PVector newSpeedNB = new PVector (mxNB* cos (ausfallsWinkelNB) * distanceNB, myNB* sin (ausfallsWinkelNB) * distanceNB);
float speedUpValue = (abs (einfallsWinkel) + abs (einfallsWinkelNB) + abs(speed.x)*2 + abs (speed.y)*2 + abs(speedUp.x) + abs(speedUp.y) + abs (nextBall.speed.x) + abs (nextBall.speed.y)) * 0.25;
float speedUpValueNB = (abs (einfallsWinkel) + abs (einfallsWinkelNB) + abs(speed.x) + abs (speed.y) + abs (nextBall.speed.x)*2 + abs (nextBall.speed.y)*2 + abs(nextBall.speedUp.x) + abs(nextBall.speedUp.y)) * 0.25;
PVector newSpeedUp = new PVector (speedUpValue * newSpeed.x, speedUpValue * newSpeed.y);
PVector newSpeedUpNB = new PVector (speedUpValueNB * newSpeedNB.x, speedUpValueNB * newSpeedNB.y);
newSpeedUp.x = constrain (newSpeedUp.x, -30,30);
newSpeedUp.y = constrain (newSpeedUp.y, -30,30);
newSpeedUpNB.x = constrain (newSpeedUpNB.x, -30,30);
newSpeedUpNB.y = constrain (newSpeedUpNB.y, -30,30);
speed = newSpeed;
speedUp = newSpeedUp;
nextBall.speed = newSpeedNB;
nextBall.speedUp = newSpeedUpNB;
if (showCrashPoint)
{
strokeWeight (connectionWeight);
stroke (strokeColor);
line(location.x, location.y, nextBall.location.x, nextBall.location.y);
strokeWeight (dotWeight);
stroke (dotColor);
point (crashLocation.x, crashLocation.y);
}
}
float createCrashAngle (PVector nextBall)
{
PVector p1 = location;
PVector p2 = nextBall;
float a = PVector.dist (p1, p2);
float b = dist (p1.x, p1.y, p2.x, p1.y);
float angle = acos (b/a);
return angle;
}
float createEinfallsWinkelNextBall (Ball nextBall)
{
PVector cl = nextBall.location;
PVector ll = new PVector (abs(cl.x-nextBall.speed.x), abs (cl.y-nextBall.speed.y) );
float b = dist (ll.x, ll.y, cl.x, ll.y);
float c = PVector.dist (cl, ll);
float angle = acos (b/c);
return angle;
}
float createEinfallsWinkelCurrentBall ()
{
PVector cl = location;
PVector ll = new PVector (abs(cl.x-speed.x), abs (cl.y-speed.y) );
float b = dist (ll.x, ll.y, cl.x, ll.y);
float c = PVector.dist (cl, ll);
float angle = acos (b/c);
return angle;
}
PVector createCrashLocation (PVector nextBall, float angle)
{
PVector p1 = location;
PVector p2 = nextBall;
float a_ = ellipseSize/2;
float b_ = cos (angle) * a_;
float c_ = sin (angle) * a_;
float mx = 1, my = 1;
if (p1.x > p2.x) mx = -1;
if (p1.y > p2.y) my = -1;
PVector crashLocation = new PVector (p1.x + mx * b_, p1.y + my * c_);
return crashLocation;
}
// shoot with a ball --------------------------------------------------
boolean mouseDistance (float mouseX_, float mouseY_)
{
boolean mouseIsOverEllipse = false;
PVector mousePosition = new PVector (mouseX_, mouseY_);
float distance = PVector.dist (mousePosition, location);
if (distance <= ellipseSize/2) mouseIsOverEllipse = true;
return mouseIsOverEllipse;
}
boolean checkMousePress (boolean mouseisOverEllipse_, boolean mouseIsPressed_)
{
boolean mouseIsPressed = false;
if (mouseisOverEllipse_ && mouseIsPressed_) mouseIsPressed = true;
return mouseIsPressed;
}
boolean checkMouseRelease (boolean mouseisOverEllipse_, boolean mouseIsReleased_)
{
boolean mouseIsReleasedTemp = false;
if (mouseisOverEllipse_ && mouseIsReleased_) mouseIsReleasedTemp = true;
return mouseIsReleasedTemp;
}
void shootWithMouse (boolean mouseIsOverEllipse_, boolean mouseIsPressed_, boolean mouseIsReleased_)
{
if (mouseIsPressed_ == false && mouseIsReleased_ == false) saveLastPos();
if (mouseIsReleased_ == true&& mouseIsOverEllipse_ == true) createShootReaction ();
}
void createShootReaction ()
{
float angle = createShoothAngle ();
float distance = dist (location.x, location.y,location.x+speed.x, location.y+speed.y);
float x = cos (angle) * distance, y = sin (angle) * distance;
int mx = 1, my = 1;
if (lastLocation.x < location.x) mx = -1;
if (lastLocation.y < location.y) my = -1;
PVector newSpeed = new PVector (mx*x,my*y );
speed = newSpeed;
speedUPerDistance ();
mouseIsReleased = false;
}
float createShoothAngle ()
{
PVector p1 = lastLocation;
PVector p2 = location;
float a = PVector.dist (p1, p2);
float b = dist (p1.x, p1.y, p2.x, p1.y);
float angle = acos (b/a);
return angle;
}
void speedUPerDistance ()
{
float distance = PVector.dist (location, lastLocation);
speedUp.x = speed.x*distance/4;
speedUp.x = constrain (speedUp.x, -40,40);
speedUp.y = speed.y*distance/4;
speedUp.y = constrain (speedUp.y, -40,40);
}
void saveLastPos ()
{
lastLocation = location;
}
}
String timeStamp ()
{
String ct = nf (year(), 4,0) + "_" + nf (month (), 2, 0) + "_" + nf (day (), 2, 0) + "__" + nf (hour (), 2, 0) + "_" + nf (minute (), 2, 0) + "_" + nf (second(), 2, 0);
return ct;
}
This is an exercise to teach myself how to write a class.
You can grab a ball and shoot with it.
Controls:
1 / 2 / 3 = toggle between color sets
Space = change background color
M = show / hide connections
N = show / hide crash location
B = create new set of balls
Right = add new ball
Left = remove a ball
Any key = pause
http://www.vimeo.com/19564417