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