/**
** Space.java
**
** Copyright 2011 by Sarah Wise, Mark Coletti, Andrew Crooks, and
** George Mason University.
**
** Licensed under the Academic Free License version 3.0
**
** See the file "LICENSE" for more information
**
** $Id$
**/
package sim.app.geo.sillypeds;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Random;
import sim.field.geo.GeomGridField;
import sim.field.grid.DoubleGrid2D;
import sim.field.grid.ObjectGrid2D;
public class Space
{
/** Indicates grid cells that are outside the building.
*
*/
private static final int NO_DATA = -9999;
int width;
int height;
ObjectGrid2D field;
/** The set of navigable tiles
*/
ArrayList<Tile> validTiles;
/** maps a certain exit to go to a given
* other space and the tile in that space where the exit dumps you
*
*/
HashMap<Tile, Entrance> exits = new HashMap<Tile, Entrance>();
Space(GeomGridField floorPlan)
{
width = floorPlan.getGridWidth();
height = floorPlan.getGridHeight();
field = new ObjectGrid2D(width, height);
// list of all tiles that can be accessed, e.g. not dead space
validTiles = new ArrayList<Tile>();
DoubleGrid2D heightField = (DoubleGrid2D) floorPlan.getGrid();
Tile t = null;
// initialize the Tiles in the correct places with the appropriate gradients
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
double baseHeight = heightField.get(x, y);
// If the current grid cell is outside the building, we need
// to ensure that the gradient is Double.MAX_VALUE because
// otherwise the value of NO_DATA, -9999, would be *lower* than
// the gradients found *inside* the building; if we didn't
// change the tiles outside to use Double.MAX_VALUE, then
// pedestrians would eagerly pass through the walls of the
// building.
if (baseHeight == NO_DATA)
{
t = new Tile(x, y, Double.MAX_VALUE);
} else
{
t = new Tile(x, y, baseHeight);
validTiles.add(t);
}
field.set(x, y, t);
}
}
}
/**
* add Pedestrians to the Space
* <p>
* XXX I <em>think</em> the idea is to randomly place pedestrians in
* valid tiles, ensuring that there is only one pedestrian per tile.
*
* @param sp - the SillyPeds object
* @param numPeds - number of Pedestrians to add to the simulation
* @return list of all new, added Pedestrians
*/
ArrayList<Pedestrian> populate(SillyPeds sp, int numPeds)
{
ArrayList<Pedestrian> peds = new ArrayList<Pedestrian>();
// Copy over the valid tiles and shuffle them. Then we iterate
// through the shuffled copy setting a pedestrian to each tile.
ArrayList<Tile> randomTiles = new ArrayList<Tile>(this.validTiles);
// Unfortunately we cannot use the MASON RNG (i.e., SillyPeds.random)
// because it isn't compatible with Colletions.shuffle(). So we have
// to fall back on the legacy Java RNG.
Random rng = new Random();
Collections.shuffle(randomTiles, rng);
// Having more pedestrians than available space is a Bad Thing.
assert validTiles.size() >= numPeds : "not enough valid tiles";
// go through all Pedestrians to create and schedule them
for (int i = 0; i < numPeds; i++)
{
Tile t = randomTiles.get(i);
Pedestrian p = new Pedestrian(sp, this, t);
t.addPed(p);
peds.add(p);
// only schedule it once: the Pedestrian will reschedule itself
// also note that it schedules its ordering according to the value
// of its current tile's gradient, i.e. distance from an exit
sp.schedule.scheduleOnce(p, (int) (1 + t.baseheight));
}
return peds;
}
// tells a Pedestrian what space a Tile/exit leads to
public Entrance exit(Tile t)
{
return exits.get(t);
}
}