package prefuse.util;
import java.awt.BasicStroke;
import java.awt.Font;
import java.awt.Stroke;
import java.awt.geom.Rectangle2D;
import prefuse.Constants;
import prefuse.Display;
import prefuse.data.Schema;
import prefuse.visual.VisualItem;
/**
* General library routines used by the prefuse toolkit.
*
* @author <a href="http://jheer.org">jeffrey heer</a>
*/
public class PrefuseLib {
/**
* Group delimiter string used to demarcate the hierarchical structure
* of nested data groups; the default value is a dot (.).
*/
private static final String GROUP_DELIMITER
= PrefuseConfig.get("data.delimiter");
/**
* Scale factor for psychophysical scaling of area values, defaults to
* 0.5, resulting in linear scaling.
*/
private static final double SIZE_SCALE_FACTOR
= PrefuseConfig.getDouble("size.scale2D");
/**
* Prefix for visualization-specific data fields, such as those used by
* VisualItem; the default is a single underscore (_).
*/
public static final String FIELD_PREFIX
= PrefuseConfig.get("data.visual.fieldPrefix");
// ------------------------------------------------------------------------
private PrefuseLib() {
// prevent instantiation
}
// ------------------------------------------------------------------------
// Memory Usage / Debugging Output
/**
* Get a String showing current JVM memory usage in kilobytes.
* @return the memory usage String in KB
*/
public static String getMemoryUsageInKB() {
long total = Runtime.getRuntime().totalMemory() / (2<<10);
long free = Runtime.getRuntime().freeMemory() / (2<<10);
long max = Runtime.getRuntime().maxMemory() / (2<<10);
return "Memory: "+(total-free)+"k / "+total+"k / "+max+"k";
}
/**
* Get a String showing current JVM memory usage in megabytes.
* @return the memory usage String in MB
*/
public static String getMemoryUsageInMB() {
long total = Runtime.getRuntime().totalMemory() / (2<<20);
long free = Runtime.getRuntime().freeMemory() / (2<<20);
long max = Runtime.getRuntime().maxMemory() / (2<<20);
return "Memory: "+(total-free)+"M / "+total+"M / "+max+"M";
}
/**
* Returns a string showing debugging info such as number of visualized
* items and the current frame rate.
* @return the debug string
*/
public static String getDisplayStats(Display d) {
float fr = Math.round(d.getFrameRate()*100f)/100f;
Runtime rt = Runtime.getRuntime();
long tm = rt.totalMemory() / (2<<20);
long fm = rt.freeMemory() / (2<<20);
long mm = rt.maxMemory() / (2<<20);
StringBuffer sb = new StringBuffer();
sb.append("frame rate: ").append(fr).append("fps - ");
sb.append(d.getVisibleItemCount()).append(" items - ");
sb.append("fonts(").append(FontLib.getCacheMissCount());
sb.append(") colors(");
sb.append(ColorLib.getCacheMissCount()).append(')');
sb.append(" mem(");
sb.append(tm-fm).append("M / ");
sb.append(mm).append("M)");
sb.append(" (x:");
sb.append(StringLib.formatNumber(d.getDisplayX(),2));
sb.append(", y:");
sb.append(StringLib.formatNumber(d.getDisplayY(),2));
sb.append(", z:");
sb.append(StringLib.formatNumber(d.getScale(),5)).append(")");
return sb.toString();
}
// ------------------------------------------------------------------------
// VisualItem Methods
/**
* Returns a scale factor by which to scale a 2D shape to grow
* the area by the desired input size value. This is used to scale shapes
* by total pixel area, rather than scaling each dimension by the
* size value itself, which grows the pixel area quadratically rather
* than linearly.
*/
public static double getSize2D(double size) {
return Math.pow(size, SIZE_SCALE_FACTOR);
}
/**
* Get the distance between the x,y points of two VisualItems.
* @param vi1 the first VisualItem
* @param vi2 the second VisualItem
* @return the distance between the items' x,y coordinates
*/
public static double distance(VisualItem vi1, VisualItem vi2) {
double dx = vi1.getX() - vi2.getX();
double dy = vi1.getY() - vi2.getY();
return Math.sqrt(dx*dx + dy*dy);
}
/**
* Update the values in an interpolated column (a set of three columns
* representing a current value along with starting and ending values).
* The current value will become the new starting value, while the given
* value will become the new current and ending values.
* @param item the VisualItem to update
* @param field the base field to update, start and ending fields will
* also be updated, too.
* @param val the value to set
*/
public static void update(VisualItem item, String field, Object val) {
item.set(getStartField(field), item.get(field));
item.set(field, val);
item.set(getEndField(field), val);
}
/**
* Update the values in an interpolated column (a set of three columns
* representing a current value along with starting and ending values).
* The current value will become the new starting value, while the given
* value will become the new current and ending values.
* @param item the VisualItem to update
* @param field the base field to update, start and ending fields will
* also be updated, too.
* @param val the value to set
*/
public static void updateInt(VisualItem item, String field, int val) {
item.setInt(getStartField(field), item.getInt(field));
item.setInt(field, val);
item.setInt(getEndField(field), val);
}
/**
* Update the values in an interpolated column (a set of three columns
* representing a current value along with starting and ending values).
* The current value will become the new starting value, while the given
* value will become the new current and ending values.
* @param item the VisualItem to update
* @param field the base field to update, start and ending fields will
* also be updated, too.
* @param val the value to set
*/
public static void updateLong(VisualItem item, String field, long val) {
item.setLong(getStartField(field), item.getLong(field));
item.setLong(field, val);
item.setLong(getEndField(field), val);
}
/**
* Update the values in an interpolated column (a set of three columns
* representing a current value along with starting and ending values).
* The current value will become the new starting value, while the given
* value will become the new current and ending values.
* @param item the VisualItem to update
* @param field the base field to update, start and ending fields will
* also be updated, too.
* @param val the value to set
*/
public static void updateFloat(VisualItem item, String field, float val)
{
item.setFloat(getStartField(field), item.getFloat(field));
item.setFloat(field, val);
item.setFloat(getEndField(field), val);
}
/**
* Update the values in an interpolated column (a set of three columns
* representing a current value along with starting and ending values).
* The current value will become the new starting value, while the given
* value will become the new current and ending values.
* @param item the VisualItem to update
* @param field the base field to update, start and ending fields will
* also be updated, too.
* @param val the value to set
*/
public static void updateDouble(VisualItem item, String field, double val)
{
item.setDouble(getStartField(field), item.getDouble(field));
item.setDouble(field, val);
item.setDouble(getEndField(field), val);
}
/**
* Update the values in an interpolated column (a set of three columns
* representing a current value along with starting and ending values).
* The current value will become the new starting value, while the given
* value will become the new current and ending values.
* @param item the VisualItem to update
* @param field the base field to update, start and ending fields will
* also be updated, too.
* @param b the value to set
*/
public static void updateBoolean(VisualItem item, String field, boolean b)
{
item.setBoolean(getStartField(field), item.getBoolean(field));
item.setBoolean(field, b);
item.setBoolean(getEndField(field), b);
}
/**
* Update the visibility of an item. The current visibility will become the
* new starting visibility, while the given visibility value will become
* the new current and ending visibility.
* @param item the VisualItem to update
* @param val the visibility value to set
*/
public static void updateVisible(VisualItem item, boolean val) {
item.setStartVisible(item.isVisible());
item.setVisible(val);
item.setEndVisible(val);
}
/**
* Update the x-coordinate of an item. The current x value will become the
* new starting x value, while the given value will become the new current
* x and ending x values. This method also supports an optional referrer
* item, whose x coordinate will become the new starting x coordinate
* of item if item's current x value is NaN.
* @param item the VisualItem to update
* @param referrer an optional referrer VisualItem
* @param x the x value to set
*/
public static void setX(VisualItem item, VisualItem referrer, double x) {
double sx = item.getX();
if ( Double.isNaN(sx) )
sx = (referrer != null ? referrer.getX() : x);
item.setStartX(sx);
item.setEndX(x);
item.setX(x);
}
/**
* Update the y-coordinate of an item. The current y value will become the
* new starting y value, while the given value will become the new current
* x and ending y values. This method also supports an optional referrer
* item, whose y coordinate will become the new starting y coordinate
* of item if item's current x value is NaN.
* @param item the VisualItem to update
* @param referrer an optional referrer VisualItem
* @param y the y value to set
*/
public static void setY(VisualItem item, VisualItem referrer, double y) {
double sy = item.getY();
if ( Double.isNaN(sy) )
sy = (referrer != null ? referrer.getY() : y);
item.setStartY(sy);
item.setEndY(y);
item.setY(y);
}
// ------------------------------------------------------------------------
// Group Name Methods
/**
* Indicates if a group is a child group, a non-top-level data group in
* a set of nested data groups (e.g., the node or edge table of a
* graph or tree).
* @return true if the group is a nested, or child, group
*/
public static boolean isChildGroup(String group) {
return group.indexOf(GROUP_DELIMITER) != -1;
}
/**
* Get the parent group string of a child group, stripping off the
* bottom-level group from the group name (e.g., graph.nodes --> graph).
* @param group the group name
* @return the stripped parent group name
*/
public static String getParentGroup(String group) {
int idx = group.lastIndexOf(GROUP_DELIMITER);
return (idx < 0 ? null : group.substring(0,idx));
}
/**
* Get the tail group name of a child group, stripping all but the
* bottom-level group from the group name (e.g., graph.nodes --> nodes).
* @param group the group name
* @return the stripped child group name
*/
public static String getChildGroup(String group) {
int idx = group.lastIndexOf(GROUP_DELIMITER);
return (idx < 0 ? null : group.substring(idx+1));
}
/**
* Get the group name for the given parent and child group, simply
* concatenating them together with a group delimiter in between.
* @param parent the parent group name
* @param child the child group name
* @return the combined group name
*/
public static String getGroupName(String parent, String child) {
return parent + GROUP_DELIMITER + child;
}
/**
* For a given interpolated field name, get the name of the start
* field.
* @param field the data field
* @return the starting field for the interpolated column
*/
public static String getStartField(String field) {
return field+":start";
}
/**
* For a given interpolated field name, get the name of the end
* field.
* @param field the data field
* @return the ending field for the interpolated column
*/
public static String getEndField(String field) {
return field+":end";
}
// ------------------------------------------------------------------------
// Schema Routines
/**
* Get an instance of the default Schema used for VisualItem instances.
* Contains all the data members commonly used to model a visual element,
* such as x,y position, stroke, fill, and text, colors, size, font,
* and validated, visibility, interactive, fixed, highlight, and mouse
* hover fields.
* @return the VisualItem data Schema
*/
public static Schema getVisualItemSchema() {
Schema s = new Schema();
// booleans
s.addColumn(VisualItem.VALIDATED, boolean.class, Boolean.FALSE);
s.addColumn(VisualItem.VISIBLE, boolean.class, Boolean.TRUE);
s.addColumn(VisualItem.STARTVISIBLE, boolean.class, Boolean.FALSE);
s.addColumn(VisualItem.ENDVISIBLE, boolean.class, Boolean.TRUE);
s.addColumn(VisualItem.INTERACTIVE, boolean.class, Boolean.TRUE);
s.addColumn(VisualItem.EXPANDED, boolean.class, Boolean.TRUE);
s.addColumn(VisualItem.FIXED, boolean.class, Boolean.FALSE);
s.addColumn(VisualItem.HIGHLIGHT, boolean.class, Boolean.FALSE);
s.addColumn(VisualItem.HOVER, boolean.class, Boolean.FALSE);
s.addInterpolatedColumn(VisualItem.X, double.class);
s.addInterpolatedColumn(VisualItem.Y, double.class);
// bounding box
s.addColumn(VisualItem.BOUNDS, Rectangle2D.class, new Rectangle2D.Double());
// color
Integer defStroke = new Integer(ColorLib.rgba(0,0,0,0));
s.addInterpolatedColumn(VisualItem.STROKECOLOR, int.class, defStroke);
Integer defFill = new Integer(ColorLib.rgba(0,0,0,0));
s.addInterpolatedColumn(VisualItem.FILLCOLOR, int.class, defFill);
Integer defTextColor = new Integer(ColorLib.rgba(0,0,0,0));
s.addInterpolatedColumn(VisualItem.TEXTCOLOR, int.class, defTextColor);
// size
s.addInterpolatedColumn(VisualItem.SIZE, double.class, new Double(1));
// shape
s.addColumn(VisualItem.SHAPE, int.class,
new Integer(Constants.SHAPE_RECTANGLE));
// stroke
s.addColumn(VisualItem.STROKE, Stroke.class, new BasicStroke());
// font
Font defFont = FontLib.getFont("SansSerif",Font.PLAIN,10);
s.addInterpolatedColumn(VisualItem.FONT, Font.class, defFont);
// degree-of-interest
s.addColumn(VisualItem.DOI, double.class, new Double(Double.MIN_VALUE));
return s;
}
/**
* Get the minimal Schema needed for a unique VisualItem. Can be useful
* for derived groups that inherit other visual properties from a
* another visual data group.
* @return the minimal VisualItem data Schema
*/
public static Schema getMinimalVisualSchema() {
Schema s = new Schema();
// booleans
s.addColumn(VisualItem.VALIDATED, boolean.class, Boolean.FALSE);
s.addColumn(VisualItem.VISIBLE, boolean.class, Boolean.TRUE);
s.addColumn(VisualItem.STARTVISIBLE, boolean.class, Boolean.FALSE);
s.addColumn(VisualItem.ENDVISIBLE, boolean.class, Boolean.TRUE);
s.addColumn(VisualItem.INTERACTIVE, boolean.class, Boolean.TRUE);
// bounding box
s.addColumn(VisualItem.BOUNDS, Rectangle2D.class, new Rectangle2D.Double());
return s;
}
/**
* Get the VisualItem Schema used for axis tick marks and labels. Extends
* the VisualItem Schema with an additional end-point coordinate, a
* String label field, and numeric value field for storing the value
* which the axis label corresponds to.
* @return the Schema for axis tick marks and labels.
*/
public static Schema getAxisLabelSchema() {
Schema s = getVisualItemSchema();
s.setDefault(VisualItem.STARTVISIBLE, Boolean.FALSE);
Integer defColor = new Integer(ColorLib.gray(230));
s.setInterpolatedDefault(VisualItem.STROKECOLOR, defColor);
defColor = new Integer(ColorLib.gray(150));
s.setInterpolatedDefault(VisualItem.TEXTCOLOR, defColor);
Double nan = new Double(Double.NaN);
s.addInterpolatedColumn(VisualItem.X2, double.class);
s.addInterpolatedColumn(VisualItem.Y2, double.class);
s.addColumn(VisualItem.LABEL, String.class);
s.addColumn(VisualItem.VALUE, double.class, nan);
return s;
}
} // end of class PrefuseLib