1 package edu.uci.ics.jung.samples;
2
3
4
5
6
7
8
9
10
11
12 import java.awt.BorderLayout;
13 import java.awt.Color;
14 import java.awt.Container;
15 import java.awt.Dimension;
16 import java.awt.Graphics;
17 import java.awt.Graphics2D;
18 import java.awt.GridLayout;
19 import java.awt.Paint;
20 import java.awt.Shape;
21 import java.awt.event.ActionEvent;
22 import java.awt.event.ActionListener;
23 import java.awt.event.ItemEvent;
24 import java.awt.event.ItemListener;
25 import java.awt.geom.Ellipse2D;
26 import java.awt.geom.Point2D;
27 import java.util.Collection;
28 import java.util.HashSet;
29 import java.util.Map;
30 import java.util.Set;
31
32 import javax.swing.BorderFactory;
33 import javax.swing.JApplet;
34 import javax.swing.JButton;
35 import javax.swing.JComboBox;
36 import javax.swing.JFrame;
37 import javax.swing.JPanel;
38 import javax.swing.JToggleButton;
39
40 import com.google.common.base.Function;
41 import com.google.common.base.Functions;
42 import com.google.common.base.Supplier;
43
44 import edu.uci.ics.jung.algorithms.layout.FRLayout;
45 import edu.uci.ics.jung.algorithms.layout.PolarPoint;
46 import edu.uci.ics.jung.algorithms.layout.RadialTreeLayout;
47 import edu.uci.ics.jung.algorithms.layout.TreeLayout;
48 import edu.uci.ics.jung.graph.DelegateForest;
49 import edu.uci.ics.jung.graph.DelegateTree;
50 import edu.uci.ics.jung.graph.DirectedGraph;
51 import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
52 import edu.uci.ics.jung.graph.Forest;
53 import edu.uci.ics.jung.graph.Graph;
54 import edu.uci.ics.jung.graph.Tree;
55 import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
56 import edu.uci.ics.jung.visualization.Layer;
57 import edu.uci.ics.jung.visualization.VisualizationServer;
58 import edu.uci.ics.jung.visualization.VisualizationViewer;
59 import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
60 import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
61 import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
62 import edu.uci.ics.jung.visualization.control.ScalingControl;
63 import edu.uci.ics.jung.visualization.decorators.EdgeShape;
64 import edu.uci.ics.jung.visualization.decorators.EllipseVertexShapeTransformer;
65 import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
66 import edu.uci.ics.jung.visualization.subLayout.TreeCollapser;
67
68
69
70
71
72
73 @SuppressWarnings("serial")
74 public class TreeCollapseDemo extends JApplet {
75
76
77
78
79 Forest<String,Integer> graph;
80
81 Supplier<DirectedGraph<String,Integer>> graphFactory =
82 new Supplier<DirectedGraph<String,Integer>>() {
83
84 public DirectedGraph<String, Integer> get() {
85 return new DirectedSparseMultigraph<String,Integer>();
86 }
87 };
88
89 Supplier<Tree<String,Integer>> treeFactory =
90 new Supplier<Tree<String,Integer>> () {
91
92 public Tree<String, Integer> get() {
93 return new DelegateTree<String,Integer>(graphFactory);
94 }
95 };
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
113
114 VisualizationViewer<String,Integer> vv;
115
116 VisualizationServer.Paintable rings;
117
118 String root;
119
120 TreeLayout<String,Integer> layout;
121 FRLayout<?, ?> layout1;
122
123 TreeCollapser collapser;
124
125 RadialTreeLayout<String,Integer> radialLayout;
126
127 public TreeCollapseDemo() {
128
129
130 graph = new DelegateForest<String,Integer>();
131
132 createTree();
133
134 layout = new TreeLayout<String,Integer>(graph);
135 collapser = new TreeCollapser();
136
137 radialLayout = new RadialTreeLayout<String,Integer>(graph);
138 radialLayout.setSize(new Dimension(600,600));
139 vv = new VisualizationViewer<String,Integer>(layout, new Dimension(600,600));
140 vv.setBackground(Color.white);
141 vv.getRenderContext().setEdgeShapeTransformer(EdgeShape.line(graph));
142 vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
143 vv.getRenderContext().setVertexShapeTransformer(new ClusterVertexShapeFunction<String>());
144
145 vv.setVertexToolTipTransformer(new ToStringLabeller());
146 vv.getRenderContext().setArrowFillPaintTransformer(Functions.<Paint>constant(Color.lightGray));
147 rings = new Rings();
148
149 Container content = getContentPane();
150 final GraphZoomScrollPane panel = new GraphZoomScrollPane(vv);
151 content.add(panel);
152
153 final DefaultModalGraphMouse<String, Integer> graphMouse
154 = new DefaultModalGraphMouse<String, Integer>();
155
156 vv.setGraphMouse(graphMouse);
157
158 JComboBox<?> modeBox = graphMouse.getModeComboBox();
159 modeBox.addItemListener(graphMouse.getModeListener());
160 graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);
161
162 final ScalingControl scaler = new CrossoverScalingControl();
163
164 JButton plus = new JButton("+");
165 plus.addActionListener(new ActionListener() {
166 public void actionPerformed(ActionEvent e) {
167 scaler.scale(vv, 1.1f, vv.getCenter());
168 }
169 });
170 JButton minus = new JButton("-");
171 minus.addActionListener(new ActionListener() {
172 public void actionPerformed(ActionEvent e) {
173 scaler.scale(vv, 1/1.1f, vv.getCenter());
174 }
175 });
176
177 JToggleButton radial = new JToggleButton("Radial");
178 radial.addItemListener(new ItemListener() {
179
180 public void itemStateChanged(ItemEvent e) {
181 if(e.getStateChange() == ItemEvent.SELECTED) {
182 vv.setGraphLayout(radialLayout);
183 vv.getRenderContext().getMultiLayerTransformer().setToIdentity();
184 vv.addPreRenderPaintable(rings);
185 } else {
186 vv.setGraphLayout(layout);
187 vv.getRenderContext().getMultiLayerTransformer().setToIdentity();
188 vv.removePreRenderPaintable(rings);
189 }
190 vv.repaint();
191 }});
192
193 JButton collapse = new JButton("Collapse");
194 collapse.addActionListener(new ActionListener() {
195
196 public void actionPerformed(ActionEvent e) {
197 Collection<String> picked
198 = new HashSet<String>(vv.getPickedVertexState().getPicked());
199 if(picked.size() == 1) {
200 Object root = picked.iterator().next();
201 Forest<String, Integer> inGraph = (Forest<String, Integer>)layout.getGraph();
202
203 try {
204 collapser.collapse(vv.getGraphLayout(), inGraph, root);
205 } catch (InstantiationException e1) {
206
207 e1.printStackTrace();
208 } catch (IllegalAccessException e1) {
209
210 e1.printStackTrace();
211 }
212
213 vv.getPickedVertexState().clear();
214 vv.repaint();
215 }
216 }});
217
218 JButton expand = new JButton("Expand");
219 expand.addActionListener(new ActionListener() {
220
221 public void actionPerformed(ActionEvent e) {
222 Collection<String> picked = vv.getPickedVertexState().getPicked();
223 for(Object v : picked) {
224 if(v instanceof Forest) {
225 Forest<String, Integer> inGraph
226 = (Forest<String, Integer>)layout.getGraph();
227 collapser.expand(inGraph, (Forest<?, ?>)v);
228 }
229 vv.getPickedVertexState().clear();
230 vv.repaint();
231 }
232 }});
233
234 JPanel scaleGrid = new JPanel(new GridLayout(1,0));
235 scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom"));
236
237 JPanel controls = new JPanel();
238 scaleGrid.add(plus);
239 scaleGrid.add(minus);
240 controls.add(radial);
241 controls.add(scaleGrid);
242 controls.add(modeBox);
243 controls.add(collapse);
244 controls.add(expand);
245 content.add(controls, BorderLayout.SOUTH);
246 }
247
248 class Rings implements VisualizationServer.Paintable {
249
250 Collection<Double> depths;
251
252 public Rings() {
253 depths = getDepths();
254 }
255
256 private Collection<Double> getDepths() {
257 Set<Double> depths = new HashSet<Double>();
258 Map<String,PolarPoint> polarLocations = radialLayout.getPolarLocations();
259 for(String v : graph.getVertices()) {
260 PolarPoint pp = polarLocations.get(v);
261 depths.add(pp.getRadius());
262 }
263 return depths;
264 }
265
266 public void paint(Graphics g) {
267 g.setColor(Color.lightGray);
268
269 Graphics2D g2d = (Graphics2D)g;
270 Point2D center = radialLayout.getCenter();
271
272 Ellipse2D ellipse = new Ellipse2D.Double();
273 for(double d : depths) {
274 ellipse.setFrameFromDiagonal(center.getX()-d, center.getY()-d,
275 center.getX()+d, center.getY()+d);
276 Shape shape = vv.getRenderContext().
277 getMultiLayerTransformer().getTransformer(Layer.LAYOUT).transform(ellipse);
278 g2d.draw(shape);
279 }
280 }
281
282 public boolean useTransform() {
283 return true;
284 }
285 }
286
287
288
289
290 private void createTree() {
291 graph.addVertex("V0");
292 graph.addEdge(edgeFactory.get(), "V0", "V1");
293 graph.addEdge(edgeFactory.get(), "V0", "V2");
294 graph.addEdge(edgeFactory.get(), "V1", "V4");
295 graph.addEdge(edgeFactory.get(), "V2", "V3");
296 graph.addEdge(edgeFactory.get(), "V2", "V5");
297 graph.addEdge(edgeFactory.get(), "V4", "V6");
298 graph.addEdge(edgeFactory.get(), "V4", "V7");
299 graph.addEdge(edgeFactory.get(), "V3", "V8");
300 graph.addEdge(edgeFactory.get(), "V6", "V9");
301 graph.addEdge(edgeFactory.get(), "V4", "V10");
302
303 graph.addVertex("A0");
304 graph.addEdge(edgeFactory.get(), "A0", "A1");
305 graph.addEdge(edgeFactory.get(), "A0", "A2");
306 graph.addEdge(edgeFactory.get(), "A0", "A3");
307
308 graph.addVertex("B0");
309 graph.addEdge(edgeFactory.get(), "B0", "B1");
310 graph.addEdge(edgeFactory.get(), "B0", "B2");
311 graph.addEdge(edgeFactory.get(), "B1", "B4");
312 graph.addEdge(edgeFactory.get(), "B2", "B3");
313 graph.addEdge(edgeFactory.get(), "B2", "B5");
314 graph.addEdge(edgeFactory.get(), "B4", "B6");
315 graph.addEdge(edgeFactory.get(), "B4", "B7");
316 graph.addEdge(edgeFactory.get(), "B3", "B8");
317 graph.addEdge(edgeFactory.get(), "B6", "B9");
318
319 }
320
321
322
323
324
325
326
327
328
329
330
331 class ClusterVertexShapeFunction<V> extends EllipseVertexShapeTransformer<V>
332 {
333
334 ClusterVertexShapeFunction() {
335 setSizeTransformer(new ClusterVertexSizeFunction<V>(20));
336 }
337 @Override
338 public Shape apply(V v) {
339 if(v instanceof Graph) {
340 @SuppressWarnings("rawtypes")
341 int size = ((Graph)v).getVertexCount();
342 if (size < 8) {
343 int sides = Math.max(size, 3);
344 return factory.getRegularPolygon(v, sides);
345 }
346 else {
347 return factory.getRegularStar(v, size);
348 }
349 }
350 return super.apply(v);
351 }
352 }
353
354
355
356
357
358
359
360
361 class ClusterVertexSizeFunction<V> implements Function<V,Integer> {
362 int size;
363 public ClusterVertexSizeFunction(Integer size) {
364 this.size = size;
365 }
366
367 public Integer apply(V v) {
368 if(v instanceof Graph) {
369 return 30;
370 }
371 return size;
372 }
373 }
374
375 public static void main(String[] args) {
376 JFrame frame = new JFrame();
377 Container content = frame.getContentPane();
378 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
379
380 content.add(new TreeCollapseDemo());
381 frame.pack();
382 frame.setVisible(true);
383 }
384 }