View Javadoc
1   /*
2    * Copyright (c) 2003, The JUNG Authors
3    * All rights reserved.
4    * 
5    * This software is open-source under the BSD license; see either "license.txt"
6    * or https://github.com/jrtom/jung/blob/master/LICENSE 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 com.google.common.base.Function;
41  import com.google.common.base.Functions;
42  
43  import edu.uci.ics.jung.algorithms.layout.FRLayout;
44  import edu.uci.ics.jung.graph.DirectedSparseGraph;
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.ModalGraphMouse.Mode;
51  import edu.uci.ics.jung.visualization.control.ScalingControl;
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 DemoLensVertexImageShaperDemo 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             "sample2"
108     };
109     
110     LensSupport viewSupport;
111     LensSupport modelSupport;
112     LensSupport magnifyLayoutSupport;
113     LensSupport magnifyViewSupport;
114     /**
115      * create an instance of a simple graph with controls to
116      * demo the zoom features.
117      * 
118      */
119     public DemoLensVertexImageShaperDemo() {
120         
121         // create a simple graph for the demo
122         graph = new DirectedSparseGraph<Number,Number>();
123         Number[] vertices = createVertices(1);
124         
125         // a Map for the labels
126         Map<Number,String> map = new HashMap<Number,String>();
127         for(int i=0; i<vertices.length; i++) {
128             map.put(vertices[i], iconNames[i%iconNames.length]);
129         }
130         
131         // a Map for the Icons
132         Map<Number,Icon> iconMap = new HashMap<Number,Icon>();
133         for(int i=0; i<vertices.length; i++) {
134             String name = "/images/topic"+iconNames[i]+".gif";
135             try {
136                 Icon icon = 
137                     new LayeredIcon(new ImageIcon(DemoLensVertexImageShaperDemo.class.getResource(name)).getImage());
138                 iconMap.put(vertices[i], icon);
139             } catch(Exception ex) {
140                 System.err.println("You need slashdoticons.jar in your classpath to see the image "+name);
141             }
142         }
143         
144         createEdges(vertices);
145         
146         FRLayout<Number, Number> layout = new FRLayout<Number, Number>(graph);
147         layout.setMaxIterations(100);
148         vv =  new VisualizationViewer<Number, Number>(layout, new Dimension(600,600));
149         
150         Function<Number,Paint> vpf = 
151             new PickableVertexPaintTransformer<Number>(vv.getPickedVertexState(), Color.white, Color.yellow);
152         vv.getRenderContext().setVertexFillPaintTransformer(vpf);
153         vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer<Number>(vv.getPickedEdgeState(), Color.black, Color.cyan));
154 
155         vv.setBackground(Color.white);
156         
157         final Function<Number,String> vertexStringerImpl = 
158             new VertexStringerImpl<Number>(map);
159         vv.getRenderContext().setVertexLabelTransformer(vertexStringerImpl);
160         vv.getRenderContext().setVertexLabelRenderer(new DefaultVertexLabelRenderer(Color.cyan));
161         vv.getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.cyan));
162         
163         
164         // features on and off. For a real application, use VertexIconAndShapeFunction instead.
165         final VertexIconShapeTransformer<Number> vertexImageShapeFunction =
166             new VertexIconShapeTransformer<Number>(new EllipseVertexShapeTransformer<Number>());
167         vertexImageShapeFunction.setIconMap(iconMap);
168 
169         final Function<Number, Icon> vertexIconFunction = Functions.forMap(iconMap);
170         
171         vv.getRenderContext().setVertexShapeTransformer(vertexImageShapeFunction);
172         vv.getRenderContext().setVertexIconTransformer(vertexIconFunction);
173         
174         // Get the pickedState and add a listener that will decorate the
175         // Vertex images with a checkmark icon when they are picked
176         PickedState<Number> ps = vv.getPickedVertexState();
177         ps.addItemListener(new PickWithIconListener(vertexIconFunction));
178         
179         vv.addPostRenderPaintable(new VisualizationViewer.Paintable(){
180             int x;
181             int y;
182             Font font;
183             FontMetrics metrics;
184             int swidth;
185             int sheight;
186             String str = "Thank You, slashdot.org, for the images!";
187             
188             public void paint(Graphics g) {
189                 Dimension d = vv.getSize();
190                 if(font == null) {
191                     font = new Font(g.getFont().getName(), Font.BOLD, 20);
192                     metrics = g.getFontMetrics(font);
193                     swidth = metrics.stringWidth(str);
194                     sheight = metrics.getMaxAscent()+metrics.getMaxDescent();
195                     x = (d.width-swidth)/2;
196                     y = (int)(d.height-sheight*1.5);
197                 }
198                 g.setFont(font);
199                 Color oldColor = g.getColor();
200                 g.setColor(Color.lightGray);
201                 g.drawString(str, x, y);
202                 g.setColor(oldColor);
203             }
204             public boolean useTransform() {
205                 return false;
206             }
207         });
208 
209         // add a listener for ToolTips
210         vv.setVertexToolTipTransformer(new ToStringLabeller());
211         
212         Container content = getContentPane();
213         final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
214         content.add(panel);
215         
216         final DefaultModalGraphMouse<Number, Number> graphMouse
217         	= new DefaultModalGraphMouse<Number, Number>();
218         vv.setGraphMouse(graphMouse);
219         
220         
221         final ScalingControl scaler = new CrossoverScalingControl();
222 
223         JButton plus = new JButton("+");
224         plus.addActionListener(new ActionListener() {
225             public void actionPerformed(ActionEvent e) {
226                 scaler.scale(vv, 1.1f, vv.getCenter());
227             }
228         });
229         JButton minus = new JButton("-");
230         minus.addActionListener(new ActionListener() {
231             public void actionPerformed(ActionEvent e) {
232                 scaler.scale(vv, 1/1.1f, vv.getCenter());
233             }
234         });
235         
236         JComboBox<Mode> modeBox = graphMouse.getModeComboBox();
237         JPanel modePanel = new JPanel();
238         modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode"));
239         modePanel.add(modeBox);
240         
241         JPanel scaleGrid = new JPanel(new GridLayout(1,0));
242         scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom"));
243         JPanel controls = new JPanel();
244         scaleGrid.add(plus);
245         scaleGrid.add(minus);
246         controls.add(scaleGrid);
247 
248         controls.add(modePanel);
249         content.add(controls, BorderLayout.SOUTH);
250         
251         this.viewSupport = new MagnifyImageLensSupport<Number,Number>(vv);
252 //        	new ViewLensSupport<Number,Number>(vv, new HyperbolicShapeTransformer(vv, 
253 //        		vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), 
254 //                new ModalLensGraphMouse());
255 
256         this.modelSupport = new LayoutLensSupport<Number,Number>(vv);
257         
258         graphMouse.addItemListener(modelSupport.getGraphMouse().getModeListener());
259         graphMouse.addItemListener(viewSupport.getGraphMouse().getModeListener());
260 
261         ButtonGroup radio = new ButtonGroup();
262         JRadioButton none = new JRadioButton("None");
263         none.addItemListener(new ItemListener(){
264             public void itemStateChanged(ItemEvent e) {
265                 if(viewSupport != null) {
266                     viewSupport.deactivate();
267                 }
268                 if(modelSupport != null) {
269                     modelSupport.deactivate();
270                 }
271             }
272         });
273         none.setSelected(true);
274 
275         JRadioButton hyperView = new JRadioButton("View");
276         hyperView.addItemListener(new ItemListener(){
277             public void itemStateChanged(ItemEvent e) {
278                 viewSupport.activate(e.getStateChange() == ItemEvent.SELECTED);
279             }
280         });
281 
282         JRadioButton hyperModel = new JRadioButton("Layout");
283         hyperModel.addItemListener(new ItemListener(){
284             public void itemStateChanged(ItemEvent e) {
285                 modelSupport.activate(e.getStateChange() == ItemEvent.SELECTED);
286             }
287         });
288         radio.add(none);
289         radio.add(hyperView);
290         radio.add(hyperModel);
291         
292         JMenuBar menubar = new JMenuBar();
293         JMenu modeMenu = graphMouse.getModeMenu();
294         menubar.add(modeMenu);
295 
296         JPanel lensPanel = new JPanel(new GridLayout(2,0));
297         lensPanel.setBorder(BorderFactory.createTitledBorder("Lens"));
298         lensPanel.add(none);
299         lensPanel.add(hyperView);
300         lensPanel.add(hyperModel);
301         controls.add(lensPanel);
302     }
303     
304     /**
305      * A simple implementation of VertexStringer that
306      * gets Vertex labels from a Map  
307      * 
308      * @author Tom Nelson 
309      *
310      *
311      */
312     class VertexStringerImpl<V> implements Function<V,String> {
313 
314         Map<V,String> map = new HashMap<V,String>();
315         
316         boolean enabled = true;
317         
318         public VertexStringerImpl(Map<V,String> map) {
319             this.map = map;
320         }
321         
322         /**
323          * @see edu.uci.ics.jung.graph.decorators.VertexStringer#getLabel(edu.uci.ics.jung.graph.Vertex)
324          */
325         public String apply(V v) {
326             if(isEnabled()) {
327                 return map.get(v);
328             } else {
329                 return "";
330             }
331         }
332 
333         /**
334          * @return Returns the enabled.
335          */
336         public boolean isEnabled() {
337             return enabled;
338         }
339 
340         /**
341          * @param enabled The enabled to set.
342          */
343         public void setEnabled(boolean enabled) {
344             this.enabled = enabled;
345         }
346     }
347     
348     /**
349      * create some vertices
350      * @param count how many to create
351      * @return the Vertices in an array
352      */
353     private Number[] createVertices(int count) {
354         Number[] v = new Number[count];
355         for (int i = 0; i < count; i++) {
356             v[i] = new Integer(i);
357             graph.addVertex(v[i]);
358         }
359         return v;
360     }
361 
362     /**
363      * create edges for this demo graph
364      * @param v an array of Vertices to connect
365      */
366     void createEdges(Number[] v) {
367 //        graph.addEdge(new Double(Math.random()), v[0], v[1], EdgeType.DIRECTED);
368 //        graph.addEdge(new Double(Math.random()), v[3], v[0], EdgeType.DIRECTED);
369 //        graph.addEdge(new Double(Math.random()), v[0], v[4], EdgeType.DIRECTED);
370 //        graph.addEdge(new Double(Math.random()), v[4], v[5], EdgeType.DIRECTED);
371 //        graph.addEdge(new Double(Math.random()), v[5], v[3], EdgeType.DIRECTED);
372 //        graph.addEdge(new Double(Math.random()), v[2], v[1], EdgeType.DIRECTED);
373 //        graph.addEdge(new Double(Math.random()), v[4], v[1], EdgeType.DIRECTED);
374 //        graph.addEdge(new Double(Math.random()), v[8], v[2], EdgeType.DIRECTED);
375 //        graph.addEdge(new Double(Math.random()), v[3], v[8], EdgeType.DIRECTED);
376 //        graph.addEdge(new Double(Math.random()), v[6], v[7], EdgeType.DIRECTED);
377 //        graph.addEdge(new Double(Math.random()), v[7], v[5], EdgeType.DIRECTED);
378 //        graph.addEdge(new Double(Math.random()), v[0], v[9], EdgeType.DIRECTED);
379 //        graph.addEdge(new Double(Math.random()), v[9], v[8], EdgeType.DIRECTED);
380 //        graph.addEdge(new Double(Math.random()), v[7], v[6], EdgeType.DIRECTED);
381 //        graph.addEdge(new Double(Math.random()), v[6], v[5], EdgeType.DIRECTED);
382 //        graph.addEdge(new Double(Math.random()), v[4], v[2], EdgeType.DIRECTED);
383 //        graph.addEdge(new Double(Math.random()), v[5], v[4], EdgeType.DIRECTED);
384 //        graph.addEdge(new Double(Math.random()), v[4], v[10], EdgeType.DIRECTED);
385 //        graph.addEdge(new Double(Math.random()), v[10], v[4], EdgeType.DIRECTED);
386     }
387     
388     public static class PickWithIconListener implements ItemListener {
389         Function<Number, Icon> imager;
390         Icon checked;
391         
392         public PickWithIconListener(Function<Number, Icon> imager) {
393             this.imager = imager;
394             checked = new Checkmark(Color.red);
395         }
396 
397         public void itemStateChanged(ItemEvent e) {
398             Icon icon = imager.apply((Number)e.getItem());
399             if(icon != null && icon instanceof LayeredIcon) {
400                 if(e.getStateChange() == ItemEvent.SELECTED) {
401                     ((LayeredIcon)icon).add(checked);
402                 } else {
403                     ((LayeredIcon)icon).remove(checked);
404                 }
405             }
406         }
407     }
408 
409 
410     public static void main(String[] args) {
411         JFrame frame = new JFrame();
412         Container content = frame.getContentPane();
413         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
414 
415         content.add(new DemoLensVertexImageShaperDemo());
416         frame.pack();
417         frame.setVisible(true);
418     }
419 }