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.Component;
14  import java.awt.Container;
15  import java.awt.Dimension;
16  import java.awt.GridLayout;
17  import java.awt.event.ActionEvent;
18  import java.awt.event.ActionListener;
19  import java.awt.event.ItemEvent;
20  import java.awt.event.ItemListener;
21  import java.awt.geom.Point2D;
22  import java.lang.reflect.Constructor;
23  import java.util.Collection;
24  import java.util.HashMap;
25  import java.util.Map;
26  
27  import javax.swing.BorderFactory;
28  import javax.swing.Box;
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 edu.uci.ics.jung.algorithms.layout.AggregateLayout;
40  import edu.uci.ics.jung.algorithms.layout.CircleLayout;
41  import edu.uci.ics.jung.algorithms.layout.FRLayout;
42  import edu.uci.ics.jung.algorithms.layout.KKLayout;
43  import edu.uci.ics.jung.algorithms.layout.Layout;
44  import edu.uci.ics.jung.algorithms.layout.SpringLayout;
45  import edu.uci.ics.jung.graph.Graph;
46  import edu.uci.ics.jung.graph.util.Pair;
47  import edu.uci.ics.jung.graph.util.TestGraphs;
48  import edu.uci.ics.jung.visualization.DefaultVisualizationModel;
49  import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
50  import edu.uci.ics.jung.visualization.VisualizationModel;
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.ScalingControl;
56  import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer;
57  import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer;
58  import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
59  import edu.uci.ics.jung.visualization.picking.PickedState;
60  
61  /**
62   * Demonstrates the AggregateLayout
63   * class. In this demo, vertices are visually clustered as they
64   * are selected. The cluster is formed in a new Layout centered at the
65   * middle locations of the selected vertices. The size and layout
66   * algorithm for each new cluster is selectable.
67   * 
68   * @author Tom Nelson
69   * 
70   */
71  @SuppressWarnings("serial")
72  public class SubLayoutDemo extends JApplet {
73  
74      String instructions =
75          "<html>"+
76          "Use the Layout combobox to select the "+
77          "<p>underlying layout."+
78          "<p>Use the SubLayout combobox to select "+
79          "<p>the type of layout for any clusters you create."+
80          "<p>To create clusters, use the mouse to select "+
81          "<p>multiple vertices, either by dragging a region, "+
82          "<p>or by shift-clicking on multiple vertices."+
83          "<p>After you select vertices, use the "+
84          "<p>Cluster Picked button to cluster them using the "+
85          "<p>layout and size specified in the Sublayout comboboxen."+
86          "<p>Use the Uncluster All button to remove all"+
87          "<p>clusters."+
88          "<p>You can drag the cluster with the mouse." +
89          "<p>Use the 'Picking'/'Transforming' combo-box to switch"+
90          "<p>between picking and transforming mode.</html>";
91      /**
92       * the graph
93       */
94      Graph<String,Number> graph;
95      
96      Map<Graph<String,Number>,Dimension> sizes = new HashMap<Graph<String,Number>,Dimension>();
97  
98      @SuppressWarnings({ "unchecked", "rawtypes" })
99  	Class<Layout>[] layoutClasses = new Class[] {
100 		CircleLayout.class,SpringLayout.class,FRLayout.class,KKLayout.class
101     };
102     /**
103      * the visual component and renderer for the graph
104      */
105     VisualizationViewer<String,Number> vv;
106 
107     AggregateLayout<String,Number> clusteringLayout;
108     
109     Dimension subLayoutSize;
110     
111     PickedState<String> ps;
112     
113     @SuppressWarnings("rawtypes")
114 	Class<CircleLayout> subLayoutType = CircleLayout.class;
115     
116     /**
117      * create an instance of a simple graph with controls to
118      * demo the zoomand hyperbolic features.
119      * 
120      */
121     public SubLayoutDemo() {
122         
123         // create a simple graph for the demo
124         graph = TestGraphs.getOneComponentGraph();
125 
126         // ClusteringLayout is a decorator class that delegates
127         // to another layout, but can also sepately manage the
128         // layout of sub-sets of vertices in circular clusters.
129         clusteringLayout = new AggregateLayout<String,Number>(new FRLayout<String,Number>(graph));
130         	//new SubLayoutDecorator<String,Number>(new FRLayout<String,Number>(graph));
131 
132         Dimension preferredSize = new Dimension(600,600);
133         final VisualizationModel<String,Number> visualizationModel = 
134             new DefaultVisualizationModel<String,Number>(clusteringLayout, preferredSize);
135         vv =  new VisualizationViewer<String,Number>(visualizationModel, preferredSize);
136         
137         ps = vv.getPickedVertexState();
138         vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer<Number>(vv.getPickedEdgeState(), Color.black, Color.red));
139         vv.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer<String>(vv.getPickedVertexState(), 
140                 Color.red, Color.yellow));
141         vv.setBackground(Color.white);
142         
143         // add a listener for ToolTips
144         vv.setVertexToolTipTransformer(new ToStringLabeller());
145         
146         /**
147          * the regular graph mouse for the normal view
148          */
149         final DefaultModalGraphMouse<?, ?> graphMouse = new DefaultModalGraphMouse<Object, Object>();
150 
151         vv.setGraphMouse(graphMouse);
152         
153         Container content = getContentPane();
154         GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv);
155         content.add(gzsp);
156         
157         JComboBox<?> modeBox = graphMouse.getModeComboBox();
158         modeBox.addItemListener(graphMouse.getModeListener());
159         graphMouse.setMode(ModalGraphMouse.Mode.PICKING);
160         
161         final ScalingControl scaler = new CrossoverScalingControl();
162 
163         JButton plus = new JButton("+");
164         plus.addActionListener(new ActionListener() {
165             public void actionPerformed(ActionEvent e) {
166                 scaler.scale(vv, 1.1f, vv.getCenter());
167             }
168         });
169         JButton minus = new JButton("-");
170         minus.addActionListener(new ActionListener() {
171             public void actionPerformed(ActionEvent e) {
172                 scaler.scale(vv, 1/1.1f, vv.getCenter());
173             }
174         });
175         
176         JButton cluster = new JButton("Cluster Picked");
177         cluster.addActionListener(new ActionListener() {
178 			public void actionPerformed(ActionEvent e) {
179 				clusterPicked();
180 			}});
181         
182         JButton uncluster = new JButton("UnCluster All");
183         uncluster.addActionListener(new ActionListener() {
184 			public void actionPerformed(ActionEvent e) {
185 				uncluster();
186 			}});
187         
188         JComboBox<?> layoutTypeComboBox = new JComboBox<Object>(layoutClasses);
189         layoutTypeComboBox.setRenderer(new DefaultListCellRenderer() {
190             public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
191                 String valueString = value.toString();
192                 valueString = valueString.substring(valueString.lastIndexOf('.')+1);
193                 return super.getListCellRendererComponent(list, valueString, index, isSelected,
194                         cellHasFocus);
195             }
196         });
197         layoutTypeComboBox.setSelectedItem(FRLayout.class);
198         layoutTypeComboBox.addItemListener(new ItemListener() {
199 
200 			public void itemStateChanged(ItemEvent e) {
201 				if(e.getStateChange() == ItemEvent.SELECTED) {
202 					@SuppressWarnings({ "unchecked", "rawtypes" })
203 					Class<CircleLayout> clazz = (Class<CircleLayout>)e.getItem();
204 					try {
205 						Layout<String,Number> layout = getLayoutFor(clazz, graph);
206 						layout.setInitializer(vv.getGraphLayout());
207 						clusteringLayout.setDelegate(layout);
208 						vv.setGraphLayout(clusteringLayout);
209 					} catch(Exception ex) {
210 						ex.printStackTrace();
211 					}
212 				}
213 			}});
214         
215         JComboBox<?> subLayoutTypeComboBox = new JComboBox<Object>(layoutClasses);
216         
217         subLayoutTypeComboBox.setRenderer(new DefaultListCellRenderer() {
218             public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
219                 String valueString = value.toString();
220                 valueString = valueString.substring(valueString.lastIndexOf('.')+1);
221                 return super.getListCellRendererComponent(list, valueString, index, isSelected,
222                         cellHasFocus);
223             }
224         });
225         subLayoutTypeComboBox.addItemListener(new ItemListener() {
226 
227 			@SuppressWarnings({ "unchecked", "rawtypes" })
228 			public void itemStateChanged(ItemEvent e) {
229 				if(e.getStateChange() == ItemEvent.SELECTED) {
230 					subLayoutType = (Class<CircleLayout>)e.getItem();
231 				}
232 			}});
233         
234         JComboBox<?> subLayoutDimensionComboBox = 
235         	new JComboBox<Object>(new Dimension[]{
236         			new Dimension(75,75),
237         			new Dimension(100,100),
238         			new Dimension(150,150),
239         			new Dimension(200,200),
240         			new Dimension(250,250),
241         			new Dimension(300,300)
242         	}	
243         	);
244         subLayoutDimensionComboBox.setRenderer(new DefaultListCellRenderer() {
245             public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
246                 String valueString = value.toString();
247                 valueString = valueString.substring(valueString.lastIndexOf('['));
248                 valueString = valueString.replaceAll("idth", "");
249                 valueString = valueString.replaceAll("eight","");
250                 return super.getListCellRendererComponent(list, valueString, index, isSelected,
251                         cellHasFocus);
252             }
253         });
254         subLayoutDimensionComboBox.addItemListener(new ItemListener() {
255 
256 			public void itemStateChanged(ItemEvent e) {
257 				if(e.getStateChange() == ItemEvent.SELECTED) {
258 					subLayoutSize = (Dimension)e.getItem();
259 				}
260 			}});
261         subLayoutDimensionComboBox.setSelectedIndex(1);
262 
263         JButton help = new JButton("Help");
264         help.addActionListener(new ActionListener() {
265             public void actionPerformed(ActionEvent e) {
266                 JOptionPane.showMessageDialog((JComponent)e.getSource(), instructions, "Help", JOptionPane.PLAIN_MESSAGE);
267             }
268         });
269         Dimension space = new Dimension(20,20);
270         Box controls = Box.createVerticalBox();
271         controls.add(Box.createRigidArea(space));
272         
273         JPanel zoomControls = new JPanel(new GridLayout(1,2));
274         zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom"));
275         zoomControls.add(plus);
276         zoomControls.add(minus);
277         heightConstrain(zoomControls);
278         controls.add(zoomControls);
279         controls.add(Box.createRigidArea(space));
280         
281         JPanel clusterControls = new JPanel(new GridLayout(0,1));
282         clusterControls.setBorder(BorderFactory.createTitledBorder("Clustering"));
283         clusterControls.add(cluster);
284         clusterControls.add(uncluster);
285         heightConstrain(clusterControls);
286         controls.add(clusterControls);
287         controls.add(Box.createRigidArea(space));
288         
289         JPanel layoutControls = new JPanel(new GridLayout(0,1));
290         layoutControls.setBorder(BorderFactory.createTitledBorder("Layout"));
291         layoutControls.add(layoutTypeComboBox);
292         heightConstrain(layoutControls);
293         controls.add(layoutControls);
294 
295         JPanel subLayoutControls = new JPanel(new GridLayout(0,1));
296         subLayoutControls.setBorder(BorderFactory.createTitledBorder("SubLayout"));
297         subLayoutControls.add(subLayoutTypeComboBox);
298         subLayoutControls.add(subLayoutDimensionComboBox);
299         heightConstrain(subLayoutControls);
300         controls.add(subLayoutControls);
301         controls.add(Box.createRigidArea(space));
302         
303         JPanel modePanel = new JPanel(new GridLayout(1,1));
304         modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode"));
305         modePanel.add(modeBox);
306         heightConstrain(modePanel);
307         controls.add(modePanel);
308         controls.add(Box.createRigidArea(space));
309 
310         controls.add(help);
311         controls.add(Box.createVerticalGlue());
312         content.add(controls, BorderLayout.EAST);
313     }
314     
315     private void heightConstrain(Component component) {
316     	Dimension d = new Dimension(component.getMaximumSize().width,
317     			component.getMinimumSize().height);
318     	component.setMaximumSize(d);
319     }
320     
321     @SuppressWarnings({ "rawtypes", "unchecked" })
322 	private Layout<String, Number> getLayoutFor(Class<CircleLayout> layoutClass, Graph<String, Number> graph) throws Exception {
323     	Object[] args = new Object[]{graph};
324     	Constructor<CircleLayout> constructor = layoutClass.getConstructor(new Class[] {Graph.class});
325     	return  constructor.newInstance(args);
326     }
327     
328     private void clusterPicked() {
329     	cluster(true);
330     }
331     
332     private void uncluster() {
333     	cluster(false);
334     }
335 
336     @SuppressWarnings("unchecked")
337 	private void cluster(boolean state) {
338     	if(state == true) {
339     		// put the picked vertices into a new sublayout 
340     		Collection<String> picked = ps.getPicked();
341     		if(picked.size() > 1) {
342     			Point2D center = new Point2D.Double();
343     			double x = 0;
344     			double y = 0;
345     			for(String vertex : picked) {
346     				Point2D p = clusteringLayout.apply(vertex);
347     				x += p.getX();
348     				y += p.getY();
349     			}
350     			x /= picked.size();
351     			y /= picked.size();
352 				center.setLocation(x,y);
353 
354     			Graph<String, Number> subGraph;
355     			try {
356     				subGraph = graph.getClass().newInstance();
357     				for(String vertex : picked) {
358     					subGraph.addVertex(vertex);
359     					Collection<Number> incidentEdges = graph.getIncidentEdges(vertex);
360     					for(Number edge : incidentEdges) {
361     						Pair<String> endpoints = graph.getEndpoints(edge);
362     						if(picked.containsAll(endpoints)) {
363     							// put this edge into the subgraph
364     							subGraph.addEdge(edge, endpoints.getFirst(), endpoints.getSecond());
365     						}
366     					}
367     				}
368 
369     				Layout<String,Number> subLayout = getLayoutFor(subLayoutType, subGraph);
370     				subLayout.setInitializer(vv.getGraphLayout());
371     				subLayout.setSize(subLayoutSize);
372     				clusteringLayout.put(subLayout,center);
373     				vv.setGraphLayout(clusteringLayout);
374 
375     			} catch (Exception e) {
376     				e.printStackTrace();
377     			}
378     		}
379     	} else {
380     		// remove all sublayouts
381     		this.clusteringLayout.removeAll();
382     		vv.setGraphLayout(clusteringLayout);
383     	}
384     }
385 
386     public static void main(String[] args) {
387         JFrame f = new JFrame();
388         f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
389         f.getContentPane().add(new SubLayoutDemo());
390         f.pack();
391         f.setVisible(true);
392     }
393 }