class Ball {// I'm not going to comment on all of this
float x, y;
float diameter;
float vx = 0;
float vy = 0;
float lastX;
float lastY;
int id;
Ball[] others;
Boolean canGrab=true;
Ball(float xin, float yin, float din, int idin, Ball[] oin) {
x = xin;
y = yin;
diameter = din;
id = idin;
others = oin;
}
void update() {
}
void collide() {//the first way of checking collison
for (int i = id + 1; i < currBallNum; i++) {
float dx = others[i].x - x;
float dy = others[i].y - y;
float distance = sqrt(dx*dx + dy*dy);
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 colorize() {
float dx=others[2].x - x;
float dy=others[2].y - y;
for (int i = id + 1; i < currBallNum; i++) {
dx = others[i].x - x;
dy = others[i].y - y;
}
float distance = sqrt(dx*dx + dy*dy);
if(keyPressed && key!=' ') numbs=key;
switch(numbs){// create more colors for keybord numbers
case('1')://done
fill(255-distance,255,distance-255, 255-ballFade);
break;
case('2')://done
fill(255,255-distance,distance-255, 255-ballFade);
break;
case('3')://done
fill(255-distance,distance-255,255, 255-ballFade);
break;
case('4')://done
fill(255,255-distance,255, 255-ballFade);
break;
case('5')://done
fill(distance-255,distance,255-distance, 255-ballFade);
break;
case('6')://done
fill(255,distance-255,distance-255, 255-ballFade);
break;
case('7')://done
fill(255-distance,distance -255,distance-255, 255-ballFade);
break;
case('8')://done
fill(distance-255,255-distance,255, 255-ballFade);
break;
case('9')://done
fill(0,distance-255,255-distance, 255-ballFade);
break;
case('0'):
fill(255, 255-ballFade);
break;
default:
fill(255-distance,255,distance-255, 255-ballFade);
break;
}
}
void gravitate() {
for (int i = id + 1; i < currBallNum; i++) {
float dx = others[i].x - x;
float dy = others[i].y - y;
float distance = sqrt(dx*dx + dy*dy);
float rto;
if(x <= others[i].x) rto = 180;
else rto = 0;
float rot = atan((y-others[i].y)/(x-others[i].x))+radians(rto);
vx -= ((diameter*(massToScale))/(distance))*cos(rot);
vy -= ((diameter*(massToScale))/(distance))*sin(rot);
others[i].vx += ((diameter*(massToScale))/(distance))*cos(rot);
others[i].vy += ((diameter*(massToScale))/(distance))*sin(rot);
}
}
void connect() {
for (int i = id + 1; i < currBallNum; i++) {
float dx = others[i].x - x;
float dy = others[i].y - y;
float distance = sqrt(dx*dx + dy*dy);
stroke(255-distance,distance-255,255,255-distance -lineFade +lineLength);
strokeWeight(abs(300-distance)/20 /(lineLength*0.001+1));
line(x,y, others[i].x,others[i].y);
}
}
void grab() {
float dx = mouseX - x;
float dy = mouseY - y;
float distance = sqrt(dx*dx + dy*dy);
if ((distance <= diameter/2 && mousePressed && canGrab && (!isOpen || mouseX<width-150))) {
lastX=x;
lastY=y;
x=mouseX-dx;
y=mouseY-dy;
vx= mouseX -lastX;
vy= mouseY -lastY;
for (int i = id + 1; i < currBallNum; i++) {
others[i].canGrab=false;
}
}
else if(!mousePressed) {
for (int i = id + 1; i < currBallNum; i++) {
others[i].canGrab=true;
}
}
}
void applyAirFriction() {
vx -= friction*vx;
vy -= friction*vy;
}
void move() {
vy += gravity;
x += vx;
y += vy;
if (x + diameter/2 > width/theScale) {
x = width/theScale - diameter/2;
vx *= -wallBounce;
//vx=-vx;
}
else if (x - diameter/2 < 0) {
x = diameter/2;
vx *= -wallBounce;
//vx=-vx;
}
if (y + diameter/2 > height/theScale) {
y = height/theScale - diameter/2;
vy *= -wallBounce;
// vy=-vy;
}
else if (y - diameter/2 < 0) {
y = diameter/2;
vy *= -wallBounce;
// vy=-vy;
}
}
void display() {
// fill(255, 204);
noStroke();
ellipse(x, y, diameter, diameter);
}
}
//node gardan with options!
int theScale=1;//had this when trying to short cut the program into fullScreen
int numBalls = 23;//variable you can edit inside the program!
float spring = 1;
float massToScale=0.1;
float gravity = 0.0;
float friction = 0.0000;
float wallBounce = 0.9;
Ball[] balls = new Ball[numBalls];
Boolean isSmooth=false;
Boolean tFR=false;
int backFade=0;
int ballFade=0;
int lineFade=0;
int lineLength=0;
char numbs='1';
void setup()
{
// size(screen.width/(3-theScale), screen.height/(3-theScale));//size math for easy changeing to fullscreen
//fullScreen is very laggy though
size(960,600);
noStroke();
for (int i = 0; i < numBalls; i++) {// here I create all the balls
balls[i] = new Ball(random(width/theScale), random(height/theScale), random(30, 100), i, balls);
}
}
void draw()
{
// scale(theScale);//pointless
if(isSmooth) smooth();//alternates between smoothing and not smoothing
else if(!isSmooth) noSmooth();
blurrbackground(0,0,0,255-backFade);// creates that blurry effect
for (int i = 0; i < currBallNum; i++) {//runs through all the balls
balls[i].applyAirFriction();
balls[i].gravitate();
if(spring==1)//switches between two different ways of calculating collision
for(int j=0; j<currBallNum;j++) {//depending on the spring input
if(i!=j)
checkCollision(balls[i],balls[j]);
}
else
balls[i].collide();
balls[i].move();
balls[i].grab();
balls[i].colorize();
balls[i].display();//then we draw the ball
}
for (int i = 0; i < currBallNum; i++) {
balls[i].connect();//a seperate array for drawing lines just because I can
}
menu(width/theScale-150,0);
theFrameRate();
}
void checkCollision(Ball ball0, Ball ball1)// the second way of checking collision
{
float dx = ball1.x - ball0.x;
float dy = ball1.y - ball0.y;
float dist = sqrt(dx*dx + dy*dy);
if(dist < ball0.diameter/2 + ball1.diameter/2)
{
//calculate angle, sine and cosine
float angle = atan2(dy, dx);
float sine = sin(angle);
float cosine = cos(angle);
//rotate ball0's position
PVector pos0 = new PVector(0, 0);
//rotate ball1's position
PVector pos1 = rotateC(dx, dy, sine, cosine, true);
//rotate ball0's velocity
PVector vel0 = rotateC(ball0.vx, ball0.vy, sine, cosine, true);//uhhh
//rotate ball1's velocity
PVector vel1 = rotateC(ball1.vx, ball1.vy, sine, cosine, true);
//collision reaction
float vxTotal = vel0.x - vel1.x;
vel0.x = ((ball0.diameter - ball1.diameter) * vel0.x + 2 * ball1.diameter * vel1.x) / (ball0.diameter + ball1.diameter);
vel1.x = vxTotal + vel0.x;
//update positions
float absV = abs(vel0.x) + abs(vel1.x);
float overlap = (ball0.diameter/2 + ball1.diameter/2) - abs(pos0.x - pos1.x);
pos0.x += vel0.x / absV * overlap;
pos1.x += vel1.x / absV * overlap;
//rotate positions back
PVector pos0F = rotateC(pos0.x, pos0.y, sine, cosine, false);
PVector pos1F = rotateC(pos1.x, pos1.y, sine, cosine, false);
//adjust positions to actual screen positions
//this stops the balls from going through each other
ball1.x = ball0.x + pos1F.x;
ball1.y = ball0.y + pos1F.y;
ball0.x = ball0.x + pos0F.x;
ball0.y = ball0.y + pos0F.y;
//rotate velocties back
PVector vel0F = rotateC(vel0.x, vel0.y, sine, cosine, false);
PVector vel1F = rotateC(vel1.x, vel1.y, sine, cosine, false);
//so this makes the balls bounce off each other
ball0.vx = vel0F.x*spring;
ball0.vy = vel0F.y*spring;
ball1.vx = vel1F.x*spring;
ball1.vy = vel1F.y*spring;
}
}
PVector rotateC(float x, float y, float sine, float cosine, boolean anticlock)
{
PVector result = new PVector(0, 0);
if(anticlock)
{
result.x = x * cosine + y * sine;
result.y = y * cosine - x * sine;
}
else
{
result.x = x * cosine - y * sine;
result.y = y * cosine + x * sine;
}
return result;
}
Boolean isOpen=false;
Boolean canOpen=true;
//this was created for using math on making this work on fullScreen
float mousex=mouseX;
float mousey=mouseY;
int currBallNum=10;
//heightGrid
float hg =screen.height/25;
void menu(int x,int y) {
hg=width/20/theScale;
mousex=mouseX/theScale;
mousey=mouseY/theScale;
stroke(56,80,255,100);
strokeWeight(1);
fill(0);
rect(x,y, 150,20);
if(mousex>x && mousex<x+150 && mousey>y && mousey<y+20 && !mousePressed)fill(36,60,240,255);
else fill(36,60,240,100);
rect(x,y, 150,20);
fill(255,200);
text("Options menu",x+5,y+15);
if(mousex>x && mousex<x+150 && mousey>y && mousey<y+20 && mousePressed && canOpen) {
isOpen=!isOpen;
canOpen=false;
fill(0,backFade);
noStroke();
rect(x,y, 150,height);
}
else if(!mousePressed) canOpen=true;
if(isOpen) {
fill(0,backFade);
rect(x,y+20, 150,height-20);
fill(36,60,240,100);
rect(x,y, 150,height);
fill(200);
text("Number of balls",x+15,y+hg);
fill(36,60,240,100);
if(mousex>x-10 && mousex<x+140 && mousey>y+hg && mousey<y+hg+20 && mousePressed) {//number of balls
currBallNum=constrain(int(mouseX-x-10)/5,0,23);
}
rect(x+10,y+hg,currBallNum*5/*(currBallNum/numBalls)*/,20);
fill(255,200);
text(currBallNum, x+15,y+hg +15);
text("Air friction",x+15,y+(hg)*2);
fill(36,60,240,100);
if(mousex>x-10 && mousex<x+140 && mousey>y+(hg)*2 && mousey<y+(hg)*2+20 && mousePressed) {//friction
friction=constrain(int(mouseX-x-10)*0.001 -0.001,0,0.118);
}
rect(x+10,y+(hg)*2,friction*1000/*(currBallNum/numBalls)*/,20);
fill(255,200);
text(friction, x+15,y+(hg)*2 +15);
text("Earth's gravity",x+15,y+(hg)*3);
fill(36,60,240,100);
if(mousex>x-10 && mousex<x+140 && mousey>y+(hg)*3 && mousey<y+(hg)*3+20 && mousePressed) {
gravity=constrain(int(mouseX-x-10)*0.09 -0.09,0,9.8);
}
rect(x+10,y+(hg)*3,gravity*11,20);
fill(255,200);
text(gravity, x+15,y+(hg)*3 +15);
text("Ball buncieness",x+15,y+(hg)*4);
fill(36,60,240,100);
if(mousex>x-10 && mousex<x+140 && mousey>y+(hg)*4 && mousey<y+(hg)*4+20 && mousePressed) {
spring=constrain(int(mouseX-x-10)*0.01 -0.01,0,1);
}
rect(x+10,y+(hg)*4,spring*100,20);
fill(255,200);
text(spring, x+15,y+(hg)*4 +15);
text("Ball attraction",x+15,y+(hg)*5);
fill(36,60,240,100);
if(mousex>x-10 && mousex<x+140 && mousey>y+(hg)*5 && mousey<y+(hg)*5+20 && mousePressed) {
massToScale=constrain(int(mouseX-x-10)*0.01 -0.01,0,1);
}
rect(x+10,y+(hg)*5,massToScale*100,20);
fill(255,200);
text(massToScale, x+15,y+(hg)*5 +15);
text("Boundery bouncieness",x+15,y+(hg)*6);
fill(36,60,240,100);
if(mousex>x-10 && mousex<x+140 && mousey>y+(hg)*6 && mousey<y+(hg)*6+20 && mousePressed) {
wallBounce=constrain(int(mouseX-x-10)*0.0085 -0.0085,0,1);
}
rect(x+10,y+(hg)*6,wallBounce*117.5,20);
fill(255,200);
text(wallBounce, x+15,y+(hg)*6 +15);
text("Blurr effect",x+15,y+(hg)*7);
fill(36,60,240,100);
if(mousex>x-10 && mousex<x+150 && mousey>y+(hg)*7 && mousey<y+(hg)*7+20 && mousePressed) {//Blurr effect
backFade=constrain(int(mouseX-x-10)*2 -2,0,255);
}
rect(x+10,y+(hg)*7,backFade*0.5,20);
fill(255,200);
text(backFade, x+15,y+(hg)*7 +15);
text("Ball fade",x+15,y+(hg)*8);
fill(36,60,240,100);
if(mousex>x-10 && mousex<x+150 && mousey>y+(hg)*8 && mousey<y+(hg)*8+20 && mousePressed) {//ball transparance
ballFade=constrain(int(mouseX-x-10)*2 -2,0,255);
}
rect(x+10,y+(hg)*8,ballFade*0.5,20);
fill(255,200);
text(ballFade, x+15,y+(hg)*8 +15);
text("line fade",x+15,y+(hg)*9);
fill(36,60,240,100);
if(mousex>x-10 && mousex<x+150 && mousey>y+(hg)*9 && mousey<y+(hg)*9+20 && mousePressed) {//line transparance
lineFade=constrain(int(mouseX-x-10)*2 -2,0,255);
}
rect(x+10,y+(hg)*9,lineFade*0.5,20);
fill(255,200);
text(lineFade, x+15,y+(hg)*9 +15);
text("Line Length",x+15,y+(hg)*10);
fill(36,60,240,100);
if(mousex>x-10 && mousex<x+150 && mousey>y+(hg)*10 && mousey<y+(hg)*10+20 && mousePressed) {//line visible length
lineLength=constrain(int(mouseX-x-10)*10 -2,0,255*4);
}
rect(x+10,y+(hg)*10,lineLength*0.1,20);
fill(255,200);
text(lineLength, x+15,y+(hg)*10 +15);
text("Smooth",x+15,y+(hg)*11);
fill(36,60,240,100);
if(mousex>x+80 && mousex<x+100 && mousey>y+(hg)*11 -15 && mousey<y+(hg)*11+5 && mousePressed
&& canOpen) {//smoothness
isSmooth=!isSmooth;
canOpen=false;
}
rect(x+80,y+(hg)*11 -15,20,20);
stroke(255);
if(isSmooth) {
strokeWeight(5);
line(x+80,y+hg*11 -15, x+95,y+hg*11);
line(x+95,y+hg*11, x+105,y+hg*11 -25);
}
strokeWeight(1);
fill(255,200);
stroke(36,60,240,200);
text("Show frameRate",x+15,y+(hg)*12);
fill(36,60,240,100);
if(mousex>x+120 && mousex<x+140 && mousey>y+(hg)*12 -15 && mousey<y+(hg)*12 +5 && mousePressed
&& canOpen) {//show the frameRate
tFR=!tFR;
fill(0);
noStroke();
rect(0,0,50,12);
canOpen=false;
}
rect(x+120,y+(hg)*12 -15,20,20);
stroke(255);
if(tFR) {
strokeWeight(5);
line(x+120,y+hg*12 -15, x+135,y+hg*12);
line(x+135,y+hg*12, x+145,y+hg*12 -25);
}
}
}
void blurrbackground(int r, int g, int b, int transparancy) {
if(keyPressed && key ==' ') fill(r,g,b);
else fill(r,g,b,transparancy);
noStroke();
rect(0,0, width,height);
fill(255);
noStroke();
}
void theFrameRate() {
if(tFR) {
noStroke();
fill(0);
rect(0,0,50,12);
fill(255);
text(frameRate,0,10);
}
}
nodeGarden, with an options menu so you can play with physics.
have fun.