package org.codehaus.mojo.graphing.pomo;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.GridLayout;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Point2D;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButton;
import org.apache.commons.collections.Predicate;
import org.apache.maven.plugin.logging.SystemStreamLog;
import org.codehaus.mojo.graphing.model.GraphModel;
import org.codehaus.mojo.graphing.model.factory.StaticGraphModelFactory;
import edu.uci.ics.jung.algorithms.importance.VoltageRanker;
import edu.uci.ics.jung.graph.DirectedEdge;
import edu.uci.ics.jung.graph.Edge;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.UndirectedEdge;
import edu.uci.ics.jung.graph.Vertex;
import edu.uci.ics.jung.graph.decorators.AbstractVertexShapeFunction;
import edu.uci.ics.jung.graph.decorators.ConstantEdgeStringer;
import edu.uci.ics.jung.graph.decorators.ConstantVertexStringer;
import edu.uci.ics.jung.graph.decorators.DefaultToolTipFunction;
import edu.uci.ics.jung.graph.decorators.EdgeFontFunction;
import edu.uci.ics.jung.graph.decorators.EdgePaintFunction;
import edu.uci.ics.jung.graph.decorators.EdgeShape;
import edu.uci.ics.jung.graph.decorators.EdgeStringer;
import edu.uci.ics.jung.graph.decorators.EdgeStrokeFunction;
import edu.uci.ics.jung.graph.decorators.GradientEdgePaintFunction;
import edu.uci.ics.jung.graph.decorators.NumberEdgeValue;
import edu.uci.ics.jung.graph.decorators.NumberEdgeValueStringer;
import edu.uci.ics.jung.graph.decorators.NumberVertexValue;
import edu.uci.ics.jung.graph.decorators.NumberVertexValueStringer;
import edu.uci.ics.jung.graph.decorators.PickableEdgePaintFunction;
import edu.uci.ics.jung.graph.decorators.UserDatumNumberEdgeValue;
import edu.uci.ics.jung.graph.decorators.UserDatumNumberVertexValue;
import edu.uci.ics.jung.graph.decorators.VertexAspectRatioFunction;
import edu.uci.ics.jung.graph.decorators.VertexFontFunction;
import edu.uci.ics.jung.graph.decorators.VertexPaintFunction;
import edu.uci.ics.jung.graph.decorators.VertexSizeFunction;
import edu.uci.ics.jung.graph.decorators.VertexStringer;
import edu.uci.ics.jung.graph.decorators.VertexStrokeFunction;
import edu.uci.ics.jung.graph.impl.DirectedSparseEdge;
import edu.uci.ics.jung.graph.impl.DirectedSparseGraph;
import edu.uci.ics.jung.graph.impl.DirectedSparseVertex;
import edu.uci.ics.jung.graph.impl.UndirectedSparseEdge;
import edu.uci.ics.jung.graph.predicates.ContainsUserDataKeyVertexPredicate;
import edu.uci.ics.jung.graph.predicates.SelfLoopEdgePredicate;
import edu.uci.ics.jung.random.generators.BarabasiAlbertGenerator;
import edu.uci.ics.jung.utils.MutableDouble;
import edu.uci.ics.jung.utils.PredicateUtils;
import edu.uci.ics.jung.utils.TestGraphs;
import edu.uci.ics.jung.visualization.contrib.*;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.HasGraphLayout;
import edu.uci.ics.jung.visualization.Layout;
import edu.uci.ics.jung.visualization.PickSupport;
import edu.uci.ics.jung.visualization.PickedInfo;
import edu.uci.ics.jung.visualization.PickedState;
import edu.uci.ics.jung.visualization.PluggableRenderer;
import edu.uci.ics.jung.visualization.ShapePickSupport;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.AbstractPopupGraphMousePlugin;
import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ScalingControl;
import edu.uci.ics.jung.visualization.transform.LayoutTransformer;
import edu.uci.ics.jung.visualization.transform.Transformer;
/**
* Shows off some of the capabilities of <code>PluggableRenderer</code>.
* This code provides examples of different ways to provide and
* change the various functions that provide property information
* to the renderer.
* <p/>
* <p>This demo creates a random mixed-mode graph with random edge
* weights using <code>TestGraph.generateMixedRandomGraph</code>.
* It then runs <code>VoltageRanker</code> on this graph, using half
* of the "seed" vertices from the random graph generation as
* voltage sources, and half of them as voltage sinks.</p>
* <p/>
* <p>What the controls do:
* <ul>
* <li/>Mouse controls:
* <ul>
* <li/>If your mouse has a scroll wheel, scrolling forward zooms out and
* scrolling backward zooms in.
* <li/>Left-clicking on a vertex or edge selects it, and unselects all others.
* <li/>Middle-clicking on a vertex or edge toggles its selection state.
* <li/>Right-clicking on a vertex brings up a pop-up menu that allows you to
* increase or decrease that vertex's transparency.
* <li/>Left-clicking on the background allows you to drag the image around.
* <li/>Hovering over a vertex tells you what its voltage is; hovering over an
* edge shows its identity; hovering over the background shows an informational
* message.
* </ul>
* <li/>Vertex stuff:
* <ul>
* <li/>"vertex seed coloring": if checked, the seed vertices are colored blue,
* and all other vertices are colored red. Otherwise, all vertices are colored
* a slightly transparent red (except the currently "picked" vertex, which is
* colored transparent purple).
* <li/>"vertex selection stroke highlighting": if checked, the picked vertex
* and its neighbors are all drawn with heavy borders. Otherwise, all vertices
* are drawn with light borders.
* <li/>"show vertex ranks (voltages)": if checked, each vertex is labeled with its
* calculated 'voltage'. Otherwise, vertices are unlabeled.
* <li/>"vertex degree shapes": if checked, vertices are drawn with a polygon with
* number of sides proportional to its degree. Otherwise, vertices are drawn
* as ellipses.
* <li/>"vertex voltage size": if checked, vertices are drawn with a size
* proportional to their voltage ranking. Otherwise, all vertices are drawn
* at the same size.
* <li/>"vertex degree ratio stretch": if checked, vertices are drawn with an
* aspect ratio (height/width ratio) proportional to the ratio of their indegree to
* their outdegree. Otherwise, vertices are drawn with an aspect ratio of 1.
* <li/>"filter vertices of degree < 4": if checked, does not display any vertices
* (or their incident edges) whose degree in the original graph is less than 4;
* otherwise, all vertices are drawn.
* </ul>
* <li/>Edge stuff:
* <ul>
* <li/>"edge shape": selects between lines, wedges, quadratic curves, and cubic curves
* for drawing edges.
* <li/>"fill edge shapes": if checked, fills the edge shapes. This will have no effect
* if "line" is selected.
* <li/>"edge paint": selects between solid colored edges, and gradient-painted edges.
* Gradient painted edges are darkest in the middle for undirected edges, and darkest
* at the destination for directed edges.
* <li/>"show edges": only edges of the checked types are drawn.
* <li/>"show arrows": only arrows whose edges are of the checked types are drawn.
* <li/>"edge weight highlighting": if checked, edges with weight greater than
* a threshold value are drawn using thick solid lines, and other edges are drawn
* using thin gray dotted lines. (This combines edge stroke and paint.) Otherwise,
* all edges are drawn with thin solid lines.
* <li/>"show edge weights": if checked, edges are labeled with their weights.
* Otherwise, edges are not labeled.
* </ul>
* <li/>Miscellaneous (center panel)
* <ul>
* <li/>"bold text": if checked, all vertex and edge labels are drawn using a
* boldface font. Otherwise, a normal-weight font is used. (Has no effect if
* no labels are currently visible.)
* <li/>zoom controls:
* <ul>
* <li/>"+" zooms in, "-" zooms out
* <li/>"zoom at mouse (wheel only)": if checked, zooming (using the mouse
* scroll wheel) is centered on the location of the mouse pointer; otherwise,
* it is centered on the center of the visualization pane.
* </ul>
* </ul>
* </p>
*
* @author Danyel Fisher, Joshua O'Madadhain
* modified by Tom Nelson for zoom/pan with the mouse wheel and mouse
*/
public class Pomo extends JApplet implements ActionListener
{
protected JCheckBox v_color;
protected JCheckBox e_color;
protected JCheckBox v_stroke;
protected JCheckBox e_uarrow_pred;
protected JCheckBox e_darrow_pred;
protected JCheckBox v_shape;
protected JCheckBox v_size;
protected JCheckBox v_aspect;
protected JCheckBox v_labels;
protected JRadioButton e_line;
protected JRadioButton e_bent;
protected JRadioButton e_wedge;
protected JRadioButton e_quad;
protected JRadioButton e_cubic;
protected JCheckBox e_labels;
protected JCheckBox font;
protected JCheckBox e_show_d;
protected JCheckBox e_show_u;
protected JCheckBox v_small;
protected JCheckBox zoom_at_mouse;
protected JCheckBox fill_edges;
protected JRadioButton no_gradient;
// protected JRadioButton gradient_absolute;
protected JRadioButton gradient_relative;
protected static final int GRADIENT_NONE = 0;
protected static final int GRADIENT_RELATIVE = 1;
// protected static final int GRADIENT_ABSOLUTE = 2;
protected static int gradient_level = GRADIENT_NONE;
protected PluggableRenderer pr;
protected SeedColor vcf;
protected EdgeWeightStrokeFunction ewcs;
protected VertexStrokeHighlight vsh;
protected VertexStringer vs;
protected VertexStringer vs_none;
protected EdgeStringer es;
protected EdgeStringer es_none;
protected FontHandler ff;
protected VertexShapeSizeAspect vssa;
protected DirectionDisplayPredicate show_edge;
protected DirectionDisplayPredicate show_arrow;
protected VertexDisplayPredicate show_vertex;
protected GradientPickedEdgePaintFunction edgePaint;
protected final static Object VOLTAGE_KEY = "voltages";
protected final static Object TRANSPARENCY = "transparency";
protected NumberEdgeValue edge_weight = new UserDatumNumberEdgeValue( "edge_weight" );
protected NumberVertexValue voltages = new UserDatumNumberVertexValue( VOLTAGE_KEY );
protected NumberVertexValue transparency = new UserDatumNumberVertexValue( TRANSPARENCY );
protected VisualizationViewer vv;
protected DefaultModalGraphMouse gm;
protected Transformer affineTransformer;
public void start()
{
getContentPane().add( startFunction() );
}
public static void main( String[] s )
{
JFrame jf = new JFrame();
jf.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
JPanel jp = new Pomo().startFunction();
jf.getContentPane().add( jp );
jf.pack();
jf.show();
}
public Pomo()
{
}
public JPanel startFunction()
{
Graph g = getGraph();
pr = new PluggableRenderer();
//Layout layout = new SpringLayout( g );
//Layout layout = new TreeLayout( g );
Layout layout = new KKLayout( g );
//Layout layout = new DAGLayout( g );
vv = new VisualizationViewer( layout, pr );
// add Shape based pick support
vv.setPickSupport( new ShapePickSupport() );
PickedState picked_state = vv.getPickedState();
affineTransformer = vv.getLayoutTransformer();
// create decorators
vcf = new SeedColor( picked_state );
ewcs = new EdgeWeightStrokeFunction( edge_weight );
vsh = new VertexStrokeHighlight( picked_state );
ff = new FontHandler();
vs_none = new ConstantVertexStringer( null );
es_none = new ConstantEdgeStringer( null );
vssa = new VertexShapeSizeAspect( voltages );
show_edge = new DirectionDisplayPredicate( true, true );
show_arrow = new DirectionDisplayPredicate( true, false );
show_vertex = new VertexDisplayPredicate( false );
// uses a gradient edge if unpicked, otherwise uses picked selection
edgePaint = new GradientPickedEdgePaintFunction( new PickableEdgePaintFunction( picked_state, Color.black, Color.cyan ),
vv, vv, picked_state );
pr.setVertexPaintFunction( vcf );
pr.setVertexStrokeFunction( vsh );
pr.setVertexStringer( vs_none );
pr.setVertexFontFunction( ff );
pr.setVertexShapeFunction( vssa );
pr.setVertexIncludePredicate( show_vertex );
pr.setEdgePaintFunction( edgePaint );
pr.setEdgeStringer( es_none );
pr.setEdgeFontFunction( ff );
pr.setEdgeStrokeFunction( ewcs );
pr.setEdgeIncludePredicate( show_edge );
pr.setEdgeShapeFunction( new EdgeShape.Line() );
pr.setEdgeArrowPredicate( show_arrow );
JPanel jp = new JPanel();
jp.setLayout( new BorderLayout() );
vv.setBackground( Color.white );
GraphZoomScrollPane scrollPane = new GraphZoomScrollPane( vv );
jp.add( scrollPane );
gm = new DefaultModalGraphMouse();
vv.setGraphMouse( gm );
gm.add( new PopupGraphMousePlugin() );
addBottomControls( jp );
vssa.setScaling( true );
vv.setToolTipFunction( new VoltageTips() );
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>" );
return jp;
}
protected Graph getGraph()
{
StaticGraphModelFactory graphfactory = new StaticGraphModelFactory(new SystemStreamLog());
GraphModel gmodel = graphfactory.getGraphModel("graph-model-dom4j.xml");
Graph graph = new DirectedSparseGraph();
Map vertexMap = new HashMap();
Iterator it = gmodel.getEdgesIterator();
while(it.hasNext())
{
org.codehaus.mojo.graphing.model.Edge edge = (org.codehaus.mojo.graphing.model.Edge) it.next();
Vertex vparent = (Vertex) vertexMap.get(edge.getNode1().getId());
if(vparent == null) {
vparent = graph.addVertex(new DirectedSparseVertex());
transparency.setNumber( vparent, new MutableDouble( 0.9 ) );
voltages.setNumber(vparent, new Double(0.9));
vertexMap.put(edge.getNode1().getId(), vparent);
}
Vertex vchild = (Vertex) vertexMap.get(edge.getNode2().getId());
if(vchild == null) {
vchild = graph.addVertex(new DirectedSparseVertex());
transparency.setNumber( vchild, new MutableDouble( 0.9 ) );
voltages.setNumber(vchild, new Double(0.9));
vertexMap.put(edge.getNode2().getId(), vchild);
}
Edge e = graph.addEdge(new DirectedSparseEdge(vparent, vchild));
}
return graph;
}
/**
* Generates a mixed-mode random graph, runs VoltageRanker on it, and
* returns the resultant graph.
*/
public Graph getGraphOld()
{
Graph g = TestGraphs.generateMixedRandomGraph( edge_weight, 20 );
vs = new NumberVertexValueStringer( voltages );
es = new NumberEdgeValueStringer( edge_weight );
// collect the seeds used to define the random graph
Collection seeds = PredicateUtils.getVertices( g,
new ContainsUserDataKeyVertexPredicate( BarabasiAlbertGenerator.SEED ) );
if ( seeds.size() < 2 )
System.out.println( "need at least 2 seeds (one source, one sink)" );
// use these seeds as source and sink vertices, run VoltageRanker
boolean source = true;
Set sources = new HashSet();
Set sinks = new HashSet();
for ( Iterator iter = seeds.iterator(); iter.hasNext(); )
{
if ( source )
sources.add( iter.next() );
else
sinks.add( iter.next() );
source = !source;
}
VoltageRanker vr = new VoltageRanker( edge_weight, voltages, 100, 0.01 );
vr.calculateVoltages( g, sources, sinks );
Set verts = g.getVertices();
// assign a transparency value of 0.9 to all vertices
for ( Iterator iter = verts.iterator(); iter.hasNext(); )
{
Vertex v = (Vertex) iter.next();
transparency.setNumber( v, new MutableDouble( 0.9 ) );
}
// add a couple of self-loops (sanity check on rendering)
Vertex v = (Vertex) verts.iterator().next();
Edge e = new DirectedSparseEdge( v, v );
edge_weight.setNumber( e, new Double( Math.random() ) );
g.addEdge( e );
e = new UndirectedSparseEdge( v, v );
edge_weight.setNumber( e, new Double( Math.random() ) );
g.addEdge( e );
return g;
}
/**
* @param jp panel to which controls will be added
*/
protected void addBottomControls( final JPanel jp )
{
final JPanel control_panel = new JPanel();
jp.add( control_panel, BorderLayout.SOUTH );
control_panel.setLayout( new BorderLayout() );
final Box vertex_panel = Box.createVerticalBox();
vertex_panel.setBorder( BorderFactory.createTitledBorder( "Vertices" ) );
final Box edge_panel = Box.createVerticalBox();
edge_panel.setBorder( BorderFactory.createTitledBorder( "Edges" ) );
final Box both_panel = Box.createVerticalBox();
control_panel.add( vertex_panel, BorderLayout.WEST );
control_panel.add( edge_panel, BorderLayout.EAST );
control_panel.add( both_panel, BorderLayout.CENTER );
// set up vertex controls
v_color = new JCheckBox( "vertex seed coloring" );
v_color.addActionListener( this );
v_stroke = new JCheckBox( "<html>vertex selection<p>stroke highlighting</html>" );
v_stroke.addActionListener( this );
v_labels = new JCheckBox( "show vertex ranks (voltages)" );
v_labels.addActionListener( this );
v_shape = new JCheckBox( "vertex degree shapes" );
v_shape.addActionListener( this );
v_size = new JCheckBox( "vertex voltage size" );
v_size.addActionListener( this );
v_size.setSelected( true );
v_aspect = new JCheckBox( "vertex degree ratio stretch" );
v_aspect.addActionListener( this );
v_small = new JCheckBox( "filter vertices of degree < " + VertexDisplayPredicate.MIN_DEGREE );
v_small.addActionListener( this );
vertex_panel.add( v_color );
vertex_panel.add( v_stroke );
vertex_panel.add( v_labels );
vertex_panel.add( v_shape );
vertex_panel.add( v_size );
vertex_panel.add( v_aspect );
vertex_panel.add( v_small );
// set up edge controls
JPanel gradient_panel = new JPanel( new GridLayout( 1, 0 ) );
gradient_panel.setBorder( BorderFactory.createTitledBorder( "Edge paint" ) );
no_gradient = new JRadioButton( "Solid color" );
no_gradient.addActionListener( this );
no_gradient.setSelected( true );
// gradient_absolute = new JRadioButton("Absolute gradient");
// gradient_absolute.addActionListener(this);
gradient_relative = new JRadioButton( "Gradient" );
gradient_relative.addActionListener( this );
ButtonGroup bg_grad = new ButtonGroup();
bg_grad.add( no_gradient );
bg_grad.add( gradient_relative );
//bg_grad.add(gradient_absolute);
gradient_panel.add( no_gradient );
//gradientGrid.add(gradient_absolute);
gradient_panel.add( gradient_relative );
JPanel shape_panel = new JPanel( new GridLayout( 3, 2 ) );
shape_panel.setBorder( BorderFactory.createTitledBorder( "Edge shape" ) );
e_line = new JRadioButton( "line" );
e_line.addActionListener( this );
e_line.setSelected( true );
// e_bent = new JRadioButton("bent line");
// e_bent.addActionListener(this);
e_wedge = new JRadioButton( "wedge" );
e_wedge.addActionListener( this );
e_quad = new JRadioButton( "quad curve" );
e_quad.addActionListener( this );
e_cubic = new JRadioButton( "cubic curve" );
e_cubic.addActionListener( this );
ButtonGroup bg_shape = new ButtonGroup();
bg_shape.add( e_line );
// bg.add(e_bent);
bg_shape.add( e_wedge );
bg_shape.add( e_quad );
bg_shape.add( e_cubic );
shape_panel.add( e_line );
// shape_panel.add(e_bent);
shape_panel.add( e_wedge );
shape_panel.add( e_quad );
shape_panel.add( e_cubic );
fill_edges = new JCheckBox( "fill edge shapes" );
fill_edges.setSelected( false );
fill_edges.addActionListener( this );
shape_panel.add( fill_edges );
shape_panel.setOpaque( true );
e_color = new JCheckBox( "edge weight highlighting" );
e_color.addActionListener( this );
e_labels = new JCheckBox( "show edge weights" );
e_labels.addActionListener( this );
e_uarrow_pred = new JCheckBox( "undirected" );
e_uarrow_pred.addActionListener( this );
e_darrow_pred = new JCheckBox( "directed" );
e_darrow_pred.addActionListener( this );
e_darrow_pred.setSelected( true );
JPanel arrow_panel = new JPanel( new GridLayout( 1, 0 ) );
arrow_panel.setBorder( BorderFactory.createTitledBorder( "Show arrows" ) );
arrow_panel.add( e_uarrow_pred );
arrow_panel.add( e_darrow_pred );
e_show_d = new JCheckBox( "directed" );
e_show_d.addActionListener( this );
e_show_d.setSelected( true );
e_show_u = new JCheckBox( "undirected" );
e_show_u.addActionListener( this );
e_show_u.setSelected( true );
JPanel show_edge_panel = new JPanel( new GridLayout( 1, 0 ) );
show_edge_panel.setBorder( BorderFactory.createTitledBorder( "Show edges" ) );
show_edge_panel.add( e_show_u );
show_edge_panel.add( e_show_d );
shape_panel.setAlignmentX( Component.LEFT_ALIGNMENT );
edge_panel.add( shape_panel );
gradient_panel.setAlignmentX( Component.LEFT_ALIGNMENT );
edge_panel.add( gradient_panel );
show_edge_panel.setAlignmentX( Component.LEFT_ALIGNMENT );
edge_panel.add( show_edge_panel );
arrow_panel.setAlignmentX( Component.LEFT_ALIGNMENT );
edge_panel.add( arrow_panel );
e_color.setAlignmentX( Component.LEFT_ALIGNMENT );
edge_panel.add( e_color );
e_labels.setAlignmentX( Component.LEFT_ALIGNMENT );
edge_panel.add( e_labels );
// set up zoom controls
zoom_at_mouse = new JCheckBox( "<html><center>zoom at mouse<p>(wheel only)</center></html>" );
zoom_at_mouse.addActionListener( this );
final ScalingControl scaler = new CrossoverScalingControl();
JButton plus = new JButton( "+" );
plus.addActionListener( new ActionListener()
{
public void actionPerformed( ActionEvent e )
{
scaler.scale( vv, 1.1f, vv.getCenter() );
}
} );
JButton minus = new JButton( "-" );
minus.addActionListener( new ActionListener()
{
public void actionPerformed( ActionEvent e )
{
scaler.scale( vv, 0.9f, vv.getCenter() );
}
} );
Box zoomPanel = Box.createVerticalBox();
zoomPanel.setBorder( BorderFactory.createTitledBorder( "Zoom" ) );
plus.setAlignmentX( Component.CENTER_ALIGNMENT );
zoomPanel.add( plus );
minus.setAlignmentX( Component.CENTER_ALIGNMENT );
zoomPanel.add( minus );
zoom_at_mouse.setAlignmentX( Component.CENTER_ALIGNMENT );
zoomPanel.add( zoom_at_mouse );
// add font and zoom controls to center panel
font = new JCheckBox( "bold text" );
font.addActionListener( this );
font.setAlignmentX( Component.CENTER_ALIGNMENT );
both_panel.add( zoomPanel );
both_panel.add( font );
JComboBox modeBox = gm.getModeComboBox();
modeBox.setAlignmentX( Component.CENTER_ALIGNMENT );
JPanel modePanel = new JPanel( new BorderLayout() )
{
public Dimension getMaximumSize()
{
return getPreferredSize();
}
};
modePanel.setBorder( BorderFactory.createTitledBorder( "Mouse Mode" ) );
modePanel.add( modeBox );
both_panel.add( modePanel );
}
public void actionPerformed( ActionEvent e )
{
AbstractButton source = (AbstractButton) e.getSource();
if ( source == v_color )
{
vcf.setSeedColoring( source.isSelected() );
}
else if ( source == e_color )
{
ewcs.setWeighted( source.isSelected() );
}
else if ( source == v_stroke )
{
vsh.setHighlight( source.isSelected() );
}
else if ( source == v_labels )
{
if ( source.isSelected() )
pr.setVertexStringer( vs );
else
pr.setVertexStringer( vs_none );
}
else if ( source == e_labels )
{
if ( source.isSelected() )
pr.setEdgeStringer( es );
else
pr.setEdgeStringer( es_none );
}
else if ( source == e_uarrow_pred )
{
show_arrow.showUndirected( source.isSelected() );
}
else if ( source == e_darrow_pred )
{
show_arrow.showDirected( source.isSelected() );
}
else if ( source == font )
{
ff.setBold( source.isSelected() );
}
else if ( source == v_shape )
{
vssa.useFunnyShapes( source.isSelected() );
}
else if ( source == v_size )
{
vssa.setScaling( source.isSelected() );
}
else if ( source == v_aspect )
{
vssa.setStretching( source.isSelected() );
}
else if ( source == e_line )
{
if ( source.isSelected() )
{
pr.setEdgeShapeFunction( new EdgeShape.Line() );
}
}
else if ( source == e_wedge )
{
if ( source.isSelected() )
pr.setEdgeShapeFunction( new EdgeShape.Wedge( 10 ) );
}
// else if (source == e_bent)
// {
// if(source.isSelected())
// {
// pr.setEdgeShapeFunction(new EdgeShape.BentLine());
// }
// }
else if ( source == e_quad )
{
if ( source.isSelected() )
{
pr.setEdgeShapeFunction( new EdgeShape.QuadCurve() );
}
}
else if ( source == e_cubic )
{
if ( source.isSelected() )
{
pr.setEdgeShapeFunction( new EdgeShape.CubicCurve() );
}
}
else if ( source == e_show_d )
{
show_edge.showDirected( source.isSelected() );
}
else if ( source == e_show_u )
{
show_edge.showUndirected( source.isSelected() );
}
else if ( source == v_small )
{
show_vertex.filterSmall( source.isSelected() );
}
else if ( source == zoom_at_mouse )
{
gm.setZoomAtMouse( source.isSelected() );
}
else if ( source == no_gradient )
{
if ( source.isSelected() )
{
gradient_level = GRADIENT_NONE;
}
// } else if (source == gradient_absolute) {
// if (source.isSelected()) {
// gradient_level = GRADIENT_ABSOLUTE;
// }
}
else if ( source == gradient_relative )
{
if ( source.isSelected() )
{
gradient_level = GRADIENT_RELATIVE;
}
}
else if ( source == fill_edges )
{
edgePaint.useFill( source.isSelected() );
}
vv.repaint();
}
private final class SeedColor implements VertexPaintFunction
{
protected PickedInfo pi;
protected final static float dark_value = 0.8f;
protected final static float light_value = 0.2f;
protected boolean seed_coloring;
public SeedColor( PickedInfo pi )
{
this.pi = pi;
seed_coloring = false;
}
public void setSeedColoring( boolean b )
{
this.seed_coloring = b;
}
public Paint getDrawPaint( Vertex v )
{
return Color.BLACK;
}
public Paint getFillPaint( Vertex v )
{
float alpha = transparency.getNumber( v ).floatValue();
if ( pi.isPicked( v ) )
{
return new Color( 1f, 1f, 0, alpha );
}
else
{
if ( seed_coloring && v.containsUserDatumKey( BarabasiAlbertGenerator.SEED ) )
{
Color dark = new Color( 0, 0, dark_value, alpha );
Color light = new Color( 0, 0, light_value, alpha );
return new GradientPaint( 0, 0, dark, 10, 0, light, true );
}
else
return new Color( 1f, 0, 0, alpha );
}
}
}
private final static class EdgeWeightStrokeFunction
implements EdgeStrokeFunction
{
protected static final Stroke basic = new BasicStroke( 1 );
protected static final Stroke heavy = new BasicStroke( 2 );
protected static final Stroke dotted = PluggableRenderer.DOTTED;
protected boolean weighted = false;
protected NumberEdgeValue edge_weight;
public EdgeWeightStrokeFunction( NumberEdgeValue edge_weight )
{
this.edge_weight = edge_weight;
}
public void setWeighted( boolean weighted )
{
this.weighted = weighted;
}
public Stroke getStroke( Edge e )
{
if ( weighted )
{
if ( drawHeavy( e ) )
return heavy;
else
return dotted;
}
else
return basic;
}
protected boolean drawHeavy( Edge e )
{
double value = edge_weight.getNumber( e ).doubleValue();
if ( value > 0.7 )
return true;
else
return false;
}
}
private final static class VertexStrokeHighlight implements VertexStrokeFunction
{
protected boolean highlight = false;
protected Stroke heavy = new BasicStroke( 5 );
protected Stroke medium = new BasicStroke( 3 );
protected Stroke light = new BasicStroke( 1 );
protected PickedInfo pi;
public VertexStrokeHighlight( PickedInfo pi )
{
this.pi = pi;
}
public void setHighlight( boolean highlight )
{
this.highlight = highlight;
}
public Stroke getStroke( Vertex v )
{
if ( highlight )
{
if ( pi.isPicked( v ) )
return heavy;
else
{
for ( Iterator iter = v.getNeighbors().iterator(); iter.hasNext(); )
{
Vertex w = (Vertex) iter.next();
if ( pi.isPicked( w ) )
return medium;
}
return light;
}
}
else
return light;
}
}
private final static class FontHandler implements VertexFontFunction, EdgeFontFunction
{
protected boolean bold = false;
Font f = new Font( "Helvetica", Font.PLAIN, 12 );
Font b = new Font( "Helvetica", Font.BOLD, 12 );
public void setBold( boolean bold )
{
this.bold = bold;
}
public Font getFont( Vertex v )
{
if ( bold )
return b;
else
return f;
}
public Font getFont( Edge e )
{
if ( bold )
return b;
else
return f;
}
}
private final static class DirectionDisplayPredicate implements Predicate
{
protected boolean show_d;
protected boolean show_u;
public DirectionDisplayPredicate( boolean show_d, boolean show_u )
{
this.show_d = show_d;
this.show_u = show_u;
}
public void showDirected( boolean b )
{
show_d = b;
}
public void showUndirected( boolean b )
{
show_u = b;
}
public boolean evaluate( Object arg0 )
{
if ( arg0 instanceof DirectedEdge && show_d )
return true;
if ( arg0 instanceof UndirectedEdge && show_u )
return true;
return false;
}
}
private final static class VertexDisplayPredicate implements Predicate
{
protected boolean filter_small;
protected final static int MIN_DEGREE = 4;
public VertexDisplayPredicate( boolean filter )
{
this.filter_small = filter;
}
public void filterSmall( boolean b )
{
filter_small = b;
}
public boolean evaluate( Object arg0 )
{
Vertex v = (Vertex) arg0;
if ( filter_small )
return ( v.degree() >= MIN_DEGREE );
else
return true;
}
}
/**
* Controls the shape, size, and aspect ratio for each vertex.
*
* @author Joshua O'Madadhain
*/
private final static class VertexShapeSizeAspect
extends AbstractVertexShapeFunction
implements VertexSizeFunction, VertexAspectRatioFunction
{
protected boolean stretch = false;
protected boolean scale = false;
protected boolean funny_shapes = false;
protected NumberVertexValue voltages;
public VertexShapeSizeAspect( NumberVertexValue voltages )
{
this.voltages = voltages;
setSizeFunction( this );
setAspectRatioFunction( this );
}
public void setStretching( boolean stretch )
{
this.stretch = stretch;
}
public void setScaling( boolean scale )
{
this.scale = scale;
}
public void useFunnyShapes( boolean use )
{
this.funny_shapes = use;
}
public int getSize( Vertex v )
{
if ( scale )
return (int) ( voltages.getNumber( v ).doubleValue() * 30 ) + 20;
else
return 20;
}
public float getAspectRatio( Vertex v )
{
if ( stretch )
return (float) ( v.inDegree() + 1 ) / ( v.outDegree() + 1 );
else
return 1.0f;
}
public Shape getShape( Vertex v )
{
if ( funny_shapes )
{
if ( v.degree() < 5 )
{
int sides = Math.max( v.degree(), 3 );
return factory.getRegularPolygon( v, sides );
}
else
return factory.getRegularStar( v, v.degree() );
}
else
return factory.getEllipse( v );
}
}
/**
* a GraphMousePlugin that offers popup
* menu support
*/
protected class PopupGraphMousePlugin extends AbstractPopupGraphMousePlugin implements MouseListener
{
public PopupGraphMousePlugin()
{
this( MouseEvent.BUTTON3_MASK );
}
public PopupGraphMousePlugin( int modifiers )
{
super( modifiers );
}
/**
* If this event is over a Vertex, pop up a menu to
* allow the user to increase/decrease the voltage
* attribute of this Vertex
*
* @param e
*/
protected void handlePopup( MouseEvent e )
{
final VisualizationViewer vv =
(VisualizationViewer) e.getSource();
Point2D p = vv.inverseViewTransform( e.getPoint() );
PickSupport pickSupport = vv.getPickSupport();
if ( pickSupport != null )
{
final Vertex v = pickSupport.getVertex( p.getX(), p.getY() );
if ( v != null )
{
JPopupMenu popup = new JPopupMenu();
popup.add( new AbstractAction( "Decrease Transparency" )
{
public void actionPerformed( ActionEvent e )
{
MutableDouble value = (MutableDouble) transparency.getNumber( v );
value.setDoubleValue( Math.min( 1, value.doubleValue() + 0.1 ) );
vv.repaint();
}
} );
popup.add( new AbstractAction( "Increase Transparency" )
{
public void actionPerformed( ActionEvent e )
{
MutableDouble value = (MutableDouble) transparency.getNumber( v );
value.setDoubleValue( Math.max( 0, value.doubleValue() - 0.1 ) );
vv.repaint();
}
} );
popup.show( vv, e.getX(), e.getY() );
}
else
{
final Edge edge = pickSupport.getEdge( p.getX(), p.getY() );
if ( edge != null )
{
JPopupMenu popup = new JPopupMenu();
popup.add( new AbstractAction( edge.toString() )
{
public void actionPerformed( ActionEvent e )
{
System.err.println( "got " + edge );
}
} );
popup.show( vv, e.getX(), e.getY() );
}
}
}
}
}
public class VoltageTips extends DefaultToolTipFunction
{
public String getToolTipText( Vertex v )
{
return "Voltage:" + voltages.getNumber( v );
}
public String getToolTipText( Edge edge )
{
return edge.toString();
}
}
public class GradientPickedEdgePaintFunction extends GradientEdgePaintFunction
{
private PickedInfo pi;
private EdgePaintFunction defaultFunc;
private final Predicate self_loop = SelfLoopEdgePredicate.getInstance();
protected boolean fill_edge = false;
public GradientPickedEdgePaintFunction( EdgePaintFunction defaultEdgePaintFunction, HasGraphLayout vv,
LayoutTransformer transformer, PickedInfo pi )
{
super( Color.WHITE, Color.BLACK, vv, transformer );
this.defaultFunc = defaultEdgePaintFunction;
this.pi = pi;
}
public void useFill( boolean b )
{
fill_edge = b;
}
public Paint getDrawPaint( Edge e )
{
if ( gradient_level == GRADIENT_NONE )
{
return defaultFunc.getDrawPaint( e );
}
else
{
return super.getDrawPaint( e );
}
}
protected Color getColor2( Edge e )
{
return pi.isPicked( e ) ? Color.CYAN : c2;
}
public Paint getFillPaint( Edge e )
{
if ( self_loop.evaluate( e ) || !fill_edge )
return null;
else
return getDrawPaint( e );
}
}
}