metaballs (Marching Squares algorithm)
@author
int num = 10;
ArrayList<Particle> particles;
int cellNum = 50;
float cellSize = 10;
float[][] cells;
void setup(){
size(500, 500);
cells = new float[cellNum + 1][cellNum + 1];
particles = new ArrayList<Particle>();
for(int i = 0; i < num; i++){
particles.add(new Particle());
}
noFill();
stroke(255, 255, 240);
strokeWeight(7);
}
void draw(){
background(128, 128, 120);
for(int y = 0; y < cellNum + 1; y++){
for(int x = 0; x < cellNum + 1; x++){
PVector c = new PVector(x * cellSize, y * cellSize);
cells[x][y] = 0.0;
for(Particle p: particles){
cells[x][y] += p.radious / PVector.sub(c, p.loc).mag();
}
}
}
for(int y = 0; y < cellNum; y++){
for(int x = 0; x < cellNum; x++){
PVector c = new PVector(cellSize * x, cellSize * y);
int state = int((cells[x][y + 1] >= 1 ? 1: 0)
+ pow(cells[x + 1][y + 1] >= 1 ? 2: 0, 1)
+ pow(cells[x + 1][y] >= 1 ? 2: 0, 2)
+ pow(cells[x][y] >= 1 ? 2: 0, 3));
float x1, y1, x2, y2;
switch(state){
case 0:
break;
case 1:
case 14:
x1 = c.x;
y1 = c.y + cellSize * ((1.0 - cells[x][y]) / (cells[x][y + 1] - cells[x][y]));
x2 = c.x + cellSize * ((1.0 - cells[x][y + 1]) / (cells[x + 1][y + 1] - cells[x][y + 1]));
y2 = c.y + cellSize;
line(x1, y1, x2, y2);
break;
case 2:
case 13:
x1 = c.x + cellSize;
y1 = c.y + cellSize * ((1.0 - cells[x + 1][y]) / (cells[x + 1][y + 1] - cells[x + 1][y]));
x2 = c.x + cellSize * ((1.0 - cells[x][y + 1]) / (cells[x + 1][y + 1] - cells[x][y + 1]));
y2 = c.y + cellSize;
line(x1, y1, x2, y2);
break;
case 3:
case 12:
x1 = c.x;
y1 = c.y + cellSize * ((1.0 - cells[x][y]) / (cells[x][y + 1] - cells[x][y]));
x2 = c.x + cellSize;
y2 = c.y + cellSize * ((1.0 - cells[x + 1][y]) / (cells[x + 1][y + 1] - cells[x + 1][y]));
line(x1, y1, x2, y2);
break;
case 4:
case 11:
x1 = c.x + cellSize * ((1.0 - cells[x][y]) / (cells[x + 1][y] - cells[x][y]));
y1 = c.y;
x2 = c.x + cellSize;
y2 = c.y + cellSize * ((1.0 - cells[x + 1][y]) / (cells[x + 1][y + 1] - cells[x + 1][y]));
line(x1, y1, x2, y2);
break;
case 5:
x1 = c.x + cellSize * ((1.0 - cells[x][y]) / (cells[x + 1][y] - cells[x][y]));
y1 = c.y;
x2 = c.x;
y2 = c.y + cellSize * ((1.0 - cells[x][y]) / (cells[x][y + 1] - cells[x][y]));
line(x1, y1, x2, y2);
x1 = c.x + cellSize;
y1 = c.y + cellSize * ((1.0 - cells[x + 1][y]) / (cells[x + 1][y + 1] - cells[x + 1][y]));
x2 = c.x + cellSize * ((1.0 - cells[x][y + 1]) / (cells[x + 1][y + 1] - cells[x][y + 1]));
y2 = c.y + cellSize;
line(x1, y1, x2, y2);
break;
case 6:
case 9:
x1 = c.x + cellSize * ((1.0 - cells[x][y]) / (cells[x + 1][y] - cells[x][y]));
y1 = c.y;
x2 = c.x + cellSize * ((1.0 - cells[x][y + 1]) / (cells[x + 1][y + 1] - cells[x][y + 1]));
y2 = c.y + cellSize;
line(x1, y1, x2, y2);
break;
case 7:
case 8:
x1 = c.x + cellSize * ((1.0 - cells[x][y]) / (cells[x + 1][y] - cells[x][y]));
y1 = c.y;
x2 = c.x;
y2 = c.y + cellSize * ((1.0 - cells[x][y]) / (cells[x][y + 1] - cells[x][y]));
line(x1, y1, x2, y2);
break;
case 10:
x1 = c.x + cellSize * ((1.0 - cells[x][y]) / (cells[x + 1][y] - cells[x][y]));
y1 = c.y;
x2 = c.x + cellSize;
y2 = c.y + cellSize * ((1.0 - cells[x + 1][y]) / (cells[x + 1][y + 1] - cells[x + 1][y]));
line(x1, y1, x2, y2);
x1 = c.x;
y1 = c.y + cellSize * ((1.0 - cells[x][y]) / (cells[x][y + 1] - cells[x][y]));
x2 = c.x + cellSize * ((1.0 - cells[x][y + 1]) / (cells[x + 1][y + 1] - cells[x][y + 1]));
y2 = c.y + cellSize;
line(x1, y1, x2, y2);
break;
case 15:
break;
}
}
}
for(Particle p: particles){
p.update();
}
}
class Particle{
float radious;
PVector loc, vel;
Particle(){
radious = random(10, 20);
loc = new PVector(random(radious, width - radious), random(radious, height - radious));
float velSize = random(2, 5);
float velAng = random(TWO_PI);
vel = new PVector(velSize * cos(velAng), velSize * sin(velAng));
}
void update(){
loc.add(vel);
if(loc.x < radious){
vel.x *= -1;
loc.x += vel.x;
}
if(loc.x >= width - radious){
vel.x *= -1;
loc.x += vel.x;
}
if(loc.y < radious){
vel.y *= -1;
loc.y += vel.y;
}
if(loc.y >= height - radious){
vel.y *= -1;
loc.y += vel.y;
}
}
}