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.Component;
14  import java.awt.Container;
15  import java.awt.Dimension;
16  import java.awt.GridLayout;
17  import java.awt.Shape;
18  import java.awt.event.ActionEvent;
19  import java.awt.event.ActionListener;
20  import java.awt.geom.Point2D;
21  import java.lang.reflect.Constructor;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Set;
27  
28  import javax.swing.BorderFactory;
29  import javax.swing.DefaultListCellRenderer;
30  import javax.swing.JApplet;
31  import javax.swing.JButton;
32  import javax.swing.JComboBox;
33  import javax.swing.JComponent;
34  import javax.swing.JFrame;
35  import javax.swing.JList;
36  import javax.swing.JOptionPane;
37  import javax.swing.JPanel;
38  
39  import org.apache.commons.collections15.Predicate;
40  import org.apache.commons.collections15.Transformer;
41  
42  import edu.uci.ics.jung.algorithms.layout.CircleLayout;
43  import edu.uci.ics.jung.algorithms.layout.FRLayout;
44  import edu.uci.ics.jung.algorithms.layout.ISOMLayout;
45  import edu.uci.ics.jung.algorithms.layout.KKLayout;
46  import edu.uci.ics.jung.algorithms.layout.Layout;
47  import edu.uci.ics.jung.algorithms.layout.SpringLayout;
48  import edu.uci.ics.jung.algorithms.layout.SpringLayout2;
49  import edu.uci.ics.jung.graph.Graph;
50  import edu.uci.ics.jung.graph.util.Pair;
51  import edu.uci.ics.jung.graph.util.TestGraphs;
52  import edu.uci.ics.jung.visualization.DefaultVisualizationModel;
53  import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
54  import edu.uci.ics.jung.visualization.VisualizationModel;
55  import edu.uci.ics.jung.visualization.VisualizationViewer;
56  import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
57  import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
58  import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
59  import edu.uci.ics.jung.visualization.control.ScalingControl;
60  import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer;
61  import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
62  import edu.uci.ics.jung.visualization.layout.LayoutTransition;
63  import edu.uci.ics.jung.visualization.subLayout.GraphCollapser;
64  import edu.uci.ics.jung.visualization.util.Animator;
65  import edu.uci.ics.jung.visualization.util.PredicatedParallelEdgeIndexFunction;
66  
67  
68  /**
69   * A demo that shows how collections of vertices can be collapsed
70   * into a single vertex. In this demo, the vertices that are
71   * collapsed are those mouse-picked by the user. Any criteria
72   * could be used to form the vertex collections to be collapsed,
73   * perhaps some common characteristic of those vertex objects.
74   * 
75   * Note that the collection types don't use generics in this
76   * demo, because the vertices are of two types: String for plain
77   * vertices, and Graph<String,Number> for the collapsed vertices.
78   * 
79   * @author Tom Nelson
80   * 
81   */
82  @SuppressWarnings("serial")
83  public class VertexCollapseDemoWithLayouts extends JApplet {
84  
85      String instructions =
86          "<html>Use the mouse to select multiple vertices"+
87          "<p>either by dragging a region, or by shift-clicking"+
88          "<p>on multiple vertices."+
89          "<p>After you select vertices, use the Collapse button"+
90          "<p>to combine them into a single vertex."+
91          "<p>Select a 'collapsed' vertex and use the Expand button"+
92          "<p>to restore the collapsed vertices."+
93          "<p>The Restore button will restore the original graph."+
94          "<p>If you select 2 (and only 2) vertices, then press"+
95          "<p>the Compress Edges button, parallel edges between"+
96          "<p>those two vertices will no longer be expanded."+
97          "<p>If you select 2 (and only 2) vertices, then press"+
98          "<p>the Expand Edges button, parallel edges between"+
99          "<p>those two vertices will be expanded."+
100         "<p>You can drag the vertices with the mouse." +
101         "<p>Use the 'Picking'/'Transforming' combo-box to switch"+
102         "<p>between picking and transforming mode.</html>";
103     /**
104      * the graph
105      */
106     Graph graph;
107     Graph collapsedGraph;
108 
109     /**
110      * the visual component and renderer for the graph
111      */
112     VisualizationViewer vv;
113     
114     Layout layout;
115     
116     GraphCollapser collapser;
117 
118     public VertexCollapseDemoWithLayouts() {
119         
120         // create a simple graph for the demo
121         graph = 
122             TestGraphs.getOneComponentGraph();
123         collapsedGraph = graph;
124         collapser = new GraphCollapser(graph);
125         
126         layout = new FRLayout(graph);
127 
128         Dimension preferredSize = new Dimension(400,400);
129         final VisualizationModel visualizationModel = 
130             new DefaultVisualizationModel(layout, preferredSize);
131         vv =  new VisualizationViewer(visualizationModel, preferredSize);
132         
133         vv.getRenderContext().setVertexShapeTransformer(new ClusterVertexShapeFunction());
134         
135         final PredicatedParallelEdgeIndexFunction eif = PredicatedParallelEdgeIndexFunction.getInstance();
136         final Set exclusions = new HashSet();
137         eif.setPredicate(new Predicate() {
138 
139 			public boolean evaluate(Object e) {
140 				
141 				return exclusions.contains(e);
142 			}});
143         
144         
145         vv.getRenderContext().setParallelEdgeIndexFunction(eif);
146 
147         vv.setBackground(Color.white);
148         
149         // add a listener for ToolTips
150         vv.setVertexToolTipTransformer(new ToStringLabeller() {
151 
152 			/* (non-Javadoc)
153 			 * @see edu.uci.ics.jung.visualization.decorators.DefaultToolTipFunction#getToolTipText(java.lang.Object)
154 			 */
155 			@Override
156 			public String transform(Object v) {
157 				if(v instanceof Graph) {
158 					return ((Graph)v).getVertices().toString();
159 				}
160 				return super.transform(v);
161 			}});
162         
163         /**
164          * the regular graph mouse for the normal view
165          */
166         final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();
167 
168         vv.setGraphMouse(graphMouse);
169         
170         Container content = getContentPane();
171         GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv);
172         content.add(gzsp);
173         
174         JComboBox modeBox = graphMouse.getModeComboBox();
175         modeBox.addItemListener(graphMouse.getModeListener());
176         graphMouse.setMode(ModalGraphMouse.Mode.PICKING);
177         
178         final ScalingControl scaler = new CrossoverScalingControl();
179 
180         JButton plus = new JButton("+");
181         plus.addActionListener(new ActionListener() {
182             public void actionPerformed(ActionEvent e) {
183                 scaler.scale(vv, 1.1f, vv.getCenter());
184             }
185         });
186         JButton minus = new JButton("-");
187         minus.addActionListener(new ActionListener() {
188             public void actionPerformed(ActionEvent e) {
189                 scaler.scale(vv, 1/1.1f, vv.getCenter());
190             }
191         });
192         
193         JButton collapse = new JButton("Collapse");
194         collapse.addActionListener(new ActionListener() {
195 
196             public void actionPerformed(ActionEvent e) {
197                 Collection picked = new HashSet(vv.getPickedVertexState().getPicked());
198                 if(picked.size() > 1) {
199                     Graph inGraph = layout.getGraph();
200                     Graph clusterGraph = collapser.getClusterGraph(inGraph, picked);
201 
202                     Graph g = collapser.collapse(layout.getGraph(), clusterGraph);
203                     collapsedGraph = g;
204                     double sumx = 0;
205                     double sumy = 0;
206                     for(Object v : picked) {
207                     	Point2D p = (Point2D)layout.transform(v);
208                     	sumx += p.getX();
209                     	sumy += p.getY();
210                     }
211                     Point2D cp = new Point2D.Double(sumx/picked.size(), sumy/picked.size());
212                     vv.getRenderContext().getParallelEdgeIndexFunction().reset();
213                     layout.setGraph(g);
214                     layout.setLocation(clusterGraph, cp);
215                     vv.getPickedVertexState().clear();
216                     vv.repaint();
217                 }
218             }});
219         
220         JButton compressEdges = new JButton("Compress Edges");
221         compressEdges.addActionListener(new ActionListener() {
222 
223 			public void actionPerformed(ActionEvent e) {
224 				Collection picked = vv.getPickedVertexState().getPicked();
225 				if(picked.size() == 2) {
226 					Pair pair = new Pair(picked);
227 					Graph graph = layout.getGraph();
228 					Collection edges = new HashSet(graph.getIncidentEdges(pair.getFirst()));
229 					edges.retainAll(graph.getIncidentEdges(pair.getSecond()));
230 					exclusions.addAll(edges);
231 					vv.repaint();
232 				}
233 				
234 			}});
235         
236         JButton expandEdges = new JButton("Expand Edges");
237         expandEdges.addActionListener(new ActionListener() {
238 
239 			public void actionPerformed(ActionEvent e) {
240 				Collection picked = vv.getPickedVertexState().getPicked();
241 				if(picked.size() == 2) {
242 					Pair pair = new Pair(picked);
243 					Graph graph = layout.getGraph();
244 					Collection edges = new HashSet(graph.getIncidentEdges(pair.getFirst()));
245 					edges.retainAll(graph.getIncidentEdges(pair.getSecond()));
246 					exclusions.removeAll(edges);
247 					vv.repaint();
248 				}
249 				
250 			}});
251         
252         JButton expand = new JButton("Expand");
253         expand.addActionListener(new ActionListener() {
254 
255             public void actionPerformed(ActionEvent e) {
256                 Collection picked = new HashSet(vv.getPickedVertexState().getPicked());
257                 for(Object v : picked) {
258                     if(v instanceof Graph) {
259                         
260                         Graph g = collapser.expand(layout.getGraph(), (Graph)v);
261                         vv.getRenderContext().getParallelEdgeIndexFunction().reset();
262                         layout.setGraph(g);
263                     }
264                     vv.getPickedVertexState().clear();
265                    vv.repaint();
266                 }
267             }});
268         
269         JButton reset = new JButton("Reset");
270         reset.addActionListener(new ActionListener() {
271 
272             public void actionPerformed(ActionEvent e) {
273                 layout.setGraph(graph);
274                 exclusions.clear();
275                 vv.repaint();
276             }});
277         
278         JButton help = new JButton("Help");
279         help.addActionListener(new ActionListener() {
280             public void actionPerformed(ActionEvent e) {
281                 JOptionPane.showMessageDialog((JComponent)e.getSource(), instructions, "Help", JOptionPane.PLAIN_MESSAGE);
282             }
283         });
284         Class[] combos = getCombos();
285         final JComboBox jcb = new JComboBox(combos);
286         // use a renderer to shorten the layout name presentation
287         jcb.setRenderer(new DefaultListCellRenderer() {
288             public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
289                 String valueString = value.toString();
290                 valueString = valueString.substring(valueString.lastIndexOf('.')+1);
291                 return super.getListCellRendererComponent(list, valueString, index, isSelected,
292                         cellHasFocus);
293             }
294         });
295         jcb.addActionListener(new LayoutChooser(jcb, vv));
296         jcb.setSelectedItem(FRLayout.class);
297 
298 
299         JPanel controls = new JPanel();
300         JPanel zoomControls = new JPanel(new GridLayout(2,1));
301         zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom"));
302         zoomControls.add(plus);
303         zoomControls.add(minus);
304         controls.add(zoomControls);
305         JPanel collapseControls = new JPanel(new GridLayout(3,1));
306         collapseControls.setBorder(BorderFactory.createTitledBorder("Picked"));
307         collapseControls.add(collapse);
308         collapseControls.add(expand);
309         collapseControls.add(compressEdges);
310         collapseControls.add(expandEdges);
311         collapseControls.add(reset);
312         controls.add(collapseControls);
313         controls.add(modeBox);
314         controls.add(help);
315         controls.add(jcb);
316         content.add(controls, BorderLayout.SOUTH);
317     }
318     
319     /**
320      * a demo class that will create a vertex shape that is either a
321      * polygon or star. The number of sides corresponds to the number
322      * of vertices that were collapsed into the vertex represented by
323      * this shape.
324      * 
325      * @author Tom Nelson
326      *
327      * @param <V>
328      */
329     class ClusterVertexShapeFunction<V> extends EllipseVertexShapeTransformer<V> {
330 
331         ClusterVertexShapeFunction() {
332             setSizeTransformer(new ClusterVertexSizeFunction<V>(20));
333         }
334         @Override
335         public Shape transform(V v) {
336             if(v instanceof Graph) {
337                 int size = ((Graph)v).getVertexCount();
338                 if (size < 8) {   
339                     int sides = Math.max(size, 3);
340                     return factory.getRegularPolygon(v, sides);
341                 }
342                 else {
343                     return factory.getRegularStar(v, size);
344                 }
345             }
346             return super.transform(v);
347         }
348     }
349     
350     /**
351      * A demo class that will make vertices larger if they represent
352      * a collapsed collection of original vertices
353      * @author Tom Nelson
354      *
355      * @param <V>
356      */
357     class ClusterVertexSizeFunction<V> implements Transformer<V,Integer> {
358     	int size;
359         public ClusterVertexSizeFunction(Integer size) {
360             this.size = size;
361         }
362 
363         public Integer transform(V v) {
364             if(v instanceof Graph) {
365                 return 30;
366             }
367             return size;
368         }
369     }
370 
371 	private class LayoutChooser implements ActionListener
372     {
373         private final JComboBox jcb;
374         private final VisualizationViewer vv;
375 
376         private LayoutChooser(JComboBox jcb, VisualizationViewer vv)
377         {
378             super();
379             this.jcb = jcb;
380             this.vv = vv;
381         }
382 
383         public void actionPerformed(ActionEvent arg0)
384         {
385             Object[] constructorArgs =
386                 { collapsedGraph };
387 
388             Class<? extends Layout> layoutC = 
389                 (Class<? extends Layout>) jcb.getSelectedItem();
390 //            Class lay = layoutC;
391             try
392             {
393                 Constructor<? extends Layout> constructor = layoutC
394                         .getConstructor(new Class[] {Graph.class});
395                 Object o = constructor.newInstance(constructorArgs);
396                 Layout l = (Layout) o;
397                 l.setInitializer(vv.getGraphLayout());
398                 l.setSize(vv.getSize());
399                 layout = l;
400 				LayoutTransition lt =
401 					new LayoutTransition(vv, vv.getGraphLayout(), l);
402 				Animator animator = new Animator(lt);
403 				animator.start();
404 				vv.getRenderContext().getMultiLayerTransformer().setToIdentity();
405 				vv.repaint();
406                 
407             }
408             catch (Exception e)
409             {
410                 e.printStackTrace();
411             }
412         }
413     }
414     /**
415      * @return
416      */
417     @SuppressWarnings("unchecked")
418     private Class<? extends Layout>[] getCombos()
419     {
420         List<Class<? extends Layout>> layouts = new ArrayList<Class<? extends Layout>>();
421         layouts.add(KKLayout.class);
422         layouts.add(FRLayout.class);
423         layouts.add(CircleLayout.class);
424         layouts.add(SpringLayout.class);
425         layouts.add(SpringLayout2.class);
426         layouts.add(ISOMLayout.class);
427         return layouts.toArray(new Class[0]);
428     }
429 
430     /**
431      * a driver for this demo
432      */
433     public static void main(String[] args) {
434         JFrame f = new JFrame();
435         f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
436         f.getContentPane().add(new VertexCollapseDemoWithLayouts());
437         f.pack();
438         f.setVisible(true);
439     }
440 }
441 
442