• fullscreen
• Ball.pde
• checkCollision.pde
• checkWalls.pde
• nodeGardenVectors.pde
• spring.pde
• wrapWalls.pde
• // Ball class
class Ball
{
PVector position;
PVector velocity;
color colour = color(0, 0, 255);

float gravity = 0.0;
float bounce = -1; // default = -0.6

// default constructor
/*  Ball()
{
}*/

Ball(PVector position, PVector velocity, float radius, color colour)
{
this.position = position;
this.velocity = velocity;
this.colour = colour;
}

void move()
{
velocity.y = velocity.y + gravity;
position.y += velocity.y;
position.x += velocity.x;
}

void display()
{
fill(colour);
}
}

void checkCollision(Ball ball0, Ball ball1)
{
float dx = ball1.position.x - ball0.position.x;
float dy = ball1.position.y - ball0.position.y;
float dist = sqrt(dx*dx + dy*dy);

{
//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.velocity.x, ball0.velocity.y, sine, cosine, true);

//rotate ball1's velocity
PVector vel1 = rotateC(ball1.velocity.x, ball1.velocity.y, sine, cosine, true);

//collision reaction
float vxTotal = vel0.x - vel1.x;
vel0.x = ((ball0.mass - ball1.mass) * vel0.x + 2 * ball1.mass * vel1.x) / (ball0.mass + ball1.mass);
vel1.x = vxTotal + vel0.x;

//update positions
float absV = abs(vel0.x) + abs(vel1.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
ball1.position.x = ball0.position.x + pos1F.x;
ball1.position.y = ball0.position.y + pos1F.y;
ball0.position.x = ball0.position.x + pos0F.x;
ball0.position.y = ball0.position.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);

ball0.velocity.x = vel0F.x;
ball0.velocity.y = vel0F.y;
ball1.velocity.x = vel1F.x;
ball1.velocity.y = vel1F.y;
}
}

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;
}

void checkWalls(Ball ball)
{
{
ball.velocity.x *= ball.bounce;
}
{
ball.velocity.x *= ball.bounce;
}
else if(ball.position.y > height - ball.radius)
{
ball.velocity.y *= ball.bounce;
}
{
ball.velocity.y *= ball.bounce;
}
}

// nodeGarden using vectors
int numBalls = 53;
Ball[]balls = new Ball[numBalls];
color[]colours = new color[numBalls];

void setup()
{
size(600, 400);
smooth();
noStroke();
for(int i = 0; i < numBalls; i++)
{
PVector startPosition = new PVector(random(width), random(height));
PVector startVelocity = new PVector(random(-4, 4), random(-4, 4));
balls[i] = new Ball(startPosition, startVelocity, radius, colours[i]);
balls[i].bounce = -1;
}
}
void draw()
{
background(255);
noStroke();
for(int i = 0; i < numBalls; i++)
{
balls[i].move();
balls[i].display();
checkWalls(balls[i]);
}
for(int i = 0; i < numBalls - 1; i++)
{
for(int j = i + 1; j < numBalls; j++)
{
checkCollision(balls[i], balls[j]);
spring(balls[i], balls[j]);
}
}
}

float minDist = 100;
float springAmount = 0.0001;

void spring(Ball ballA, Ball ballB)
{
float dx = ballA.position.x - ballB.position.x;
float dy = ballA.position.y - ballB.position.y;
float dist = sqrt(dx*dx + dy*dy);

if(dist < minDist)
{
PVector a = new PVector(dx * springAmount, dy * springAmount);
//    float ax = dx * springAmount;
//    float ay = dy * springAmount;
ballA.velocity.x += a.x / ballA.mass;
ballA.velocity.y += a.y / ballA.mass;
ballB.velocity.x -= a.x / ballB.mass;
ballB.velocity.y -= a.y / ballB.mass;

strokeWeight(1);
stroke(0, 255, 128, 255 * (1 - dist / minDist));
line(ballA.position.x, ballA.position.y, ballB.position.x, ballB.position.y);

}
}

void wrapWalls(Ball ball)
{
if(ball.position.x > width)
{
ball.position.x = 0;
}
else if(ball.position.x < 0)
{
ball.position.x = width;
}
if(ball.position.y > height)
{
ball.position.y = 0;
}
else if(ball.position.y < 0)
{
ball.position.y = height;
}
}

### tweaks (0)

This sketch is running as Java applet, exported from Processing.