View Javadoc

1   /*
2    * Copyright (c) 2004, the JUNG Project and the Regents of the University of
3    * California All rights reserved.
4    * 
5    * This software is open-source under the BSD license; see either "license.txt"
6    * or http://jung.sourceforge.net/license.txt for a description.
7    * 
8    *
9    * Created on Nov 7, 2004
10   */
11  package edu.uci.ics.jung.samples;
12  
13  import java.awt.BasicStroke;
14  import java.awt.BorderLayout;
15  import java.awt.Color;
16  import java.awt.Component;
17  import java.awt.Dimension;
18  import java.awt.Font;
19  import java.awt.GradientPaint;
20  import java.awt.GridLayout;
21  import java.awt.Paint;
22  import java.awt.Shape;
23  import java.awt.Stroke;
24  import java.awt.event.ActionEvent;
25  import java.awt.event.ActionListener;
26  import java.awt.event.ItemEvent;
27  import java.awt.event.ItemListener;
28  import java.awt.event.MouseEvent;
29  import java.awt.event.MouseListener;
30  import java.awt.geom.Point2D;
31  import java.util.Collection;
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.Map;
35  import java.util.Set;
36  
37  import javax.swing.AbstractAction;
38  import javax.swing.AbstractButton;
39  import javax.swing.BorderFactory;
40  import javax.swing.Box;
41  import javax.swing.ButtonGroup;
42  import javax.swing.JApplet;
43  import javax.swing.JButton;
44  import javax.swing.JCheckBox;
45  import javax.swing.JComboBox;
46  import javax.swing.JFrame;
47  import javax.swing.JPanel;
48  import javax.swing.JPopupMenu;
49  import javax.swing.JRadioButton;
50  
51  import org.apache.commons.collections15.Factory;
52  import org.apache.commons.collections15.Predicate;
53  import org.apache.commons.collections15.Transformer;
54  import org.apache.commons.collections15.functors.ConstantTransformer;
55  import org.apache.commons.collections15.functors.MapTransformer;
56  
57  import edu.uci.ics.jung.algorithms.generators.random.MixedRandomGraphGenerator;
58  import edu.uci.ics.jung.algorithms.layout.FRLayout;
59  import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
60  import edu.uci.ics.jung.algorithms.layout.Layout;
61  import edu.uci.ics.jung.algorithms.scoring.VoltageScorer;
62  import edu.uci.ics.jung.algorithms.scoring.util.VertexScoreTransformer;
63  import edu.uci.ics.jung.algorithms.util.SelfLoopEdgePredicate;
64  import edu.uci.ics.jung.graph.Graph;
65  import edu.uci.ics.jung.graph.SparseMultigraph;
66  import edu.uci.ics.jung.graph.util.Context;
67  import edu.uci.ics.jung.graph.util.EdgeType;
68  import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
69  import edu.uci.ics.jung.visualization.RenderContext;
70  import edu.uci.ics.jung.visualization.VisualizationViewer;
71  import edu.uci.ics.jung.visualization.control.AbstractPopupGraphMousePlugin;
72  import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
73  import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
74  import edu.uci.ics.jung.visualization.control.ScalingControl;
75  import edu.uci.ics.jung.visualization.decorators.AbstractVertexShapeTransformer;
76  import edu.uci.ics.jung.visualization.decorators.EdgeShape;
77  import edu.uci.ics.jung.visualization.decorators.GradientEdgePaintTransformer;
78  import edu.uci.ics.jung.visualization.decorators.NumberFormattingTransformer;
79  import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer;
80  import edu.uci.ics.jung.visualization.picking.PickedInfo;
81  import edu.uci.ics.jung.visualization.picking.PickedState;
82  import edu.uci.ics.jung.visualization.renderers.BasicEdgeArrowRenderingSupport;
83  import edu.uci.ics.jung.visualization.renderers.BasicEdgeRenderer;
84  import edu.uci.ics.jung.visualization.renderers.CenterEdgeArrowRenderingSupport;
85  import edu.uci.ics.jung.visualization.renderers.Renderer;
86  
87  
88  /**
89   * Shows off some of the capabilities of <code>PluggableRenderer</code>.
90   * This code provides examples of different ways to provide and
91   * change the various functions that provide property information
92   * to the renderer.
93   * 
94   * <p>This demo creates a random mixed-mode graph with random edge
95   * weights using <code>TestGraph.generateMixedRandomGraph</code>.
96   * It then runs <code>VoltageRanker</code> on this graph, using half
97   * of the "seed" vertices from the random graph generation as 
98   * voltage sources, and half of them as voltage sinks.</p>
99   * 
100  * <p>What the controls do:
101  * <ul>
102  * <li/>Mouse controls:
103  * <ul>
104  * <li/>If your mouse has a scroll wheel, scrolling forward zooms out and 
105  * scrolling backward zooms in.
106  * <li/>Left-clicking on a vertex or edge selects it, and unselects all others.
107  * <li/>Middle-clicking on a vertex or edge toggles its selection state.
108  * <li/>Right-clicking on a vertex brings up a pop-up menu that allows you to
109  * increase or decrease that vertex's transparency.
110  * <li/>Left-clicking on the background allows you to drag the image around.
111  * <li/>Hovering over a vertex tells you what its voltage is; hovering over an
112  * edge shows its identity; hovering over the background shows an informational 
113  * message.
114 </ul>
115  * <li/>Vertex stuff:
116  * <ul>
117  * <li/>"vertex seed coloring": if checked, the seed vertices are colored blue, 
118  * and all other vertices are colored red.  Otherwise, all vertices are colored
119  * a slightly transparent red (except the currently "picked" vertex, which is
120  * colored transparent purple).
121  * <li/>"vertex selection stroke highlighting": if checked, the picked vertex
122  * and its neighbors are all drawn with heavy borders.  Otherwise, all vertices
123  * are drawn with light borders.
124  * <li/>"show vertex ranks (voltages)": if checked, each vertex is labeled with its
125  * calculated 'voltage'.  Otherwise, vertices are unlabeled.
126  * <li/>"vertex degree shapes": if checked, vertices are drawn with a polygon with
127  * number of sides proportional to its degree.  Otherwise, vertices are drawn
128  * as ellipses.
129  * <li/>"vertex voltage size": if checked, vertices are drawn with a size 
130  * proportional to their voltage ranking.  Otherwise, all vertices are drawn 
131  * at the same size.
132  * <li/>"vertex degree ratio stretch": if checked, vertices are drawn with an
133  * aspect ratio (height/width ratio) proportional to the ratio of their indegree to
134  * their outdegree.  Otherwise, vertices are drawn with an aspect ratio of 1.
135  * <li/>"filter vertices of degree &lt; 4": if checked, does not display any vertices
136  * (or their incident edges) whose degree in the original graph is less than 4; 
137  * otherwise, all vertices are drawn.
138  * </ul>
139  * <li/>Edge stuff:
140  * <ul>
141  * <li/>"edge shape": selects between lines, wedges, quadratic curves, and cubic curves
142  * for drawing edges.  
143  * <li/>"fill edge shapes": if checked, fills the edge shapes.  This will have no effect
144  * if "line" is selected.
145  * <li/>"edge paint": selects between solid colored edges, and gradient-painted edges.
146  * Gradient painted edges are darkest in the middle for undirected edges, and darkest
147  * at the destination for directed edges.
148  * <li/>"show edges": only edges of the checked types are drawn.
149  * <li/>"show arrows": only arrows whose edges are of the checked types are drawn.
150  * <li/>"edge weight highlighting": if checked, edges with weight greater than
151  * a threshold value are drawn using thick solid lines, and other edges are drawn
152  * using thin gray dotted lines.  (This combines edge stroke and paint.) Otherwise,
153  * all edges are drawn with thin solid lines.
154  * <li/>"show edge weights": if checked, edges are labeled with their weights.
155  * Otherwise, edges are not labeled.
156  * </ul>
157  * <li/>Miscellaneous (center panel)
158  * <ul>
159  * <li/>"bold text": if checked, all vertex and edge labels are drawn using a
160  * boldface font.  Otherwise, a normal-weight font is used.  (Has no effect if
161  * no labels are currently visible.)
162  * <li/>zoom controls: 
163  * <ul>
164  * <li/>"+" zooms in, "-" zooms out
165  * <li/>"zoom at mouse (wheel only)": if checked, zooming (using the mouse 
166  * scroll wheel) is centered on the location of the mouse pointer; otherwise,
167  * it is centered on the center of the visualization pane.
168  * </ul>
169  * </ul>
170  * </p>
171  * 
172  * @author Danyel Fisher, Joshua O'Madadhain, Tom Nelson
173  */
174 @SuppressWarnings("serial")
175 public class PluggableRendererDemo extends JApplet implements ActionListener 
176 {
177     protected JCheckBox v_color;
178     protected JCheckBox e_color;
179     protected JCheckBox v_stroke;
180     protected JCheckBox e_uarrow_pred;
181     protected JCheckBox e_darrow_pred;
182     protected JCheckBox e_arrow_centered;
183     protected JCheckBox v_shape;
184     protected JCheckBox v_size;
185     protected JCheckBox v_aspect;
186     protected JCheckBox v_labels;
187     protected JRadioButton e_line;
188     protected JRadioButton e_bent;
189     protected JRadioButton e_wedge;
190     protected JRadioButton e_quad;
191     protected JRadioButton e_ortho;
192     protected JRadioButton e_cubic;
193     protected JCheckBox e_labels;
194     protected JCheckBox font;
195     protected JCheckBox e_show_d;
196     protected JCheckBox e_show_u;
197     protected JCheckBox v_small;
198     protected JCheckBox zoom_at_mouse;
199     protected JCheckBox fill_edges;
200     
201 	protected JRadioButton no_gradient;
202 	protected JRadioButton gradient_relative;
203 
204 	protected static final int GRADIENT_NONE = 0;
205 	protected static final int GRADIENT_RELATIVE = 1;
206 	protected static int gradient_level = GRADIENT_NONE;
207 
208     protected SeedFillColor<Integer> seedFillColor;
209     protected SeedDrawColor<Integer> seedDrawColor;
210     protected EdgeWeightStrokeFunction<Number> ewcs;
211     protected VertexStrokeHighlight<Integer,Number> vsh;
212     protected Transformer<Integer,String> vs;
213     protected Transformer<Integer,String> vs_none;
214     protected Transformer<Number,String> es;
215     protected Transformer<Number,String> es_none;
216     protected VertexFontTransformer<Integer> vff;
217     protected EdgeFontTransformer<Number> eff;
218     protected VertexShapeSizeAspect<Integer,Number> vssa;
219     protected DirectionDisplayPredicate<Integer,Number> show_edge;
220     protected DirectionDisplayPredicate<Integer,Number> show_arrow;
221     protected VertexDisplayPredicate<Integer,Number> show_vertex;
222     protected Predicate<Context<Graph<Integer,Number>,Number>> self_loop;
223     protected GradientPickedEdgePaintFunction<Integer,Number> edgeDrawPaint;
224     protected GradientPickedEdgePaintFunction<Integer,Number> edgeFillPaint;
225     protected final static Object VOLTAGE_KEY = "voltages";
226     protected final static Object TRANSPARENCY = "transparency";
227     
228     protected Map<Number,Number> edge_weight = new HashMap<Number,Number>();
229     protected Transformer<Integer, Double> voltages;
230     protected Map<Integer,Number> transparency = new HashMap<Integer,Number>();
231     
232     protected VisualizationViewer<Integer,Number> vv;
233     protected DefaultModalGraphMouse<Integer, Number> gm;
234     protected Set<Integer> seedVertices = new HashSet<Integer>();
235     
236     public void start()
237     {
238         getContentPane().add( startFunction() );
239     }
240     
241     public static void main(String[] s ) 
242     {
243         JFrame jf = new JFrame();
244         jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
245         JPanel jp = new PluggableRendererDemo().startFunction();
246         jf.getContentPane().add(jp);
247         jf.pack();
248         jf.setVisible(true);
249     }
250     
251     
252     public JPanel startFunction() {
253         Graph<Integer,Number> g = getGraph();
254         
255         Layout<Integer,Number> layout = new FRLayout<Integer,Number>(g);
256 //        layout.setSize(new Dimension(5000,5000));
257         vv = new VisualizationViewer<Integer,Number>(layout);
258 
259         PickedState<Integer> picked_state = vv.getPickedVertexState();
260 
261 //        affineTransformer = vv.getLayoutTransformer();
262         self_loop = new SelfLoopEdgePredicate<Integer,Number>();
263         // create decorators
264         seedFillColor = new SeedFillColor<Integer>(picked_state);
265         seedDrawColor = new SeedDrawColor<Integer>(picked_state);
266         ewcs = 
267             new EdgeWeightStrokeFunction<Number>(edge_weight);
268         vsh = new VertexStrokeHighlight<Integer,Number>(g, picked_state);
269         vff = new VertexFontTransformer<Integer>();
270         eff = new EdgeFontTransformer<Number>();
271         vs_none = new ConstantTransformer(null);
272         es_none = new ConstantTransformer(null);
273         vssa = new VertexShapeSizeAspect<Integer,Number>(g, voltages);
274         show_edge = new DirectionDisplayPredicate<Integer,Number>(true, true);
275         show_arrow = new DirectionDisplayPredicate<Integer,Number>(true, false);
276         show_vertex = new VertexDisplayPredicate<Integer,Number>(false);
277 
278         // uses a gradient edge if unpicked, otherwise uses picked selection
279         edgeDrawPaint = 
280             new GradientPickedEdgePaintFunction<Integer,Number>(
281                     new PickableEdgePaintTransformer<Number>(
282                             vv.getPickedEdgeState(),Color.black,Color.cyan), vv);
283         edgeFillPaint = 
284             new GradientPickedEdgePaintFunction<Integer,Number>(
285                     new PickableEdgePaintTransformer<Number>(
286                             vv.getPickedEdgeState(),Color.black,Color.cyan), vv);
287         
288         vv.getRenderContext().setVertexFillPaintTransformer(seedFillColor);
289         vv.getRenderContext().setVertexDrawPaintTransformer(seedDrawColor);
290         vv.getRenderContext().setVertexStrokeTransformer(vsh);
291         vv.getRenderContext().setVertexLabelTransformer(vs_none);
292         vv.getRenderContext().setVertexFontTransformer(vff);
293         vv.getRenderContext().setVertexShapeTransformer(vssa);
294         vv.getRenderContext().setVertexIncludePredicate(show_vertex);
295         
296         vv.getRenderContext().setEdgeDrawPaintTransformer( edgeDrawPaint );
297         vv.getRenderContext().setEdgeLabelTransformer(es_none);
298         vv.getRenderContext().setEdgeFontTransformer(eff);
299         vv.getRenderContext().setEdgeStrokeTransformer(ewcs);
300         vv.getRenderContext().setEdgeIncludePredicate(show_edge);
301         vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line<Integer,Number>());
302         vv.getRenderContext().setEdgeArrowPredicate(show_arrow);
303         
304         vv.getRenderContext().setArrowFillPaintTransformer(new ConstantTransformer(Color.lightGray));
305         vv.getRenderContext().setArrowDrawPaintTransformer(new ConstantTransformer(Color.black));
306         JPanel jp = new JPanel();
307         jp.setLayout(new BorderLayout());
308         
309         vv.setBackground(Color.white);
310         GraphZoomScrollPane scrollPane = new GraphZoomScrollPane(vv);
311         jp.add(scrollPane);
312         gm = new DefaultModalGraphMouse<Integer, Number>();
313         vv.setGraphMouse(gm);
314         gm.add(new PopupGraphMousePlugin());
315 
316         addBottomControls( jp );
317         vssa.setScaling(true);
318 
319         vv.setVertexToolTipTransformer(new VoltageTips<Number>());
320         vv.setToolTipText("<html><center>Use the mouse wheel to zoom<p>Click and Drag the mouse to pan<p>Shift-click and Drag to Rotate</center></html>");
321         
322 
323         
324         return jp;
325     }
326     
327     /**
328      * Generates a mixed-mode random graph, runs VoltageRanker on it, and
329      * returns the resultant graph.
330      */
331     public Graph<Integer,Number> getGraph() {
332     	Factory<Graph<Integer,Number>> graphFactory =
333     		new Factory<Graph<Integer,Number>>() {
334     		public Graph<Integer,Number> create() {
335     			return new SparseMultigraph<Integer,Number>();
336     		}
337     	};
338     	Factory<Integer> vertexFactory = 
339     		new Factory<Integer>() {
340     			int count;
341 				public Integer create() {
342 					return count++;
343 				}};
344 		Factory<Number> edgeFactory = 
345 		    new Factory<Number>() {
346 			    int count;
347 				public Number create() {
348 					return count++;
349 				}};
350         Graph<Integer,Number> g = 
351         	MixedRandomGraphGenerator.<Integer,Number>generateMixedRandomGraph(graphFactory, vertexFactory, edgeFactory,
352         		edge_weight, 20, false, seedVertices);
353         es = new NumberFormattingTransformer<Number>(MapTransformer.getInstance(edge_weight));
354         
355         // collect the seeds used to define the random graph
356 
357         if (seedVertices.size() < 2)
358             System.out.println("need at least 2 seeds (one source, one sink)");
359         
360         // use these seeds as source and sink vertices, run VoltageRanker
361         boolean source = true;
362         Set<Integer> sources = new HashSet<Integer>();
363         Set<Integer> sinks = new HashSet<Integer>();
364         for(Integer v : seedVertices)
365         {
366             if (source)
367                 sources.add(v);
368             else
369                 sinks.add(v);
370             source = !source;
371         }
372         VoltageScorer<Integer, Number> voltage_scores = 
373             new VoltageScorer<Integer, Number>(g, 
374                     MapTransformer.getInstance(edge_weight), sources, sinks);
375         voltage_scores.evaluate();
376         voltages = new VertexScoreTransformer<Integer, Double>(voltage_scores);
377         vs = new NumberFormattingTransformer<Integer>(voltages);
378         
379         Collection<Integer> verts = g.getVertices();
380         
381         // assign a transparency value of 0.9 to all vertices
382         for(Integer v : verts) {
383             transparency.put(v, new Double(0.9));
384         }
385 
386         // add a couple of self-loops (sanity check on rendering)
387         Integer v = verts.iterator().next(); 
388         Number e = new Float(Math.random());
389         edge_weight.put(e, e);
390         g.addEdge(e, v, v);
391         e = new Float(Math.random());
392         edge_weight.put(e, e);
393         g.addEdge(e, v, v);
394         return g;  
395     }
396     
397     /**
398      * @param jp    panel to which controls will be added
399      */
400     @SuppressWarnings("serial")
401 	protected void addBottomControls(final JPanel jp) 
402     {
403         final JPanel control_panel = new JPanel();
404         jp.add(control_panel, BorderLayout.EAST);
405         control_panel.setLayout(new BorderLayout());
406         final Box vertex_panel = Box.createVerticalBox();
407         vertex_panel.setBorder(BorderFactory.createTitledBorder("Vertices"));
408         final Box edge_panel = Box.createVerticalBox();
409         edge_panel.setBorder(BorderFactory.createTitledBorder("Edges"));
410         final Box both_panel = Box.createVerticalBox();
411 
412         control_panel.add(vertex_panel, BorderLayout.NORTH);
413         control_panel.add(edge_panel, BorderLayout.SOUTH);
414         control_panel.add(both_panel, BorderLayout.CENTER);
415         
416         // set up vertex controls
417         v_color = new JCheckBox("seed highlight");
418         v_color.addActionListener(this);
419         v_stroke = new JCheckBox("stroke highlight on selection");
420         v_stroke.addActionListener(this);
421         v_labels = new JCheckBox("show voltage values");
422         v_labels.addActionListener(this);
423         v_shape = new JCheckBox("shape by degree");
424         v_shape.addActionListener(this);
425         v_size = new JCheckBox("size by voltage");
426         v_size.addActionListener(this);
427         v_size.setSelected(true);
428         v_aspect = new JCheckBox("stretch by degree ratio");
429         v_aspect.addActionListener(this);
430         v_small = new JCheckBox("filter when degree < " + VertexDisplayPredicate.MIN_DEGREE);
431         v_small.addActionListener(this);
432 
433         vertex_panel.add(v_color);
434         vertex_panel.add(v_stroke);
435         vertex_panel.add(v_labels);
436         vertex_panel.add(v_shape);
437         vertex_panel.add(v_size);
438         vertex_panel.add(v_aspect);
439         vertex_panel.add(v_small);
440         
441         // set up edge controls
442 		JPanel gradient_panel = new JPanel(new GridLayout(1, 0));
443         gradient_panel.setBorder(BorderFactory.createTitledBorder("Edge paint"));
444 		no_gradient = new JRadioButton("Solid color");
445 		no_gradient.addActionListener(this);
446 		no_gradient.setSelected(true);
447 //		gradient_absolute = new JRadioButton("Absolute gradient");
448 //		gradient_absolute.addActionListener(this);
449 		gradient_relative = new JRadioButton("Gradient");
450 		gradient_relative.addActionListener(this);
451 		ButtonGroup bg_grad = new ButtonGroup();
452 		bg_grad.add(no_gradient);
453 		bg_grad.add(gradient_relative);
454 		//bg_grad.add(gradient_absolute);
455 		gradient_panel.add(no_gradient);
456 		//gradientGrid.add(gradient_absolute);
457 		gradient_panel.add(gradient_relative);
458         
459         JPanel shape_panel = new JPanel(new GridLayout(3,2));
460         shape_panel.setBorder(BorderFactory.createTitledBorder("Edge shape"));
461         e_line = new JRadioButton("line");
462         e_line.addActionListener(this);
463         e_line.setSelected(true);
464 //        e_bent = new JRadioButton("bent line");
465 //        e_bent.addActionListener(this);
466         e_wedge = new JRadioButton("wedge");
467         e_wedge.addActionListener(this);
468         e_quad = new JRadioButton("quad curve");
469         e_quad.addActionListener(this);
470         e_cubic = new JRadioButton("cubic curve");
471         e_cubic.addActionListener(this);
472         e_ortho = new JRadioButton("orthogonal");
473         e_ortho.addActionListener(this);
474         ButtonGroup bg_shape = new ButtonGroup();
475         bg_shape.add(e_line);
476 //        bg.add(e_bent);
477         bg_shape.add(e_wedge);
478         bg_shape.add(e_quad);
479         bg_shape.add(e_ortho);
480         bg_shape.add(e_cubic);
481         shape_panel.add(e_line);
482 //        shape_panel.add(e_bent);
483         shape_panel.add(e_wedge);
484         shape_panel.add(e_quad);
485         shape_panel.add(e_cubic);
486         shape_panel.add(e_ortho);
487         fill_edges = new JCheckBox("fill edge shapes");
488         fill_edges.setSelected(false);
489         fill_edges.addActionListener(this);
490         shape_panel.add(fill_edges);
491         shape_panel.setOpaque(true);
492         e_color = new JCheckBox("highlight edge weights");
493         e_color.addActionListener(this);
494         e_labels = new JCheckBox("show edge weight values");
495         e_labels.addActionListener(this);
496         e_uarrow_pred = new JCheckBox("undirected");
497         e_uarrow_pred.addActionListener(this);
498         e_darrow_pred = new JCheckBox("directed");
499         e_darrow_pred.addActionListener(this);
500         e_darrow_pred.setSelected(true);
501         e_arrow_centered = new JCheckBox("centered");
502         e_arrow_centered.addActionListener(this);
503         JPanel arrow_panel = new JPanel(new GridLayout(1,0));
504         arrow_panel.setBorder(BorderFactory.createTitledBorder("Show arrows"));
505         arrow_panel.add(e_uarrow_pred);
506         arrow_panel.add(e_darrow_pred);
507         arrow_panel.add(e_arrow_centered);
508         
509         e_show_d = new JCheckBox("directed");
510         e_show_d.addActionListener(this);
511         e_show_d.setSelected(true);
512         e_show_u = new JCheckBox("undirected");
513         e_show_u.addActionListener(this);
514         e_show_u.setSelected(true);
515         JPanel show_edge_panel = new JPanel(new GridLayout(1,0));
516         show_edge_panel.setBorder(BorderFactory.createTitledBorder("Show edges"));
517         show_edge_panel.add(e_show_u);
518         show_edge_panel.add(e_show_d);
519         
520         shape_panel.setAlignmentX(Component.LEFT_ALIGNMENT);
521         edge_panel.add(shape_panel);
522         gradient_panel.setAlignmentX(Component.LEFT_ALIGNMENT);
523         edge_panel.add(gradient_panel);
524         show_edge_panel.setAlignmentX(Component.LEFT_ALIGNMENT);
525         edge_panel.add(show_edge_panel);
526         arrow_panel.setAlignmentX(Component.LEFT_ALIGNMENT);
527         edge_panel.add(arrow_panel);
528         
529         e_color.setAlignmentX(Component.LEFT_ALIGNMENT);
530         edge_panel.add(e_color);
531         e_labels.setAlignmentX(Component.LEFT_ALIGNMENT);
532         edge_panel.add(e_labels);
533 
534         // set up zoom controls
535         zoom_at_mouse = new JCheckBox("<html><center>zoom at mouse<p>(wheel only)</center></html>");
536         zoom_at_mouse.addActionListener(this);
537         zoom_at_mouse.setSelected(true);
538         
539         final ScalingControl scaler = new CrossoverScalingControl();
540 
541         JButton plus = new JButton("+");
542         plus.addActionListener(new ActionListener() {
543             public void actionPerformed(ActionEvent e) {
544                 scaler.scale(vv, 1.1f, vv.getCenter());
545             }
546         });
547         JButton minus = new JButton("-");
548         minus.addActionListener(new ActionListener() {
549             public void actionPerformed(ActionEvent e) {
550                 scaler.scale(vv, 1/1.1f, vv.getCenter());
551             }
552         });
553 
554         JPanel zoomPanel = new JPanel();
555         zoomPanel.setBorder(BorderFactory.createTitledBorder("Zoom"));
556         plus.setAlignmentX(Component.CENTER_ALIGNMENT);
557         zoomPanel.add(plus);
558         minus.setAlignmentX(Component.CENTER_ALIGNMENT);
559         zoomPanel.add(minus);
560         zoom_at_mouse.setAlignmentX(Component.CENTER_ALIGNMENT);
561         zoomPanel.add(zoom_at_mouse);
562         
563         JPanel fontPanel = new JPanel();
564         // add font and zoom controls to center panel
565         font = new JCheckBox("bold text");
566         font.addActionListener(this);
567         font.setAlignmentX(Component.CENTER_ALIGNMENT);
568         fontPanel.add(font);
569         
570         both_panel.add(zoomPanel);
571         both_panel.add(fontPanel);
572         
573         JComboBox modeBox = gm.getModeComboBox();
574         modeBox.setAlignmentX(Component.CENTER_ALIGNMENT);
575         JPanel modePanel = new JPanel(new BorderLayout()) {
576             public Dimension getMaximumSize() {
577                 return getPreferredSize();
578             }
579         };
580         modePanel.setBorder(BorderFactory.createTitledBorder("Mouse Mode"));
581         modePanel.add(modeBox);
582         JPanel comboGrid = new JPanel(new GridLayout(0,1));
583         comboGrid.add(modePanel);
584         fontPanel.add(comboGrid);
585         
586         
587         JComboBox cb = new JComboBox();
588         cb.addItem(Renderer.VertexLabel.Position.N);
589         cb.addItem(Renderer.VertexLabel.Position.NE);
590         cb.addItem(Renderer.VertexLabel.Position.E);
591         cb.addItem(Renderer.VertexLabel.Position.SE);
592         cb.addItem(Renderer.VertexLabel.Position.S);
593         cb.addItem(Renderer.VertexLabel.Position.SW);
594         cb.addItem(Renderer.VertexLabel.Position.W);
595         cb.addItem(Renderer.VertexLabel.Position.NW);
596         cb.addItem(Renderer.VertexLabel.Position.N);
597         cb.addItem(Renderer.VertexLabel.Position.CNTR);
598         cb.addItem(Renderer.VertexLabel.Position.AUTO);
599         cb.addItemListener(new ItemListener() {
600 			public void itemStateChanged(ItemEvent e) {
601 				Renderer.VertexLabel.Position position = 
602 					(Renderer.VertexLabel.Position)e.getItem();
603 				vv.getRenderer().getVertexLabelRenderer().setPosition(position);
604 				vv.repaint();
605 			}});
606         cb.setSelectedItem(Renderer.VertexLabel.Position.SE);
607         JPanel positionPanel = new JPanel();
608         positionPanel.setBorder(BorderFactory.createTitledBorder("Label Position"));
609         positionPanel.add(cb);
610 
611         comboGrid.add(positionPanel);
612 
613     }
614     
615     public void actionPerformed(ActionEvent e)
616     {
617         AbstractButton source = (AbstractButton)e.getSource();
618         if (source == v_color)
619         {
620             seedDrawColor.setSeedColoring(source.isSelected());
621             seedFillColor.setSeedColoring(source.isSelected());
622         }
623         else if (source == e_color)
624         {
625             ewcs.setWeighted(source.isSelected());
626         }
627         else if (source == v_stroke) 
628         {
629             vsh.setHighlight(source.isSelected());
630         }
631         else if (source == v_labels)
632         {
633             if (source.isSelected())
634                 vv.getRenderContext().setVertexLabelTransformer(vs);
635             else
636                 vv.getRenderContext().setVertexLabelTransformer(vs_none);
637         }
638         else if (source == e_labels)
639         {
640             if (source.isSelected())
641                 vv.getRenderContext().setEdgeLabelTransformer(es);
642             else
643                 vv.getRenderContext().setEdgeLabelTransformer(es_none);
644         }
645         else if (source == e_uarrow_pred)
646         {
647             show_arrow.showUndirected(source.isSelected());
648         }
649         else if (source == e_darrow_pred)
650         {
651             show_arrow.showDirected(source.isSelected());
652         }
653         else if (source == e_arrow_centered)
654         {
655         	if(source.isSelected()) 
656         	{
657         		vv.getRenderer().getEdgeRenderer().setEdgeArrowRenderingSupport(new CenterEdgeArrowRenderingSupport());
658         	} 
659         	else
660         	{
661         		vv.getRenderer().getEdgeRenderer().setEdgeArrowRenderingSupport(new BasicEdgeArrowRenderingSupport());
662         	}
663         }
664         else if (source == font)
665         {
666             vff.setBold(source.isSelected());
667             eff.setBold(source.isSelected());
668         }
669         else if (source == v_shape)
670         {
671             vssa.useFunnyShapes(source.isSelected());
672         }
673         else if (source == v_size)
674         {
675             vssa.setScaling(source.isSelected());
676         }
677         else if (source == v_aspect)
678         {
679             vssa.setStretching(source.isSelected());
680         }
681         else if (source == e_line) 
682         {
683             if(source.isSelected())
684             {
685                 vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line<Integer,Number>());
686             }
687         }
688         else if (source == e_ortho)
689         {
690             if (source.isSelected())
691                 vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Orthogonal<Integer,Number>());
692         }
693         else if (source == e_wedge)
694         {
695             if (source.isSelected())
696                 vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Wedge<Integer,Number>(10));
697         }
698 //        else if (source == e_bent) 
699 //        {
700 //            if(source.isSelected())
701 //            {
702 //                vv.getRenderContext().setEdgeShapeFunction(new EdgeShape.BentLine());
703 //            }
704 //        }
705         else if (source == e_quad) 
706         {
707             if(source.isSelected())
708             {
709                 vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.QuadCurve<Integer,Number>());
710             }
711         }
712         else if (source == e_cubic) 
713         {
714             if(source.isSelected())
715             {
716                 vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.CubicCurve<Integer,Number>());
717             }
718         }
719        else if (source == e_show_d)
720         {
721             show_edge.showDirected(source.isSelected());
722         }
723         else if (source == e_show_u)
724         {
725             show_edge.showUndirected(source.isSelected());
726         }
727         else if (source == v_small)
728         {
729             show_vertex.filterSmall(source.isSelected());
730         }
731         else if(source == zoom_at_mouse)
732         {
733             gm.setZoomAtMouse(source.isSelected());
734         } 
735         else if (source == no_gradient) {
736 			if (source.isSelected()) {
737 				gradient_level = GRADIENT_NONE;
738 			}
739 //		} else if (source == gradient_absolute) {
740 //			if (source.isSelected()) {
741 //				gradient_level = GRADIENT_ABSOLUTE;
742 //			}
743 		} 
744         else if (source == gradient_relative) {
745 			if (source.isSelected()) {
746 				gradient_level = GRADIENT_RELATIVE;
747 			}
748 		}
749         else if (source == fill_edges)
750         {
751         	if(source.isSelected()) {
752         		vv.getRenderContext().setEdgeFillPaintTransformer( edgeFillPaint );
753         	} else {
754         		vv.getRenderContext().setEdgeFillPaintTransformer( new ConstantTransformer(null) );
755         	}
756 //            edgePaint.useFill(source.isSelected());
757         }
758         vv.repaint();
759     }
760     
761     private final class SeedDrawColor<V> implements Transformer<V,Paint>
762     {
763         protected PickedInfo<V> pi;
764         protected final static float dark_value = 0.8f;
765         protected final static float light_value = 0.2f;
766         protected boolean seed_coloring;
767         
768         public SeedDrawColor(PickedInfo<V> pi)
769         {
770             this.pi = pi;
771             seed_coloring = false;
772         }
773 
774         public void setSeedColoring(boolean b)
775         {
776             this.seed_coloring = b;
777         }
778         
779         public Paint transform(V v)
780         {
781             return Color.BLACK;
782         }
783         
784 //        public Paint getFillPaint(V v)
785 //        {
786 //            float alpha = transparency.get(v).floatValue();
787 //            if (pi.isPicked(v))
788 //            {
789 //                return new Color(1f, 1f, 0, alpha); 
790 //            }
791 //            else
792 //            {
793 //                if (seed_coloring && seedVertices.contains(v))
794 //                {
795 //                    Color dark = new Color(0, 0, dark_value, alpha);
796 //                    Color light = new Color(0, 0, light_value, alpha);
797 //                    return new GradientPaint( 0, 0, dark, 10, 0, light, true);
798 //                }
799 //                else
800 //                    return new Color(1f, 0, 0, alpha);
801 //            }
802 //                
803 //        }
804     }
805     
806     private final class SeedFillColor<V> implements Transformer<V,Paint>
807     {
808         protected PickedInfo<V> pi;
809         protected final static float dark_value = 0.8f;
810         protected final static float light_value = 0.2f;
811         protected boolean seed_coloring;
812         
813         public SeedFillColor(PickedInfo<V> pi)
814         {
815             this.pi = pi;
816             seed_coloring = false;
817         }
818 
819         public void setSeedColoring(boolean b)
820         {
821             this.seed_coloring = b;
822         }
823         
824 //        public Paint getDrawPaint(V v)
825 //        {
826 //            return Color.BLACK;
827 //        }
828         
829         public Paint transform(V v)
830         {
831             float alpha = transparency.get(v).floatValue();
832             if (pi.isPicked(v))
833             {
834                 return new Color(1f, 1f, 0, alpha); 
835             }
836             else
837             {
838                 if (seed_coloring && seedVertices.contains(v))
839                 {
840                     Color dark = new Color(0, 0, dark_value, alpha);
841                     Color light = new Color(0, 0, light_value, alpha);
842                     return new GradientPaint( 0, 0, dark, 10, 0, light, true);
843                 }
844                 else
845                     return new Color(1f, 0, 0, alpha);
846             }
847                 
848         }
849     }
850 
851     private final static class EdgeWeightStrokeFunction<E>
852     implements Transformer<E,Stroke>
853     {
854         protected static final Stroke basic = new BasicStroke(1);
855         protected static final Stroke heavy = new BasicStroke(2);
856         protected static final Stroke dotted = RenderContext.DOTTED;
857         
858         protected boolean weighted = false;
859         protected Map<E,Number> edge_weight;
860         
861         public EdgeWeightStrokeFunction(Map<E,Number> edge_weight)
862         {
863             this.edge_weight = edge_weight;
864         }
865         
866         public void setWeighted(boolean weighted)
867         {
868             this.weighted = weighted;
869         }
870         
871         public Stroke transform(E e)
872         {
873             if (weighted)
874             {
875                 if (drawHeavy(e))
876                     return heavy;
877                 else
878                     return dotted;
879             }
880             else
881                 return basic;
882         }
883         
884         protected boolean drawHeavy(E e)
885         {
886             double value = edge_weight.get(e).doubleValue();
887             if (value > 0.7)
888                 return true;
889             else
890                 return false;
891         }
892         
893     }
894     
895     private final static class VertexStrokeHighlight<V,E> implements
896     Transformer<V,Stroke>
897     {
898         protected boolean highlight = false;
899         protected Stroke heavy = new BasicStroke(5);
900         protected Stroke medium = new BasicStroke(3);
901         protected Stroke light = new BasicStroke(1);
902         protected PickedInfo<V> pi;
903         protected Graph<V,E> graph;
904         
905         public VertexStrokeHighlight(Graph<V,E> graph, PickedInfo<V> pi)
906         {
907         	this.graph = graph;
908             this.pi = pi;
909         }
910         
911         public void setHighlight(boolean highlight)
912         {
913             this.highlight = highlight;
914         }
915         
916         public Stroke transform(V v)
917         {
918             if (highlight)
919             {
920                 if (pi.isPicked(v))
921                     return heavy;
922                 else
923                 {
924                 	for(V w : graph.getNeighbors(v)) {
925 //                    for (Iterator iter = graph.getNeighbors(v)v.getNeighbors().iterator(); iter.hasNext(); )
926 //                    {
927 //                        Vertex w = (Vertex)iter.next();
928                         if (pi.isPicked(w))
929                             return medium;
930                     }
931                     return light;
932                 }
933             }
934             else
935                 return light; 
936         }
937 
938     }
939     
940     private final static class VertexFontTransformer<V> 
941     	implements Transformer<V,Font>
942     {
943         protected boolean bold = false;
944         Font f = new Font("Helvetica", Font.PLAIN, 12);
945         Font b = new Font("Helvetica", Font.BOLD, 12);
946         
947         public void setBold(boolean bold)
948         {
949             this.bold = bold;
950         }
951         
952         public Font transform(V v)
953         {
954             if (bold)
955                 return b;
956             else
957                 return f;
958         }
959     }
960 
961     private final static class EdgeFontTransformer<E> 
962         implements Transformer<E,Font>
963 {
964     protected boolean bold = false;
965     Font f = new Font("Helvetica", Font.PLAIN, 12);
966     Font b = new Font("Helvetica", Font.BOLD, 12);
967     
968     public void setBold(boolean bold)
969     {
970         this.bold = bold;
971     }
972     
973     public Font transform(E e)
974     {
975         if (bold)
976             return b;
977         else 
978             return f;
979     }
980 }
981     private final static class DirectionDisplayPredicate<V,E> 
982     	implements Predicate<Context<Graph<V,E>,E>>
983     	//extends AbstractGraphPredicate<V,E>
984     {
985         protected boolean show_d;
986         protected boolean show_u;
987         
988         public DirectionDisplayPredicate(boolean show_d, boolean show_u)
989         {
990             this.show_d = show_d;
991             this.show_u = show_u;
992         }
993         
994         public void showDirected(boolean b)
995         {
996             show_d = b;
997         }
998         
999         public void showUndirected(boolean b)
1000         {
1001             show_u = b;
1002         }
1003         
1004         public boolean evaluate(Context<Graph<V,E>,E> context)
1005         {
1006         	Graph<V,E> graph = context.graph;
1007         	E e = context.element;
1008             if (graph.getEdgeType(e) == EdgeType.DIRECTED && show_d) {
1009                 return true;
1010             }
1011             if (graph.getEdgeType(e) == EdgeType.UNDIRECTED && show_u) {
1012                 return true;
1013             }
1014             return false;
1015         }
1016     }
1017     
1018     private final static class VertexDisplayPredicate<V,E>
1019     	implements Predicate<Context<Graph<V,E>,V>>
1020 //    	extends  AbstractGraphPredicate<V,E>
1021     {
1022         protected boolean filter_small;
1023         protected final static int MIN_DEGREE = 4;
1024         
1025         public VertexDisplayPredicate(boolean filter)
1026         {
1027             this.filter_small = filter;
1028         }
1029         
1030         public void filterSmall(boolean b)
1031         {
1032             filter_small = b;
1033         }
1034         
1035         public boolean evaluate(Context<Graph<V,E>,V> context) {
1036         	Graph<V,E> graph = context.graph;
1037         	V v = context.element;
1038 //            Vertex v = (Vertex)arg0;
1039             if (filter_small)
1040                 return (graph.degree(v) >= MIN_DEGREE);
1041             else
1042                 return true;
1043         }
1044     }
1045     
1046     /**
1047      * Controls the shape, size, and aspect ratio for each vertex.
1048      * 
1049      * @author Joshua O'Madadhain
1050      */
1051     private final static class VertexShapeSizeAspect<V,E>
1052     extends AbstractVertexShapeTransformer <V>
1053     implements Transformer<V,Shape>  {
1054     	
1055         protected boolean stretch = false;
1056         protected boolean scale = false;
1057         protected boolean funny_shapes = false;
1058         protected Transformer<V,Double> voltages;
1059         protected Graph<V,E> graph;
1060 //        protected AffineTransform scaleTransform = new AffineTransform();
1061         
1062         public VertexShapeSizeAspect(Graph<V,E> graphIn, Transformer<V,Double> voltagesIn)
1063         {
1064         	this.graph = graphIn;
1065             this.voltages = voltagesIn;
1066             setSizeTransformer(new Transformer<V,Integer>() {
1067 
1068 				public Integer transform(V v) {
1069 		            if (scale)
1070 		                return (int)(voltages.transform(v) * 30) + 20;
1071 		            else
1072 		                return 20;
1073 
1074 				}});
1075             setAspectRatioTransformer(new Transformer<V,Float>() {
1076 
1077 				public Float transform(V v) {
1078 		            if (stretch) {
1079 		                return (float)(graph.inDegree(v) + 1) / 
1080 		                	(graph.outDegree(v) + 1);
1081 		            } else {
1082 		                return 1.0f;
1083 		            }
1084 				}});
1085         }
1086         
1087 		public void setStretching(boolean stretch)
1088         {
1089             this.stretch = stretch;
1090         }
1091         
1092         public void setScaling(boolean scale)
1093         {
1094             this.scale = scale;
1095         }
1096         
1097         public void useFunnyShapes(boolean use)
1098         {
1099             this.funny_shapes = use;
1100         }
1101         
1102         public Shape transform(V v)
1103         {
1104             if (funny_shapes)
1105             {
1106                 if (graph.degree(v) < 5)
1107                 {	
1108                     int sides = Math.max(graph.degree(v), 3);
1109                     return factory.getRegularPolygon(v, sides);
1110                 }
1111                 else
1112                     return factory.getRegularStar(v, graph.degree(v));
1113             }
1114             else
1115                 return factory.getEllipse(v);
1116         }
1117     }
1118     
1119     /**
1120      * a GraphMousePlugin that offers popup
1121      * menu support
1122      */
1123     protected class PopupGraphMousePlugin extends AbstractPopupGraphMousePlugin
1124     	implements MouseListener {
1125         
1126         public PopupGraphMousePlugin() {
1127             this(MouseEvent.BUTTON3_MASK);
1128         }
1129         public PopupGraphMousePlugin(int modifiers) {
1130             super(modifiers);
1131         }
1132         
1133         /**
1134          * If this event is over a Vertex, pop up a menu to
1135          * allow the user to increase/decrease the voltage
1136          * attribute of this Vertex
1137          * @param e
1138          */
1139         @SuppressWarnings("unchecked")
1140         protected void handlePopup(MouseEvent e) {
1141             final VisualizationViewer<Integer,Number> vv = 
1142                 (VisualizationViewer<Integer,Number>)e.getSource();
1143             Point2D p = e.getPoint();//vv.getRenderContext().getBasicTransformer().inverseViewTransform(e.getPoint());
1144             
1145             GraphElementAccessor<Integer,Number> pickSupport = vv.getPickSupport();
1146             if(pickSupport != null) {
1147                 final Integer v = pickSupport.getVertex(vv.getGraphLayout(), p.getX(), p.getY());
1148                 if(v != null) {
1149                     JPopupMenu popup = new JPopupMenu();
1150                     popup.add(new AbstractAction("Decrease Transparency") {
1151                         public void actionPerformed(ActionEvent e) {
1152                         	Double value = Math.min(1, 
1153                         		transparency.get(v).doubleValue()+0.1);
1154                         	transparency.put(v, value);
1155 //                        	transparency.put(v, )transparency.get(v);
1156 //                            MutableDouble value = (MutableDouble)transparency.getNumber(v);
1157 //                            value.setDoubleValue(Math.min(1, value.doubleValue() + 0.1));
1158                             vv.repaint();
1159                         }
1160                     });
1161                     popup.add(new AbstractAction("Increase Transparency"){
1162                         public void actionPerformed(ActionEvent e) {
1163                         	Double value = Math.max(0, 
1164                             		transparency.get(v).doubleValue()-0.1);
1165                             	transparency.put(v, value);
1166 //                            MutableDouble value = (MutableDouble)transparency.getNumber(v);
1167 //                            value.setDoubleValue(Math.max(0, value.doubleValue() - 0.1));
1168                             vv.repaint();
1169                         }
1170                     });
1171                     popup.show(vv, e.getX(), e.getY());
1172                 } else {
1173                     final Number edge = pickSupport.getEdge(vv.getGraphLayout(), p.getX(), p.getY());
1174                     if(edge != null) {
1175                         JPopupMenu popup = new JPopupMenu();
1176                         popup.add(new AbstractAction(edge.toString()) {
1177                             public void actionPerformed(ActionEvent e) {
1178                                 System.err.println("got "+edge);
1179                             }
1180                         });
1181                         popup.show(vv, e.getX(), e.getY());
1182                        
1183                     }
1184                 }
1185             }
1186         }
1187     }
1188     
1189     public class VoltageTips<E>
1190     	implements Transformer<Integer,String> {
1191         
1192         public String transform(Integer vertex) {
1193            return "Voltage:"+voltages.transform(vertex);
1194         }
1195     }
1196     
1197     public class GradientPickedEdgePaintFunction<V,E> extends GradientEdgePaintTransformer<V,E> 
1198     {
1199         private Transformer<E,Paint> defaultFunc;
1200         protected boolean fill_edge = false;
1201         Predicate<Context<Graph<V,E>,E>> selfLoop = new SelfLoopEdgePredicate<V,E>();
1202         
1203         public GradientPickedEdgePaintFunction(Transformer<E,Paint> defaultEdgePaintFunction, 
1204                 VisualizationViewer<V,E> vv) 
1205         {
1206             super(Color.WHITE, Color.BLACK, vv);
1207             this.defaultFunc = defaultEdgePaintFunction;
1208         }
1209         
1210         public void useFill(boolean b)
1211         {
1212             fill_edge = b;
1213         }
1214         
1215         public Paint transform(E e) {
1216             if (gradient_level == GRADIENT_NONE) {
1217                 return defaultFunc.transform(e);
1218             } else {
1219             	return super.transform(e);
1220             }
1221         }
1222         
1223         protected Color getColor2(E e)
1224         {
1225             return vv.getPickedEdgeState().isPicked(e)? Color.CYAN : c2;
1226         }
1227         
1228 //        public Paint getFillPaint(E e)
1229 //        {
1230 //            if (selfLoop.evaluateEdge(vv.getGraphLayout().getGraph(), e) || !fill_edge)
1231 //                return null;
1232 //            else
1233 //                return getDrawPaint(e);
1234 //        }
1235         
1236     }
1237     
1238 }
1239