Prefuse offers a set of actions and renderers to create nice and neat visualization applications.
Based on three weeks digging into the source codes of prefuse and trail-and-fail in a tabluer data demo, my understanding of part of the Actions and Renderers are related blow:
The basic logic is to use actions to assign visual features, such as colors, shapes, sizes, and position. And use renderers to finally paint these visual units with above features.
In terms of colorAction, dataColorAction, the key component is the "process" method, which assign the visual features to a VisualItem.
In shapeRenderer class, the key component is the "getShape" method and "render" method. Particularly, the "render" method that initialize the final painting of a VisualItem. In general "render" method will call PrefuseLib's "paint" method to do the painting.
Therefore, if the prefuse's default actions cannot help your customized application, it is necessary to write customized actions and renders. Here are one example for my work.
Design idea: draw a visualitem with a sequence of color-coded rectangles whose width is also customized. See the image.
In prefuse's default setting, one visualitem only has one color and one shape assigned with it (seen in the previous post). To my best knowledge, default color and shape actions cannot fulfill my design idea because my design require one visualitem to have multiple colors and shapes.In order to achieve my design idea, I have to add a column into the data table, named length, which will store an integer array for lengths of these rectangles. Also to give the rectangles different colors, I need to add another column, named colors, which is used to store an integer array assigned to each rectangle.
The default colorAction and shapeAction process each VisualItem with its colors and shape. Therefore I need to write my own coloraction and renderer to deal with the arrays.
For my own coloraction, I create a class, named CompositedColorAction, which extends EncoderAction. And override its
public void process(VisualItem vi, double frac)
method. Here are the method codes:
@Override
public void process(VisualItem vi, double d) {
List l=getTypeList(vi);
ArrayList colors=new ArrayList();
for (int i=0;i
if (l.get(i) instanceof Integer){
colors.add(m_palette[(Integer) l.get(i)]);
}else throw new IllegalArgumentException(
"Unrecognized data type");
}
vi.set(m_colorListField, colors);
}
In these codes add an array of colors to the column "color" in a visual item for render to use.
To render multiple rectangles, I write a CompositedShapeRenderer class, which extends ShapeRender, and override its
public void Render(Graphics2D g, VisualItem vi)
method.
Here are the source codes.
@Override
public void render(Graphics2D g, VisualItem vi){
Shape[] shapes=getRawShapes(vi);
int[] colors=getRawColors(vi);
if (shapes!=null){
GraphicsLibPlus.compositedPaint(g, vi, shapes, colors, vi.getStroke(), getRenderType(vi));
}
}
Here the getRawShapes() and getRawColors() method is as below:
protected Shape[] getRawShapes(VisualItem vi) {
// a temp shape array to store all rectganles
Shape[] tempShapes=null;
// create shapes
double x=vi.getX();
double y=vi.getY();
if ( Double.isNaN(x) || Double.isInfinite(x) )
x = 0;
if ( Double.isNaN(y) || Double.isInfinite(y) )
y = 0;
ArrayList sizes=(ArrayList) vi.get(m_type);
tempShapes=new Shape[sizes.size()];
for (int i=0;i
m_shape=new RoundRectangle2D.Double();
m_shape.setRoundRect(x, y, (Integer)sizes.get(i)*m_baseSize,
m_baseSize, m_arcWidth, m_arcHeight);
tempShapes[i]=m_shape;
x=x+(Integer)sizes.get(i)*m_baseSize;
}
return tempShapes;
}
protected int[] getRawColors(VisualItem vi){
int[] tempColors=null;
ArrayList colors=(ArrayList) vi.get(m_colors);
tempColors=new int[colors.size()];
for (int i=0;i
tempColors[i]=(Integer) colors.get(i);
}
return tempColors;
} //end of getRawColors method
The two methods read the colors and widths from the visual item's "types" and "colors" columns and ask the paint method to render the rectangles with different colors.
Here the render method use a new painting method, called
GraphicsLibPlus.compositedpait.nnt()
I rewrite this method from prefuse' original painting method, called GraphicsLib.paint(). Because the original method only paint one shape, I have create a new painting method.
Summary:
In order to design your own visualization, you need to do a lot on actions and renderers. Usually you specify some new columns in a data tuple (a data row), and use actions to change values of these columns, and ask renderers to paint this visual item by looking at these rows and paint accordingly. If the original GraphicsLib.paint() method does not work, you need to write a new class and new paint() method for your purposes.
Welcome to add comments and ask questions from my site (see upper left conner of this page).

thanks a lot , I was trying to do the same, for example drawing a pie chart inside a node , or drawing an edge with multiple color, and I think this post will be very helpful for that.
ReplyDelete