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