sim.portrayal.grid.HexaSparseGridPortrayal2D Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mason Show documentation
Show all versions of mason Show documentation
MASON is a fast discrete-event multiagent simulation library core in Java, designed to be the foundation for large custom-purpose Java simulations, and also to provide more than enough functionality for many lightweight simulation needs. MASON contains both a model library and an optional suite of visualization tools in 2D and 3D.
The newest version!
/*
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.field.grid.*;
import sim.portrayal.*;
import sim.portrayal.simple.*;
import sim.util.*;
import java.awt.*;
import java.util.*;
import java.awt.geom.*;
/**
Portrayal for hexagonal grids (each cell has six equally-distanced neighbors). It can draw
either continuous and descrete sparse fields.
The 'location' passed
into the DrawInfo2D handed to the SimplePortryal2D is an Int2D.
*/
public class HexaSparseGridPortrayal2D extends SparseGridPortrayal2D
{
int[] xPoints = new int[6];
int[] yPoints = new int[6];
double[] xyC = new double[2];
double[] xyC_ul = new double[2];
double[] xyC_up = new double[2];
double[] xyC_ur = new double[2];
final static void getxyC( final int x, final int y, final double xScale, final double yScale, final double tx, final double ty, final double[] xyC )
{
xyC[0] = tx + xScale * (1.5 * x + 1);
xyC[1] = ty + yScale * (1.0 + 2.0 * y + (x<0?(-x)%2:x%2) );
}
public HexaSparseGridPortrayal2D()
{
super();
defaultPortrayal = new HexagonalPortrayal2D();
}
/** @deprecated Use setDrawPolicy. */
public HexaSparseGridPortrayal2D (DrawPolicy policy)
{
super(policy);
defaultPortrayal = new HexagonalPortrayal2D();
}
/** The ratio of the width of a hexagon to its height: 1 / Sin(60 degrees), otherwise known as 2 / Sqrt(3) */
static final double HEXAGONAL_RATIO = 2/Math.sqrt(3);
public void setObjectPosition(Object object, Point2D.Double position, DrawInfo2D fieldPortrayalInfo)
{
final SparseGrid2D field = (SparseGrid2D)this.field;
if (field==null) return;
if (field.getObjectLocation(object) == null) return;
Int2D location = (Int2D)(getPositionLocation(position, fieldPortrayalInfo));
if (location != null)
{
if (object instanceof Fixed2D && (!((Fixed2D)object).maySetLocation(field, location)))
return; // this is deprecated and will be deleted
//if (object instanceof Constrained)
// location = (Int2D)((Constrained)object).constrainLocation(field, location);
// if (location != null)
field.setObjectLocation(object, location);
}
}
public Double2D getScale(DrawInfo2D info)
{
synchronized(info.gui.state.schedule)
{
final Grid2D field = (Grid2D) this.field;
if (field==null) return null;
int maxX = field.getWidth();
int maxY = field.getHeight();
if (maxX == 0 || maxY == 0) return null;
final double divideByX = ((maxX%2==0)?(3.0*maxX/2.0+0.5):(3.0*maxX/2.0+2.0));
final double divideByY = (1.0+2.0*maxY);
final double xScale = info.draw.width / divideByX;
final double yScale = info.draw.height / divideByY;
return new Double2D(xScale, yScale);
}
}
public Object getPositionLocation(Point2D.Double position, DrawInfo2D info)
{
Double2D scale = getScale(info);
double xScale = scale.x;
double yScale = scale.y;
int startx = (int)Math.floor(((position.getX() - info.draw.x)/xScale-0.5)/1.5);
int starty = (int)Math.floor((position.getY() - info.draw.y)/(yScale*2.0));
return new Int2D(startx, starty);
}
public Point2D.Double getLocationPosition(Object location, DrawInfo2D info)
{
synchronized(info.gui.state.schedule)
{
final Grid2D field = (Grid2D) this.field;
if (field==null) return null;
int maxX = field.getWidth();
int maxY = field.getHeight();
if (maxX == 0 || maxY == 0) return null;
final double divideByX = ((maxX%2==0)?(3.0*maxX/2.0+0.5):(3.0*maxX/2.0+2.0));
final double divideByY = (1.0+2.0*maxY);
final double xScale = info.draw.width / divideByX;
final double yScale = info.draw.height / divideByY;
//int startx = (int)Math.floor(((info.clip.x - info.draw.x)/xScale-0.5)/1.5)-2;
//int starty = (int)Math.floor((info.clip.y - info.draw.y)/(yScale*2.0))-2;
//int endx = /*startx +*/ (int)Math.floor(((info.clip.x - info.draw.x + info.clip.width)/xScale-0.5)/1.5) + 4; // with rounding, width be as much as 1 off
//int endy = /*starty +*/ (int)Math.floor((info.clip.y - info.draw.y + info.clip.height)/(yScale*2.0)) + 4; // with rounding, height be as much as 1 off
DrawInfo2D newinfo = new DrawInfo2D(info.gui, info.fieldPortrayal, new Rectangle2D.Double(0,0,
Math.ceil(info.draw.width / (HEXAGONAL_RATIO * ((maxX - 1) * 3.0 / 4.0 + 1))),
Math.ceil(info.draw.height / (maxY + 0.5))),
info.clip/*, xPoints, yPoints*/); // we don't do further clipping
newinfo.precise = info.precise;
Int2D loc = (Int2D) location;
if (loc == null) return null;
final int x = loc.x;
final int y = loc.y;
getxyC( x, y, xScale, yScale, info.draw.x, info.draw.y, xyC );
getxyC( field.ulx(x,y), field.uly(x,y), xScale, yScale, info.draw.x, info.draw.y, xyC_ul );
getxyC( field.upx(x,y), field.upy(x,y), xScale, yScale, info.draw.x, info.draw.y, xyC_up );
getxyC( field.urx(x,y), field.ury(x,y), xScale, yScale, info.draw.x, info.draw.y, xyC_ur );
xPoints[0] = (int)Math.floor(xyC_ur[0]-0.5*xScale);
//yPoints[0] = (int)Math.floor(xyC_ur[1]+yScale);
//xPoints[1] = (int)Math.floor(xyC_up[0]+0.5*xScale);
yPoints[1] = (int)Math.floor(xyC_up[1]+yScale);
//xPoints[2] = (int)Math.floor(xyC_up[0]-0.5*xScale);
//yPoints[2] = (int)Math.floor(xyC_up[1]+yScale);
xPoints[3] = (int)Math.floor(xyC_ul[0]+0.5*xScale);
//yPoints[3] = (int)Math.floor(xyC_ul[1]+yScale);
//xPoints[4] = (int)Math.floor(xyC[0]-0.5*xScale);
yPoints[4] = (int)Math.floor(xyC[1]+yScale);
//xPoints[5] = (int)Math.floor(xyC[0]+0.5*xScale);
//yPoints[5] = (int)Math.floor(xyC[1]+yScale);
// compute the width of the object -- we tried computing the EXACT width each time, but
// it results in weird-shaped circles etc, so instead we precomputed a standard width
// and height, and just compute the x values here.
newinfo.draw.x = xPoints[3];
newinfo.draw.y = yPoints[1];
// adjust drawX and drawY to center
newinfo.draw.x +=(xPoints[0]-xPoints[3]) / 2.0;
newinfo.draw.y += (yPoints[4]-yPoints[1]) / 2.0;
return new Point2D.Double(newinfo.draw.x, newinfo.draw.y);
}
}
protected void hitOrDraw(Graphics2D graphics, DrawInfo2D info, Bag putInHere)
{
final SparseGrid2D field = (SparseGrid2D) this.field;
if (field==null) return;
boolean objectSelected = !selectedWrappers.isEmpty();
int maxX = field.getWidth();
int maxY = field.getHeight();
if (maxX == 0 || maxY == 0) return;
final double divideByX = ((maxX%2==0)?(3.0*maxX/2.0+0.5):(3.0*maxX/2.0+2.0));
final double divideByY = (1.0+2.0*maxY);
final double xScale = info.draw.width / divideByX;
final double yScale = info.draw.height / divideByY;
int startx = (int)Math.floor(((info.clip.x - info.draw.x)/xScale-0.5)/1.5)-2;
int starty = (int)Math.floor((info.clip.y - info.draw.y)/(yScale*2.0))-2;
int endx = /*startx +*/ (int)Math.floor(((info.clip.x - info.draw.x + info.clip.width)/xScale-0.5)/1.5) + 4; // with rounding, width be as much as 1 off
int endy = /*starty +*/ (int)Math.floor((info.clip.y - info.draw.y + info.clip.height)/(yScale*2.0)) + 4; // with rounding, height be as much as 1 off
// double precomputedWidth = -1; // see discussion further below
// double precomputedHeight = -1; // see discussion further below
//
//
// CAUTION!
//
// At some point we should triple check the math for rounding such
// that the margins are drawn properly
//
//
// Horizontal hexagons are staggered. This complicates computations. Thus
// if you have a M x N grid scaled to SCALE, then
// your height is (N + 0.5) * SCALE
// and your width is ((M - 1) * (3/4) + 1) * HEXAGONAL_RATIO * SCALE
// we invert these calculations here to compute the rough width and height
// for the newinfo here. Additionally, because the original screen sizes were likely
// converted from floats to ints, there's a round down there, so we round up to
// compensate. This usually results in nice circles.
// final Rectangle clip = (graphics==null ? null : graphics.getClipBounds());
DrawInfo2D newinfo = new DrawInfo2D(info.gui, info.fieldPortrayal, new Rectangle2D.Double(0,0,
Math.ceil(info.draw.width / (HEXAGONAL_RATIO * ((maxX - 1) * 3.0 / 4.0 + 1))),
Math.ceil(info.draw.height / (maxY + 0.5))),
info.clip/*, xPoints, yPoints*/); // we don't do further clipping
newinfo.precise = info.precise;
newinfo.fieldPortrayal = this;
// If the person has specified a policy, we have to iterate through the
// bags. At present we have to do this by using a hash table iterator
// (yuck -- possibly expensive, have to search through empty locations).
//
// We never use the policy to determine hitting. hence this only works if graphics != null
if (policy != null && graphics != null)
{
Bag policyBag = new Bag();
Iterator iterator = field.locationBagIterator();
while(iterator.hasNext())
{
Bag objects = (Bag)(iterator.next());
// restrict the number of objects to draw
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.
// draw 'em
for(int xO=0;xO= startx -2 && loc.x < endx + 4 &&
loc.y >= starty -2 && loc.y < endy + 4)
{
Portrayal p = getPortrayalForObject(portrayedObject);
if (!(p instanceof SimplePortrayal2D))
throw new RuntimeException("Unexpected Portrayal " + p + " for object " +
portrayedObject + " -- expected a SimplePortrayal2D");
SimplePortrayal2D portrayal = (SimplePortrayal2D) p;
getxyC( x, y, xScale, yScale, info.draw.x, info.draw.y, xyC );
getxyC( field.ulx(x,y), field.uly(x,y), xScale, yScale, info.draw.x, info.draw.y, xyC_ul );
getxyC( field.upx(x,y), field.upy(x,y), xScale, yScale, info.draw.x, info.draw.y, xyC_up );
getxyC( field.urx(x,y), field.ury(x,y), xScale, yScale, info.draw.x, info.draw.y, xyC_ur );
xPoints[0] = (int)Math.floor(xyC_ur[0]-0.5*xScale);
//yPoints[0] = (int)Math.floor(xyC_ur[1]+yScale);
//xPoints[1] = (int)Math.floor(xyC_up[0]+0.5*xScale);
yPoints[1] = (int)Math.floor(xyC_up[1]+yScale);
//xPoints[2] = (int)Math.floor(xyC_up[0]-0.5*xScale);
//yPoints[2] = (int)Math.floor(xyC_up[1]+yScale);
xPoints[3] = (int)Math.floor(xyC_ul[0]+0.5*xScale);
//yPoints[3] = (int)Math.floor(xyC_ul[1]+yScale);
//xPoints[4] = (int)Math.floor(xyC[0]-0.5*xScale);
yPoints[4] = (int)Math.floor(xyC[1]+yScale);
//xPoints[5] = (int)Math.floor(xyC[0]+0.5*xScale);
//yPoints[5] = (int)Math.floor(xyC[1]+yScale);
// compute the width of the object -- we tried computing the EXACT width each time, but
// it results in weird-shaped circles etc, so instead we precomputed a standard width
// and height, and just compute the x values here.
newinfo.draw.x = xPoints[3];
newinfo.draw.y = yPoints[1];
// adjust drawX and drawY to center
newinfo.draw.x +=(xPoints[0]-xPoints[3]) / 2.0;
newinfo.draw.y += (yPoints[4]-yPoints[1]) / 2.0;
newinfo.location = loc;
// we never use this policy for hitting -- see above
//if (graphics == null)
// {
// if (portrayal.hitObject(portrayedObject, newinfo))
// putInHere.add(getWrapper(portrayedObject));
// }
//else
{
// MacOS X 10.3 Panther has a bug which resets the clip, YUCK
// graphics.setClip(clip);
newinfo.selected = (objectSelected && // there's something there
selectedWrappers.get(portrayedObject) != null);
/* {
LocationWrapper wrapper = (LocationWrapper)(selectedWrappers.get(portrayedObject));
portrayal.setSelected(wrapper,true);
portrayal.draw(portrayedObject, graphics, newinfo);
portrayal.setSelected(wrapper,false);
}
else */ portrayal.draw(portrayedObject, graphics, newinfo);
}
}
}
}
}
else // the easy way -- draw the objects one by one
{
Bag objects = field.getAllObjects();
for(int xO=0;xO= startx -2 && loc.x < endx + 4 &&
loc.y >= starty -2 && loc.y < endy + 4)
{
Portrayal p = getPortrayalForObject(portrayedObject);
if (!(p instanceof SimplePortrayal2D))
throw new RuntimeException("Unexpected Portrayal " + p + " for object " +
portrayedObject + " -- expected a SimplePortrayal2D");
SimplePortrayal2D portrayal = (SimplePortrayal2D) p;
getxyC( x, y, xScale, yScale, info.draw.x, info.draw.y, xyC );
getxyC( field.ulx(x,y), field.uly(x,y), xScale, yScale, info.draw.x, info.draw.y, xyC_ul );
getxyC( field.upx(x,y), field.upy(x,y), xScale, yScale, info.draw.x, info.draw.y, xyC_up );
getxyC( field.urx(x,y), field.ury(x,y), xScale, yScale, info.draw.x, info.draw.y, xyC_ur );
xPoints[0] = (int)Math.floor(xyC_ur[0]-0.5*xScale);
//yPoints[0] = (int)Math.floor(xyC_ur[1]+yScale);
//xPoints[1] = (int)Math.floor(xyC_up[0]+0.5*xScale);
yPoints[1] = (int)Math.floor(xyC_up[1]+yScale);
//xPoints[2] = (int)Math.floor(xyC_up[0]-0.5*xScale);
//yPoints[2] = (int)Math.floor(xyC_up[1]+yScale);
xPoints[3] = (int)Math.floor(xyC_ul[0]+0.5*xScale);
//yPoints[3] = (int)Math.floor(xyC_ul[1]+yScale);
//xPoints[4] = (int)Math.floor(xyC[0]-0.5*xScale);
yPoints[4] = (int)Math.floor(xyC[1]+yScale);
//xPoints[5] = (int)Math.floor(xyC[0]+0.5*xScale);
//yPoints[5] = (int)Math.floor(xyC[1]+yScale);
// compute the width of the object -- we tried computing the EXACT width each time, but
// it results in weird-shaped circles etc, so instead we precomputed a standard width
// and height, and just compute the x values here.
newinfo.draw.x = xPoints[3];
newinfo.draw.y = yPoints[1];
// adjust drawX and drawY to center
newinfo.draw.x +=(xPoints[0]-xPoints[3]) / 2.0;
newinfo.draw.y += (yPoints[4]-yPoints[1]) / 2.0;
if (graphics == null)
{
if (portrayal.hitObject(portrayedObject, newinfo))
putInHere.add(getWrapper(portrayedObject));
}
else
{
// MacOS X 10.3 Panther has a bug which resets the clip, YUCK
// graphics.setClip(clip);
if (objectSelected && // there's something there
selectedWrappers.get(portrayedObject) != null)
{
LocationWrapper wrapper = (LocationWrapper)(selectedWrappers.get(portrayedObject));
portrayal.setSelected(wrapper,true);
portrayal.draw(portrayedObject, graphics, newinfo);
portrayal.setSelected(wrapper,false);
}
else portrayal.draw(portrayedObject, graphics, newinfo);
}
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy