interface ParticleSpringGridRenderer { void draw(Particle fixed, Particle free, int row, int col); } class ParticleSpringGrid { private ParticleSystem phys; private int rows, cols; private Particle[][] fixedGrid; private Particle[][] freeGrid; private ParticleSpringGridRenderer psgr; // creates a ParticleSpringGrid with the upper left particle at (0,0) ParticleSpringGrid(int rows, int cols, int spacing, ParticleSpringGridRenderer psgr, float xoff, float yoff) { this.rows = rows; this.cols = cols; phys = new ParticleSystem(0, GLOBAL_DAMPING); fixedGrid = new Particle[rows][cols]; freeGrid = new Particle[rows][cols]; // create the two grids of particles. connect the fixed and free counterparts for(int r = 0; r < rows; r++) { for(int c = 0; c < cols; c++) { Particle fixed = phys.makeParticle(1, c*spacing + xoff, r*spacing + yoff, 0); fixed.makeFixed(); Particle free = phys.makeParticle(1, c*spacing + xoff, r*spacing + yoff, 0); fixedGrid[r][c] = fixed; freeGrid[r][c] = free; // connect them with a spring // particle 1, particle 2, strength, damping, restlength connect(fixed, free); } } this.psgr = psgr; } int rows() { return rows; } int cols() { return cols; } Particle get(int row, int col) { return freeGrid[row][col]; } Particle get(GridSpace gs) { return freeGrid[gs.r()][gs.c()]; } GridSpace loc(Particle p) { for(int r = 0; r < rows; r++) { for(int c = 0; c < cols; c++) { if ( freeGrid[r][c] == p ) { return new GridSpace(r, c); } } } return new GridSpace(-1, -1); } // shifts all particles down into free positions (dead free particles) // deletes and recreates fixed particles and springs as necessary void regen() { // first, move all live particles down into dead or empty slots for(int c = 0; c < cols; c++) { // start search for dead cells from the bottom for(int r = rows - 1; r > 0; r--) { // if the free particle is dead or the slot is empty, // look above until we find a live one and move it to this grid position if ( freeGrid[r][c] == null || freeGrid[r][c].isDead() ) { int rowAbove = r - 1; while ( rowAbove >= 0 ) { // is the one above alive? Particle p = freeGrid[rowAbove][c]; // make sure we aren't looking at a dead particle or an empty slot if ( p != null && !p.isDead() ) { // move it freeGrid[r][c] = p; freeGrid[rowAbove][c] = null; // create a new spring to connect to the corresponding fixed particle connect(fixedGrid[r][c], p); // delete and recreate the fixed particle p used to be connected to // so that the spring connecting p to that fixed particle is destroyed Particle f = fixedGrid[rowAbove][c]; // new fixed particle in that position fixedGrid[rowAbove][c] = phys.makeParticle(1, f.position().x(), f.position().y(), f.position().z()); fixedGrid[rowAbove][c].makeFixed(); // kill the old one f.kill(); // break out of the loop because we found a replacement break; } rowAbove--; } // there weren't any to drop down, so remove the dead particle from the grid if ( rowAbove < 0 ) { freeGrid[r][c] = null; } } // just empty out dead slots at the top if ( freeGrid[0][c] != null && freeGrid[0][c].isDead() ) { freeGrid[0][c] = null; } } } // now repopulate the empty free slots for(int r = 0; r < rows; r++) { for(int c = 0; c < cols; c++) { if ( freeGrid[r][c] == null ) { Particle f = fixedGrid[r][c]; Particle p = phys.makeParticle(1, f.position().x(), f.position().y(), -10); freeGrid[r][c] = p; connect(f, p); } } } } private void connect(Particle fixed, Particle free) { phys.makeSpring(fixed, free, SPRING_STRENGTH, SPRING_DAMPING, 0); } void kill(int row, int col) { freeGrid[row][col].kill(); } void kill(GridSpace gs) { freeGrid[gs.r()][gs.c()].kill(); } void killAll(ArrayList gridSpaces) { for( int i = 0; i < gridSpaces.size(); i++) { kill( (GridSpace)gridSpaces.get(i) ); } } void update() { phys.tick(); } void draw() { for(int r = 0; r < rows; r++) { for(int c = 0; c < cols; c++) { psgr.draw(fixedGrid[r][c], freeGrid[r][c], r, c); } } } }