Nicely done!
fullscreen static final int MAX_NUM_PARTICLES = 400;
Particle[] particles = new Particle[MAX_NUM_PARTICLES];
private float dt;
private int oldMillis;
static final double GRAVITY = 0.0015;
static final float BOUNCE_DAMPENING = (float) 0.025;
private int numberOfInstances = 200;
private float radius = 6;
private boolean grav = true;
void setup() {
oldMillis = millis();
size(640, 480);
for (int i = 0; i < MAX_NUM_PARTICLES; i++) {
float x = random(0, width);
float y = random(0, height);
particles[i] = new Particle(x, y, radius);
}
smooth();
PFont myFont = loadFont("Ziggurat-HTF-Black-32.vlw");
textFont(myFont, 12);
fill(255);
noStroke();
}
void draw() {
updateParticles();
background(51);
for (int i = 0; i < numberOfInstances; i++) {
int d = parseInt(particles[i].radius * 2);
ellipse((float) particles[i].posX, (float) particles[i].posY, d, d);
}
text("fps: " + parseInt(1000 / dt), 10, 20);
text("click left to push or right to pull", width/2, 20);
text("[+/-] objects: " + numberOfInstances, 10, 40);
text("[ g ] gravity: " + grav, 10, 60);
}
void keyPressed() {
if (keyPressed) {
if (key == 'g' || key == 'G') {
grav = !grav;
}
if (key == '+' && numberOfInstances < MAX_NUM_PARTICLES) {
numberOfInstances++;
}
if (key == '-' && numberOfInstances > 1) {
numberOfInstances--;
}
}
}
void updateParticles() {
for (int i = 0; i < numberOfInstances; i++) {
Particle particle = particles[i];
// bounce off bottom
if (particle.posY > height - particle.radius) {
particle.vY = -abs(particle.vY) * (1 - BOUNCE_DAMPENING);
particle.posY = height - particle.radius;
}
// bounce off ceiling
if (particle.posY < particle.radius) {
particle.vY = abs(particle.vY) * (1 - BOUNCE_DAMPENING);
particle.posY = particle.radius;
}
// bounce off left border
if (particle.posX < particle.radius) {
particle.vX = abs(particle.vX) * (1 - BOUNCE_DAMPENING);
particle.posX = particle.radius;
}
// bounce off right border
if (particle.posX > width - particle.radius) {
particle.vX = -abs(particle.vX) * (1 - BOUNCE_DAMPENING);
particle.posX = width - particle.radius;
}
// inter particle
for (int j = 0; j < numberOfInstances; j++) {
// bounce
bounce(particles[i], particles[j]);
}
// apply Gravity
if(grav){
particle.vY += GRAVITY*0.1*dt;
}
// apply interactive gravity
applyMouseGravity(particle);
// move it
particle.tick();
}
dt = millis() - oldMillis;
oldMillis = millis();
}
void applyMouseGravity(Particle particle) {
if (mousePressed) {
float d = sqrt(pow(particle.posX - mouseX, 2)
+ pow(particle.posY - mouseY, 2))
* (float) 2.0;
float ang = atan2(particle.posX - mouseX, particle.posY - mouseY);
float F = (float) 16 * dt;
F = min(1 / d, 3);
if (mouseButton == RIGHT) {
F = -F;
}
particle.vX += sin(ang) * F;
particle.vY += cos(ang) * F;
}
}
void bounce(Particle a, Particle b) {
if (sqrt(pow(a.posX - b.posX, 2) + pow(a.posY - b.posY, 2)) < (a.radius + b.radius)) {
if (sqrt(pow(a.posX - b.posX, 2) + pow(a.posY - b.posY, 2)) > sqrt(pow(
a.posX + a.vX - b.posX - b.vX, 2)
+ pow(a.posY + a.vY - b.posY - b.vY, 2))) {
float commonTangentAngle = atan2(b.posX - a.posX, b.posY
- a.posY)
+ asin(1);
float v1 = a.getVelocity();
float v2 = b.getVelocity();
float w1 = a.getMotionDirection();
float w2 = b.getMotionDirection();
a.vX = sin(commonTangentAngle) * v1 * cos(w1 - commonTangentAngle) + cos(commonTangentAngle) * v2 * sin(w2 - commonTangentAngle);
a.vY = cos(commonTangentAngle) * v1 * cos(w1 - commonTangentAngle) - sin(commonTangentAngle) * v2 * sin(w2 - commonTangentAngle);
b.vX = sin(commonTangentAngle) * v2 * cos(w2 - commonTangentAngle) + cos(commonTangentAngle) * v1 * sin(w1 - commonTangentAngle);
b.vY = cos(commonTangentAngle) * v2 * cos(w2 - commonTangentAngle) - sin(commonTangentAngle) * v1 * sin(w1 - commonTangentAngle);
a.vX *= (1 - BOUNCE_DAMPENING);
a.vY *= (1 - BOUNCE_DAMPENING);
b.vX *= (1 - BOUNCE_DAMPENING);
b.vY *= (1 - BOUNCE_DAMPENING);
}
}
}
class Particle {
float posX;
float posY;
float vX = 0;
float vY = 0;
float radius;
Particle(float x, float y, float r) {
posX = x;
posY = y;
radius = r;
}
float getVelocity() {
return sqrt(vX * vX + vY * vY);
}
public float getMotionDirection() {
return atan2(vX, vY);
}
void tick() {
posX += vX * dt;
posY += vY * dt;
}
}