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.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 com.google.common.base.Function;
34  import com.google.common.base.Predicate;
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 {@code Graph<String,Number>} for the collapsed vertices.
65   * 
66   * @author Tom Nelson
67   * 
68   */
69  @SuppressWarnings({"serial", "rawtypes", "unchecked"})
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 apply(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 			@Override
138 			public String apply(Object v) {
139 				if(v instanceof Graph) {
140 					return ((Graph)v).getVertices().toString();
141 				}
142 				return super.apply(v);
143 			}});
144         
145         /**
146          * the regular graph mouse for the normal view
147          */
148         final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();
149 
150         vv.setGraphMouse(graphMouse);
151         
152         Container content = getContentPane();
153         GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv);
154         content.add(gzsp);
155         
156         JComboBox modeBox = graphMouse.getModeComboBox();
157         modeBox.addItemListener(graphMouse.getModeListener());
158         graphMouse.setMode(ModalGraphMouse.Mode.PICKING);
159         
160         final ScalingControl scaler = new CrossoverScalingControl();
161 
162         JButton plus = new JButton("+");
163         plus.addActionListener(new ActionListener() {
164             public void actionPerformed(ActionEvent e) {
165                 scaler.scale(vv, 1.1f, vv.getCenter());
166             }
167         });
168         JButton minus = new JButton("-");
169         minus.addActionListener(new ActionListener() {
170             public void actionPerformed(ActionEvent e) {
171                 scaler.scale(vv, 1/1.1f, vv.getCenter());
172             }
173         });
174         
175         JButton collapse = new JButton("Collapse");
176         collapse.addActionListener(new ActionListener() {
177 
178             public void actionPerformed(ActionEvent e) {
179                 Collection picked = new HashSet(vv.getPickedVertexState().getPicked());
180                 if(picked.size() > 1) {
181                     Graph inGraph = layout.getGraph();
182                     Graph clusterGraph = collapser.getClusterGraph(inGraph, picked);
183 
184                     Graph g = collapser.collapse(layout.getGraph(), clusterGraph);
185                     double sumx = 0;
186                     double sumy = 0;
187                     for(Object v : picked) {
188                     	Point2D p = (Point2D)layout.apply(v);
189                     	sumx += p.getX();
190                     	sumy += p.getY();
191                     }
192                     Point2D cp = new Point2D.Double(sumx/picked.size(), sumy/picked.size());
193                     vv.getRenderContext().getParallelEdgeIndexFunction().reset();
194                     layout.setGraph(g);
195                     layout.setLocation(clusterGraph, cp);
196                     vv.getPickedVertexState().clear();
197                     vv.repaint();
198                 }
199             }});
200         
201         JButton compressEdges = new JButton("Compress Edges");
202         compressEdges.addActionListener(new ActionListener() {
203 
204 			public void actionPerformed(ActionEvent e) {
205 				Collection picked = vv.getPickedVertexState().getPicked();
206 				if(picked.size() == 2) {
207 					Pair pair = new Pair(picked);
208 					Graph graph = layout.getGraph();
209 					Collection edges = new HashSet(graph.getIncidentEdges(pair.getFirst()));
210 					edges.retainAll(graph.getIncidentEdges(pair.getSecond()));
211 					exclusions.addAll(edges);
212 					vv.repaint();
213 				}
214 				
215 			}});
216         
217         JButton expandEdges = new JButton("Expand Edges");
218         expandEdges.addActionListener(new ActionListener() {
219 
220 			public void actionPerformed(ActionEvent e) {
221 				Collection picked = vv.getPickedVertexState().getPicked();
222 				if(picked.size() == 2) {
223 					Pair pair = new Pair(picked);
224 					Graph graph = layout.getGraph();
225 					Collection edges = new HashSet(graph.getIncidentEdges(pair.getFirst()));
226 					edges.retainAll(graph.getIncidentEdges(pair.getSecond()));
227 					exclusions.removeAll(edges);
228 					vv.repaint();
229 				}
230 				
231 			}});
232         
233         JButton expand = new JButton("Expand");
234         expand.addActionListener(new ActionListener() {
235 
236             public void actionPerformed(ActionEvent e) {
237                 Collection picked = new HashSet(vv.getPickedVertexState().getPicked());
238                 for(Object v : picked) {
239                     if(v instanceof Graph) {
240                         
241                         Graph g = collapser.expand(layout.getGraph(), (Graph)v);
242                         vv.getRenderContext().getParallelEdgeIndexFunction().reset();
243                         layout.setGraph(g);
244                     }
245                     vv.getPickedVertexState().clear();
246                    vv.repaint();
247                 }
248             }});
249         
250         JButton reset = new JButton("Reset");
251         reset.addActionListener(new ActionListener() {
252 
253             public void actionPerformed(ActionEvent e) {
254                 layout.setGraph(graph);
255                 exclusions.clear();
256                 vv.repaint();
257             }});
258         
259         JButton help = new JButton("Help");
260         help.addActionListener(new ActionListener() {
261             public void actionPerformed(ActionEvent e) {
262                 JOptionPane.showMessageDialog((JComponent)e.getSource(), instructions, "Help", JOptionPane.PLAIN_MESSAGE);
263             }
264         });
265 
266         JPanel controls = new JPanel();
267         JPanel zoomControls = new JPanel(new GridLayout(2,1));
268         zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom"));
269         zoomControls.add(plus);
270         zoomControls.add(minus);
271         controls.add(zoomControls);
272         JPanel collapseControls = new JPanel(new GridLayout(3,1));
273         collapseControls.setBorder(BorderFactory.createTitledBorder("Picked"));
274         collapseControls.add(collapse);
275         collapseControls.add(expand);
276         collapseControls.add(compressEdges);
277         collapseControls.add(expandEdges);
278         collapseControls.add(reset);
279         controls.add(collapseControls);
280         controls.add(modeBox);
281         controls.add(help);
282         content.add(controls, BorderLayout.SOUTH);
283     }
284     
285     /**
286      * a demo class that will create a vertex shape that is either a
287      * polygon or star. The number of sides corresponds to the number
288      * of vertices that were collapsed into the vertex represented by
289      * this shape.
290      * 
291      * @author Tom Nelson
292      *
293      * @param <V> the vertex type
294      */
295     class ClusterVertexShapeFunction<V> extends EllipseVertexShapeTransformer<V> {
296 
297         ClusterVertexShapeFunction() {
298             setSizeTransformer(new ClusterVertexSizeFunction<V>(20));
299         }
300         @Override
301         public Shape apply(V v) {
302             if(v instanceof Graph) {
303                 int size = ((Graph)v).getVertexCount();
304                 if (size < 8) {   
305                     int sides = Math.max(size, 3);
306                     return factory.getRegularPolygon(v, sides);
307                 }
308                 else {
309                     return factory.getRegularStar(v, size);
310                 }
311             }
312             return super.apply(v);
313         }
314     }
315     
316     /**
317      * A demo class that will make vertices larger if they represent
318      * a collapsed collection of original vertices
319      * @author Tom Nelson
320      *
321      * @param <V> the vertex type
322      */
323     class ClusterVertexSizeFunction<V> implements Function<V,Integer> {
324     	int size;
325         public ClusterVertexSizeFunction(Integer size) {
326             this.size = size;
327         }
328 
329         public Integer apply(V v) {
330             if(v instanceof Graph) {
331                 return 30;
332             }
333             return size;
334         }
335     }
336 
337     public static void main(String[] args) {
338         JFrame f = new JFrame();
339         f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
340         f.getContentPane().add(new VertexCollapseDemo());
341         f.pack();
342         f.setVisible(true);
343     }
344 }
345 
346