View Javadoc
1   package edu.uci.ics.jung.samples;
2   /*
3    * Copyright (c) 2003, The JUNG Authors
4    * All rights reserved.
5    * 
6    * This software is open-source under the BSD license; see either "license.txt"
7    * or https://github.com/jrtom/jung/blob/master/LICENSE for a description.
8    * 
9    */
10  
11  
12  import java.awt.BorderLayout;
13  import java.awt.Color;
14  import java.awt.Container;
15  import java.awt.Dimension;
16  import java.awt.Graphics;
17  import java.awt.Graphics2D;
18  import java.awt.GridLayout;
19  import java.awt.Paint;
20  import java.awt.Shape;
21  import java.awt.event.ActionEvent;
22  import java.awt.event.ActionListener;
23  import java.awt.event.ItemEvent;
24  import java.awt.event.ItemListener;
25  import java.awt.geom.Ellipse2D;
26  import java.awt.geom.Point2D;
27  import java.util.Collection;
28  import java.util.HashSet;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import javax.swing.BorderFactory;
33  import javax.swing.JApplet;
34  import javax.swing.JButton;
35  import javax.swing.JComboBox;
36  import javax.swing.JFrame;
37  import javax.swing.JPanel;
38  import javax.swing.JToggleButton;
39  
40  import com.google.common.base.Function;
41  import com.google.common.base.Functions;
42  import com.google.common.base.Supplier;
43  
44  import edu.uci.ics.jung.algorithms.layout.FRLayout;
45  import edu.uci.ics.jung.algorithms.layout.PolarPoint;
46  import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout;
47  import edu.uci.ics.jung.algorithms.layout.TreeLayout;
48  import edu.uci.ics.jung.graph.DelegateForest;
49  import edu.uci.ics.jung.graph.DelegateTree;
50  import edu.uci.ics.jung.graph.DirectedGraph;
51  import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
52  import edu.uci.ics.jung.graph.Forest;
53  import edu.uci.ics.jung.graph.Graph;
54  import edu.uci.ics.jung.graph.Tree;
55  import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
56  import edu.uci.ics.jung.visualization.Layer;
57  import edu.uci.ics.jung.visualization.VisualizationServer;
58  import edu.uci.ics.jung.visualization.VisualizationViewer;
59  import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
60  import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
61  import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
62  import edu.uci.ics.jung.visualization.control.ScalingControl;
63  import edu.uci.ics.jung.visualization.decorators.EdgeShape;
64  import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer;
65  import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
66  import edu.uci.ics.jung.visualization.subLayout.TreeCollapser;
67  
68  /**
69   * Demonstrates "collapsing"/"expanding" of a tree's subtrees.
70   * @author Tom Nelson
71   * 
72   */
73  @SuppressWarnings("serial")
74  public class TreeCollapseDemo extends JApplet {
75  
76      /**
77       * the graph
78       */
79      Forest<String,Integer> graph;
80  
81      Supplier<DirectedGraph<String,Integer>> graphFactory = 
82      	new Supplier<DirectedGraph<String,Integer>>() {
83  
84  			public DirectedGraph<String, Integer> get() {
85  				return new DirectedSparseMultigraph<String,Integer>();
86  			}
87  		};
88  			
89          Supplier<Tree<String,Integer>> treeFactory =
90  		new Supplier<Tree<String,Integer>> () {
91  
92  		public Tree<String, Integer> get() {
93  			return new DelegateTree<String,Integer>(graphFactory);
94  		}
95  	};
96  	
97  	
98  	
99  	Supplier<Integer> edgeFactory = new Supplier<Integer>() {
100 		int i=0;
101 		public Integer get() {
102 			return i++;
103 		}};
104 
105     Supplier<String> vertexFactory = new Supplier<String>() {
106     	int i=0;
107 		public String get() {
108 			return "V"+i++;
109 		}};
110 
111     /**
112      * the visual component and renderer for the graph
113      */
114     VisualizationViewer<String,Integer> vv;
115 
116     VisualizationServer.Paintable rings;
117 
118     String root;
119 
120     TreeLayout<String,Integer> layout;
121 	FRLayout<?, ?> layout1;
122 
123     TreeCollapser collapser;
124 
125     RadialTreeLayout<String,Integer> radialLayout;
126 
127 	public TreeCollapseDemo() {
128 
129         // create a simple graph for the demo
130         graph = new DelegateForest<String,Integer>();
131 
132         createTree();
133 
134         layout = new TreeLayout<String,Integer>(graph);
135         collapser = new TreeCollapser();
136 
137         radialLayout = new RadialTreeLayout<String,Integer>(graph);
138         radialLayout.setSize(new Dimension(600,600));
139         vv =  new VisualizationViewer<String,Integer>(layout, new Dimension(600,600));
140         vv.setBackground(Color.white);
141         vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph));
142         vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
143         vv.getRenderContext().setVertexShapeTransformer(new ClusterVertexShapeFunction<String>());
144         // add a listener for ToolTips
145         vv.setVertexToolTipTransformer(new ToStringLabeller());
146         vv.getRenderContext().setArrowFillPaintTransformer(Functions.<Paint>constant(Color.lightGray));
147         rings = new Rings();
148 
149         Container content = getContentPane();
150         final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
151         content.add(panel);
152 
153         final DefaultModalGraphMouse<String, Integer> graphMouse
154         	= new DefaultModalGraphMouse<String, Integer>();
155 
156         vv.setGraphMouse(graphMouse);
157 
158         JComboBox<?> modeBox = graphMouse.getModeComboBox();
159         modeBox.addItemListener(graphMouse.getModeListener());
160         graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);
161 
162         final ScalingControl scaler = new CrossoverScalingControl();
163 
164         JButton plus = new JButton("+");
165         plus.addActionListener(new ActionListener() {
166             public void actionPerformed(ActionEvent e) {
167                 scaler.scale(vv, 1.1f, vv.getCenter());
168             }
169         });
170         JButton minus = new JButton("-");
171         minus.addActionListener(new ActionListener() {
172             public void actionPerformed(ActionEvent e) {
173                 scaler.scale(vv, 1/1.1f, vv.getCenter());
174             }
175         });
176 
177         JToggleButton radial = new JToggleButton("Radial");
178         radial.addItemListener(new ItemListener() {
179 
180 			public void itemStateChanged(ItemEvent e) {
181 				if(e.getStateChange() == ItemEvent.SELECTED) {
182 					vv.setGraphLayout(radialLayout);
183 					vv.getRenderContext().getMultiLayerTransformer().setToIdentity();
184 					vv.addPreRenderPaintable(rings);
185 				} else {
186 					vv.setGraphLayout(layout);
187 					vv.getRenderContext().getMultiLayerTransformer().setToIdentity();
188 					vv.removePreRenderPaintable(rings);
189 				}
190 				vv.repaint();
191 			}});
192 
193         JButton collapse = new JButton("Collapse");
194         collapse.addActionListener(new ActionListener() {
195 
196             public void actionPerformed(ActionEvent e) {
197                 Collection<String> picked
198                 	= new HashSet<String>(vv.getPickedVertexState().getPicked());
199                 if(picked.size() == 1) {
200                 	Object root = picked.iterator().next();
201                     Forest<String, Integer> inGraph = (Forest<String, Integer>)layout.getGraph();
202 
203                     try {
204 						collapser.collapse(vv.getGraphLayout(), inGraph, root);
205 					} catch (InstantiationException e1) {
206 						// TODO Auto-generated catch block
207 						e1.printStackTrace();
208 					} catch (IllegalAccessException e1) {
209 						// TODO Auto-generated catch block
210 						e1.printStackTrace();
211 					}
212 
213                     vv.getPickedVertexState().clear();
214                     vv.repaint();
215                 }
216             }});
217 
218         JButton expand = new JButton("Expand");
219         expand.addActionListener(new ActionListener() {
220 
221             public void actionPerformed(ActionEvent e) {
222                 Collection<String> picked = vv.getPickedVertexState().getPicked();
223                 for(Object v : picked) {
224                     if(v instanceof Forest) {
225                         Forest<String, Integer> inGraph
226                         	= (Forest<String, Integer>)layout.getGraph();
227             			collapser.expand(inGraph, (Forest<?, ?>)v);
228                     }
229                     vv.getPickedVertexState().clear();
230                    vv.repaint();
231                 }
232             }});
233 
234         JPanel scaleGrid = new JPanel(new GridLayout(1,0));
235         scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom"));
236 
237         JPanel controls = new JPanel();
238         scaleGrid.add(plus);
239         scaleGrid.add(minus);
240         controls.add(radial);
241         controls.add(scaleGrid);
242         controls.add(modeBox);
243         controls.add(collapse);
244         controls.add(expand);
245         content.add(controls, BorderLayout.SOUTH);
246     }
247 
248     class Rings implements VisualizationServer.Paintable {
249     	
250     	Collection<Double> depths;
251     	
252     	public Rings() {
253     		depths = getDepths();
254     	}
255     	
256     	private Collection<Double> getDepths() {
257     		Set<Double> depths = new HashSet<Double>();
258     		Map<String,PolarPoint> polarLocations = radialLayout.getPolarLocations();
259     		for(String v : graph.getVertices()) {
260     			PolarPoint pp = polarLocations.get(v);
261     			depths.add(pp.getRadius());
262     		}
263     		return depths;
264     	}
265 
266 		public void paint(Graphics g) {
267 			g.setColor(Color.lightGray);
268 		
269 			Graphics2D g2d = (Graphics2D)g;
270 			Point2D center = radialLayout.getCenter();
271 
272 			Ellipse2D ellipse = new Ellipse2D.Double();
273 			for(double d : depths) {
274 				ellipse.setFrameFromDiagonal(center.getX()-d, center.getY()-d, 
275 						center.getX()+d, center.getY()+d);
276 				Shape shape = vv.getRenderContext().
277 						getMultiLayerTransformer().getTransformer(Layer.LAYOUT).transform(ellipse);
278 				g2d.draw(shape);
279 			}
280 		}
281 
282 		public boolean useTransform() {
283 			return true;
284 		}
285     }
286 
287     /**
288      * 
289      */
290     private void createTree() {
291     	graph.addVertex("V0");
292     	graph.addEdge(edgeFactory.get(), "V0", "V1");
293     	graph.addEdge(edgeFactory.get(), "V0", "V2");
294     	graph.addEdge(edgeFactory.get(), "V1", "V4");
295     	graph.addEdge(edgeFactory.get(), "V2", "V3");
296     	graph.addEdge(edgeFactory.get(), "V2", "V5");
297     	graph.addEdge(edgeFactory.get(), "V4", "V6");
298     	graph.addEdge(edgeFactory.get(), "V4", "V7");
299     	graph.addEdge(edgeFactory.get(), "V3", "V8");
300     	graph.addEdge(edgeFactory.get(), "V6", "V9");
301     	graph.addEdge(edgeFactory.get(), "V4", "V10");
302     	
303        	graph.addVertex("A0");
304        	graph.addEdge(edgeFactory.get(), "A0", "A1");
305        	graph.addEdge(edgeFactory.get(), "A0", "A2");
306        	graph.addEdge(edgeFactory.get(), "A0", "A3");
307        	
308        	graph.addVertex("B0");
309     	graph.addEdge(edgeFactory.get(), "B0", "B1");
310     	graph.addEdge(edgeFactory.get(), "B0", "B2");
311     	graph.addEdge(edgeFactory.get(), "B1", "B4");
312     	graph.addEdge(edgeFactory.get(), "B2", "B3");
313     	graph.addEdge(edgeFactory.get(), "B2", "B5");
314     	graph.addEdge(edgeFactory.get(), "B4", "B6");
315     	graph.addEdge(edgeFactory.get(), "B4", "B7");
316     	graph.addEdge(edgeFactory.get(), "B3", "B8");
317     	graph.addEdge(edgeFactory.get(), "B6", "B9");
318        	
319     }
320 
321         /**
322      * a demo class that will create a vertex shape that is either a
323      * polygon or star. The number of sides corresponds to the number
324      * of vertices that were collapsed into the vertex represented by
325      * this shape.
326      * 
327      * @author Tom Nelson
328      *
329      * @param <V> the vertex type
330      */
331     class ClusterVertexShapeFunction<V> extends EllipseVertexShapeTransformer<V>
332 {
333 
334         ClusterVertexShapeFunction() {
335             setSizeTransformer(new ClusterVertexSizeFunction<V>(20));
336         }
337 		@Override
338         public Shape apply(V v) {
339             if(v instanceof Graph) {
340                 @SuppressWarnings("rawtypes")
341 				int size = ((Graph)v).getVertexCount();
342                 if (size < 8) {   
343                     int sides = Math.max(size, 3);
344                     return factory.getRegularPolygon(v, sides);
345                 }
346                 else {
347                     return factory.getRegularStar(v, size);
348                 }
349             }
350             return super.apply(v);
351         }
352     }
353 
354     /**
355      * A demo class that will make vertices larger if they represent
356      * a collapsed collection of original vertices
357      * @author Tom Nelson
358      *
359      * @param <V> the vertex type
360      */
361     class ClusterVertexSizeFunction<V> implements Function<V,Integer> {
362     	int size;
363         public ClusterVertexSizeFunction(Integer size) {
364             this.size = size;
365         }
366 
367         public Integer apply(V v) {
368             if(v instanceof Graph) {
369                 return 30;
370             }
371             return size;
372         }
373     }
374 
375     public static void main(String[] args) {
376         JFrame frame = new JFrame();
377         Container content = frame.getContentPane();
378         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
379 
380         content.add(new TreeCollapseDemo());
381         frame.pack();
382         frame.setVisible(true);
383     }
384 }