package iamrescue.agent.firebrigade;
import iamrescue.agent.ISimulationTimer;
import iamrescue.belief.IAMWorldModel;
import iamrescue.routing.costs.BlockCheckerUtil;
import java.awt.geom.Rectangle2D;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javolution.util.FastMap;
import javolution.util.FastSet;
import rescuecore2.misc.geometry.GeometryTools2D;
import rescuecore2.misc.geometry.Line2D;
import rescuecore2.misc.geometry.Point2D;
import rescuecore2.standard.entities.Building;
import rescuecore2.standard.entities.StandardEntity;
import rescuecore2.worldmodel.EntityID;
public class FastFireSite{
private Building center;
private Map<EntityID, Building> buildingsOnFire;
private Map<Float, FastFireDistanceArray> fireArrays;
private int NUMBER_OF_ARRAYS = 8;
private int FIRE_DISTANCE = 50000; // 50m
private int MAX_ALLOWED_DISTANCE = 100000; // 100m
private ISimulationTimer timer;
private IAMWorldModel model;
private HeatTransferGraph graph;
private int maxDistance = 0;
/**
* Constructor of the class it initialises the center of the firesite, the graph, the world model, and
* some other useful parameters
*
* @param center building at the center of the FireSite
* @param graph connection graph between buildings
* @param model world model to consider
*
*/
public FastFireSite(ISimulationTimer timer, Building center, IAMWorldModel model, HeatTransferGraph graph){
this.center = center;
this.model = model;
this.timer = timer;
this.graph = graph;
// all the buildings within the FireSite that are on fire
this.buildingsOnFire = new FastMap<EntityID, Building>();
this.buildingsOnFire.put(center.getID(), center);
// bounds of the map
Rectangle2D bounds = model.getBounds();
this.fireArrays = new FastMap<Float, FastFireDistanceArray>();
float angle = 0f; // angle of the fire array
for(int i = 0; i < NUMBER_OF_ARRAYS ; i++){
if(center.getX() == bounds.getMinX() && center.getY() == bounds.getMinY())
// bottom left corner
if( angle < 0.0 || angle > 90.0 ) continue;
if(center.getX() == bounds.getMinX() && center.getY() == bounds.getMaxY())
// top left corner
if( angle > 270.0 ) continue;
if(center.getX() == bounds.getMaxX() && center.getY() == bounds.getMaxY())
// top right corner
if( angle < 180.0 || angle > 270.0) continue;
if(center.getX() == bounds.getMaxX() && center.getY() == bounds.getMinY())
// bottom right corner
if( angle < 90.0 || angle > 180.0 ) continue;
FastFireDistanceArray fd_array = new FastFireDistanceArray(center, angle);
fireArrays.put(angle, fd_array);
angle += (double) 360 / NUMBER_OF_ARRAYS;
}
}
public boolean containsBuilding(Building building) {
return buildingsOnFire.containsValue(building);
}
public boolean containsBuildingID(Building building) {
return buildingsOnFire.containsKey(building.getID());
}
public void addBuildingOnFire(Building building) {
buildingsOnFire.put(building.getID(), building);
updateFireArrays(building);
}
public Collection<Building> getFringe(){
FastSet<Building> fringe = new FastSet<Building>();
for (Building building : buildingsOnFire.values()) {
if(building.isFierynessDefined())
if(building.getFieryness() == 1 || building.getFieryness() == 2)
fringe.add(building);
}
return fringe;
}
public Collection<Building> getCenter() {
FastSet<Building> centers = new FastSet<Building>();
for (Building building : buildingsOnFire.values()) {
if(building.isFierynessDefined())
if(building.getFieryness() == 3)
centers.add(building);
}
return centers;
}
private void updateFireArrays(Building building) {
double buildingAngle = Math.atan((building.getY() - center.getY()) / ((double) building.getX() - center.getX()));
double maxDistance = 0.0;
FastFireDistanceArray closerArray = null;
for (float fireSpreadingAngle : fireArrays.keySet()) {
double distance = Math.sqrt((fireSpreadingAngle - buildingAngle)*(fireSpreadingAngle - buildingAngle));
if(distance > maxDistance){
maxDistance = distance;
closerArray = fireArrays.get(fireSpreadingAngle);
}
}
//TODO update closerarray
updateSingleFireArray(closerArray, building);
}
/**
* update a single fireArray
*
* @param time current time
* @param fireArray array to update
*
*/
private void updateSingleFireArray(FastFireDistanceArray fireArray, Building building) {
Line2D fireLine = new Line2D(new Point2D(center.getX(), center.getY()),
new Point2D(center.getX() + (int) FIRE_DISTANCE*Math.cos(Math.toRadians(fireArray.getAngle())),
center.getY() + (int) FIRE_DISTANCE*Math.sin(Math.toRadians(fireArray.getAngle()))));
if(BlockCheckerUtil.isIntersecting(building, fireLine, true)){
double distance = Math.sqrt(
( (double) building.getY() - center.getY())*(building.getY() - center.getY())
+ ( (double) building.getX() - center.getX())*(building.getX() - center.getX())
);
fireArray.setFrontierBuilding(building);
fireArray.addDistance(timer.getTime(), (float) distance);
}
}
public void removeBuilding(Building building) {
buildingsOnFire.remove(building);
}
public Map<EntityID, Building> getBuildingsOnFire() {
return buildingsOnFire;
}
public void setBuildingsOnFire(Map<EntityID, Building> allBuildings) {
buildingsOnFire = allBuildings;
}
public void updateArraysSpeed(List<FastFireSite> containedFireSites) {
// we build a map with all the average speed for all the angles
Map<Float, Float> averageSpeeds = new FastMap<Float, Float>();
for (FastFireSite fastFireSite : containedFireSites) {
// we consider all the fireArrays of the current site
Map<Float, FastFireDistanceArray> currentSiteArrays = fastFireSite.getFireArrays();
for (Float angle : currentSiteArrays.keySet()) {
// for each fire site we get the current speed we already store for
// the specific angle
Float currentAngleSpeed = averageSpeeds.get(angle);
// then we get the speed for the current array
float currentArraySpeed = currentSiteArrays.get(angle).getAverageSpeed();
if(currentAngleSpeed == null){
averageSpeeds.put(angle, currentArraySpeed);
continue;
}
float updatedSpeed = currentAngleSpeed + currentAngleSpeed;
averageSpeeds.put(angle, updatedSpeed);
}
}
for (Float angle : averageSpeeds.keySet()) {
float newValue = averageSpeeds.get(angle) / containedFireSites.size();
averageSpeeds.put(angle, newValue);
}
// we finally update the averageSpeed of each fire array
for (Float arrayAngle : fireArrays.keySet()) {
FastFireDistanceArray currentArray = fireArrays.get(arrayAngle);
currentArray.setAverageSpeed(averageSpeeds.get(arrayAngle));
}
}
/**
* This method predict the construct estimates the fireSite @param time
* time steps in the future
*
* @param time the time at which we want the prediction
* @return a predicted copy of the current fireSite
*
*/
public FastFireSite predict(int time){
// we make a copy
FastFireSite copy = null;
try {
copy = (FastFireSite) this.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
copy.predictFireArrays(time);
copy.predictBuildingsOnFire();
return copy;
}
/**
* extend the fireArrays of the fireSite, considering the current prediction model
*
* @param time time for which the prediction must be considered
*
*/
public void predictFireArrays(int time) {
for (FastFireDistanceArray fireArray : fireArrays.values()) {
predictFireArray(time, fireArray);
}
}
/**
* this method extends the specified @param fireArray to the given @param time
*
* @param time time of prediction
* @param fireArray fireArray to be predicted
*
*/
private void predictFireArray(int time, FastFireDistanceArray fireArray) {
FastSet<Building> visitedBuildings = new FastSet<Building>();
boolean frontierHasChanged = true;
while(frontierHasChanged){
Building currentFrontier = fireArray.getFrontierBuilding();
// we visited the frontier
visitedBuildings.add(currentFrontier);
// and the previous frontier
visitedBuildings.add(fireArray.getPreviousFrontier());
double predictedDistance = fireArray.getPredictedDistance(time);
Line2D fireLine = new Line2D(new Point2D(currentFrontier.getX(), currentFrontier.getY()),
new Point2D(currentFrontier.getX() + (int) predictedDistance*Math.cos(Math.toRadians(fireArray.getAngle())),
currentFrontier.getY() + (int) predictedDistance*Math.sin(Math.toRadians(fireArray.getAngle()))));
Collection<Building> neighbours = graph.getNeighbouringBuildings(currentFrontier);
//un-set the flag
frontierHasChanged = false;
for (Building building : neighbours) { // TODO this portion of code might be BUGGED
// for each neighbour we consider the FAREST ONE
double maxNeighbourDistance = 0.0;
// System.out.println("entering the NEIGHBOOURS loop");
if(visitedBuildings.contains(building))
// if we already visited the building then we move on
continue;
// get current neighbours lines
if(BlockCheckerUtil.isIntersecting(building, fireLine, true)){
// building intersects the line and is on fire
double distanceToCurrentFrontier = Math.sqrt(
((double) building.getY() - currentFrontier.getY())*(building.getY() - currentFrontier.getY())
+ ((double) building.getX() - currentFrontier.getX())*(building.getX() - currentFrontier.getX())
);
if(distanceToCurrentFrontier > maxNeighbourDistance){
maxNeighbourDistance = distanceToCurrentFrontier;
currentFrontier = building;
frontierHasChanged = true;
}
}
// the building has now been visited
visitedBuildings.add(building);
}
if(frontierHasChanged) {
// update the current frontier
fireArray.setPreviousFrontier(fireArray.getFrontierBuilding());
// update the fireSite
fireArray.setFrontierBuilding(currentFrontier);
// memorize the distance
double distance = Math.sqrt(
((double) currentFrontier.getY() - center.getY())*(currentFrontier.getY() - center.getY())
+ ((double) currentFrontier.getX() - center.getX())*(currentFrontier.getX() - center.getX())
);
if(distance > maxDistance) maxDistance = (int) (distance + 0.5);
}
}
}
/**
* same as method updateBuildingsOnFire
*/
private void predictBuildingsOnFire(){
// we limit the size of predictions
if(maxDistance > MAX_ALLOWED_DISTANCE)
maxDistance = MAX_ALLOWED_DISTANCE;
Collection<StandardEntity> buildingsToAdd = model.getObjectsInRange(center, maxDistance);
for (StandardEntity standardEntity : buildingsToAdd) {
if(standardEntity instanceof Building){
Building building = (Building) standardEntity;
if(building.isFierynessDefined()){
if(building.getFieryness() == 8)
buildingsOnFire.remove(building);
}
else
buildingsOnFire.put(building.getID(), building);
}
}
}
public Map<Float, FastFireDistanceArray> getFireArrays(){
return fireArrays;
}
public Collection<Building> getSetOfBuildingsOnFire(){
return buildingsOnFire.values();
}
/* (non-Javadoc)
* @see java.lang.Object#clone()
*/
@Override
protected Object clone() throws CloneNotSupportedException {
FastFireSite copy = new FastFireSite(this.timer, this.center, this.model, this.graph);
copy.buildingsOnFire.putAll(this.buildingsOnFire);
copy.fireArrays = new FastMap<Float, FastFireDistanceArray>();
for (Float fireArrayAngle : this.fireArrays.keySet())
copy.fireArrays.put(fireArrayAngle, (FastFireDistanceArray) this.fireArrays.get(fireArrayAngle).clone());
return copy;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return super.equals(obj);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// /**
// * this method updates the FireSite with the current information of the
// * buildings on fire
// *
// * @param time the current time step
// * @param buildingsOnFire2 new buildingsOnFire
// *
// */
// public void update(int time) {
// }
//
// /**
// * update the buildingsOnFire vector
// *
// */
// private void updateBuildingsOnFire() {
// Set<PositionXY> positionsSet = new FastSet<PositionXY>();
//
// if(maxDistance > MAX_ALLOWED_DISTANCE)
// maxDistance = MAX_ALLOWED_DISTANCE;
//
// Collection<StandardEntity> buildingsToAdd = model.getObjectsInRange(center, maxDistance);
//
// for (StandardEntity standardEntity : buildingsToAdd) {
// if(standardEntity instanceof Building){
// Building building = (Building) standardEntity;
// if(building.isFierynessDefined()){
// if(building.getFieryness() <= 3 && building.getFieryness() >= 1)
// buildingsOnFire.add(building);
// else
// buildingsOnFire.remove(building);
// }
// }
// }
// }
//
// /**
// * update the fire arrays of the site, with the most up to date information
// *
// * @param time current time
// */
// public void updateFireArrays(int time){
// for (FireDistanceArray fireArray : fireArrays)
// updateFireArray(time, fireArray);
// }
//
//
//
//
//
// /**
// * this method checks if the current fireSite has AT LEAST one building that is contained in
// * another fireSite
// *
// * @param otherSite
// * @return true if the 2 fireSites intersects
// */
// public boolean intersects(FireSite otherSite) {
// for (Building building : buildingsOnFire)
// if(otherSite.containsBuilding(building)) return true;
//
// return false;
// }
//
// public Building getCenter() {
// return center;
// }
//
//
}