package firesimulator.world; import java.awt.Polygon; import java.io.DataOutputStream; import java.io.IOException; import java.util.Collection; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import rescuecore.OutputBuffer; import firesimulator.simulator.Simulator; import firesimulator.util.Geometry; import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.Log; import org.uncommons.maths.number.NumberGenerator; /** * @author tn * */ public class Building extends StationaryObject { private static final Log LOG = LogFactory.getLog(Building.class); private static final double STEFAN_BOLTZMANN_CONSTANT = 0.000000056704; public static int WATER_EXTINGUISH_PARAM; public static float woodSpeed; public static float steelSpeed; public static float concreteSpeed; public static float WATER_CAPCITY; public static boolean POLICE_INFALMEABLE=false; public static boolean AMBULANCE_INFALMEABLE=false; public static boolean FIRE_INFALMEABLE=false; public static boolean REFUGE_INFALMEABLE=false; public static NumberGenerator<Double> burnRate; public boolean fierynessChanged; private int waterQuantity; private int floors=1; private int attributes=0; private int ignition=0; protected int fieryness=0; private int brokenness=0; public int[][] cells; private int[] entrances; private int code=0; private float buildingAreaGround=0; private float buildingAreaTotal=0; private int[] apexes; private Polygon polygon; public float fuel; private float initFuel; private float prevBurned; public float volume; public float capacity; private double energy; static final int FLOOR_HEIGHT=3; public float cooling=0; public Collection walls; public Hashtable connectedBuildings; public Building[] connectedBuilding; public float[] connectedValues; public double totalWallArea; private int lwater = 0; private int lwTime = -1; private boolean wasEverWatered = false; public boolean inflameable = true; public static float woodCapacity=4; public static float steelCapacity=4; public static float concreteCapacity=4; public static float woodIgnition=400; public static float steelIgnition=400; public static float concreteIgnition=400; public static float woodEnergie=1; public static float steelEnergie=1; public static float concreteEnergie=1; public static float woodBurning=800; public static float steelBurning=800; public static float concreteBurning=800; public static final int NORMAL=0; public static final int HEATING=1; public static final int BURNING=2; public static final int COOLING_DOWN=3; public static final int EXTINGUISHED=5; public static final int BURNED_DOWN=4; public Building(int id) { super(id); polygon=null; connectedBuildings=new Hashtable(30); initFuel = -1; prevBurned=0; } public float getBurningTemp(){ switch(code){ case 0: return woodBurning; case 1: return steelBurning; default: return concreteBurning; } } public float getIgnitionPoint(){ switch(code){ case 0: return woodIgnition; case 1: return steelIgnition; default: return concreteIgnition; } } public void ignite() { energy=getCapacity()*getIgnitionPoint()*1.5; } public float getBuildingAreaGround(){ return buildingAreaGround; } public int getFloors(){ return floors; } public void initialize(World world){ initWalls(world); setFieryness(0); setWaterQuantity(0); fierynessChanged=false; if(polygon==null){ polygon=new Polygon(); for(int n=0;n<apexes.length;n++) polygon.addPoint(apexes[n],apexes[++n]); } volume=buildingAreaGround*floors*FLOOR_HEIGHT; fuel=getInitialFuel(); setCapacity(volume*getThermoCapacity()); energy=0; initFuel=-1; prevBurned=0; lwTime = -1; lwater = 0; wasEverWatered = false; LOG.debug("Initialised building " + id + ": ground area = " + buildingAreaGround + ", floors = " + floors + ", volume = " + volume + ", initial fuel = " + fuel + ", energy capacity = " + getCapacity()); } public void reset(World w) { setFieryness(0); setWaterQuantity(0); initialize(w); } private void initWalls(World world){ if(walls != null) return; totalWallArea=0; walls=new LinkedList(); int fx=apexes[0]; int fy=apexes[1]; int lx=fx; int ly=fy; for(int n=2;n<apexes.length;n++){ int tx=apexes[n]; int ty=apexes[++n]; Wall w=new Wall(lx,ly,tx,ty,this); if(w.validate()){ walls.add(w); totalWallArea+=FLOOR_HEIGHT*1000*w.length; } else LOG.warn("Ignoring odd wall at building "+getID()); lx=tx; ly=ty; } Wall w=new Wall(lx,ly,fx,fy,this); walls.add(w); world.allWalls.addAll(walls); totalWallArea=totalWallArea/1000000d; } public int hashCode(){ return id; } public void initWallValues(World world){ int totalHits=0; int totalRays=0; int selfHits=0; int strange=0; for(Iterator w=walls.iterator();w.hasNext();){ Wall wall=(Wall)w.next(); wall.findHits(world); totalHits+=wall.hits; selfHits+=wall.selfHits; totalRays+=wall.rays; strange=wall.strange; } int c=0; connectedBuilding=new Building[connectedBuildings.size()]; connectedValues=new float[connectedBuildings.size()]; float base = totalRays; for(Enumeration e=connectedBuildings.keys();e.hasMoreElements();c++){ Building b=(Building)e.nextElement(); Integer value=(Integer)connectedBuildings.get(b); connectedBuilding[c]=b; connectedValues[c]=value.floatValue()/base; } LOG.debug("{"+(((float)totalHits)*100/((float)totalRays))+","+totalRays+","+totalHits+","+selfHits+","+strange+"}"); } public float getInitialFuel(){ if(initFuel<0){ initFuel = (float)(getFuelDensity()*volume); } return initFuel; } private float getFuelDensity(){ switch(code){ case 0: return woodEnergie; case 1: return steelEnergie; default: return concreteEnergie; } } private float getThermoCapacity(){ switch(code){ case 0: return woodCapacity; case 1: return steelCapacity; default: return concreteCapacity; } } public Polygon getPolygon(){ return polygon; } public String getType(){ return "BUILDING"; } public void setAttributes(int atrb){ this.attributes=atrb; } public int getCode(){ return code; } public void setIgnition(int ignition){ this.ignition=ignition; } public int getIgnition(){ return ignition; } public void setFieryness(int fieryness){ this.fieryness=fieryness; } public float getFuel(){ return fuel; } public int getFieryness(){ if(!isInflameable()) return 0; if(getTemperature()>=getIgnitionPoint()){ if(fuel>=getInitialFuel()*0.66) return 1; // burning, slightly damaged if(fuel>=getInitialFuel()*0.33) return 2; // burning, more damaged if(fuel>0) return 3; // burning, severly damaged } if(fuel==getInitialFuel()) if (wasEverWatered) return 4; // not burnt, but watered-damaged else return 0; // not burnt, no water damage if(fuel>=getInitialFuel()*0.66) return 5; // extinguished, slightly damaged if(fuel>=getInitialFuel()*0.33) return 6; // extinguished, more damaged if(fuel>0) return 7; // extinguished, severely damaged return 8; // completely burnt down } public void setBrokenness(int brk){ this.brokenness=brk; } public void setEntrances(int[] ent){ this.entrances=ent; } public void setCode(int code){ this.code=code; } public void setBuildingAreaGround(float area){ this.buildingAreaGround=area; } public void setBuildingAreaTotal(float area){ this.buildingAreaTotal=area; } public void setApexes(int[] apx){ this.apexes=apx; } public int[] getApexes(){ return apexes; } public void setFloors(int floors){ this.floors=1;//floors; revise by bing } public void findCells(World w) { LinkedList tmp=new LinkedList(); for(int x=0;x<w.getAirTemp().length;x++) for(int y=0;y<w.getAirTemp()[0].length;y++){ int xv=x*w.SAMPLE_SIZE+w.getMinX(); int yv=y*w.SAMPLE_SIZE+w.getMinY(); if(Geometry.boundingTest(polygon,xv,yv,w.SAMPLE_SIZE,w.SAMPLE_SIZE)){ int pc=Geometry.percent((float)xv,(float)yv,(float)w.SAMPLE_SIZE,(float)w.SAMPLE_SIZE,polygon); if(pc>0){ tmp.add(new Integer(x)); tmp.add(new Integer(y)); tmp.add(new Integer(pc)); Object[] o=new Object[]{this,new Float(pc)}; w.gridToBuilding[x][y].add(o); } } } if(tmp.size()>0){ cells=new int[tmp.size()/3][3]; Iterator i=tmp.iterator(); for(int c=0;c<cells.length;c++){ cells[c][0]=((Integer)i.next()).intValue(); cells[c][1]=((Integer)i.next()).intValue(); cells[c][2]=((Integer)i.next()).intValue(); } } else { LOG.warn(getID()+" has no cell"); LOG.warn("Sample size: " + w.SAMPLE_SIZE); LOG.warn("World min X, Y: " + w.getMinX() + ", " + w.getMinY()); LOG.warn("Air grid size: " + w.getAirTemp().length + " x " + w.getAirTemp()[0].length); LOG.warn("Building polygon: "); for (int i = 0; i < apexes.length; i += 2) { LOG.warn(apexes[i] + ", " + apexes[i + 1]); } int expectedCellX = (apexes[0] - w.getMinX()) / w.SAMPLE_SIZE; int expectedCellY = (apexes[1] - w.getMinY()) / w.SAMPLE_SIZE; LOG.warn("Building should be in cell " + expectedCellX + ", " + expectedCellY); for(int x=0;x<w.getAirTemp().length;x++) { for(int y=0;y<w.getAirTemp()[0].length;y++){ int xv=x*w.SAMPLE_SIZE+w.getMinX(); int yv=y*w.SAMPLE_SIZE+w.getMinY(); if (Geometry.boundingTest(polygon,xv,yv,w.SAMPLE_SIZE,w.SAMPLE_SIZE)) { LOG.warn("Cell " + x + ", " + y); LOG.warn("boundingTest(polygon, " + xv + ", " + yv + ", " + w.SAMPLE_SIZE + ", " + w.SAMPLE_SIZE + ") = " + Geometry.boundingTest(polygon,xv,yv,w.SAMPLE_SIZE,w.SAMPLE_SIZE)); LOG.warn("pc = " + Geometry.percent((float)xv,(float)yv,(float)w.SAMPLE_SIZE,(float)w.SAMPLE_SIZE,polygon)); int counter=0; double dx=w.SAMPLE_SIZE/100; double dy=w.SAMPLE_SIZE/100; for(int i=0;i<100;i++) { for(int j=0;j<100;j++){ double testX = dx*i+xv; double testY = dy*j+yv; if(polygon.contains(dx*i+xv,dy*j+yv)) { counter++; LOG.warn("Point " + testX + ", " + testY + " is inside"); } } } LOG.warn("Counted " + counter + " interior points"); } } } } } public double getTemperature() { double rv=energy/getCapacity(); if (Double.isNaN(rv)) { LOG.warn("Building " + id + " getTemperature returned NaN"); new RuntimeException().printStackTrace(); LOG.warn("Energy: " + energy); LOG.warn("Capacity: " + getCapacity()); LOG.warn("Volume: " + volume); LOG.warn("Thermal capacity: " + getThermoCapacity()); LOG.warn("Ground area: " + buildingAreaGround); LOG.warn("Floors: " + floors); } if(rv==Double.NaN||rv==Double.POSITIVE_INFINITY||rv==Double.NEGATIVE_INFINITY) rv=Double.MAX_VALUE*0.75; return rv; } public String codeToString() { switch(code){ case 0: return "wooden"; case 1: return "steelframe"; default: return "concret"; } } public int getLastWater(){ return lwater; } public boolean getLastWatered(){ return lwTime == World.getWorld().getTime(); } public boolean wasEverWatered() { return wasEverWatered; } public int getWaterQuantity() { return waterQuantity; } public void setWaterQuantity(int i) { if(i > waterQuantity){ lwTime = World.getWorld().getTime(); lwater = i - waterQuantity; wasEverWatered = true; } waterQuantity = i; } public float getCapacity() { return capacity; } public void setCapacity(float f) { capacity = f; } public String toString(){ String rv="building "+getID()+"\n"; for(Iterator i=walls.iterator();i.hasNext();rv+=i.next()+"\n"); return rv; } public double getRadiationEnergy() { double t=getTemperature()+293; // Assume ambient temperature is 293 Kelvin. double radEn = (t * t * t * t) * totalWallArea * Simulator.RADIATION_COEFFICENT * STEFAN_BOLTZMANN_CONSTANT; if (id == 23545) { LOG.debug("Getting radiation energy for building " + id); LOG.debug("t = " + t); LOG.debug("t^4 = " + (t * t * t * t)); LOG.debug("Total wall area: " + totalWallArea); LOG.debug("Radiation coefficient: " + Simulator.RADIATION_COEFFICENT); LOG.debug("Stefan-Boltzmann constant: " + STEFAN_BOLTZMANN_CONSTANT); LOG.debug("Radiation energy: " + radEn); LOG.debug("Building energy: " + getEnergy()); } if(radEn==Double.NaN||radEn==Double.POSITIVE_INFINITY||radEn==Double.NEGATIVE_INFINITY) radEn=Double.MAX_VALUE*0.75; if (radEn > getEnergy()) { radEn = getEnergy(); } return radEn; } public boolean isBuilding(int x,int y){ return getX()==x&&getY()==y; } public double getEnergy() { if(energy==Double.NaN||energy==Double.POSITIVE_INFINITY||energy==Double.NEGATIVE_INFINITY) energy=Double.MAX_VALUE*0.75d; return energy; } public void setEnergy(double energy) { if(energy==Double.NaN||energy==Double.POSITIVE_INFINITY||energy==Double.NEGATIVE_INFINITY){ energy=Double.MAX_VALUE*0.75d; } this.energy = energy; } public float getConsum() { if(fuel==0){ return 0; } float tf = (float) (getTemperature()/1000f); float lf = getFuel()/getInitialFuel(); float f = (float)(tf*lf*burnRate.nextValue()); if(f<0.005f) f=0.005f; return getInitialFuel()*f; } public float getPrevBurned() { return prevBurned; } public void setPrevBurned(float prevBurned) { this.prevBurned = prevBurned; } public void setInflameable(boolean inflameable){ this.inflameable=inflameable; } public boolean isInflameable(){ return inflameable; } }