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.Graphics;
16  import java.awt.Graphics2D;
17  import java.awt.GridLayout;
18  import java.awt.Paint;
19  import java.awt.Shape;
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.awt.geom.AffineTransform;
25  import java.awt.geom.Ellipse2D;
26  import java.awt.geom.Point2D;
27  
28  import javax.swing.BorderFactory;
29  import javax.swing.JApplet;
30  import javax.swing.JButton;
31  import javax.swing.JComboBox;
32  import javax.swing.JFrame;
33  import javax.swing.JPanel;
34  import javax.swing.JRadioButton;
35  import javax.swing.JToggleButton;
36  
37  import com.google.common.base.Functions;
38  import com.google.common.base.Supplier;
39  
40  import edu.uci.ics.jung.algorithms.layout.BalloonLayout;
41  import edu.uci.ics.jung.algorithms.layout.TreeLayout;
42  import edu.uci.ics.jung.graph.DelegateForest;
43  import edu.uci.ics.jung.graph.DelegateTree;
44  import edu.uci.ics.jung.graph.DirectedGraph;
45  import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
46  import edu.uci.ics.jung.graph.Forest;
47  import edu.uci.ics.jung.graph.Tree;
48  import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
49  import edu.uci.ics.jung.visualization.Layer;
50  import edu.uci.ics.jung.visualization.VisualizationServer;
51  import edu.uci.ics.jung.visualization.VisualizationViewer;
52  import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
53  import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
54  import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
55  import edu.uci.ics.jung.visualization.control.ModalLensGraphMouse;
56  import edu.uci.ics.jung.visualization.control.ScalingControl;
57  import edu.uci.ics.jung.visualization.decorators.EdgeShape;
58  import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
59  import edu.uci.ics.jung.visualization.layout.LayoutTransition;
60  import edu.uci.ics.jung.visualization.transform.LensSupport;
61  import edu.uci.ics.jung.visualization.transform.MutableTransformer;
62  import edu.uci.ics.jung.visualization.transform.MutableTransformerDecorator;
63  import edu.uci.ics.jung.visualization.transform.shape.HyperbolicShapeTransformer;
64  import edu.uci.ics.jung.visualization.transform.shape.ViewLensSupport;
65  import edu.uci.ics.jung.visualization.util.Animator;
66  
67  /**
68   * Demonstrates the visualization of a Tree using TreeLayout
69   * and BalloonLayout. An examiner lens performing a hyperbolic
70   * transformation of the view is also included.
71   * 
72   * @author Tom Nelson
73   * 
74   */
75  @SuppressWarnings("serial")
76  public class BalloonLayoutDemo extends JApplet {
77  
78      /**
79       * the graph
80       */
81  	Forest<String,Integer> graph;
82  
83  	Supplier<DirectedGraph<String,Integer>> graphFactory = 
84  		new Supplier<DirectedGraph<String,Integer>>() {
85  
86  		public DirectedGraph<String, Integer> get() {
87  			return new DirectedSparseMultigraph<String,Integer>();
88  		}
89  	};
90  
91  	Supplier<Tree<String,Integer>> treeFactory =
92  		new Supplier<Tree<String,Integer>> () {
93  
94  		public Tree<String, Integer> get() {
95  			return new DelegateTree<String,Integer>(graphFactory);
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     
122     BalloonLayout<String,Integer> radialLayout;
123     /**
124      * provides a Hyperbolic lens for the view
125      */
126     LensSupport hyperbolicViewSupport;
127     
128     public BalloonLayoutDemo() {
129         
130         // create a simple graph for the demo
131         graph = new DelegateForest<String,Integer>();
132 
133         createTree();
134         
135         layout = new TreeLayout<String,Integer>(graph);
136         radialLayout = new BalloonLayout<String,Integer>(graph);
137         radialLayout.setSize(new Dimension(900,900));
138         vv =  new VisualizationViewer<String,Integer>(layout, new Dimension(600,600));
139         vv.setBackground(Color.white);
140         vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.quadCurve(graph));
141         vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
142         // add a listener for ToolTips
143         vv.setVertexToolTipTransformer(new ToStringLabeller());
144         vv.getRenderContext().setArrowFillPaintTransformer(Functions.<Paint>constant(Color.lightGray));
145         rings = new Rings(radialLayout);
146         
147         Container content = getContentPane();
148         final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
149         content.add(panel);
150         
151         final DefaultModalGraphMouse<String, Integer> graphMouse =
152         		new DefaultModalGraphMouse<String, Integer>();
153 
154         vv.setGraphMouse(graphMouse);
155         vv.addKeyListener(graphMouse.getModeKeyListener());
156         
157         hyperbolicViewSupport = 
158             new ViewLensSupport<String,Integer>(vv, new HyperbolicShapeTransformer(vv, 
159             		vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), 
160                     new ModalLensGraphMouse());
161 
162 
163         graphMouse.addItemListener(hyperbolicViewSupport.getGraphMouse().getModeListener());
164 
165         JComboBox<?> modeBox = graphMouse.getModeComboBox();
166         modeBox.addItemListener(graphMouse.getModeListener());
167         graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);
168 
169         final ScalingControl scaler = new CrossoverScalingControl();
170         
171         vv.scaleToLayout(scaler);
172 
173         JButton plus = new JButton("+");
174         plus.addActionListener(new ActionListener() {
175             public void actionPerformed(ActionEvent e) {
176                 scaler.scale(vv, 1.1f, vv.getCenter());
177             }
178         });
179         JButton minus = new JButton("-");
180         minus.addActionListener(new ActionListener() {
181             public void actionPerformed(ActionEvent e) {
182                 scaler.scale(vv, 1/1.1f, vv.getCenter());
183             }
184         });
185         
186         JToggleButton radial = new JToggleButton("Balloon");
187         radial.addItemListener(new ItemListener() {
188 
189 			public void itemStateChanged(ItemEvent e) {
190 				if(e.getStateChange() == ItemEvent.SELECTED) {
191 
192 					LayoutTransition<String,Integer> lt =
193 						new LayoutTransition<String,Integer>(vv, layout, radialLayout);
194 					Animator animator = new Animator(lt);
195 					animator.start();
196 					vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).setToIdentity();
197 					vv.addPreRenderPaintable(rings);
198 				} else {
199 
200 					LayoutTransition<String,Integer> lt =
201 						new LayoutTransition<String,Integer>(vv, radialLayout, layout);
202 					Animator animator = new Animator(lt);
203 					animator.start();
204 					vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT).setToIdentity();
205 					vv.removePreRenderPaintable(rings);
206 				}
207 				vv.repaint();
208 			}});
209         final JRadioButton hyperView = new JRadioButton("Hyperbolic View");
210         hyperView.addItemListener(new ItemListener(){
211             public void itemStateChanged(ItemEvent e) {
212                 hyperbolicViewSupport.activate(e.getStateChange() == ItemEvent.SELECTED);
213             }
214         });
215 
216         JPanel scaleGrid = new JPanel(new GridLayout(1,0));
217         scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom"));
218 
219         JPanel controls = new JPanel();
220         scaleGrid.add(plus);
221         scaleGrid.add(minus);
222         controls.add(radial);
223         controls.add(scaleGrid);
224         controls.add(modeBox);
225         controls.add(hyperView);
226         content.add(controls, BorderLayout.SOUTH);
227     }
228     
229     class Rings implements VisualizationServer.Paintable {
230     	
231     	BalloonLayout<String,Integer> layout;
232     	
233     	public Rings(BalloonLayout<String,Integer> layout) {
234     		this.layout = layout;
235     	}
236     	
237 		public void paint(Graphics g) {
238 			g.setColor(Color.gray);
239 		
240 			Graphics2D g2d = (Graphics2D)g;
241 
242 			Ellipse2D ellipse = new Ellipse2D.Double();
243 			for(String v : layout.getGraph().getVertices()) {
244 				Double radius = layout.getRadii().get(v);
245 				if(radius == null) continue;
246 				Point2D p = layout.apply(v);
247 				ellipse.setFrame(-radius, -radius, 2*radius, 2*radius);
248 				AffineTransform at = AffineTransform.getTranslateInstance(p.getX(), p.getY());
249 				Shape shape = at.createTransformedShape(ellipse);
250 				
251 				MutableTransformer viewTransformer =
252 					vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW);
253 				
254 				if(viewTransformer instanceof MutableTransformerDecorator) {
255 					shape = vv.getRenderContext().getMultiLayerTransformer().transform(shape);
256 				} else {
257 					shape = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT,shape);
258 				}
259 
260 				g2d.draw(shape);
261 			}
262 		}
263 
264 		public boolean useTransform() {
265 			return true;
266 		}
267     }
268     
269     /**
270      * 
271      */
272     private void createTree() {
273     	
274        	graph.addVertex("A0");
275        	graph.addEdge(edgeFactory.get(), "A0", "B0");
276        	graph.addEdge(edgeFactory.get(), "A0", "B1");
277        	graph.addEdge(edgeFactory.get(), "A0", "B2");
278        	
279        	graph.addEdge(edgeFactory.get(), "B0", "C0");
280        	graph.addEdge(edgeFactory.get(), "B0", "C1");
281        	graph.addEdge(edgeFactory.get(), "B0", "C2");
282        	graph.addEdge(edgeFactory.get(), "B0", "C3");
283 
284        	graph.addEdge(edgeFactory.get(), "C2", "H0");
285        	graph.addEdge(edgeFactory.get(), "C2", "H1");
286 
287        	graph.addEdge(edgeFactory.get(), "B1", "D0");
288        	graph.addEdge(edgeFactory.get(), "B1", "D1");
289        	graph.addEdge(edgeFactory.get(), "B1", "D2");
290 
291        	graph.addEdge(edgeFactory.get(), "B2", "E0");
292        	graph.addEdge(edgeFactory.get(), "B2", "E1");
293        	graph.addEdge(edgeFactory.get(), "B2", "E2");
294 
295        	graph.addEdge(edgeFactory.get(), "D0", "F0");
296        	graph.addEdge(edgeFactory.get(), "D0", "F1");
297        	graph.addEdge(edgeFactory.get(), "D0", "F2");
298        	
299        	graph.addEdge(edgeFactory.get(), "D1", "G0");
300        	graph.addEdge(edgeFactory.get(), "D1", "G1");
301        	graph.addEdge(edgeFactory.get(), "D1", "G2");
302        	graph.addEdge(edgeFactory.get(), "D1", "G3");
303        	graph.addEdge(edgeFactory.get(), "D1", "G4");
304        	graph.addEdge(edgeFactory.get(), "D1", "G5");
305        	graph.addEdge(edgeFactory.get(), "D1", "G6");
306        	graph.addEdge(edgeFactory.get(), "D1", "G7");
307        	
308        	// uncomment this to make it a Forest:
309 //       	graph.addVertex("K0");
310 //       	graph.addEdge(edgeFactory.get(), "K0", "K1");
311 //       	graph.addEdge(edgeFactory.get(), "K0", "K2");
312 //       	graph.addEdge(edgeFactory.get(), "K0", "K3");
313 //       	
314 //       	graph.addVertex("J0");
315 //    	graph.addEdge(edgeFactory.get(), "J0", "J1");
316 //    	graph.addEdge(edgeFactory.get(), "J0", "J2");
317 //    	graph.addEdge(edgeFactory.get(), "J1", "J4");
318 //    	graph.addEdge(edgeFactory.get(), "J2", "J3");
319 ////    	graph.addEdge(edgeFactory.get(), "J2", "J5");
320 ////    	graph.addEdge(edgeFactory.get(), "J4", "J6");
321 ////    	graph.addEdge(edgeFactory.get(), "J4", "J7");
322 ////    	graph.addEdge(edgeFactory.get(), "J3", "J8");
323 ////    	graph.addEdge(edgeFactory.get(), "J6", "B9");
324 
325        	
326     }
327 
328     public static void main(String[] args) {
329         JFrame frame = new JFrame();
330         Container content = frame.getContentPane();
331         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
332 
333         content.add(new BalloonLayoutDemo());
334         frame.pack();
335         frame.setVisible(true);
336     }
337 }