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.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 org.apache.commons.collections15.Predicate;
34 import org.apache.commons.collections15.Transformer;
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
57
58
59
60
61
62
63
64
65
66
67
68
69 @SuppressWarnings("serial")
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
92
93 Graph graph;
94
95
96
97
98 VisualizationViewer vv;
99
100 Layout layout;
101
102 GraphCollapser collapser;
103
104 public VertexCollapseDemo() {
105
106
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 evaluate(Object e) {
125
126 return exclusions.contains(e);
127 }});
128
129
130 vv.getRenderContext().setParallelEdgeIndexFunction(eif);
131
132 vv.setBackground(Color.white);
133
134
135 vv.setVertexToolTipTransformer(new ToStringLabeller() {
136
137
138
139
140 @Override
141 public String transform(Object v) {
142 if(v instanceof Graph) {
143 return ((Graph)v).getVertices().toString();
144 }
145 return super.transform(v);
146 }});
147
148
149
150
151 final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse();
152
153 vv.setGraphMouse(graphMouse);
154
155 Container content = getContentPane();
156 GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv);
157 content.add(gzsp);
158
159 JComboBox modeBox = graphMouse.getModeComboBox();
160 modeBox.addItemListener(graphMouse.getModeListener());
161 graphMouse.setMode(ModalGraphMouse.Mode.PICKING);
162
163 final ScalingControl scaler = new CrossoverScalingControl();
164
165 JButton plus = new JButton("+");
166 plus.addActionListener(new ActionListener() {
167 public void actionPerformed(ActionEvent e) {
168 scaler.scale(vv, 1.1f, vv.getCenter());
169 }
170 });
171 JButton minus = new JButton("-");
172 minus.addActionListener(new ActionListener() {
173 public void actionPerformed(ActionEvent e) {
174 scaler.scale(vv, 1/1.1f, vv.getCenter());
175 }
176 });
177
178 JButton collapse = new JButton("Collapse");
179 collapse.addActionListener(new ActionListener() {
180
181 public void actionPerformed(ActionEvent e) {
182 Collection picked = new HashSet(vv.getPickedVertexState().getPicked());
183 if(picked.size() > 1) {
184 Graph inGraph = layout.getGraph();
185 Graph clusterGraph = collapser.getClusterGraph(inGraph, picked);
186
187 Graph g = collapser.collapse(layout.getGraph(), clusterGraph);
188 double sumx = 0;
189 double sumy = 0;
190 for(Object v : picked) {
191 Point2D p = (Point2D)layout.transform(v);
192 sumx += p.getX();
193 sumy += p.getY();
194 }
195 Point2D cp = new Point2D.Double(sumx/picked.size(), sumy/picked.size());
196 vv.getRenderContext().getParallelEdgeIndexFunction().reset();
197 layout.setGraph(g);
198 layout.setLocation(clusterGraph, cp);
199 vv.getPickedVertexState().clear();
200 vv.repaint();
201 }
202 }});
203
204 JButton compressEdges = new JButton("Compress Edges");
205 compressEdges.addActionListener(new ActionListener() {
206
207 public void actionPerformed(ActionEvent e) {
208 Collection picked = vv.getPickedVertexState().getPicked();
209 if(picked.size() == 2) {
210 Pair pair = new Pair(picked);
211 Graph graph = layout.getGraph();
212 Collection edges = new HashSet(graph.getIncidentEdges(pair.getFirst()));
213 edges.retainAll(graph.getIncidentEdges(pair.getSecond()));
214 exclusions.addAll(edges);
215 vv.repaint();
216 }
217
218 }});
219
220 JButton expandEdges = new JButton("Expand Edges");
221 expandEdges.addActionListener(new ActionListener() {
222
223 public void actionPerformed(ActionEvent e) {
224 Collection picked = vv.getPickedVertexState().getPicked();
225 if(picked.size() == 2) {
226 Pair pair = new Pair(picked);
227 Graph graph = layout.getGraph();
228 Collection edges = new HashSet(graph.getIncidentEdges(pair.getFirst()));
229 edges.retainAll(graph.getIncidentEdges(pair.getSecond()));
230 exclusions.removeAll(edges);
231 vv.repaint();
232 }
233
234 }});
235
236 JButton expand = new JButton("Expand");
237 expand.addActionListener(new ActionListener() {
238
239 public void actionPerformed(ActionEvent e) {
240 Collection picked = new HashSet(vv.getPickedVertexState().getPicked());
241 for(Object v : picked) {
242 if(v instanceof Graph) {
243
244 Graph g = collapser.expand(layout.getGraph(), (Graph)v);
245 vv.getRenderContext().getParallelEdgeIndexFunction().reset();
246 layout.setGraph(g);
247 }
248 vv.getPickedVertexState().clear();
249 vv.repaint();
250 }
251 }});
252
253 JButton reset = new JButton("Reset");
254 reset.addActionListener(new ActionListener() {
255
256 public void actionPerformed(ActionEvent e) {
257 layout.setGraph(graph);
258 exclusions.clear();
259 vv.repaint();
260 }});
261
262 JButton help = new JButton("Help");
263 help.addActionListener(new ActionListener() {
264 public void actionPerformed(ActionEvent e) {
265 JOptionPane.showMessageDialog((JComponent)e.getSource(), instructions, "Help", JOptionPane.PLAIN_MESSAGE);
266 }
267 });
268
269 JPanel controls = new JPanel();
270 JPanel zoomControls = new JPanel(new GridLayout(2,1));
271 zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom"));
272 zoomControls.add(plus);
273 zoomControls.add(minus);
274 controls.add(zoomControls);
275 JPanel collapseControls = new JPanel(new GridLayout(3,1));
276 collapseControls.setBorder(BorderFactory.createTitledBorder("Picked"));
277 collapseControls.add(collapse);
278 collapseControls.add(expand);
279 collapseControls.add(compressEdges);
280 collapseControls.add(expandEdges);
281 collapseControls.add(reset);
282 controls.add(collapseControls);
283 controls.add(modeBox);
284 controls.add(help);
285 content.add(controls, BorderLayout.SOUTH);
286 }
287
288
289
290
291
292
293
294
295
296
297
298 class ClusterVertexShapeFunction<V> extends EllipseVertexShapeTransformer<V> {
299
300 ClusterVertexShapeFunction() {
301 setSizeTransformer(new ClusterVertexSizeFunction<V>(20));
302 }
303 @Override
304 public Shape transform(V v) {
305 if(v instanceof Graph) {
306 int size = ((Graph)v).getVertexCount();
307 if (size < 8) {
308 int sides = Math.max(size, 3);
309 return factory.getRegularPolygon(v, sides);
310 }
311 else {
312 return factory.getRegularStar(v, size);
313 }
314 }
315 return super.transform(v);
316 }
317 }
318
319
320
321
322
323
324
325
326 class ClusterVertexSizeFunction<V> implements Transformer<V,Integer> {
327 int size;
328 public ClusterVertexSizeFunction(Integer size) {
329 this.size = size;
330 }
331
332 public Integer transform(V v) {
333 if(v instanceof Graph) {
334 return 30;
335 }
336 return size;
337 }
338 }
339
340
341
342
343 public static void main(String[] args) {
344 JFrame f = new JFrame();
345 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
346 f.getContentPane().add(new VertexCollapseDemo());
347 f.pack();
348 f.setVisible(true);
349 }
350 }
351
352