1
2
3
4
5
6
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.Shape;
18 import java.awt.event.ActionEvent;
19 import java.awt.event.ActionListener;
20 import java.awt.geom.Point2D;
21 import java.lang.reflect.Constructor;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Set;
27
28 import javax.swing.BorderFactory;
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 com.google.common.base.Function;
40 import com.google.common.base.Predicate;
41
42 import edu.uci.ics.jung.algorithms.layout.CircleLayout;
43 import edu.uci.ics.jung.algorithms.layout.FRLayout;
44 import edu.uci.ics.jung.algorithms.layout.ISOMLayout;
45 import edu.uci.ics.jung.algorithms.layout.KKLayout;
46 import edu.uci.ics.jung.algorithms.layout.Layout;
47 import edu.uci.ics.jung.algorithms.layout.SpringLayout;
48 import edu.uci.ics.jung.algorithms.layout.SpringLayout2;
49 import edu.uci.ics.jung.graph.Graph;
50 import edu.uci.ics.jung.graph.util.Pair;
51 import edu.uci.ics.jung.graph.util.TestGraphs;
52 import edu.uci.ics.jung.visualization.DefaultVisualizationModel;
53 import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
54 import edu.uci.ics.jung.visualization.VisualizationModel;
55 import edu.uci.ics.jung.visualization.VisualizationViewer;
56 import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
57 import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
58 import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
59 import edu.uci.ics.jung.visualization.control.ScalingControl;
60 import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer;
61 import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
62 import edu.uci.ics.jung.visualization.layout.LayoutTransition;
63 import edu.uci.ics.jung.visualization.subLayout.GraphCollapser;
64 import edu.uci.ics.jung.visualization.util.Animator;
65 import edu.uci.ics.jung.visualization.util.PredicatedParallelEdgeIndexFunction;
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 @SuppressWarnings("serial")
83 public class VertexCollapseDemoWithLayouts extends JApplet {
84
85 String instructions =
86 "<html>Use the mouse to select multiple vertices"+
87 "<p>either by dragging a region, or by shift-clicking"+
88 "<p>on multiple vertices."+
89 "<p>After you select vertices, use the Collapse button"+
90 "<p>to combine them into a single vertex."+
91 "<p>Select a 'collapsed' vertex and use the Expand button"+
92 "<p>to restore the collapsed vertices."+
93 "<p>The Restore button will restore the original graph."+
94 "<p>If you select 2 (and only 2) vertices, then press"+
95 "<p>the Compress Edges button, parallel edges between"+
96 "<p>those two vertices will no longer be expanded."+
97 "<p>If you select 2 (and only 2) vertices, then press"+
98 "<p>the Expand Edges button, parallel edges between"+
99 "<p>those two vertices will be expanded."+
100 "<p>You can drag the vertices with the mouse." +
101 "<p>Use the 'Picking'/'Transforming' combo-box to switch"+
102 "<p>between picking and transforming mode.</html>";
103
104
105
106 @SuppressWarnings("rawtypes")
107 Graph graph;
108 @SuppressWarnings("rawtypes")
109 Graph collapsedGraph;
110
111
112
113
114 @SuppressWarnings("rawtypes")
115 VisualizationViewer vv;
116
117 @SuppressWarnings("rawtypes")
118 Layout layout;
119
120 GraphCollapser collapser;
121
122 @SuppressWarnings({ "unchecked", "rawtypes" })
123 public VertexCollapseDemoWithLayouts() {
124
125
126 graph =
127 TestGraphs.getOneComponentGraph();
128 collapsedGraph = graph;
129 collapser = new GraphCollapser(graph);
130
131 layout = new FRLayout(graph);
132
133 Dimension preferredSize = new Dimension(400,400);
134 final VisualizationModel visualizationModel =
135 new DefaultVisualizationModel(layout, preferredSize);
136 vv = new VisualizationViewer(visualizationModel, preferredSize);
137
138 vv.getRenderContext().setVertexShapeTransformer(new ClusterVertexShapeFunction());
139
140 final PredicatedParallelEdgeIndexFunction eif = PredicatedParallelEdgeIndexFunction.getInstance();
141 final Set exclusions = new HashSet();
142 eif.setPredicate(new Predicate() {
143
144 public boolean apply(Object e) {
145
146 return exclusions.contains(e);
147 }});
148
149
150 vv.getRenderContext().setParallelEdgeIndexFunction(eif);
151
152 vv.setBackground(Color.white);
153
154
155 vv.setVertexToolTipTransformer(new ToStringLabeller() {
156
157
158
159
160 @Override
161 public String apply(Object v) {
162 if(v instanceof Graph) {
163 return ((Graph)v).getVertices().toString();
164 }
165 return super.apply(v);
166 }});
167
168
169
170
171 final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();
172
173 vv.setGraphMouse(graphMouse);
174
175 Container content = getContentPane();
176 GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv);
177 content.add(gzsp);
178
179 JComboBox modeBox = graphMouse.getModeComboBox();
180 modeBox.addItemListener(graphMouse.getModeListener());
181 graphMouse.setMode(ModalGraphMouse.Mode.PICKING);
182
183 final ScalingControl scaler = new CrossoverScalingControl();
184
185 JButton plus = new JButton("+");
186 plus.addActionListener(new ActionListener() {
187 public void actionPerformed(ActionEvent e) {
188 scaler.scale(vv, 1.1f, vv.getCenter());
189 }
190 });
191 JButton minus = new JButton("-");
192 minus.addActionListener(new ActionListener() {
193 public void actionPerformed(ActionEvent e) {
194 scaler.scale(vv, 1/1.1f, vv.getCenter());
195 }
196 });
197
198 JButton collapse = new JButton("Collapse");
199 collapse.addActionListener(new ActionListener() {
200
201 public void actionPerformed(ActionEvent e) {
202 Collection picked = new HashSet(vv.getPickedVertexState().getPicked());
203 if(picked.size() > 1) {
204 Graph inGraph = layout.getGraph();
205 Graph clusterGraph = collapser.getClusterGraph(inGraph, picked);
206
207 Graph g = collapser.collapse(layout.getGraph(), clusterGraph);
208 collapsedGraph = g;
209 double sumx = 0;
210 double sumy = 0;
211 for(Object v : picked) {
212 Point2D p = (Point2D)layout.apply(v);
213 sumx += p.getX();
214 sumy += p.getY();
215 }
216 Point2D cp = new Point2D.Double(sumx/picked.size(), sumy/picked.size());
217 vv.getRenderContext().getParallelEdgeIndexFunction().reset();
218 layout.setGraph(g);
219 layout.setLocation(clusterGraph, cp);
220 vv.getPickedVertexState().clear();
221 vv.repaint();
222 }
223 }});
224
225 JButton compressEdges = new JButton("Compress Edges");
226 compressEdges.addActionListener(new ActionListener() {
227
228 public void actionPerformed(ActionEvent e) {
229 Collection picked = vv.getPickedVertexState().getPicked();
230 if(picked.size() == 2) {
231 Pair pair = new Pair(picked);
232 Graph graph = layout.getGraph();
233 Collection edges = new HashSet(graph.getIncidentEdges(pair.getFirst()));
234 edges.retainAll(graph.getIncidentEdges(pair.getSecond()));
235 exclusions.addAll(edges);
236 vv.repaint();
237 }
238
239 }});
240
241 JButton expandEdges = new JButton("Expand Edges");
242 expandEdges.addActionListener(new ActionListener() {
243
244 public void actionPerformed(ActionEvent e) {
245 Collection picked = vv.getPickedVertexState().getPicked();
246 if(picked.size() == 2) {
247 Pair pair = new Pair(picked);
248 Graph graph = layout.getGraph();
249 Collection edges = new HashSet(graph.getIncidentEdges(pair.getFirst()));
250 edges.retainAll(graph.getIncidentEdges(pair.getSecond()));
251 exclusions.removeAll(edges);
252 vv.repaint();
253 }
254
255 }});
256
257 JButton expand = new JButton("Expand");
258 expand.addActionListener(new ActionListener() {
259
260 public void actionPerformed(ActionEvent e) {
261 Collection picked = new HashSet(vv.getPickedVertexState().getPicked());
262 for(Object v : picked) {
263 if(v instanceof Graph) {
264
265 Graph g = collapser.expand(layout.getGraph(), (Graph)v);
266 vv.getRenderContext().getParallelEdgeIndexFunction().reset();
267 layout.setGraph(g);
268 }
269 vv.getPickedVertexState().clear();
270 vv.repaint();
271 }
272 }});
273
274 JButton reset = new JButton("Reset");
275 reset.addActionListener(new ActionListener() {
276
277 public void actionPerformed(ActionEvent e) {
278 layout.setGraph(graph);
279 exclusions.clear();
280 vv.repaint();
281 }});
282
283 JButton help = new JButton("Help");
284 help.addActionListener(new ActionListener() {
285 public void actionPerformed(ActionEvent e) {
286 JOptionPane.showMessageDialog((JComponent)e.getSource(), instructions, "Help", JOptionPane.PLAIN_MESSAGE);
287 }
288 });
289 Class[] combos = getCombos();
290 final JComboBox jcb = new JComboBox(combos);
291
292 jcb.setRenderer(new DefaultListCellRenderer() {
293 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
294 String valueString = value.toString();
295 valueString = valueString.substring(valueString.lastIndexOf('.')+1);
296 return super.getListCellRendererComponent(list, valueString, index, isSelected,
297 cellHasFocus);
298 }
299 });
300 jcb.addActionListener(new LayoutChooser(jcb, vv));
301 jcb.setSelectedItem(FRLayout.class);
302
303
304 JPanel controls = new JPanel();
305 JPanel zoomControls = new JPanel(new GridLayout(2,1));
306 zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom"));
307 zoomControls.add(plus);
308 zoomControls.add(minus);
309 controls.add(zoomControls);
310 JPanel collapseControls = new JPanel(new GridLayout(3,1));
311 collapseControls.setBorder(BorderFactory.createTitledBorder("Picked"));
312 collapseControls.add(collapse);
313 collapseControls.add(expand);
314 collapseControls.add(compressEdges);
315 collapseControls.add(expandEdges);
316 collapseControls.add(reset);
317 controls.add(collapseControls);
318 controls.add(modeBox);
319 controls.add(help);
320 controls.add(jcb);
321 content.add(controls, BorderLayout.SOUTH);
322 }
323
324
325
326
327
328
329
330
331
332
333
334 class ClusterVertexShapeFunction<V> extends EllipseVertexShapeTransformer<V> {
335
336 ClusterVertexShapeFunction() {
337 setSizeTransformer(new ClusterVertexSizeFunction<V>(20));
338 }
339 @Override
340 public Shape apply(V v) {
341 if(v instanceof Graph) {
342 @SuppressWarnings("rawtypes")
343 int size = ((Graph)v).getVertexCount();
344 if (size < 8) {
345 int sides = Math.max(size, 3);
346 return factory.getRegularPolygon(v, sides);
347 }
348 else {
349 return factory.getRegularStar(v, size);
350 }
351 }
352 return super.apply(v);
353 }
354 }
355
356
357
358
359
360
361
362
363 class ClusterVertexSizeFunction<V> implements Function<V,Integer> {
364 int size;
365 public ClusterVertexSizeFunction(Integer size) {
366 this.size = size;
367 }
368
369 public Integer apply(V v) {
370 if(v instanceof Graph) {
371 return 30;
372 }
373 return size;
374 }
375 }
376
377 private class LayoutChooser implements ActionListener
378 {
379 private final JComboBox<?> jcb;
380 @SuppressWarnings("rawtypes")
381 private final VisualizationViewer vv;
382
383 private LayoutChooser(JComboBox<?> jcb, VisualizationViewer<Object, ?> vv)
384 {
385 super();
386 this.jcb = jcb;
387 this.vv = vv;
388 }
389
390 @SuppressWarnings({ "unchecked", "rawtypes" })
391 public void actionPerformed(ActionEvent arg0)
392 {
393 Object[] constructorArgs =
394 { collapsedGraph };
395
396 Class<? extends Layout> layoutC =
397 (Class<? extends Layout>) jcb.getSelectedItem();
398 try
399 {
400 Constructor<? extends Layout> constructor = layoutC
401 .getConstructor(new Class[] {Graph.class});
402 Object o = constructor.newInstance(constructorArgs);
403 Layout l = (Layout) o;
404 l.setInitializer(vv.getGraphLayout());
405 l.setSize(vv.getSize());
406 layout = l;
407 LayoutTransition lt =
408 new LayoutTransition(vv, vv.getGraphLayout(), l);
409 Animator animator = new Animator(lt);
410 animator.start();
411 vv.getRenderContext().getMultiLayerTransformer().setToIdentity();
412 vv.repaint();
413
414 }
415 catch (Exception e)
416 {
417 e.printStackTrace();
418 }
419 }
420 }
421
422
423
424 @SuppressWarnings({ "unchecked", "rawtypes" })
425 private Class<? extends Layout>[] getCombos()
426 {
427 List<Class<? extends Layout>> layouts = new ArrayList<Class<? extends Layout>>();
428 layouts.add(KKLayout.class);
429 layouts.add(FRLayout.class);
430 layouts.add(CircleLayout.class);
431 layouts.add(SpringLayout.class);
432 layouts.add(SpringLayout2.class);
433 layouts.add(ISOMLayout.class);
434 return layouts.toArray(new Class[0]);
435 }
436
437 public static void main(String[] args) {
438 JFrame f = new JFrame();
439 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
440 f.getContentPane().add(new VertexCollapseDemoWithLayouts());
441 f.pack();
442 f.setVisible(true);
443 }
444 }
445
446