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.ActionEvent;
008: import java.awt.event.MouseEvent;
009: import java.awt.geom.Point2D;
010:
011: import javax.swing.AbstractAction;
012: import javax.swing.BorderFactory;
013: import javax.swing.Box;
014: import javax.swing.BoxLayout;
015: import javax.swing.JComponent;
016: import javax.swing.JFrame;
017: import javax.swing.JPanel;
018: import javax.swing.KeyStroke;
019: import javax.swing.SwingConstants;
020:
021: import prefuse.Constants;
022: import prefuse.Display;
023: import prefuse.Visualization;
024: import prefuse.action.Action;
025: import prefuse.action.ActionList;
026: import prefuse.action.ItemAction;
027: import prefuse.action.RepaintAction;
028: import prefuse.action.animate.ColorAnimator;
029: import prefuse.action.animate.LocationAnimator;
030: import prefuse.action.animate.QualityControlAnimator;
031: import prefuse.action.animate.VisibilityAnimator;
032: import prefuse.action.assignment.ColorAction;
033: import prefuse.action.assignment.FontAction;
034: import prefuse.action.filter.FisheyeTreeFilter;
035: import prefuse.action.layout.CollapsedSubtreeLayout;
036: import prefuse.action.layout.graph.NodeLinkTreeLayout;
037: import prefuse.activity.SlowInSlowOutPacer;
038: import prefuse.controls.ControlAdapter;
039: import prefuse.controls.FocusControl;
040: import prefuse.controls.PanControl;
041: import prefuse.controls.WheelZoomControl;
042: import prefuse.controls.ZoomControl;
043: import prefuse.controls.ZoomToFitControl;
044: import prefuse.data.Tree;
045: import prefuse.data.Tuple;
046: import prefuse.data.event.TupleSetListener;
047: import prefuse.data.io.TreeMLReader;
048: import prefuse.data.search.PrefixSearchTupleSet;
049: import prefuse.data.tuple.TupleSet;
050: import prefuse.render.DefaultRendererFactory;
051: import prefuse.render.EdgeRenderer;
052: import prefuse.render.AbstractShapeRenderer;
053: import prefuse.render.LabelRenderer;
054: import prefuse.util.ColorLib;
055: import prefuse.util.FontLib;
056: import prefuse.util.ui.JFastLabel;
057: import prefuse.util.ui.JSearchPanel;
058: import prefuse.visual.VisualItem;
059: import prefuse.visual.expression.InGroupPredicate;
060: import prefuse.visual.sort.TreeDepthItemSorter;
061:
062: /**
063: * Demonstration of a node-link tree viewer
064: *
065: * @version 1.0
066: * @author <a href="http://jheer.org">jeffrey heer</a>
067: */
068: public class TreeView extends Display {
069:
070: public static final String TREE_CHI = "/chi-ontology.xml.gz";
071:
072: private static final String tree = "tree";
073: private static final String treeNodes = "tree.nodes";
074: private static final String treeEdges = "tree.edges";
075:
076: private LabelRenderer m_nodeRenderer;
077: private EdgeRenderer m_edgeRenderer;
078:
079: private String m_label = "label";
080: private int m_orientation = Constants.ORIENT_LEFT_RIGHT;
081:
082: public TreeView(Tree t, String label) {
083: super (new Visualization());
084: m_label = label;
085:
086: m_vis.add(tree, t);
087:
088: m_nodeRenderer = new LabelRenderer(m_label);
089: m_nodeRenderer
090: .setRenderType(AbstractShapeRenderer.RENDER_TYPE_FILL);
091: m_nodeRenderer.setHorizontalAlignment(Constants.LEFT);
092: m_nodeRenderer.setRoundedCorner(8, 8);
093: m_edgeRenderer = new EdgeRenderer(Constants.EDGE_TYPE_CURVE);
094:
095: DefaultRendererFactory rf = new DefaultRendererFactory(
096: m_nodeRenderer);
097: rf.add(new InGroupPredicate(treeEdges), m_edgeRenderer);
098: m_vis.setRendererFactory(rf);
099:
100: // colors
101: ItemAction nodeColor = new NodeColorAction(treeNodes);
102: ItemAction textColor = new ColorAction(treeNodes,
103: VisualItem.TEXTCOLOR, ColorLib.rgb(0, 0, 0));
104: m_vis.putAction("textColor", textColor);
105:
106: ItemAction edgeColor = new ColorAction(treeEdges,
107: VisualItem.STROKECOLOR, ColorLib.rgb(200, 200, 200));
108:
109: // quick repaint
110: ActionList repaint = new ActionList();
111: repaint.add(nodeColor);
112: repaint.add(new RepaintAction());
113: m_vis.putAction("repaint", repaint);
114:
115: // full paint
116: ActionList fullPaint = new ActionList();
117: fullPaint.add(nodeColor);
118: m_vis.putAction("fullPaint", fullPaint);
119:
120: // animate paint change
121: ActionList animatePaint = new ActionList(400);
122: animatePaint.add(new ColorAnimator(treeNodes));
123: animatePaint.add(new RepaintAction());
124: m_vis.putAction("animatePaint", animatePaint);
125:
126: // create the tree layout action
127: NodeLinkTreeLayout treeLayout = new NodeLinkTreeLayout(tree,
128: m_orientation, 50, 0, 8);
129: treeLayout.setLayoutAnchor(new Point2D.Double(25, 300));
130: m_vis.putAction("treeLayout", treeLayout);
131:
132: CollapsedSubtreeLayout subLayout = new CollapsedSubtreeLayout(
133: tree, m_orientation);
134: m_vis.putAction("subLayout", subLayout);
135:
136: AutoPanAction autoPan = new AutoPanAction();
137:
138: // create the filtering and layout
139: ActionList filter = new ActionList();
140: filter.add(new FisheyeTreeFilter(tree, 2));
141: filter.add(new FontAction(treeNodes, FontLib.getFont("Tahoma",
142: 16)));
143: filter.add(treeLayout);
144: filter.add(subLayout);
145: filter.add(textColor);
146: filter.add(nodeColor);
147: filter.add(edgeColor);
148: m_vis.putAction("filter", filter);
149:
150: // animated transition
151: ActionList animate = new ActionList(1000);
152: animate.setPacingFunction(new SlowInSlowOutPacer());
153: animate.add(autoPan);
154: animate.add(new QualityControlAnimator());
155: animate.add(new VisibilityAnimator(tree));
156: animate.add(new LocationAnimator(treeNodes));
157: animate.add(new ColorAnimator(treeNodes));
158: animate.add(new RepaintAction());
159: m_vis.putAction("animate", animate);
160: m_vis.alwaysRunAfter("filter", "animate");
161:
162: // create animator for orientation changes
163: ActionList orient = new ActionList(2000);
164: orient.setPacingFunction(new SlowInSlowOutPacer());
165: orient.add(autoPan);
166: orient.add(new QualityControlAnimator());
167: orient.add(new LocationAnimator(treeNodes));
168: orient.add(new RepaintAction());
169: m_vis.putAction("orient", orient);
170:
171: // ------------------------------------------------
172:
173: // initialize the display
174: setSize(700, 600);
175: setItemSorter(new TreeDepthItemSorter());
176: addControlListener(new ZoomToFitControl());
177: addControlListener(new ZoomControl());
178: addControlListener(new WheelZoomControl());
179: addControlListener(new PanControl());
180: addControlListener(new FocusControl(1, "filter"));
181:
182: registerKeyboardAction(new OrientAction(
183: Constants.ORIENT_LEFT_RIGHT), "left-to-right",
184: KeyStroke.getKeyStroke("ctrl 1"), WHEN_FOCUSED);
185: registerKeyboardAction(new OrientAction(
186: Constants.ORIENT_TOP_BOTTOM), "top-to-bottom",
187: KeyStroke.getKeyStroke("ctrl 2"), WHEN_FOCUSED);
188: registerKeyboardAction(new OrientAction(
189: Constants.ORIENT_RIGHT_LEFT), "right-to-left",
190: KeyStroke.getKeyStroke("ctrl 3"), WHEN_FOCUSED);
191: registerKeyboardAction(new OrientAction(
192: Constants.ORIENT_BOTTOM_TOP), "bottom-to-top",
193: KeyStroke.getKeyStroke("ctrl 4"), WHEN_FOCUSED);
194:
195: // ------------------------------------------------
196:
197: // filter graph and perform layout
198: setOrientation(m_orientation);
199: m_vis.run("filter");
200:
201: TupleSet search = new PrefixSearchTupleSet();
202: m_vis.addFocusGroup(Visualization.SEARCH_ITEMS, search);
203: search.addTupleSetListener(new TupleSetListener() {
204: public void tupleSetChanged(TupleSet t, Tuple[] add,
205: Tuple[] rem) {
206: m_vis.cancel("animatePaint");
207: m_vis.run("fullPaint");
208: m_vis.run("animatePaint");
209: }
210: });
211: }
212:
213: // ------------------------------------------------------------------------
214:
215: public void setOrientation(int orientation) {
216: NodeLinkTreeLayout rtl = (NodeLinkTreeLayout) m_vis
217: .getAction("treeLayout");
218: CollapsedSubtreeLayout stl = (CollapsedSubtreeLayout) m_vis
219: .getAction("subLayout");
220: switch (orientation) {
221: case Constants.ORIENT_LEFT_RIGHT:
222: m_nodeRenderer.setHorizontalAlignment(Constants.LEFT);
223: m_edgeRenderer.setHorizontalAlignment1(Constants.RIGHT);
224: m_edgeRenderer.setHorizontalAlignment2(Constants.LEFT);
225: m_edgeRenderer.setVerticalAlignment1(Constants.CENTER);
226: m_edgeRenderer.setVerticalAlignment2(Constants.CENTER);
227: break;
228: case Constants.ORIENT_RIGHT_LEFT:
229: m_nodeRenderer.setHorizontalAlignment(Constants.RIGHT);
230: m_edgeRenderer.setHorizontalAlignment1(Constants.LEFT);
231: m_edgeRenderer.setHorizontalAlignment2(Constants.RIGHT);
232: m_edgeRenderer.setVerticalAlignment1(Constants.CENTER);
233: m_edgeRenderer.setVerticalAlignment2(Constants.CENTER);
234: break;
235: case Constants.ORIENT_TOP_BOTTOM:
236: m_nodeRenderer.setHorizontalAlignment(Constants.CENTER);
237: m_edgeRenderer.setHorizontalAlignment1(Constants.CENTER);
238: m_edgeRenderer.setHorizontalAlignment2(Constants.CENTER);
239: m_edgeRenderer.setVerticalAlignment1(Constants.BOTTOM);
240: m_edgeRenderer.setVerticalAlignment2(Constants.TOP);
241: break;
242: case Constants.ORIENT_BOTTOM_TOP:
243: m_nodeRenderer.setHorizontalAlignment(Constants.CENTER);
244: m_edgeRenderer.setHorizontalAlignment1(Constants.CENTER);
245: m_edgeRenderer.setHorizontalAlignment2(Constants.CENTER);
246: m_edgeRenderer.setVerticalAlignment1(Constants.TOP);
247: m_edgeRenderer.setVerticalAlignment2(Constants.BOTTOM);
248: break;
249: default:
250: throw new IllegalArgumentException(
251: "Unrecognized orientation value: " + orientation);
252: }
253: m_orientation = orientation;
254: rtl.setOrientation(orientation);
255: stl.setOrientation(orientation);
256: }
257:
258: public int getOrientation() {
259: return m_orientation;
260: }
261:
262: // ------------------------------------------------------------------------
263:
264: public static void main(String argv[]) {
265: String infile = TREE_CHI;
266: String label = "name";
267: if (argv.length > 1) {
268: infile = argv[0];
269: label = argv[1];
270: }
271: JComponent treeview = demo(infile, label);
272:
273: JFrame frame = new JFrame("p r e f u s e | t r e e v i e w");
274: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
275: frame.setContentPane(treeview);
276: frame.pack();
277: frame.setVisible(true);
278: }
279:
280: public static JComponent demo() {
281: return demo(TREE_CHI, "name");
282: }
283:
284: public static JComponent demo(String datafile, final String label) {
285: Color BACKGROUND = Color.WHITE;
286: Color FOREGROUND = Color.BLACK;
287:
288: Tree t = null;
289: try {
290: t = (Tree) new TreeMLReader().readGraph(datafile);
291: } catch (Exception e) {
292: e.printStackTrace();
293: System.exit(1);
294: }
295:
296: // create a new treemap
297: final TreeView tview = new TreeView(t, label);
298: tview.setBackground(BACKGROUND);
299: tview.setForeground(FOREGROUND);
300:
301: // create a search panel for the tree map
302: JSearchPanel search = new JSearchPanel(
303: tview.getVisualization(), treeNodes,
304: Visualization.SEARCH_ITEMS, label, true, true);
305: search.setShowResultCount(true);
306: search.setBorder(BorderFactory.createEmptyBorder(5, 5, 4, 0));
307: search.setFont(FontLib.getFont("Tahoma", Font.PLAIN, 11));
308: search.setBackground(BACKGROUND);
309: search.setForeground(FOREGROUND);
310:
311: final JFastLabel title = new JFastLabel(" ");
312: title.setPreferredSize(new Dimension(350, 20));
313: title.setVerticalAlignment(SwingConstants.BOTTOM);
314: title.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0));
315: title.setFont(FontLib.getFont("Tahoma", Font.PLAIN, 16));
316: title.setBackground(BACKGROUND);
317: title.setForeground(FOREGROUND);
318:
319: tview.addControlListener(new ControlAdapter() {
320: public void itemEntered(VisualItem item, MouseEvent e) {
321: if (item.canGetString(label))
322: title.setText(item.getString(label));
323: }
324:
325: public void itemExited(VisualItem item, MouseEvent e) {
326: title.setText(null);
327: }
328: });
329:
330: Box box = new Box(BoxLayout.X_AXIS);
331: box.add(Box.createHorizontalStrut(10));
332: box.add(title);
333: box.add(Box.createHorizontalGlue());
334: box.add(search);
335: box.add(Box.createHorizontalStrut(3));
336: box.setBackground(BACKGROUND);
337:
338: JPanel panel = new JPanel(new BorderLayout());
339: panel.setBackground(BACKGROUND);
340: panel.setForeground(FOREGROUND);
341: panel.add(tview, BorderLayout.CENTER);
342: panel.add(box, BorderLayout.SOUTH);
343: return panel;
344: }
345:
346: // ------------------------------------------------------------------------
347:
348: public class OrientAction extends AbstractAction {
349: private int orientation;
350:
351: public OrientAction(int orientation) {
352: this .orientation = orientation;
353: }
354:
355: public void actionPerformed(ActionEvent evt) {
356: setOrientation(orientation);
357: getVisualization().cancel("orient");
358: getVisualization().run("treeLayout");
359: getVisualization().run("orient");
360: }
361: }
362:
363: public class AutoPanAction extends Action {
364: private Point2D m_start = new Point2D.Double();
365: private Point2D m_end = new Point2D.Double();
366: private Point2D m_cur = new Point2D.Double();
367: private int m_bias = 150;
368:
369: public void run(double frac) {
370: TupleSet ts = m_vis
371: .getFocusGroup(Visualization.FOCUS_ITEMS);
372: if (ts.getTupleCount() == 0)
373: return;
374:
375: if (frac == 0.0) {
376: int xbias = 0, ybias = 0;
377: switch (m_orientation) {
378: case Constants.ORIENT_LEFT_RIGHT:
379: xbias = m_bias;
380: break;
381: case Constants.ORIENT_RIGHT_LEFT:
382: xbias = -m_bias;
383: break;
384: case Constants.ORIENT_TOP_BOTTOM:
385: ybias = m_bias;
386: break;
387: case Constants.ORIENT_BOTTOM_TOP:
388: ybias = -m_bias;
389: break;
390: }
391:
392: VisualItem vi = (VisualItem) ts.tuples().next();
393: m_cur.setLocation(getWidth() / 2, getHeight() / 2);
394: getAbsoluteCoordinate(m_cur, m_start);
395: m_end.setLocation(vi.getX() + xbias, vi.getY() + ybias);
396: } else {
397: m_cur.setLocation(m_start.getX() + frac
398: * (m_end.getX() - m_start.getX()), m_start
399: .getY()
400: + frac * (m_end.getY() - m_start.getY()));
401: panToAbs(m_cur);
402: }
403: }
404: }
405:
406: public static class NodeColorAction extends ColorAction {
407:
408: public NodeColorAction(String group) {
409: super (group, VisualItem.FILLCOLOR);
410: }
411:
412: public int getColor(VisualItem item) {
413: if (m_vis.isInGroup(item, Visualization.SEARCH_ITEMS))
414: return ColorLib.rgb(255, 190, 190);
415: else if (m_vis.isInGroup(item, Visualization.FOCUS_ITEMS))
416: return ColorLib.rgb(198, 229, 229);
417: else if (item.getDOI() > -1)
418: return ColorLib.rgb(164, 193, 193);
419: else
420: return ColorLib.rgba(255, 255, 255, 0);
421: }
422:
423: } // end of inner class TreeMapColorAction
424:
425: } // end of class TreeMap
|