/*
* IndexGrid.java
*
* Copyright 2011 Chris Martin.
*
* This file is part of Flowy.
*
* Flowy is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Flowy is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Flowy. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package org.cmj.flowy.simulation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.cmj.flowy.simulation.math.RectangleF;
import org.cmj.flowy.simulation.particles.FluidParticle;
/**
* Class to represent a 2-d grid of discrete points in space. The particles are
* mapped to this grid based on their position in 2-d space. This eases the job
* of finding out their neighbours.
* @author Chris Martin.
*/
public class IndexGrid {
/** The grid itself. Each list contains the particles at that position in space. */
private List<Integer>[][] theGrid;
/** The distance between the cell points. */
private float theCellSpacing;
/** The simulation domain */
private RectangleF theDomain;
/** The width of the grid (in cells) */
public int theWidth;
/** The height of the grid (in cells) */
public int theHeight;
/** A map which maps a particle to a list containing all it's neighbouring particles. */
private Map<FluidParticle, List<Integer>> theNeighboursIndex = new HashMap<FluidParticle, List<Integer>>();
/**
* Create a new Grid, using the dimensions layed out in the domain and using
* the supplied cell spacing
* @param cellSpace The spacing between cells to use.
* @param domain The simulation domain.
*/
public IndexGrid(final float cellSpace, final RectangleF domain) {
theCellSpacing = cellSpace;
theDomain = domain;
theWidth = (int) (theDomain.width / theCellSpacing);
theHeight = (int) (theDomain.height / theCellSpacing);
}
/**
* Update the grid using the new list of particles.
* @param particles The new particles with which to update the grid.
*/
@SuppressWarnings("unchecked")
public void refresh(final List<FluidParticle> particles) {
theGrid = new ArrayList[this.theWidth][this.theHeight];
theNeighboursIndex.clear();
final int numOfParticles = particles.size();
for (int i = 0; i < numOfParticles; ++i) {
final FluidParticle thisParticle = particles.get(i);
thisParticle.gridIndexX = GetGridIndexX(thisParticle);
thisParticle.gridIndexY = GetGridIndexY(thisParticle);
// Add particle to list
if (theGrid[thisParticle.gridIndexX][thisParticle.gridIndexY] == null) {
theGrid[thisParticle.gridIndexX][thisParticle.gridIndexY] = new ArrayList<Integer>();
}
theGrid[thisParticle.gridIndexX][thisParticle.gridIndexY].add(i);
}
// Build up the cache of neighbours for each fluid particle.
for (int i = 0; i < numOfParticles; ++i) {
final FluidParticle thisParticle = particles.get(i);
theNeighboursIndex.put(thisParticle, getNeighbourIndexForCache(thisParticle));
}
}
/**
* Get the x-point on the simulation grid for the supplied particle. If the
* particle is outside the grid then it will appear at the edge of the grid.
* @param particle The particle whose position on the grid is to be determined.
* @return The x-point on the grid which represents the position of this particle
*/
private int GetGridIndexX(final FluidParticle particle) {
int gridIndexX = (int) (particle.position.x /theCellSpacing);
// Clamp
if (gridIndexX < 0) {
gridIndexX = 0;
}
else if (gridIndexX >= theWidth) {
gridIndexX = theWidth - 1;
}
return gridIndexX;
}
/**
* Get the y-point on the simulation grid for the supplied particle. If the
* particle is outside the grid then it will appear at the edge of the grid.
* @param particle The particle whose position on the grid is to be determined.
* @return The y-point on the grid which represents the position of this particle
*/
private int GetGridIndexY(final FluidParticle particle) {
int gridIndexY = (int) (particle.position.y / theCellSpacing);
// Clamp
if (gridIndexY < 0) {
gridIndexY = 0;
}
else if (gridIndexY >= this.theHeight) {
gridIndexY = this.theHeight - 1;
}
return gridIndexY;
}
/**
* Returns the indexes (in the particle list) of all of the particles which
* are neighbours of the supplied particle. Also included in this list will
* be the particle itself.
* @param particle The particle whose neighbours are to be returned.
* @return The list of indexes for particles that neighbour the supplied particle.
*/
private List<Integer> getNeighbourIndexForCache(FluidParticle particle)
{
List<Integer> neighbourIndices = new ArrayList<Integer>();
for (int xOff = -1; xOff < 2; xOff++)
{
for (int yOff = -1; yOff < 2; yOff++)
{
// Neighbour index
int x = particle.gridIndexX + xOff;
int y = particle.gridIndexY + yOff;
// Clamp
if (x > -1 && x < this.theWidth && y > -1 && y < this.theHeight)
{
List<Integer> indexList = theGrid[x][y];
if (indexList != null)
{
// Return neighbours index
for(int i=0; i<indexList.size(); ++i)
{
neighbourIndices.add(indexList.get(i));
}
}
}
}
}
return neighbourIndices;
}
/**
* Returns the indexes (in the particle list) of all of the particles which
* are neighbours of the supplied particle. Also included in this list will
* be the particle itself. This method should be relatively quick as the results should have been cached.
* @param particle The particle whose neighbours are to be returned.
* @return The list of indexes for particles that neighbour the supplied particle.
*/
public List<Integer> GetNeighbourIndex(FluidParticle particle) {
return theNeighboursIndex.get(particle);
}
}