sim.portrayal.grid.FastValueGridPortrayal2D 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.portrayal.*;
import sim.field.grid.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import sim.util.gui.ColorMap;
/**
This class works like a ValueGridPortrayal2D, except that it doesn't use an underlying Portrayal for the object
(instead it always draws a rectangle), and may ignore the getColor() method, so you shouldn't override that method to customize
the color function in nonlinear ways any more. setColorTable() and setLevels() are still supported.
Use this class instead of
ValueGridPortrayal2D unless you need to customize how the values are drawn (other than the color range or lookup table).
Additionally, FastValueGridPortrayal2D is useful if your grid never changes past its first drawing. For example, if
you're drawing static obstacles, cities, etc., which never change in value during the invocation of the simulation, then
FastValueGridPortrayal2D can draw them efficiently by just drawing once into its buffer and re-displaying the buffer over
and over again. Just pass in true in the constructor.
If your grid does change but only occasionally, you can also use this technique as well; just manually call reset()
whenever the grid changes to inform the FastValueGridPortrayal2D that it needs to re-compute the buffer. reset() is threadsafe.
Important Note on Drawing Methods
FastValueGridPortrayal2D can draw a grid in two ways. First, it can draw each of the rects individually ("USE_BUFFER"). Second, it can create a bitmap the size of the grid (one pixel per grid location), poke the colors into the bitmap, then stretch the bitmap over the area and draw it ("DONT_USE_BUFFER"). You can specify the method by calling the setBuffering() method; optionally you can just let FastValueGridPortrayal2D guess which to use ("DEFAULT"). But you should know what you're doing, as methods can be much faster than each other depending on the situation. Use the following as guides
- MacOS X and X Windows
- USE_BUFFER is much faster than DONT_USE_BUFFER in all cases, but can draw incorrectly aliased ("fuzzed out") rectangles when writing to media (a movie or a snapshot). The DEFAULT is for MacOS X is set to USE_BUFFER in ordinary drawing, and DONT_USE_BUFFER when writing to media.
- Windows and X Windows
- If you're not using any transparency (alpha), then DONT_USE_BUFFER is a tad faster than USE_BUFFER. But if you're using transparency, then DONT_USE_BUFFER is very slow -- in this case, try USE_BUFFER. Note however that in any case USE_BUFFER requires a lot of memory on Windows and X Windows due to poor implementation by Sun. You'll want to increase the default memory capacity, and expect occasional pauses for full garbage collection. You can test how often full garbage collection by running with java -verbose:gc ... and looking at how often
the FULL GC printouts happen. Ordinary GC you shouldn't worry about. You can increase the default memory capacity to 50 Megabytes, for example (the default is about 20) by running with java -Xms50M ... The DEFAULT is for XWindows and Windows to use DONT_USE_BUFFER. You'll want to change this for sure if you're doing any transparency.
*/
public class FastValueGridPortrayal2D extends ValueGridPortrayal2D
{
/** If immutableField is true, we presume that the grid doesn't change. This allows us to just
re-splat the buffer. */
public FastValueGridPortrayal2D(String valueName, boolean immutableField)
{
super(valueName);
setImmutableField(immutableField);
}
public FastValueGridPortrayal2D(String valueName)
{
this(valueName,false);
}
/** If immutableField is true, we presume that the grid doesn't change. This allows us to just
re-splat the buffer. */
public FastValueGridPortrayal2D(boolean immutableField)
{
super();
setImmutableField(immutableField);
}
public FastValueGridPortrayal2D()
{
this(false);
}
/*
public void reset()
{
synchronized(this)
{
buffer = null;
}
}
*/
// Determines if we should buffer
boolean shouldBuffer(Graphics2D graphics)
{
// We can either draw lots of rects, or we can pixels to a small bitmap, then
// stretch the bitmap into rects using drawImage. Which technique is faster depends
// on the OS unfortunately. Solaris prefers the bitmap. Linux prefers the rects
// very much [ NOTE: Not true any more ]. Windows prefers the rects,
// except for small draws where bitmaps have a slight edge (which we'll not consider).
// MacOS X prefers bitmaps, but will not stretch and draw to an image buffer
// without doing fancy-pants interpolation which looks horrible, so we have to check for that.
// For now we'll do:
// The user can override us if he likes in the options pane. Otherwise...
// in Windows, only use the buffer if it's an immutable grid
// in MacOS X, use the buffer for all non-image writes ONLY
// in X Windows don't use the buffer ever
// ...this puts Solaris at a disadvantage but given that Linux is more common...
int buffering = getBuffering();
if (buffering==USE_BUFFER) return true;
else if (buffering==DONT_USE_BUFFER) return false;
else if (sim.display.Display2D.isMacOSX)
return (graphics.getDeviceConfiguration().
getDevice().getType() != GraphicsDevice.TYPE_IMAGE_BUFFER);
else if (sim.display.Display2D.isWindows)
return (immutableField);
else // it's Linux or Solaris
{
return (graphics.getDeviceConfiguration().
getDevice().getType() != GraphicsDevice.TYPE_IMAGE_BUFFER);
}
}
BufferedImage buffer;
WritableRaster raster;
int[] data = new int[0];
// Should draw itself within the box from (0,0) to (1,1)
public void draw(Object object, Graphics2D graphics, DrawInfo2D info)
{
final Grid2D field = (Grid2D)this.field;
if (field==null) return;
// first question: determine the range in which we need to draw.
final int maxX = field.getWidth();
final int maxY = field.getHeight();
if (maxX == 0 || maxY == 0) return;
// precise values
final double xScale = info.draw.width / maxX;
final double yScale = info.draw.height / maxY;
double startxd = ((info.clip.x - info.draw.x) / xScale);
double startyd = ((info.clip.y - info.draw.y) / yScale);
double endxd = ((info.clip.x - info.draw.x + info.clip.width) / xScale);
double endyd = ((info.clip.y - info.draw.y + info.clip.height) / yScale);
// converted to ints
int startx = (int)startxd;
int starty = (int)startyd;
int endx = ((int)endxd) + /*2*/ 1; // with rounding, width may be as much as 1 off
int endy = ((int)endyd) + /*2*/ 1; // with rounding, height may be as much as 1 off
// next we determine if this is a DoubleGrid2D or an IntGrid2D
// final Rectangle clip = (graphics==null ? null : graphics.getClipBounds());
final boolean isDoubleGrid2D = (field instanceof DoubleGrid2D);
final double[][] doubleField = (isDoubleGrid2D ? ((DoubleGrid2D) field).field : null);
final int[][] intField = (isDoubleGrid2D ? null : ((IntGrid2D) field).field);
if (shouldBuffer(graphics))
{
// create new buffer if needed
boolean newBuffer = false;
//BufferedImage _buffer = null; // make compiler happy
// synchronized(this)
// {
if (buffer==null || buffer.getWidth() != maxX || buffer.getHeight() != maxY)
{
// interestingly, this is not quite as fast as just making a BufferedImage directly!
// at present, transparent images can't take advantage of new Sun efficiency improvements.
// Perhaps we should have a new option for opaque images...
//buffer = graphics.getDeviceConfiguration().createCompatibleImage(maxX,maxY,Transparency.TRANSLUCENT);
// oops, it looks like createCompatibleImage has big-time HILARIOUS bugs on OS X Java 1.3.1!
// So for the time being we're sticking with the (very slightly faster)
// new BufferedImage(...)
if (buffer != null) buffer.flush(); // in case Java forgets to clear memory -- bug in OS X
buffer = new BufferedImage(maxX,maxY,BufferedImage.TYPE_INT_ARGB); // transparency allowed
// I had thought that TYPE_INT_ARGB_PRE would be faster because
// it's natively supported by MacOS X CoreGraphics so no optimization needs to be done
// in 1.4.1 -- but in fact it is SLOWER on 1.3.1 by 2/3. So for the time being we're
// going to stay with the orgiginal.
// see http://developer.apple.com/documentation/Java/Reference/Java14SysProperties/System_Properties/chapter_2_section_6.html
//
// UPDATE: Running with Quartz and Java 1.6, it doesn't seem that ARGB and ARGB_PRE have
// any really significant difference in speed. Maybe 5%. Sticking with ARGB to be more compatible with
// Windows.
raster = buffer.getRaster();
newBuffer = true;
}
//_buffer = buffer;
//}
if (newBuffer || !immutableField || isDirtyField()) // we have to load the buffer
{
if (endx > maxX) endx = maxX;
if (endy > maxY) endy = maxY;
if( startx < 0 ) startx = 0;
if( starty < 0 ) starty = 0;
final int ex = endx;
final int ey = endy;
final int sx = startx;
final int sy = starty;
if (immutableField)
{
// must load ENTIRE buffer
startx = 0; starty = 0; endx = maxX; endy = maxY;
}
final ColorMap map = this.map;
if (ex-sx > 0 && ey-sy > 0) // could be otherwise if drawing off-screen...
{
int[] data = this.data; // reuse
if (data.length != (ex-sx)*(ey-sy))
data = this.data = new int[(ex-sx)*(ey-sy)];
int i = 0;
if (isDoubleGrid2D)
for(int y=sy;y maxX) endx = maxX;
if (endy > maxY) endy = maxY;
if( startx < 0 ) startx = 0;
if( starty < 0 ) starty = 0;
final int ex = endx;
final int ey = endy;
final int sx = startx;
final int sy = starty;
int _x = 0;
int _y = 0;
int _width = 0;
int _height = 0;
// locals are faster...
final ColorMap map = this.map;
final double infodrawx = info.draw.x;
final double infodrawy = info.draw.y;
// 1.3.1 doesn't hoist -- does 1.4.1?
if (isDoubleGrid2D)
for(int x=sx;x maxX) endxd = maxX;
if (endyd > maxY) endyd = maxY;
if( startxd < 0 ) startxd = 0;
if( startyd < 0 ) startyd = 0;
double _x = 0;
double _y = 0;
double _width = 0;
double _height = 0;
// locals are faster...
final ColorMap map = this.map;
final double infodrawx = info.draw.x;
final double infodrawy = info.draw.y;
// 1.3.1 doesn't hoist -- does 1.4.1?
if (isDoubleGrid2D)
for(double x=startxd;x
© 2015 - 2025 Weber Informatics LLC | Privacy Policy