1
2
3
4
5
6
7
8
9
10 package edu.uci.ics.jung.samples;
11
12 import java.awt.BasicStroke;
13 import java.awt.BorderLayout;
14 import java.awt.Color;
15 import java.awt.Container;
16 import java.awt.Dimension;
17 import java.awt.GridLayout;
18 import java.awt.Paint;
19 import java.awt.Stroke;
20 import java.awt.event.ActionEvent;
21 import java.awt.event.ActionListener;
22 import java.awt.event.ItemEvent;
23 import java.awt.event.ItemListener;
24 import java.awt.geom.Point2D;
25 import java.io.BufferedReader;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.InputStreamReader;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Set;
32
33 import javax.swing.BorderFactory;
34 import javax.swing.Box;
35 import javax.swing.BoxLayout;
36 import javax.swing.JApplet;
37 import javax.swing.JButton;
38 import javax.swing.JFrame;
39 import javax.swing.JPanel;
40 import javax.swing.JSlider;
41 import javax.swing.JToggleButton;
42 import javax.swing.border.TitledBorder;
43 import javax.swing.event.ChangeEvent;
44 import javax.swing.event.ChangeListener;
45
46 import com.google.common.base.Function;
47 import com.google.common.base.Functions;
48 import com.google.common.base.Supplier;
49 import com.google.common.cache.CacheBuilder;
50 import com.google.common.cache.CacheLoader;
51 import com.google.common.cache.LoadingCache;
52
53 import edu.uci.ics.jung.algorithms.cluster.EdgeBetweennessClusterer;
54 import edu.uci.ics.jung.algorithms.layout.AggregateLayout;
55 import edu.uci.ics.jung.algorithms.layout.CircleLayout;
56 import edu.uci.ics.jung.algorithms.layout.FRLayout;
57 import edu.uci.ics.jung.algorithms.layout.Layout;
58 import edu.uci.ics.jung.algorithms.layout.util.Relaxer;
59 import edu.uci.ics.jung.graph.Graph;
60 import edu.uci.ics.jung.graph.SparseMultigraph;
61 import edu.uci.ics.jung.io.PajekNetReader;
62 import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
63 import edu.uci.ics.jung.visualization.VisualizationViewer;
64 import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
65
66
67
68
69
70
71
72
73
74 @SuppressWarnings("serial")
75 public class ClusteringDemo extends JApplet {
76
77 VisualizationViewer<Number,Number> vv;
78
79 LoadingCache<Number, Paint> vertexPaints =
80 CacheBuilder.newBuilder().build(
81 CacheLoader.from(Functions.<Paint>constant(Color.white)));
82 LoadingCache<Number, Paint> edgePaints =
83 CacheBuilder.newBuilder().build(
84 CacheLoader.from(Functions.<Paint>constant(Color.blue)));
85
86 public final Color[] similarColors =
87 {
88 new Color(216, 134, 134),
89 new Color(135, 137, 211),
90 new Color(134, 206, 189),
91 new Color(206, 176, 134),
92 new Color(194, 204, 134),
93 new Color(145, 214, 134),
94 new Color(133, 178, 209),
95 new Color(103, 148, 255),
96 new Color(60, 220, 220),
97 new Color(30, 250, 100)
98 };
99
100 public static void main(String[] args) throws IOException {
101
102 ClusteringDemo cd = new ClusteringDemo();
103 cd.start();
104
105 JFrame jf = new JFrame();
106 jf.getContentPane().add(cd);
107
108 jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
109 jf.pack();
110 jf.setVisible(true);
111 }
112
113 public void start() {
114 InputStream is = this.getClass().getClassLoader().getResourceAsStream("datasets/zachary.net");
115 BufferedReader br = new BufferedReader( new InputStreamReader( is ));
116
117 try
118 {
119 setUpView(br);
120 }
121 catch (IOException e)
122 {
123 System.out.println("Error in loading graph");
124 e.printStackTrace();
125 }
126 }
127
128 private void setUpView(BufferedReader br) throws IOException {
129
130 Supplier<Number> vertexFactory = new Supplier<Number>() {
131 int n = 0;
132 public Number get() { return n++; }
133 };
134 Supplier<Number> edgeFactory = new Supplier<Number>() {
135 int n = 0;
136 public Number get() { return n++; }
137 };
138
139 PajekNetReader<Graph<Number, Number>, Number,Number> pnr =
140 new PajekNetReader<Graph<Number, Number>, Number,Number>(vertexFactory, edgeFactory);
141
142 final Graph<Number,Number> graph = new SparseMultigraph<Number, Number>();
143
144 pnr.load(br, graph);
145
146
147
148 final AggregateLayout<Number,Number> layout =
149 new AggregateLayout<Number,Number>(new FRLayout<Number,Number>(graph));
150
151 vv = new VisualizationViewer<Number,Number>(layout);
152 vv.setBackground( Color.white );
153
154 vv.getRenderContext().setVertexFillPaintTransformer(vertexPaints);
155 vv.getRenderContext().setVertexDrawPaintTransformer(new Function<Number,Paint>() {
156 public Paint apply(Number v) {
157 if(vv.getPickedVertexState().isPicked(v)) {
158 return Color.cyan;
159 } else {
160 return Color.BLACK;
161 }
162 }
163 });
164
165 vv.getRenderContext().setEdgeDrawPaintTransformer(edgePaints);
166
167 vv.getRenderContext().setEdgeStrokeTransformer(new Function<Number,Stroke>() {
168 protected final Stroke THIN = new BasicStroke(1);
169 protected final Stroke THICK= new BasicStroke(2);
170 public Stroke apply(Number e)
171 {
172 Paint c = edgePaints.getUnchecked(e);
173 if (c == Color.LIGHT_GRAY)
174 return THIN;
175 else
176 return THICK;
177 }
178 });
179
180
181 JButton scramble = new JButton("Restart");
182 scramble.addActionListener(new ActionListener() {
183 public void actionPerformed(ActionEvent arg0) {
184 Layout<Number, Number> layout = vv.getGraphLayout();
185 layout.initialize();
186 Relaxer relaxer = vv.getModel().getRelaxer();
187 if(relaxer != null) {
188 relaxer.stop();
189 relaxer.prerelax();
190 relaxer.relax();
191 }
192 }
193
194 });
195
196 DefaultModalGraphMouse<Number, Number> gm = new DefaultModalGraphMouse<Number, Number>();
197 vv.setGraphMouse(gm);
198
199 final JToggleButton groupVertices = new JToggleButton("Group Clusters");
200
201
202 final JSlider edgeBetweennessSlider = new JSlider(JSlider.HORIZONTAL);
203 edgeBetweennessSlider.setBackground(Color.WHITE);
204 edgeBetweennessSlider.setPreferredSize(new Dimension(210, 50));
205 edgeBetweennessSlider.setPaintTicks(true);
206 edgeBetweennessSlider.setMaximum(graph.getEdgeCount());
207 edgeBetweennessSlider.setMinimum(0);
208 edgeBetweennessSlider.setValue(0);
209 edgeBetweennessSlider.setMajorTickSpacing(10);
210 edgeBetweennessSlider.setPaintLabels(true);
211 edgeBetweennessSlider.setPaintTicks(true);
212
213
214
215
216 final JPanel eastControls = new JPanel();
217 eastControls.setOpaque(true);
218 eastControls.setLayout(new BoxLayout(eastControls, BoxLayout.Y_AXIS));
219 eastControls.add(Box.createVerticalGlue());
220 eastControls.add(edgeBetweennessSlider);
221
222 final String COMMANDSTRING = "Edges removed for clusters: ";
223 final String eastSize = COMMANDSTRING + edgeBetweennessSlider.getValue();
224
225 final TitledBorder sliderBorder = BorderFactory.createTitledBorder(eastSize);
226 eastControls.setBorder(sliderBorder);
227
228 eastControls.add(Box.createVerticalGlue());
229
230 groupVertices.addItemListener(new ItemListener() {
231 public void itemStateChanged(ItemEvent e) {
232 clusterAndRecolor(layout, edgeBetweennessSlider.getValue(),
233 similarColors, e.getStateChange() == ItemEvent.SELECTED);
234 vv.repaint();
235 }});
236
237
238 clusterAndRecolor(layout, 0, similarColors, groupVertices.isSelected());
239
240 edgeBetweennessSlider.addChangeListener(new ChangeListener() {
241 public void stateChanged(ChangeEvent e) {
242 JSlider source = (JSlider) e.getSource();
243 if (!source.getValueIsAdjusting()) {
244 int numEdgesToRemove = source.getValue();
245 clusterAndRecolor(layout, numEdgesToRemove, similarColors,
246 groupVertices.isSelected());
247 sliderBorder.setTitle(
248 COMMANDSTRING + edgeBetweennessSlider.getValue());
249 eastControls.repaint();
250 vv.validate();
251 vv.repaint();
252 }
253 }
254 });
255
256 Container content = getContentPane();
257 content.add(new GraphZoomScrollPane(vv));
258 JPanel south = new JPanel();
259 JPanel grid = new JPanel(new GridLayout(2,1));
260 grid.add(scramble);
261 grid.add(groupVertices);
262 south.add(grid);
263 south.add(eastControls);
264 JPanel p = new JPanel();
265 p.setBorder(BorderFactory.createTitledBorder("Mouse Mode"));
266 p.add(gm.getModeComboBox());
267 south.add(p);
268 content.add(south, BorderLayout.SOUTH);
269 }
270
271 public void clusterAndRecolor(AggregateLayout<Number,Number> layout,
272 int numEdgesToRemove,
273 Color[] colors, boolean groupClusters) {
274
275
276
277
278
279 Graph<Number,Number> g = layout.getGraph();
280 layout.removeAll();
281
282 EdgeBetweennessClusterer<Number,Number> clusterer =
283 new EdgeBetweennessClusterer<Number,Number>(numEdgesToRemove);
284 Set<Set<Number>> clusterSet = clusterer.apply(g);
285 List<Number> edges = clusterer.getEdgesRemoved();
286
287 int i = 0;
288
289 for (Iterator<Set<Number>> cIt = clusterSet.iterator(); cIt.hasNext();) {
290
291 Set<Number> vertices = cIt.next();
292 Color c = colors[i % colors.length];
293
294 colorCluster(vertices, c);
295 if(groupClusters == true) {
296 groupCluster(layout, vertices);
297 }
298 i++;
299 }
300 for (Number e : g.getEdges()) {
301
302 if (edges.contains(e)) {
303 edgePaints.put(e, Color.lightGray);
304 } else {
305 edgePaints.put(e, Color.black);
306 }
307 }
308
309 }
310
311 private void colorCluster(Set<Number> vertices, Color c) {
312 for (Number v : vertices) {
313 vertexPaints.put(v, c);
314 }
315 }
316
317 private void groupCluster(AggregateLayout<Number,Number> layout, Set<Number> vertices) {
318 if(vertices.size() < layout.getGraph().getVertexCount()) {
319 Point2D center = layout.apply(vertices.iterator().next());
320 Graph<Number,Number> subGraph = SparseMultigraph.<Number,Number>getFactory().get();
321 for(Number v : vertices) {
322 subGraph.addVertex(v);
323 }
324 Layout<Number,Number> subLayout =
325 new CircleLayout<Number,Number>(subGraph);
326 subLayout.setInitializer(vv.getGraphLayout());
327 subLayout.setSize(new Dimension(40,40));
328
329 layout.put(subLayout,center);
330 vv.repaint();
331 }
332 }
333 }