/*
Copyright 2006 by Sean Luke and George Mason University
Licensed under the Academic Free License version 3.0
See the file "LICENSE" for more information
*/
package sim.portrayal.grid;
import sim.portrayal.*;
import sim.portrayal.simple.*;
import sim.field.grid.*;
import java.awt.*;
import java.awt.geom.*;
import sim.util.*;
import java.util.*;
import sim.display.*;
/**
Portrayal for Dense grids: grids of Bags of objects.
The 'location' passed
into the DrawInfo2D handed to the SimplePortryal2D is a MutableInt2D.
*/
public class DenseGridPortrayal2D extends ObjectGridPortrayal2D
{
public DrawPolicy policy;
public DenseGridPortrayal2D()
{
super();
}
/** @IgnoreWarnings
@deprecated Use setDrawPolicy. */
public DenseGridPortrayal2D (DrawPolicy policy)
{
super();
this.policy = policy;
}
public void setDrawPolicy(DrawPolicy policy)
{
this.policy = policy;
}
public DrawPolicy getDrawPolicy()
{
return policy;
}
public void setField(Object field)
{
if (field instanceof DenseGrid2D ) super.setFieldBypass(field); // see ObjectGridPortrayal2D.setFieldBypass
else throw new RuntimeException("Invalid field for DenseGridPortrayal2D: " + field);
}
public Object getObjectLocation(Object object, GUIState gui)
{
synchronized(gui.state.schedule)
{
final DenseGrid2D field = (DenseGrid2D) this.field;
if (field==null) return null;
int maxX = field.getWidth();
int maxY = field.getHeight();
if (maxX == 0 || maxY == 0) return null;
// find the object.
for(int x=0; x < maxX; x++)
{
Object[] fieldx = field.field[x];
for(int y = 0; y < maxY; y++)
{
Bag objects = (Bag)(fieldx[y]);
if (objects == null || objects.size()==0) continue;
for(int i=0;i<objects.numObjs;i++)
if (objects.objs[i] == object) // found it!
return new Int2D(x,y);
}
}
return null; // it wasn't there
}
}
// our location to pass to the portrayal
protected final MutableInt2D locationToPass = new MutableInt2D(0,0);
protected void hitOrDraw(Graphics2D graphics, DrawInfo2D info, Bag putInHere)
{
final DenseGrid2D field = (DenseGrid2D)(this.field);
Bag policyBag = new Bag();
if (field==null) return;
boolean objectSelected = !selectedWrappers.isEmpty();
Object selectedObject = (selectedWrapper == null ? null : selectedWrapper.getObject());
// Scale graphics to desired shape -- according to p. 90 of Java2D book,
// this will change the line widths etc. as well. Maybe that's not what we
// want.
// first question: determine the range in which we need to draw.
// We assume that we will fill exactly the info.draw rectangle.
// We can do the item below because we're an expensive operation ourselves
final int maxX = field.getWidth();
final int maxY = field.getHeight();
if (maxX == 0 || maxY == 0) return;
final double xScale = info.draw.width / maxX;
final double yScale = info.draw.height / maxY;
int startx = (int)((info.clip.x - info.draw.x) / xScale);
int starty = (int)((info.clip.y - info.draw.y) / yScale); // assume that the X coordinate is proportional -- and yes, it's _width_
int endx = /*startx +*/ (int)((info.clip.x - info.draw.x + info.clip.width) / xScale) + /*2*/ 1; // with rounding, width be as much as 1 off
int endy = /*starty +*/ (int)((info.clip.y - info.draw.y + info.clip.height) / yScale) + /*2*/ 1; // with rounding, height be as much as 1 off
DrawInfo2D newinfo = new DrawInfo2D(info.gui, info.fieldPortrayal, new Rectangle2D.Double(0,0, xScale, yScale), info.clip, info); // we don't do further clipping
newinfo.precise = info.precise;
newinfo.location = locationToPass;
if (endx > maxX) endx = maxX;
if (endy > maxY) endy = maxY;
if( startx < 0 ) startx = 0;
if( starty < 0 ) starty = 0;
for(int x=startx;x<endx;x++)
for(int y=starty;y<endy;y++)
{
Bag objects = field.field[x][y];
if (objects == null || objects.size()==0) continue;
if (policy != null & graphics != null)
{
policyBag.clear(); // fast
if (policy.objectToDraw(objects,policyBag)) // if this function returns FALSE, we should use objects as is, else use the policy bag.
objects = policyBag; // returned TRUE, so we're going to use the modified policyBag instead.
}
locationToPass.x = x;
locationToPass.y = y;
for(int i=0;i<objects.numObjs;i++)
{
final Object portrayedObject = objects.objs[i];
Portrayal p = getPortrayalForObject(portrayedObject);
if (!(p instanceof SimplePortrayal2D))
throw new RuntimeException("Unexpected Portrayal " + p + " for object " +
portrayedObject + " -- expected a SimplePortrayal2D");
SimplePortrayal2D portrayal = (SimplePortrayal2D)p;
// translate --- the + newinfo.width/2.0 etc. moves us to the center of the object
newinfo.draw.x = (int)(info.draw.x + (xScale) * x);
newinfo.draw.y = (int)(info.draw.y + (yScale) * y);
newinfo.draw.width = (int)(info.draw.x + (xScale) * (x+1)) - newinfo.draw.x;
newinfo.draw.height = (int)(info.draw.y + (yScale) * (y+1)) - newinfo.draw.y;
// adjust drawX and drawY to center
newinfo.draw.x += newinfo.draw.width / 2.0;
newinfo.draw.y += newinfo.draw.height / 2.0;
if (graphics == null)
{
if (portrayedObject != null && portrayal.hitObject(portrayedObject, newinfo))
putInHere.add(getWrapper(portrayedObject, new Int2D(x,y)));
}
else
{
// MacOS X 10.3 Panther has a bug which resets the clip, YUCK
// graphics.setClip(clip);
newinfo.selected = (objectSelected && // there's something there
(selectedObject==portrayedObject || selectedWrappers.get(portrayedObject) != null));
/*{
LocationWrapper wrapper = null;
if (selectedObject == portrayedObject)
wrapper = selectedWrapper;
else wrapper = (LocationWrapper)(selectedWrappers.get(portrayedObject));
portrayal.setSelected(wrapper,true);
portrayal.draw(portrayedObject, graphics, newinfo);
portrayal.setSelected(wrapper,false);
}
else */ portrayal.draw(portrayedObject, graphics, newinfo);
}
}
}
drawGrid(graphics, xScale, yScale, maxX, maxY, info);
drawBorder(graphics, xScale, info);
}
// Overrides objectGridPortrayal2D
Int2D searchForObject(Object object, Int2D loc)
{
DenseGrid2D field = (DenseGrid2D)(this.field);
Object[][] grid = field.field;
if (grid[loc.x][loc.y] != null)
{
Bag b = (Bag)(grid[loc.x][loc.y]);
if (b.contains(object))
return new Int2D(loc.x, loc.y);
}
//field.getNeighborsMaxDistance(loc.x, loc.y, SEARCH_DISTANCE, true, xPos, yPos);
field.getMooreLocations(loc.x, loc.y, SEARCH_DISTANCE, Grid2D.TOROIDAL, false, xPos, yPos); // we remove the origin: there might be a big useless bag there
for(int i=0;i<xPos.numObjs;i++)
{
if (grid[xPos.get(i)][yPos.get(i)] != null)
{
Bag b = (Bag)(grid[xPos.get(i)][yPos.get(i)]);
if (b.contains(object))
return new Int2D(xPos.get(i), yPos.get(i));
}
}
return null;
}
final Message unknown = new Message("It's too costly to figure out where the object went.");
// overrides same version in ObjectGrid2D
public LocationWrapper getWrapper(Object object, Int2D location)
{
final DenseGrid2D field = (DenseGrid2D)(this.field);
return new LocationWrapper(object, location, this)
{
public Object getLocation()
{
Int2D loc = (Int2D) super.getLocation();
if (field.field[loc.x][loc.y] != null &&
field.field[loc.x][loc.y].contains(getObject())) // it's still there!
{
return loc;
}
else
{
Int2D result = searchForObject(object, loc);
if (result != null) // found it nearby
{
location = result;
return result;
}
else // it's moved on!
{
return unknown;
}
}
}
public String getLocationName()
{
Object loc = getLocation();
if (loc instanceof Int2D)
return ((Int2D)this.location).toCoordinates();
else return "Location Unknown";
}
};
}
}