I want to display a simple Graph with nodes IDs inside Nodes using Prefuse but this seems to be more complicated than it sounds.
Graph g = new Graph();
for (int i = 0; i < 3; ++i) {
Node n1 = g.addNode();
n1.setInt("label", 1); // I am trying to add a field in a node
Node n2 = g.addNode();
Node n3 = g.addNode();
g.addEdge(n1, n2);
g.addEdge(n1, n3);
g.addEdge(n2, n3);
}
g.addEdge(0, 3);
g.addEdge(3, 6);
g.addEdge(6, 0);
// add visual data groups
VisualGraph vg = m_vis.addGraph(GRAPH, g);
m_vis.setInteractive(EDGES, null, false);
m_vis.setValue(NODES, null, VisualItem.SHAPE, new Integer(Constants.SHAPE_STAR));
However, it seems that this field doesn't exist, it makes sense since I didn't add this field but there isn't an option to add a field neither. I am getting this exception referring to the n1.setInt("DEFAULT_NODE_KEY", 1) line:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.ArrayList.elementData(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at prefuse.data.Table.getColumn(Table.java:457)
at prefuse.data.Table.setInt(Table.java:1032)
at prefuse.data.tuple.TableTuple.setInt(TableTuple.java:215)
at prefuse.demos.AggregateDemo.initDataGroups(AggregateDemo.java:141)
at prefuse.demos.AggregateDemo.<init>(AggregateDemo.java:72)
at prefuse.demos.AggregateDemo.demo(AggregateDemo.java:182)
at prefuse.demos.AggregateDemo.main(AggregateDemo.java:176)
I am not sure how to use fields in Nodes. I tried to read the library's help but I don't manage to figure that out.
You may be looking for a LabelRenderer. In the example below, a LabelRenderer is constructed in such a way as to render nodes having the label GraphLib.LABEL, defined as "label":
LabelRenderer r = new LabelRenderer(GraphLib.LABEL);
The nodes comprising the Graph are given text via setString() using the same key, GraphLib.LABEL. For example, the root Node added to the Graph returned by GraphLib.getDiamondTree() is assigned the key GraphLib.LABEL and the value "0,0".
Node r = t.addRoot();
r.setString(LABEL, "0,0");
Later, when the Visualization runs, the renderer attempts to use text from the GraphLib.LABEL field.
import java.awt.Dimension;
import javax.swing.JFrame;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.activity.Activity;
import prefuse.controls.DragControl;
import prefuse.controls.PanControl;
import prefuse.controls.ZoomControl;
import prefuse.data.Graph;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.LabelRenderer;
import prefuse.util.ColorLib;
import prefuse.util.GraphLib;
import prefuse.visual.VisualItem;
/** #see https://stackoverflow.com/a/44274886/230513 */
public class Example {
private static final int W = 640;
private static final int H = 480;
public static void main(String[] argv) {
// -- 1. create the data ------------------------------------------------
Graph graph = GraphLib.getDiamondTree(3, 2, 1);
// -- 2. the visualization --------------------------------------------
// add the graph to the visualization as the data group "graph"
// nodes and edges are accessible as "graph.nodes" and "graph.edges"
Visualization vis = new Visualization();
vis.add("graph", graph);
vis.setInteractive("graph.edges", null, false);
// -- 3. the renderers and renderer factory ---------------------------
LabelRenderer r = new LabelRenderer(GraphLib.LABEL);
r.setRoundedCorner(8, 8); // round the corners
// create a new default renderer factory
// return our name label renderer as the default for all non-EdgeItems
// includes straight line edges for EdgeItems by default
vis.setRendererFactory(new DefaultRendererFactory(r));
// -- 4. the processing actions ---------------------------------------
ColorAction fill = new ColorAction("graph.nodes",
VisualItem.FILLCOLOR, ColorLib.rgb(200, 200, 255));
// use black for node text
ColorAction text = new ColorAction("graph.nodes",
VisualItem.TEXTCOLOR, ColorLib.gray(0));
// use light grey for edges
ColorAction edges = new ColorAction("graph.edges",
VisualItem.STROKECOLOR, ColorLib.gray(200));
// create an action list containing all color assignments
ActionList color = new ActionList();
color.add(fill);
color.add(text);
color.add(edges);
// create an action list with an animated layout
ActionList layout = new ActionList(Activity.INFINITY);
layout.add(new ForceDirectedLayout("graph"));
layout.add(new RepaintAction());
// add the actions to the visualization
vis.putAction("color", color);
vis.putAction("layout", layout);
// -- 5. the display and interactive controls -------------------------
Display d = new Display(vis) {
#Override
public Dimension getPreferredSize() {
return new Dimension(W, H);
}
};
d.setSize(W, H); // set display size
d.pan(W / 2, H / 2); // pan to center
d.addControlListener(new DragControl());
d.addControlListener(new PanControl());
d.addControlListener(new ZoomControl());
// -- 6. launch the visualization -------------------------------------
JFrame frame = new JFrame("prefuse label example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(d);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true); // show the window
vis.run("color");
vis.run("layout");
}
}
Related
I'm a complete novice to JavaFX, and fairly new to Java overall. I'm designing a graphical representation of an undirected graph for use in a self-teaching project. Right now, I'm trying to make nodes draggable such that the edges will stretch to stay connected with their nodes. I have achieved that in the case of 2 nodes with a connection. However, adding a third does something weird.
Say we have this situation:
Cell testOne = new Cell ("testOne", 123);
Cell testTwo = new Cell ("testTwo", 456);
Cell testThree = new Cell ("testThree", 200);
testOne.addConnection(testTwo);
testOne.addConnection(testThree);
What I get is three nodes with two lines strewn randomly in their general area (worth noting the nodes are positioned in a crudely random way). If I move around testTwo or testThree, a single line will trade off being connected to testOne. The second line remains unchanged no matter what. I have to think that somehow what's happening is that one of the EventHandlers is getting "unplugged" from their respective cells, or else somehow one of the lines is getting lost in memory. Here's the code to draw lines (I know it's really clunky). This method is in the Graph class, which controls graphic (oop) representation of the class. "cells" is the ArrayList storing all its nodes, "connections" is the arrayList in the Cell instance that keeps track of all the nodes it's connected to, and "LinesBetween" is a HashMap the Cell instance keeping track of whether a line has already been drawn between the two nodes.
public void drawAndManageEdgeLines(){
if (cells.size() > 1) { //don't wanna make connections if there's only one cell, or none
int count = 0;
for (Cell cell : cells) { // for every cell on the graph
List<Cell> connectionsList = cell.getConnections(); // look at that cell's connections
if (!connectionsList.isEmpty()) {// validate that the cell is actually supposed to be connected to something
for (Cell connection : connectionsList) { // go through all their connections
if (!cell.getLinesBetween().get(connection) && cell.getLinesBetween().get(connection) != null) { //check to see whether there is already a line between them
Circle sourceCircle = cell.getCellView();
Circle targetCircle = connection.getCellView();
Bounds sourceBound = sourceCircle.localToScene(sourceCircle.getBoundsInLocal());
Bounds targetBound = targetCircle.localToScene(targetCircle.getBoundsInLocal());
double targetX = targetBound.getCenterX();
double targetY = targetBound.getCenterY();
double sourceX = sourceBound.getCenterX();
double sourceY = sourceBound.getCenterY();
edge = new Line(sourceX, sourceY, targetX, targetY);
edge.setStroke(Color.BLACK);
edge.setStrokeWidth(2);
getChildren().add(edge);
edge.toBack();
cell.setLinesBetweenEntry(connection, true);
connection.setLinesBetweenEntry(cell, true);
// these handlers control where the line is dragged to
cell.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
edge.setStartX(e.getSceneX()); //this is a really cool method
edge.setStartY(e.getSceneY());
e.consume();
}
});
System.out.println("on round " + count + " we got there: ");
connection.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
edge.setEndX(e.getSceneX());
edge.setEndY(e.getSceneY());
e.consume();
}
});
}
}
}
}
}
}
It's hard to tell what's going wrong without a proper minimal reproducible example, but you seem to be making this more complicated than it needs to be. If you want the edges to be "linked" to the nodes then I recommend you use bindings. Here's a proof-of-concept:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
Circle node1 = new Circle(100, 100, 50, Color.FIREBRICK);
Circle node2 = new Circle(900, 550, 50, Color.DARKBLUE);
Circle node3 = new Circle(900, 100, 50, Color.DARKGREEN);
addDragHandling(node1);
addDragHandling(node2);
addDragHandling(node3);
Line edge1 = createEdge(node1, node2);
Line edge2 = createEdge(node1, node3);
Pane root = new Pane(edge1, edge2, node1, node2, node3);
primaryStage.setScene(new Scene(root, 1000, 650));
primaryStage.show();
}
private Line createEdge(Circle from, Circle to) {
Line edge = new Line();
edge.setStrokeWidth(2);
edge.startXProperty().bind(from.centerXProperty());
edge.startYProperty().bind(from.centerYProperty());
edge.endXProperty().bind(to.centerXProperty());
edge.endYProperty().bind(to.centerYProperty());
return edge;
}
private void addDragHandling(Circle circle) {
// changing cursors not necessary, only included to help indicate
// when something can be dragged and is being dragged
circle
.cursorProperty()
.bind(
Bindings.when(circle.pressedProperty())
.then(Cursor.CLOSED_HAND)
.otherwise(Cursor.OPEN_HAND));
double[] offset = {0, 0}; // (x, y)
circle.setOnMousePressed(
e -> {
offset[0] = e.getX() - circle.getCenterX();
offset[1] = e.getY() - circle.getCenterY();
e.consume();
});
circle.setOnMouseDragged(
e -> {
circle.setCenterX(e.getX() - offset[0]);
circle.setCenterY(e.getY() - offset[1]);
e.consume();
});
}
}
Note I added the edges to the Pane first so that they were drawn under the nodes. See Z-Order in JavaFX. Also, your drag logic may look different depending on what Node you use to represent your graph nodes.
Since you are representing a graph your application will be more complex. If the graph is dynamic and you want the view to update in real time then you'll need to keep references to the nodes and their associated edges to add and remove them at will. But remember the view is only a visual representation of the model. Don't use the view to store model information (e.g. what nodes and edges actually exist).
I want to show values on top of the linechart. I've seen this answer which is quite helpful but it changes the linechart nodes. What I want is same idea but showing values not on nodes, but near them (maybe right and above of them) something like:
53
O
/ \
/ \
/42 \ 21 21
O O------------O
EDIT:
I've tried to code below but sadly only lines appear, doesnt show nodes.
for (int index = 0; index < value.getData().size(); index++) {
XYChart.Data dataPoint = value.getData().get(index);
Node lineSymbol = dataPoint.getNode().lookup(".chart-line-symbol");
lineSymbol.setStyle("-fx-background-color: " + definedColor + " , white;");
Label label = new Label(value.getName());
label.toFront();
Pane nodeWithText = new Pane();
label.setStyle("-fx-font-size: 20; -fx-font-weight: bold;");
StackPane stackPane = new StackPane();
Group group = new Group(label);
group.getChildren().add(lineSymbol);
group.toFront();
group.setLayoutX(-10);
group.setLayoutY(-30);
StackPane.setAlignment(group, Pos.TOP_RIGHT);
StackPane.setMargin(group,new Insets(0,0,5,0));
nodeWithText.getChildren().add(group);
nodeWithText.getChildren().add(lineSymbol);
dataPoint.setNode(nodeWithText);
}
One option is to set a custom Node for each Data in your chart. Here's a crude example:
import javafx.application.Application;
import javafx.beans.binding.ObjectExpression;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
var chart = new LineChart<>(new NumberAxis(), new NumberAxis());
chart.getData().add(new Series<>(createData()));
primaryStage.setScene(new Scene(chart, 600, 400));
primaryStage.show();
}
private static ObservableList<Data<Number, Number>> createData() {
var list = FXCollections.<Data<Number, Number>>observableArrayList();
for (int x = 0; x < 10; x++) {
var data = new Data<Number, Number>(x, Math.pow(x, 2));
data.setNode(createDataNode(data.YValueProperty()));
list.add(data);
}
return list;
}
private static Node createDataNode(ObjectExpression<Number> value) {
var label = new Label();
label.textProperty().bind(value.asString("%,.2f"));
var pane = new Pane(label);
pane.setShape(new Circle(6.0));
pane.setScaleShape(false);
label.translateYProperty().bind(label.heightProperty().divide(-1.5));
return pane;
}
}
The above doesn't do anything complex when it comes to positioning the text. For instance, it won't take into account where the line is nor whether or not some of the text is cut off by the chart's clip. Basically, it's a proof-of-concept; the result looks like:
Some other possible options for adding text to the chart include:
Put the LineChart in a Group or Pane where the chart is the first child. Then, for each Data in your chart, you'd add a Label or Text to the parent and position it relative to the position of the Data's node (when it becomes available). You can calculate these positions using methods such as Node#localToScene and Node#sceneToLocal.
Subclass LineChart and add the Text or Label to the chart directly. As I've never subclassed a chart before, I'm not sure how to implement this option.
Something else I'm not thinking of.
Note that, no matter what you do, if your chart has a lot of data points very close to each other then displaying all the text in a visually pleasing way will be difficult—if not impossible.
I want to add new vertices and edges and create a graph using JGraph Library. I always get this java.lang.NullPointer exception. I have created a class function createvertex to create new cells/vertices and drawing edges my connecting to ports. But the port is always shown as null even when I haven't declared it as null. Below is my code. Is anything wrong in my code?
public class HelloWorld {
public static void main(String[] args) {
// Construct Model and Graph
GraphModel model = new DefaultGraphModel();
GraphLayoutCache view= new GraphLayoutCache(model,new DefaultCellViewFactory());
JGraph graph = new JGraph(model,view);
// Control-drag should clone selection
graph.setCloneable(true);
// Enable edit without final RETURN keystroke
graph.setInvokesStopCellEditing(true);
// When over a cell, jump to its default port (we only have one, anyway)
graph.setJumpToDefaultPort(true);
// Insert all three cells in one call, so we need an array to store them
DefaultGraphCell[] cells = new DefaultGraphCell[5];
DefaultPort[] port = new DefaultPort[4];
// Create Hello Vertex
cells[0] = createVertex("Hello", 20, 20, 40, 20, Color.BLACK, true);
port[0].setParent(cells[0]);
// Create World Vertex
cells[1] = createVertex("World", 140, 140, 40, 20, Color.ORANGE, true);
cells[1].add(port[1]);
cells[1].add(port[2]);
port[1].setParent(cells[1]);
port[2].setParent(cells[1]);
cells[3]= createVertex("Optical Cards",150,150,20,40,Color.GREEN, true);
cells[3].add(port[3]);
port[3].setParent(cells[3]);
// Create Edge
DefaultEdge[] edge = new DefaultEdge[2];
// Fetch the ports from the new vertices, and connect them with the edge
edge[0].setSource(cells[0].getChildAt(0));
edge[0].setTarget(cells[1].getChildAt(0));
cells[2] = edge[0];
edge[1].setSource(cells[1].getChildAt(1));
edge[1].setTarget(cells[3].getChildAt(0));
cells[4]=edge[1];
// Set Arrow Style for edge
int arrow = GraphConstants.ARROW_CLASSIC;
GraphConstants.setLineEnd(edge[0].getAttributes(), arrow);
GraphConstants.setEndFill(edge[0].getAttributes(), true);
GraphConstants.setLineEnd(edge[1].getAttributes(), arrow);
GraphConstants.setEndFill(edge[1].getAttributes(), true);
// Insert the cells via the cache, so they get selected
graph.getGraphLayoutCache().insert(cells);
// Show in Frame
JFrame frame = new JFrame();
frame.getContentPane().add(new JScrollPane(graph));
//frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static DefaultGraphCell createVertex(String name, double x,double y,double w,double h, Color bg, boolean raised) {
// Create vertex with the given name
DefaultGraphCell cell = new DefaultGraphCell(name);
// Set bounds
GraphConstants.setBounds(cell.getAttributes(), new Rectangle2D.Double(
x, y, w, h));
// Set fill color
GraphConstants.setGradientColor(cell.getAttributes(), Color.orange);
GraphConstants.setOpaque(cell.getAttributes(), true);
// Set raised border
if (raised)
GraphConstants.setBorder(cell.getAttributes(), BorderFactory
.createRaisedBevelBorder());
else
// Set black border
GraphConstants.setBorderColor(cell.getAttributes(), Color.black);
// Add a Port
DefaultPort port = new DefaultPort();
cell.add(port);
return cell;
}
}
DefaultPort[] port = new DefaultPort[4];
// Create Hello Vertex
cells[0] = createVertex("Hello", 20, 20, 40, 20, Color.BLACK, true);
port[0].setParent(cells[0]); // <---------- port[0] is null here, so you
// cannot call setParent()
Just because you initialized the port array doesn't mean that you can simply use the first DefaultPort element in the array. You need to populate the array with valid, non-null DefaultPort objects before trying to dereference an item in the array.
I want to use Prefuse to visualise a graph. I followed their tutorial and tried their sample application. Its sourcecode can be found here
However, even if I simply copy the full code, the resulting graph does not look as displayed in the tutorial. It is only half visible, stuck in JPanel's upper left corner. Some parts of it are missing becuase they would have to be displayed outside the panel.
I tried with some graphs of my own, but I keep running into the same phenomenon.
I suppose this is not expected behaviour, but I have no idea where to hunt for the problem. I don't know if this is a problem with Swing(x) or prefuse or ... ?
Update:
This is the revised code. I did not change much from the example, only added what trashgod suggested.
package visualise;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.DataColorAction;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.activity.Activity;
import prefuse.controls.DragControl;
import prefuse.controls.PanControl;
import prefuse.controls.ZoomControl;
import prefuse.data.Graph;
import prefuse.data.io.DataIOException;
import prefuse.data.io.GraphMLReader;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.LabelRenderer;
import prefuse.util.ColorLib;
import prefuse.visual.VisualItem;
public class PrefuseExample {
public static void main(String[] argv) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// -- 1. load the data ------------------------------------------------
// load the socialnet.xml file. it is assumed that the file can be
// found at the root of the java classpath
Graph graph = null;
try {
graph = new GraphMLReader().readGraph("../../resources/visualisation/prefuse/Prefuse-master/data/socialnet.xml");
} catch ( DataIOException e ) {
e.printStackTrace();
System.err.println("Error loading graph. Exiting...");
System.exit(1);
}
// -- 2. the visualization --------------------------------------------
// add the graph to the visualization as the data group "graph"
// nodes and edges are accessible as "graph.nodes" and "graph.edges"
Visualization vis = new Visualization();
vis.add("graph", graph);
vis.setInteractive("graph.edges", null, false);
// -- 3. the renderers and renderer factory ---------------------------
// draw the "name" label for NodeItems
LabelRenderer r = new LabelRenderer("name");
r.setRoundedCorner(8, 8); // round the corners
// create a new default renderer factory
// return our name label renderer as the default for all non-EdgeItems
// includes straight line edges for EdgeItems by default
vis.setRendererFactory(new DefaultRendererFactory(r));
// -- 4. the processing actions ---------------------------------------
// create our nominal color palette
// pink for females, baby blue for males
int[] palette = new int[] {
ColorLib.rgb(255,180,180), ColorLib.rgb(190,190,255)
};
// map nominal data values to colors using our provided palette
DataColorAction fill = new DataColorAction("graph.nodes", "gender",
Constants.NOMINAL, VisualItem.FILLCOLOR, palette);
// use black for node text
ColorAction text = new ColorAction("graph.nodes",
VisualItem.TEXTCOLOR, ColorLib.gray(0));
// use light grey for edges
ColorAction edges = new ColorAction("graph.edges",
VisualItem.STROKECOLOR, ColorLib.gray(200));
// create an action list containing all color assignments
ActionList color = new ActionList();
color.add(fill);
color.add(text);
color.add(edges);
// create an action list with an animated layout
ActionList layout = new ActionList(Activity.INFINITY);
layout.add(new ForceDirectedLayout("graph"));
layout.add(new RepaintAction());
// add the actions to the visualization
vis.putAction("color", color);
vis.putAction("layout", layout);
// -- 5. the display and interactive controls -------------------------
Display d = new Display(vis);
d.setSize(720, 500); // set display size
// drag individual items around
d.addControlListener(new DragControl());
// pan with left-click drag on background
d.addControlListener(new PanControl());
// zoom with right-click drag
d.addControlListener(new ZoomControl());
// -- 6. launch the visualization -------------------------------------
// create a new window to hold the visualization
JFrame frame = new JFrame("prefuse example");
// ensure application exits when window is closed
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(d);
frame.pack(); // layout components in window
frame.setVisible(true); // show the window
// assign the colors
vis.run("color");
// start up the animated layout
vis.run("layout");
}
});
}
}
I'm new to Prefuse, but a number of common errors can contribute to the observed problem. Looking at the example,
As discussed here, don't use setSize() on the Display when you really mean to override getPreferredSize().
Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
The initial clustering is an artifact of the graph's origin falling on the top-left corner of the Display component at the point (0, 0). Having chosen a preferred size, one can pan() to the center.
private static final int W = 640;
private static final int H = 480;
…
Display d = new Display(vis) {
#Override
public Dimension getPreferredSize() {
return new Dimension(W, H);
}
};
d.pan(W / 2, H / 2);
I would like to make a stacked area chart with prefuse similar to the folowing:
http://prefuse.org/gallery/namevoyager/
I'm not quite sure where to start however, and there's no sample code for these charts. I did find prefuse.action.layout.StackedAreaChart, but am not sure what to do with it.
Here's a compilable example for using the StackedAreaChart layout. I'm including it here becase I couldn't find it anywhere else, with the hope that it will be useful as a reference for others. The key here is to understand that the StackedAreaChart assumes that your table follows the following schema:
One column for the id, say "name",
One or more columns for the actual data that corresponds to the id.
Three columns for the calculated polygon named "_polygon", "_polygon:start" and "_polygon:end". That is just the way the StackedAreaChart class has been designed. "_polygon" is actually the constant VisualItem.POLYGON so you can use that instead as shown in the following example.
Here it is:
import javax.swing.JFrame;
import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.DataColorAction;
import prefuse.action.layout.StackedAreaChart;
import prefuse.data.Table;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.PolygonRenderer;
import prefuse.util.ColorLib;
import prefuse.visual.VisualItem;
class Main {
public static void main(String[] args) {
ActionList color = new ActionList();
int[] palette = new int[] {
ColorLib.rgba(255,200,200,150),
ColorLib.rgba(200,255,200,150)
};
ColorAction fillColor = new DataColorAction("table", "name",
Constants.NOMINAL, VisualItem.FILLCOLOR, palette);
color.add(fillColor);
ActionList layout = new ActionList();
layout.add(new RepaintAction());
String[] fields = { "1980s", "1990s", "2000s" };
layout.add(new StackedAreaChart("table", VisualItem.POLYGON, fields));
Visualization vis = new Visualization();
Table table = new Table();
vis.add("table", table);
table.addColumn("name", String.class);
table.addColumn("1980s", int.class);
table.addColumn("1990s", int.class);
table.addColumn("2000s", int.class);
table.addColumn(VisualItem.POLYGON, float[].class, null);
table.addColumn(VisualItem.POLYGON+":start", float[].class, null);
table.addColumn(VisualItem.POLYGON+":end", float[].class, null);
int rowNumber = table.addRow();
table.setString(rowNumber, "name", "Bob");
table.setInt(rowNumber, "1980s", 1000);
table.setInt(rowNumber, "1990s", 500);
table.setInt(rowNumber, "2000s", 300);
rowNumber = table.addRow();
table.setString(rowNumber, "name", "Mary");
table.setInt(rowNumber, "1980s", 800);
table.setInt(rowNumber, "1990s", 1500);
table.setInt(rowNumber, "2000s", 3200);
vis.putAction("layout", layout);
vis.putAction("color", color);
DefaultRendererFactory drf = new DefaultRendererFactory();
drf.add("ingroup('table')", new PolygonRenderer());
vis.setRendererFactory(drf);
Display display = new Display(vis);
display.setSize(720, 500);
JFrame frame = new JFrame("Prefuse StackedAreaChart Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(display);
frame.pack();
frame.setVisible(true);
vis.run("layout");
vis.run("color");
}
}
To get it to display the axis refer to the Congress.java demo included in the prefuse distribution.
Have you checked the Prefuse manual? (not too complete, but it's something to start with).
In it, you can find an sample application which shows you how to load some data on a Graph element, and how to deploy it to a Visualization item.
For generating a StackedAreaChart you'll need to load your data into a prefuse.data.Table object, which you could load, by the example, from a CSV file:
CSVTableReader reader=new CSVTableReader();
Table myTable=reader.readTable("/myDataFile.csv");
Then, add the table to the visualization as a data group, i.e "table"
Visualization vis = new Visualization();
vis.add("table", myTable);
Then, create the StackedAreaChart, and add it to the visualization actions collection:
//params: name of the data group to layout, name of the data field in which to store computed polygons, and an array containing the names of the various data fields, in sorted order, that should be referenced for each consecutive point of a stack layer
StackedAreaChart chart=new StackedAreaChart ("table", fieldName, csvColumnsToCompute);
//add the layout action with a unique key
vis.putAction("myChartLayout", chart);
Then, you can config various layout actions,or other visual aspects (see the linked example).
At last, for displaying the chart, you'll have to create a Display object, bind the visualization, and run the layout actions on it:
//this Display initialization is extracted from the Example app
Display d = new Display(vis);
d.setSize(720, 500); // set display size
// drag individual items around
d.addControlListener(new DragControl());
// pan with left-click drag on background
d.addControlListener(new PanControl());
// zoom with right-click drag
d.addControlListener(new ZoomControl());
// create a new window to hold the visualization
JFrame frame = new JFrame("prefuse example");
// ensure application exits when window is closed
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(d);
frame.pack(); // layout components in window
frame.setVisible(true); // show the window
//At the end: RUN THE CHART ACTION:
vis.run("myChartLayout");
Hope this helps, at least as a first start (the code snippets are not intended for copy-paste and can contain some compile errors).
Good luck.