The main problem still persists: Your Ball calls has an ARRAY OF BALLS in it. So for every Ball in the gloab array, you are storing a copy of that array for each ball in it! Argh!
A Ball object represents ONE ball. There is no need to have an array of them inside your class!
Instead, you need to check pass a Ball to your collide() function. Then the collision is checking between the Ball you are calling collide on, and that other Ball, the one being passed in.
int numBalls = 4;
int numBlips = 15;
Ball[] balls = new Ball[numBalls+numBlips];
float spring = 1.6;
float gravity = 0.001;
float friction = -0.8;
void setup() {
//frameRate(80); // Sketch will run as fast as it can anyway, usually 60fps. This doesn't increase that, much as you would like.
size(displayWidth, displayHeight, OPENGL, P2D);
for (int i = 0; i < numBalls+numBlips; i++) {
boolean is_a_ball = i < numBalls;
balls[i] = new Ball(random(300, 600), random(450, 650), ((is_a_ball)?(80):(10)), i, is_a_ball);
//println( "" + i + " " + ((is_a_ball)?(10):(80)) );
}
//smooth(); // Enabled by default.
}
int index = 0;
void draw() {
background(0, 10);
// For each ball,
for (int i = 0; i < balls.length-1; i++) {
// For all the balls after it,
for ( int j = i+1; j < balls.length; j++) {
// Determine if they collide.
balls[i].collide(balls[j]);
}
}
// Then move and draw all the balls.
for (int i = 0; i < balls.length-1; i++) {
balls[i].move();
balls[i].display();
}
}
class Ball {
float x, y;
float diameter;
float vx = 0;
float vy = 0;
int id;
boolean is_ball;
Ball(float xin, float yin, float din, int idin, boolean iis_ball) {
x = xin;
y = yin;
diameter = din;
id = idin;
is_ball = iis_ball;
}
void collide(Ball other) {
float dx = other.x - x;
float dy = other.y - y;
float distance = sqrt(dx*dx + dy*dy);
float minDist = other.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 - other.x) * spring*1.1;
float ay = (targetY - other.y) * spring*1.1;
vx -= ax;
vy -= ay;
other.vx += ax;
other.vy += ay;
}
}
void move() {
vy += gravity * (is_ball?1:2);
x += vx;
y += vy;
if (x + diameter/2 > width) {
x = width - diameter/2;
vx *= friction;
} else if (x - diameter/2 < 0) {
x = diameter/2;
vx *= friction;
}
if (y + diameter/2 > height) {
y = height - diameter/2;
vy *= friction;
} else if (y - diameter/2 < 0) {
y = diameter/2;
vy *= friction;
}
}
void display() {
if ( is_ball ) {
display_ball();
} else {
display_blip();
}
}
void display_ball() {
float disc=40;
gradientdisc(
x,
y,
40,
disc,
color(0, 0, 255),
color(255, 255, 255)
);
noStroke();
}
void gradientdisc( float x, float y, float radiusX, float radiusY, int fromC, int toC ) {
noStroke();
beginShape(TRIANGLE_STRIP);
int halfC = lerpColor(fromC, toC, 0.4);
for (float theta=0; theta<TWO_PI; theta+=TWO_PI/36) {
fill(halfC);
vertex(x, y);
if ( theta <= PI ) {
fill(lerpColor(fromC, toC, (theta%PI)/PI ));
} else {
fill(lerpColor(toC, fromC, (theta%PI)/PI ));
}
vertex(x+radiusX*cos(theta), y+radiusY*sin(theta));
}
endShape();
float disc2=50;
stroke(0, 10);
strokeWeight(1);
noFill();
ellipse(x, y, 100, disc2*2);
}
void display_blip() {
stroke(255, 0, 144);
strokeWeight(1);
noFill();
ellipse(x, y, diameter, diameter);
}
}
Anyway, I merged the Blips with the Balls. Notice that the Ball class now has a is_ball boolean that knows if a given ball is a blip or a ball.
Also notice that there is ONLY ONE ARRAY OF OBJECTS: balls. Each single ball does not have a copy of this array!
And the collides() function is being passed a ball to check for collision against.