001: package prefuse.demos;
002:
003: import java.awt.BorderLayout;
004: import java.awt.Color;
005: import java.awt.Dimension;
006: import java.awt.Font;
007: import java.awt.event.MouseEvent;
008: import java.util.Iterator;
009:
010: import javax.swing.BorderFactory;
011: import javax.swing.Box;
012: import javax.swing.BoxLayout;
013: import javax.swing.JFrame;
014: import javax.swing.JPanel;
015: import javax.swing.SwingConstants;
016:
017: import prefuse.Constants;
018: import prefuse.Display;
019: import prefuse.Visualization;
020: import prefuse.action.ActionList;
021: import prefuse.action.GroupAction;
022: import prefuse.action.ItemAction;
023: import prefuse.action.RepaintAction;
024: import prefuse.action.animate.ColorAnimator;
025: import prefuse.action.animate.PolarLocationAnimator;
026: import prefuse.action.animate.QualityControlAnimator;
027: import prefuse.action.animate.VisibilityAnimator;
028: import prefuse.action.assignment.ColorAction;
029: import prefuse.action.assignment.FontAction;
030: import prefuse.action.layout.CollapsedSubtreeLayout;
031: import prefuse.action.layout.graph.RadialTreeLayout;
032: import prefuse.activity.SlowInSlowOutPacer;
033: import prefuse.controls.ControlAdapter;
034: import prefuse.controls.DragControl;
035: import prefuse.controls.FocusControl;
036: import prefuse.controls.HoverActionControl;
037: import prefuse.controls.PanControl;
038: import prefuse.controls.ZoomControl;
039: import prefuse.controls.ZoomToFitControl;
040: import prefuse.data.Graph;
041: import prefuse.data.Node;
042: import prefuse.data.Table;
043: import prefuse.data.Tuple;
044: import prefuse.data.event.TupleSetListener;
045: import prefuse.data.io.GraphMLReader;
046: import prefuse.data.query.SearchQueryBinding;
047: import prefuse.data.search.PrefixSearchTupleSet;
048: import prefuse.data.search.SearchTupleSet;
049: import prefuse.data.tuple.DefaultTupleSet;
050: import prefuse.data.tuple.TupleSet;
051: import prefuse.render.AbstractShapeRenderer;
052: import prefuse.render.DefaultRendererFactory;
053: import prefuse.render.EdgeRenderer;
054: import prefuse.render.LabelRenderer;
055: import prefuse.util.ColorLib;
056: import prefuse.util.FontLib;
057: import prefuse.util.ui.JFastLabel;
058: import prefuse.util.ui.JSearchPanel;
059: import prefuse.util.ui.UILib;
060: import prefuse.visual.VisualItem;
061: import prefuse.visual.expression.InGroupPredicate;
062: import prefuse.visual.sort.TreeDepthItemSorter;
063:
064: /**
065: * Demonstration of a node-link tree viewer
066: *
067: * @version 1.0
068: * @author <a href="http://jheer.org">jeffrey heer</a>
069: */
070: public class RadialGraphView extends Display {
071:
072: public static final String DATA_FILE = "/socialnet.xml";
073:
074: private static final String tree = "tree";
075: private static final String treeNodes = "tree.nodes";
076: private static final String treeEdges = "tree.edges";
077: private static final String linear = "linear";
078:
079: private LabelRenderer m_nodeRenderer;
080: private EdgeRenderer m_edgeRenderer;
081:
082: private String m_label = "label";
083:
084: public RadialGraphView(Graph g, String label) {
085: super (new Visualization());
086: m_label = label;
087:
088: // -- set up visualization --
089: m_vis.add(tree, g);
090: m_vis.setInteractive(treeEdges, null, false);
091:
092: // -- set up renderers --
093: m_nodeRenderer = new LabelRenderer(m_label);
094: m_nodeRenderer
095: .setRenderType(AbstractShapeRenderer.RENDER_TYPE_FILL);
096: m_nodeRenderer.setHorizontalAlignment(Constants.CENTER);
097: m_nodeRenderer.setRoundedCorner(8, 8);
098: m_edgeRenderer = new EdgeRenderer();
099:
100: DefaultRendererFactory rf = new DefaultRendererFactory(
101: m_nodeRenderer);
102: rf.add(new InGroupPredicate(treeEdges), m_edgeRenderer);
103: m_vis.setRendererFactory(rf);
104:
105: // -- set up processing actions --
106:
107: // colors
108: ItemAction nodeColor = new NodeColorAction(treeNodes);
109: ItemAction textColor = new TextColorAction(treeNodes);
110: m_vis.putAction("textColor", textColor);
111:
112: ItemAction edgeColor = new ColorAction(treeEdges,
113: VisualItem.STROKECOLOR, ColorLib.rgb(200, 200, 200));
114:
115: FontAction fonts = new FontAction(treeNodes, FontLib.getFont(
116: "Tahoma", 10));
117: fonts.add("ingroup('_focus_')", FontLib.getFont("Tahoma", 11));
118:
119: // recolor
120: ActionList recolor = new ActionList();
121: recolor.add(nodeColor);
122: recolor.add(textColor);
123: m_vis.putAction("recolor", recolor);
124:
125: // repaint
126: ActionList repaint = new ActionList();
127: repaint.add(recolor);
128: repaint.add(new RepaintAction());
129: m_vis.putAction("repaint", repaint);
130:
131: // animate paint change
132: ActionList animatePaint = new ActionList(400);
133: animatePaint.add(new ColorAnimator(treeNodes));
134: animatePaint.add(new RepaintAction());
135: m_vis.putAction("animatePaint", animatePaint);
136:
137: // create the tree layout action
138: RadialTreeLayout treeLayout = new RadialTreeLayout(tree);
139: //treeLayout.setAngularBounds(-Math.PI/2, Math.PI);
140: m_vis.putAction("treeLayout", treeLayout);
141:
142: CollapsedSubtreeLayout subLayout = new CollapsedSubtreeLayout(
143: tree);
144: m_vis.putAction("subLayout", subLayout);
145:
146: // create the filtering and layout
147: ActionList filter = new ActionList();
148: filter.add(new TreeRootAction(tree));
149: filter.add(fonts);
150: filter.add(treeLayout);
151: filter.add(subLayout);
152: filter.add(textColor);
153: filter.add(nodeColor);
154: filter.add(edgeColor);
155: m_vis.putAction("filter", filter);
156:
157: // animated transition
158: ActionList animate = new ActionList(1250);
159: animate.setPacingFunction(new SlowInSlowOutPacer());
160: animate.add(new QualityControlAnimator());
161: animate.add(new VisibilityAnimator(tree));
162: animate.add(new PolarLocationAnimator(treeNodes, linear));
163: animate.add(new ColorAnimator(treeNodes));
164: animate.add(new RepaintAction());
165: m_vis.putAction("animate", animate);
166: m_vis.alwaysRunAfter("filter", "animate");
167:
168: // ------------------------------------------------
169:
170: // initialize the display
171: setSize(600, 600);
172: setItemSorter(new TreeDepthItemSorter());
173: addControlListener(new DragControl());
174: addControlListener(new ZoomToFitControl());
175: addControlListener(new ZoomControl());
176: addControlListener(new PanControl());
177: addControlListener(new FocusControl(1, "filter"));
178: addControlListener(new HoverActionControl("repaint"));
179:
180: // ------------------------------------------------
181:
182: // filter graph and perform layout
183: m_vis.run("filter");
184:
185: // maintain a set of items that should be interpolated linearly
186: // this isn't absolutely necessary, but makes the animations nicer
187: // the PolarLocationAnimator should read this set and act accordingly
188: m_vis.addFocusGroup(linear, new DefaultTupleSet());
189: m_vis.getGroup(Visualization.FOCUS_ITEMS).addTupleSetListener(
190: new TupleSetListener() {
191: public void tupleSetChanged(TupleSet t,
192: Tuple[] add, Tuple[] rem) {
193: TupleSet linearInterp = m_vis.getGroup(linear);
194: if (add.length < 1)
195: return;
196: linearInterp.clear();
197: for (Node n = (Node) add[0]; n != null; n = n
198: .getParent())
199: linearInterp.addTuple(n);
200: }
201: });
202:
203: SearchTupleSet search = new PrefixSearchTupleSet();
204: m_vis.addFocusGroup(Visualization.SEARCH_ITEMS, search);
205: search.addTupleSetListener(new TupleSetListener() {
206: public void tupleSetChanged(TupleSet t, Tuple[] add,
207: Tuple[] rem) {
208: m_vis.cancel("animatePaint");
209: m_vis.run("recolor");
210: m_vis.run("animatePaint");
211: }
212: });
213: }
214:
215: // ------------------------------------------------------------------------
216:
217: public static void main(String argv[]) {
218: String infile = DATA_FILE;
219: String label = "name";
220:
221: if (argv.length > 1) {
222: infile = argv[0];
223: label = argv[1];
224: }
225:
226: UILib.setPlatformLookAndFeel();
227:
228: JFrame frame = new JFrame(
229: "p r e f u s e | r a d i a l g r a p h v i e w");
230: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
231: frame.setContentPane(demo(infile, label));
232: frame.pack();
233: frame.setVisible(true);
234: }
235:
236: public static JPanel demo() {
237: return demo(DATA_FILE, "name");
238: }
239:
240: public static JPanel demo(String datafile, final String label) {
241: Graph g = null;
242: try {
243: g = new GraphMLReader().readGraph(datafile);
244: } catch (Exception e) {
245: e.printStackTrace();
246: System.exit(1);
247: }
248: return demo(g, label);
249: }
250:
251: public static JPanel demo(Graph g, final String label) {
252: // create a new radial tree view
253: final RadialGraphView gview = new RadialGraphView(g, label);
254: Visualization vis = gview.getVisualization();
255:
256: // create a search panel for the tree map
257: SearchQueryBinding sq = new SearchQueryBinding((Table) vis
258: .getGroup(treeNodes), label, (SearchTupleSet) vis
259: .getGroup(Visualization.SEARCH_ITEMS));
260: JSearchPanel search = sq.createSearchPanel();
261: search.setShowResultCount(true);
262: search.setBorder(BorderFactory.createEmptyBorder(5, 5, 4, 0));
263: search.setFont(FontLib.getFont("Tahoma", Font.PLAIN, 11));
264:
265: final JFastLabel title = new JFastLabel(" ");
266: title.setPreferredSize(new Dimension(350, 20));
267: title.setVerticalAlignment(SwingConstants.BOTTOM);
268: title.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0));
269: title.setFont(FontLib.getFont("Tahoma", Font.PLAIN, 16));
270:
271: gview.addControlListener(new ControlAdapter() {
272: public void itemEntered(VisualItem item, MouseEvent e) {
273: if (item.canGetString(label))
274: title.setText(item.getString(label));
275: }
276:
277: public void itemExited(VisualItem item, MouseEvent e) {
278: title.setText(null);
279: }
280: });
281:
282: Box box = new Box(BoxLayout.X_AXIS);
283: box.add(Box.createHorizontalStrut(10));
284: box.add(title);
285: box.add(Box.createHorizontalGlue());
286: box.add(search);
287: box.add(Box.createHorizontalStrut(3));
288:
289: JPanel panel = new JPanel(new BorderLayout());
290: panel.add(gview, BorderLayout.CENTER);
291: panel.add(box, BorderLayout.SOUTH);
292:
293: Color BACKGROUND = Color.WHITE;
294: Color FOREGROUND = Color.DARK_GRAY;
295: UILib.setColor(panel, BACKGROUND, FOREGROUND);
296:
297: return panel;
298: }
299:
300: // ------------------------------------------------------------------------
301:
302: /**
303: * Switch the root of the tree by requesting a new spanning tree
304: * at the desired root
305: */
306: public static class TreeRootAction extends GroupAction {
307: public TreeRootAction(String graphGroup) {
308: super (graphGroup);
309: }
310:
311: public void run(double frac) {
312: TupleSet focus = m_vis.getGroup(Visualization.FOCUS_ITEMS);
313: if (focus == null || focus.getTupleCount() == 0)
314: return;
315:
316: Graph g = (Graph) m_vis.getGroup(m_group);
317: Node f = null;
318: Iterator tuples = focus.tuples();
319: while (tuples.hasNext()
320: && !g.containsTuple(f = (Node) tuples.next())) {
321: f = null;
322: }
323: if (f == null)
324: return;
325: g.getSpanningTree(f);
326: }
327: }
328:
329: /**
330: * Set node fill colors
331: */
332: public static class NodeColorAction extends ColorAction {
333: public NodeColorAction(String group) {
334: super (group, VisualItem.FILLCOLOR, ColorLib.rgba(255, 255,
335: 255, 0));
336: add("_hover", ColorLib.gray(220, 230));
337: add("ingroup('_search_')", ColorLib.rgb(255, 190, 190));
338: add("ingroup('_focus_')", ColorLib.rgb(198, 229, 229));
339: }
340:
341: } // end of inner class NodeColorAction
342:
343: /**
344: * Set node text colors
345: */
346: public static class TextColorAction extends ColorAction {
347: public TextColorAction(String group) {
348: super (group, VisualItem.TEXTCOLOR, ColorLib.gray(0));
349: add("_hover", ColorLib.rgb(255, 0, 0));
350: }
351: } // end of inner class TextColorAction
352:
353: } // end of class RadialGraphView
|