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