View Javadoc

1   /*
2    * Copyright (c) 2003, the JUNG Project and the Regents of the University of
3    * California All rights reserved.
4    * 
5    * This software is open-source under the BSD license; see either "license.txt"
6    * or http://jung.sourceforge.net/license.txt for a description.
7    * 
8    */
9   package edu.uci.ics.jung.samples;
10  
11  import java.awt.BorderLayout;
12  import java.awt.Color;
13  import java.awt.Container;
14  import java.awt.Dimension;
15  import java.awt.Font;
16  import java.awt.FontMetrics;
17  import java.awt.Graphics;
18  import java.awt.GridLayout;
19  import java.awt.Paint;
20  import java.awt.event.ActionEvent;
21  import java.awt.event.ActionListener;
22  import java.awt.event.ItemEvent;
23  import java.awt.event.ItemListener;
24  import java.util.HashMap;
25  import java.util.Map;
26  
27  import javax.swing.BorderFactory;
28  import javax.swing.ButtonGroup;
29  import javax.swing.Icon;
30  import javax.swing.ImageIcon;
31  import javax.swing.JApplet;
32  import javax.swing.JButton;
33  import javax.swing.JComboBox;
34  import javax.swing.JFrame;
35  import javax.swing.JMenu;
36  import javax.swing.JMenuBar;
37  import javax.swing.JPanel;
38  import javax.swing.JRadioButton;
39  
40  import org.apache.commons.collections15.Transformer;
41  
42  import edu.uci.ics.jung.algorithms.layout.FRLayout;
43  import edu.uci.ics.jung.graph.DirectedSparseGraph;
44  import edu.uci.ics.jung.graph.util.EdgeType;
45  import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
46  import edu.uci.ics.jung.visualization.LayeredIcon;
47  import edu.uci.ics.jung.visualization.VisualizationViewer;
48  import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
49  import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
50  import edu.uci.ics.jung.visualization.control.ScalingControl;
51  import edu.uci.ics.jung.visualization.decorators.DefaultVertexIconTransformer;
52  import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer;
53  import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer;
54  import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer;
55  import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
56  import edu.uci.ics.jung.visualization.decorators.VertexIconShapeTransformer;
57  import edu.uci.ics.jung.visualization.picking.PickedState;
58  import edu.uci.ics.jung.visualization.renderers.Checkmark;
59  import edu.uci.ics.jung.visualization.renderers.DefaultEdgeLabelRenderer;
60  import edu.uci.ics.jung.visualization.renderers.DefaultVertexLabelRenderer;
61  import edu.uci.ics.jung.visualization.transform.LayoutLensSupport;
62  import edu.uci.ics.jung.visualization.transform.LensSupport;
63  import edu.uci.ics.jung.visualization.transform.shape.MagnifyImageLensSupport;
64  
65  
66  /**
67   * Demonstrates the use of images to represent graph vertices.
68   * The images are added to the DefaultGraphLabelRenderer and can
69   * either be offset from the vertex, or centered on the vertex.
70   * Additionally, the relative positioning of the label and
71   * image is controlled by subclassing the DefaultGraphLabelRenderer
72   * and setting the appropriate properties on its JLabel superclass
73   *  FancyGraphLabelRenderer
74   * 
75   * The images used in this demo (courtesy of slashdot.org) are
76   * rectangular but with a transparent background. When vertices
77   * are represented by these images, it looks better if the actual
78   * shape of the opaque part of the image is computed so that the
79   * edge arrowheads follow the visual shape of the image. This demo
80   * uses the FourPassImageShaper class to compute the Shape from
81   * an image with transparent background.
82   * 
83   * @author Tom Nelson
84   * 
85   */
86  public class LensVertexImageShaperDemo extends JApplet {
87  
88  	   /**
89  	 * 
90  	 */
91  	private static final long serialVersionUID = 5432239991020505763L;
92  
93  	/**
94       * the graph
95       */
96      DirectedSparseGraph<Number, Number> graph;
97  
98      /**
99       * the visual component and renderer for the graph
100      */
101     VisualizationViewer<Number,Number> vv;
102     
103     /**
104      * some icon names to use
105      */
106     String[] iconNames = {
107             "apple",
108             "os",
109             "x",
110             "linux",
111             "inputdevices",
112             "wireless",
113             "graphics3",
114             "gamespcgames",
115             "humor",
116             "music",
117             "privacy"
118     };
119     
120     LensSupport viewSupport;
121     LensSupport modelSupport;
122     LensSupport magnifyLayoutSupport;
123     LensSupport magnifyViewSupport;
124     /**
125      * create an instance of a simple graph with controls to
126      * demo the zoom features.
127      * 
128      */
129     public LensVertexImageShaperDemo() {
130         
131         // create a simple graph for the demo
132         graph = new DirectedSparseGraph<Number,Number>();
133         Number[] vertices = createVertices(11);
134         
135         // a Map for the labels
136         Map<Number,String> map = new HashMap<Number,String>();
137         for(int i=0; i<vertices.length; i++) {
138             map.put(vertices[i], iconNames[i%iconNames.length]);
139         }
140         
141         // a Map for the Icons
142         Map<Number,Icon> iconMap = new HashMap<Number,Icon>();
143         for(int i=0; i<vertices.length; i++) {
144             String name = "/images/topic"+iconNames[i]+".gif";
145             try {
146                 Icon icon = 
147                     new LayeredIcon(new ImageIcon(LensVertexImageShaperDemo.class.getResource(name)).getImage());
148                 iconMap.put(vertices[i], icon);
149             } catch(Exception ex) {
150                 System.err.println("You need slashdoticons.jar in your classpath to see the image "+name);
151             }
152         }
153         
154         createEdges(vertices);
155         
156         FRLayout<Number, Number> layout = new FRLayout<Number, Number>(graph);
157         layout.setMaxIterations(100);
158         vv =  new VisualizationViewer<Number, Number>(layout, new Dimension(600,600));
159         
160         Transformer<Number,Paint> vpf = 
161             new PickableVertexPaintTransformer<Number>(vv.getPickedVertexState(), Color.white, Color.yellow);
162         vv.getRenderContext().setVertexFillPaintTransformer(vpf);
163         vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer<Number>(vv.getPickedEdgeState(), Color.black, Color.cyan));
164 
165         vv.setBackground(Color.white);
166         
167         final Transformer<Number,String> vertexStringerImpl = 
168             new VertexStringerImpl<Number>(map);
169         vv.getRenderContext().setVertexLabelTransformer(vertexStringerImpl);
170         vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan));
171         vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan));
172         
173         
174         // features on and off. For a real application, use VertexIconAndShapeFunction instead.
175         final VertexIconShapeTransformer<Number> vertexImageShapeFunction =
176             new VertexIconShapeTransformer<Number>(new EllipseVertexShapeTransformer<Number>());
177 
178         final DefaultVertexIconTransformer<Number> vertexIconFunction =
179         	new DefaultVertexIconTransformer<Number>();
180         
181         vertexImageShapeFunction.setIconMap(iconMap);
182         vertexIconFunction.setIconMap(iconMap);
183         
184         vv.getRenderContext().setVertexShapeTransformer(vertexImageShapeFunction);
185         vv.getRenderContext().setVertexIconTransformer(vertexIconFunction);
186 
187         
188         // Get the pickedState and add a listener that will decorate the
189         // Vertex images with a checkmark icon when they are picked
190         PickedState<Number> ps = vv.getPickedVertexState();
191         ps.addItemListener(new PickWithIconListener(vertexIconFunction));
192         
193         vv.addPostRenderPaintable(new VisualizationViewer.Paintable(){
194             int x;
195             int y;
196             Font font;
197             FontMetrics metrics;
198             int swidth;
199             int sheight;
200             String str = "Thank You, slashdot.org, for the images!";
201             
202             public void paint(Graphics g) {
203                 Dimension d = vv.getSize();
204                 if(font == null) {
205                     font = new Font(g.getFont().getName(), Font.BOLD, 20);
206                     metrics = g.getFontMetrics(font);
207                     swidth = metrics.stringWidth(str);
208                     sheight = metrics.getMaxAscent()+metrics.getMaxDescent();
209                     x = (d.width-swidth)/2;
210                     y = (int)(d.height-sheight*1.5);
211                 }
212                 g.setFont(font);
213                 Color oldColor = g.getColor();
214                 g.setColor(Color.lightGray);
215                 g.drawString(str, x, y);
216                 g.setColor(oldColor);
217             }
218             public boolean useTransform() {
219                 return false;
220             }
221         });
222 
223         // add a listener for ToolTips
224         vv.setVertexToolTipTransformer(new ToStringLabeller<Number>());
225         
226         Container content = getContentPane();
227         final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
228         content.add(panel);
229         
230         final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();
231         vv.setGraphMouse(graphMouse);
232         
233         
234         final ScalingControl scaler = new CrossoverScalingControl();
235 
236         JButton plus = new JButton("+");
237         plus.addActionListener(new ActionListener() {
238             public void actionPerformed(ActionEvent e) {
239                 scaler.scale(vv, 1.1f, vv.getCenter());
240             }
241         });
242         JButton minus = new JButton("-");
243         minus.addActionListener(new ActionListener() {
244             public void actionPerformed(ActionEvent e) {
245                 scaler.scale(vv, 1/1.1f, vv.getCenter());
246             }
247         });
248         
249         JComboBox modeBox = graphMouse.getModeComboBox();
250         JPanel modePanel = new JPanel();
251         modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode"));
252         modePanel.add(modeBox);
253         
254         JPanel scaleGrid = new JPanel(new GridLayout(1,0));
255         scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom"));
256         JPanel controls = new JPanel();
257         scaleGrid.add(plus);
258         scaleGrid.add(minus);
259         controls.add(scaleGrid);
260 
261         controls.add(modePanel);
262         content.add(controls, BorderLayout.SOUTH);
263         
264         this.viewSupport = new MagnifyImageLensSupport<Number,Number>(vv);
265 //        	new ViewLensSupport<Number,Number>(vv, new HyperbolicShapeTransformer(vv, 
266 //        		vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), 
267 //                new ModalLensGraphMouse());
268 
269         this.modelSupport = new LayoutLensSupport<Number,Number>(vv);
270         
271         graphMouse.addItemListener(modelSupport.getGraphMouse().getModeListener());
272         graphMouse.addItemListener(viewSupport.getGraphMouse().getModeListener());
273 
274         ButtonGroup radio = new ButtonGroup();
275         JRadioButton none = new JRadioButton("None");
276         none.addItemListener(new ItemListener(){
277             public void itemStateChanged(ItemEvent e) {
278                 if(viewSupport != null) {
279                     viewSupport.deactivate();
280                 }
281                 if(modelSupport != null) {
282                     modelSupport.deactivate();
283                 }
284             }
285         });
286         none.setSelected(true);
287 
288         JRadioButton hyperView = new JRadioButton("View");
289         hyperView.addItemListener(new ItemListener(){
290             public void itemStateChanged(ItemEvent e) {
291                 viewSupport.activate(e.getStateChange() == ItemEvent.SELECTED);
292             }
293         });
294 
295         JRadioButton hyperModel = new JRadioButton("Layout");
296         hyperModel.addItemListener(new ItemListener(){
297             public void itemStateChanged(ItemEvent e) {
298                 modelSupport.activate(e.getStateChange() == ItemEvent.SELECTED);
299             }
300         });
301         radio.add(none);
302         radio.add(hyperView);
303         radio.add(hyperModel);
304         
305         JMenuBar menubar = new JMenuBar();
306         JMenu modeMenu = graphMouse.getModeMenu();
307         menubar.add(modeMenu);
308 
309         JPanel lensPanel = new JPanel(new GridLayout(2,0));
310         lensPanel.setBorder(BorderFactory.createTitledBorder("Lens"));
311         lensPanel.add(none);
312         lensPanel.add(hyperView);
313         lensPanel.add(hyperModel);
314         controls.add(lensPanel);
315     }
316     
317     /**
318      * A simple implementation of VertexStringer that
319      * gets Vertex labels from a Map  
320      * 
321      * @author Tom Nelson 
322      *
323      *
324      */
325     class VertexStringerImpl<V> implements Transformer<V,String> {
326 
327         Map<V,String> map = new HashMap<V,String>();
328         
329         boolean enabled = true;
330         
331         public VertexStringerImpl(Map<V,String> map) {
332             this.map = map;
333         }
334         
335         /**
336          * @see edu.uci.ics.jung.graph.decorators.VertexStringer#getLabel(edu.uci.ics.jung.graph.Vertex)
337          */
338         public String transform(V v) {
339             if(isEnabled()) {
340                 return map.get(v);
341             } else {
342                 return "";
343             }
344         }
345 
346         /**
347          * @return Returns the enabled.
348          */
349         public boolean isEnabled() {
350             return enabled;
351         }
352 
353         /**
354          * @param enabled The enabled to set.
355          */
356         public void setEnabled(boolean enabled) {
357             this.enabled = enabled;
358         }
359     }
360     
361     /**
362      * create some vertices
363      * @param count how many to create
364      * @return the Vertices in an array
365      */
366     private Number[] createVertices(int count) {
367         Number[] v = new Number[count];
368         for (int i = 0; i < count; i++) {
369             v[i] = new Integer(i);
370             graph.addVertex(v[i]);
371         }
372         return v;
373     }
374 
375     /**
376      * create edges for this demo graph
377      * @param v an array of Vertices to connect
378      */
379     void createEdges(Number[] v) {
380         graph.addEdge(new Double(Math.random()), v[0], v[1], EdgeType.DIRECTED);
381         graph.addEdge(new Double(Math.random()), v[3], v[0], EdgeType.DIRECTED);
382         graph.addEdge(new Double(Math.random()), v[0], v[4], EdgeType.DIRECTED);
383         graph.addEdge(new Double(Math.random()), v[4], v[5], EdgeType.DIRECTED);
384         graph.addEdge(new Double(Math.random()), v[5], v[3], EdgeType.DIRECTED);
385         graph.addEdge(new Double(Math.random()), v[2], v[1], EdgeType.DIRECTED);
386         graph.addEdge(new Double(Math.random()), v[4], v[1], EdgeType.DIRECTED);
387         graph.addEdge(new Double(Math.random()), v[8], v[2], EdgeType.DIRECTED);
388         graph.addEdge(new Double(Math.random()), v[3], v[8], EdgeType.DIRECTED);
389         graph.addEdge(new Double(Math.random()), v[6], v[7], EdgeType.DIRECTED);
390         graph.addEdge(new Double(Math.random()), v[7], v[5], EdgeType.DIRECTED);
391         graph.addEdge(new Double(Math.random()), v[0], v[9], EdgeType.DIRECTED);
392         graph.addEdge(new Double(Math.random()), v[9], v[8], EdgeType.DIRECTED);
393         graph.addEdge(new Double(Math.random()), v[7], v[6], EdgeType.DIRECTED);
394         graph.addEdge(new Double(Math.random()), v[6], v[5], EdgeType.DIRECTED);
395         graph.addEdge(new Double(Math.random()), v[4], v[2], EdgeType.DIRECTED);
396         graph.addEdge(new Double(Math.random()), v[5], v[4], EdgeType.DIRECTED);
397         graph.addEdge(new Double(Math.random()), v[4], v[10], EdgeType.DIRECTED);
398         graph.addEdge(new Double(Math.random()), v[10], v[4], EdgeType.DIRECTED);
399     }
400     
401     public static class PickWithIconListener implements ItemListener {
402         DefaultVertexIconTransformer<Number> imager;
403         Icon checked;
404         
405         public PickWithIconListener(DefaultVertexIconTransformer<Number> imager) {
406             this.imager = imager;
407             checked = new Checkmark(Color.red);
408         }
409 
410         public void itemStateChanged(ItemEvent e) {
411             Icon icon = imager.transform((Number)e.getItem());
412             if(icon != null && icon instanceof LayeredIcon) {
413                 if(e.getStateChange() == ItemEvent.SELECTED) {
414                     ((LayeredIcon)icon).add(checked);
415                 } else {
416                     ((LayeredIcon)icon).remove(checked);
417                 }
418             }
419         }
420     }
421 
422 
423     /**
424      * a driver for this demo
425      */
426     public static void main(String[] args) {
427         JFrame frame = new JFrame();
428         Container content = frame.getContentPane();
429         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
430 
431         content.add(new LensVertexImageShaperDemo());
432         frame.pack();
433         frame.setVisible(true);
434     }
435 }