/*
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.network;
import sim.portrayal.*;
import sim.field.continuous.*;
import sim.field.network.*;
import sim.field.*;
import sim.util.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
/**
Portrays network fields. Only draws the edges. To draw the nodes, use a
ContinuousPortrayal2D or SparseGridPortrayal2D. The 'location' passed
into the DrawInfo2D handed to the SimplePortryal2D is the Edge itself,
while the 'object' passed to the SimplePortryal2D is the Edge's info object.
*/
public class NetworkPortrayal2D extends FieldPortrayal2D
{
// a line with a label
SimpleEdgePortrayal2D defaultPortrayal = new SimpleEdgePortrayal2D();
public Portrayal getDefaultPortrayal() { return defaultPortrayal; }
public void setField(Object field)
{
if (field instanceof SpatialNetwork2D ) super.setField(field);
else throw new RuntimeException("Invalid field for FieldPortrayal2D: " + field);
}
protected void hitOrDraw(Graphics2D graphics, DrawInfo2D info, Bag putInHere)
{
final SpatialNetwork2D field = (SpatialNetwork2D)this.field;
if( field == null ) return;
// compute the field for the second endpoint
SparseField2D otherField = field.field2; // do we have an auxiliary field?
if (otherField == null) otherField = field.field; // I guess not, use the main field
Double2D dimensions = field.field.getDimensions(); // dimensions of the main field
double xScale = info.draw.width / dimensions.x;
double yScale = info.draw.height / dimensions.y;
EdgeDrawInfo2D newinfo = new EdgeDrawInfo2D(
info.gui,
info.fieldPortrayal,
new Rectangle2D.Double(0,0, xScale, yScale), // the first two will get replaced
info.clip, // we don't do further clipping
new Point2D.Double(0,0), info); // these will also get replaced
newinfo.fieldPortrayal = this;
newinfo.precise = info.precise;
// draw ALL the edges -- one never knows if an edge will cross into our boundary
Bag nodes = field.network.getAllNodes();
HashMap edgemap = new HashMap();
for(int x=0;x<nodes.numObjs;x++)
{
Object node = nodes.objs[x];
Bag edges = field.network.getEdgesOut(node);
Double2D locStart = field.field.getObjectLocationAsDouble2D(node);
if (locStart == null) continue;
// coordinates of first endpoint
if (field.field instanceof Continuous2D) // it's continuous
{
newinfo.draw.x = (info.draw.x + (xScale) * locStart.x);
newinfo.draw.y = (info.draw.y + (yScale) * locStart.y);
}
else // it's a grid
{
newinfo.draw.x = (int)Math.floor(info.draw.x + (xScale) * locStart.x);
newinfo.draw.y = (int)Math.floor(info.draw.y + (yScale) * locStart.y);
double width = (int)Math.floor(info.draw.x + (xScale) * (locStart.x+1)) - newinfo.draw.x;
double height = (int)Math.floor(info.draw.y + (yScale) * (locStart.y+1)) - newinfo.draw.y;
// adjust drawX and drawY to center
newinfo.draw.x += width / 2.0;
newinfo.draw.y += height / 2.0;
}
for(int y=0;y<edges.numObjs;y++)
{
Edge edge = (Edge)edges.objs[y];
Double2D locStop = otherField.getObjectLocationAsDouble2D(edge.getOtherNode(node));
if (locStop == null) continue;
// only include the edge if we've not included it already.
if (!field.network.isDirected())
{
if (edgemap.containsKey(edge)) continue;
edgemap.put(edge, edge);
}
// coordinates of second endpoint
if (otherField instanceof Continuous2D) // it's continuous
{
newinfo.secondPoint.x = (info.draw.x + (xScale) * locStop.x);
newinfo.secondPoint.y = (info.draw.y + (yScale) * locStop.y);
}
else // it's a grid
{
newinfo.secondPoint.x = (int)Math.floor(info.draw.x + (xScale) * locStop.x);
newinfo.secondPoint.y = (int)Math.floor(info.draw.y + (yScale) * locStop.y);
double width = (int)Math.floor(info.draw.x + (xScale) * (locStop.x+1)) - newinfo.secondPoint.x;
double height = (int)Math.floor(info.draw.y + (yScale) * (locStop.y+1)) - newinfo.secondPoint.y;
// adjust drawX and drawY to center
newinfo.secondPoint.x += width / 2.0;
newinfo.secondPoint.y += height / 2.0;
}
// here's how we could reduce it if we knew that it intersected with the clip.... [cool job, Liviu -- Sean]
// Line2D.Double line = new Line2D.Double(newinfo.draw.x, newinfo.draw.y, newinfo.secondPoint.x, newinfo.secondPoint.y);
// if (line.intersects (info.clip))
{
Portrayal p = getPortrayalForObject(edge);
if (!(p instanceof SimpleEdgePortrayal2D))
throw new RuntimeException("Unexpected Portrayal " + p + " for object " +
edge + " -- expected a SimpleEdgePortrayal2D");
SimpleEdgePortrayal2D portrayal = (SimpleEdgePortrayal2D) p;
newinfo.location = edge;
if (graphics == null)
{
if (portrayal.hitObject(edge, newinfo))
{
putInHere.add(getWrapper(edge));
}
}
else
{
// MacOS X 10.3 Panther has a bug which resets the clip, YUCK
// graphics.setClip(clip);
portrayal.draw(edge, graphics, newinfo);
}
}
}
}
}
String edgeLocation(Edge edge)
{
// don't use toString, too much info
if (edge == null)
return "(Null)";
else if (edge.owner() == null)
return "(Unowned)" + edge.from() + " --> " + edge.to();
else if (edge.owner().isDirected())
return edge.from() + " --> " +edge.to();
else
return edge.from() + " <-> " + edge.to();
}
// The easiest way to make an inspector which gives the location of my objects
public LocationWrapper getWrapper(Edge edge)
{
final SpatialNetwork2D field = (SpatialNetwork2D)this.field;
return new LocationWrapper( edge.info, edge, this )
{
public String getLocationName()
{
Edge edge = (Edge)getLocation();
if (field != null && field.network != null)
{
// do I still exist in the field? Check the from() value
Bag b = field.network.getEdgesOut(edge.from());
// if (b != null) // no longer necessary
for(int x=0;x<b.numObjs;x++)
if (b.objs[x] == edge)
return edgeLocation(edge);
}
return "Gone. Was: " + edgeLocation(edge);
}
};
}
}