View Javadoc
1   /*
2    * Copyright (c) 2003, 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   package edu.uci.ics.jung.samples;
10  
11  import java.awt.BorderLayout;
12  import java.awt.Color;
13  import java.awt.Container;
14  import java.awt.Dimension;
15  import java.awt.FontMetrics;
16  import java.awt.Graphics;
17  import java.awt.Graphics2D;
18  import java.awt.GridLayout;
19  import java.awt.Insets;
20  import java.awt.Rectangle;
21  import java.awt.Shape;
22  import java.awt.event.ActionEvent;
23  import java.awt.event.ActionListener;
24  import java.awt.event.ItemEvent;
25  import java.awt.event.ItemListener;
26  import java.awt.geom.AffineTransform;
27  import java.awt.geom.Point2D;
28  import java.awt.geom.Rectangle2D;
29  import java.util.HashMap;
30  import java.util.Map;
31  
32  import javax.swing.BorderFactory;
33  import javax.swing.Box;
34  import javax.swing.ButtonGroup;
35  import javax.swing.Icon;
36  import javax.swing.JApplet;
37  import javax.swing.JButton;
38  import javax.swing.JComponent;
39  import javax.swing.JFrame;
40  import javax.swing.JLabel;
41  import javax.swing.JMenuBar;
42  import javax.swing.JPanel;
43  import javax.swing.JRadioButton;
44  import javax.swing.plaf.basic.BasicLabelUI;
45  
46  import com.google.common.base.Function;
47  import com.google.common.base.Functions;
48  
49  import edu.uci.ics.jung.algorithms.layout.FRLayout;
50  import edu.uci.ics.jung.algorithms.layout.Layout;
51  import edu.uci.ics.jung.algorithms.layout.StaticLayout;
52  import edu.uci.ics.jung.graph.Graph;
53  import edu.uci.ics.jung.graph.SparseGraph;
54  import edu.uci.ics.jung.graph.util.TestGraphs;
55  import edu.uci.ics.jung.visualization.DefaultVisualizationModel;
56  import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
57  import edu.uci.ics.jung.visualization.Layer;
58  import edu.uci.ics.jung.visualization.VisualizationModel;
59  import edu.uci.ics.jung.visualization.VisualizationViewer;
60  import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
61  import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
62  import edu.uci.ics.jung.visualization.control.LensMagnificationGraphMousePlugin;
63  import edu.uci.ics.jung.visualization.control.ModalLensGraphMouse;
64  import edu.uci.ics.jung.visualization.control.ScalingControl;
65  import edu.uci.ics.jung.visualization.decorators.PickableEdgePaintTransformer;
66  import edu.uci.ics.jung.visualization.decorators.PickableVertexPaintTransformer;
67  import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
68  import edu.uci.ics.jung.visualization.picking.PickedState;
69  import edu.uci.ics.jung.visualization.transform.HyperbolicTransformer;
70  import edu.uci.ics.jung.visualization.transform.LayoutLensSupport;
71  import edu.uci.ics.jung.visualization.transform.LensSupport;
72  import edu.uci.ics.jung.visualization.transform.MagnifyTransformer;
73  import edu.uci.ics.jung.visualization.transform.shape.HyperbolicShapeTransformer;
74  import edu.uci.ics.jung.visualization.transform.shape.MagnifyShapeTransformer;
75  import edu.uci.ics.jung.visualization.transform.shape.ViewLensSupport;
76  
77  /**
78   * Demonstrates the use of <code>HyperbolicTransform</code>
79   * and <code>MagnifyTransform</code>
80   * applied to either the model (graph layout) or the view
81   * (VisualizationViewer)
82   * The hyperbolic transform is applied in an elliptical lens
83   * that affects that part of the visualization.
84   * 
85   * @author Tom Nelson
86   * 
87   */
88  @SuppressWarnings("serial")
89  public class LensDemo extends JApplet {
90  
91      /**
92       * the graph
93       */
94      Graph<String,Number> graph;
95      
96      FRLayout<String,Number> graphLayout;
97      
98      /**
99       * a grid shaped graph
100      */
101     Graph<String,Number> grid;
102     
103     Layout<String,Number> gridLayout;
104 
105     /**
106      * the visual component and renderer for the graph
107      */
108     VisualizationViewer<String,Number> vv;
109 
110     /**
111      * provides a Hyperbolic lens for the view
112      */
113     LensSupport hyperbolicViewSupport;
114     /**
115      * provides a magnification lens for the view
116      */
117     LensSupport magnifyViewSupport;
118     
119     /**
120      * provides a Hyperbolic lens for the model
121      */
122     LensSupport hyperbolicLayoutSupport;
123     /**
124      * provides a magnification lens for the model
125      */
126     LensSupport magnifyLayoutSupport;
127     
128     ScalingControl scaler;
129     
130     /**
131      * create an instance of a simple graph with controls to
132      * demo the zoomand hyperbolic features.
133      * 
134      */
135     public LensDemo() {
136         
137         // create a simple graph for the demo
138         graph = TestGraphs.getOneComponentGraph();
139         
140         graphLayout = new FRLayout<String,Number>(graph);
141         graphLayout.setMaxIterations(1000);
142 
143         Dimension preferredSize = new Dimension(600,600);
144         Map<String,Point2D> map = new HashMap<String,Point2D>();
145         Function<String,Point2D> vlf =
146         	Functions.forMap(map);
147         grid = this.generateVertexGrid(map, preferredSize, 25);
148         gridLayout = new StaticLayout<String,Number>(grid, vlf, preferredSize);
149         
150         final VisualizationModel<String,Number> visualizationModel = 
151             new DefaultVisualizationModel<String,Number>(graphLayout, preferredSize);
152         vv =  new VisualizationViewer<String,Number>(visualizationModel, preferredSize);
153 
154         PickedState<String> ps = vv.getPickedVertexState();
155         PickedState<Number> pes = vv.getPickedEdgeState();
156         vv.getRenderContext().setVertexFillPaintTransformer(new PickableVertexPaintTransformer<String>(ps, Color.red, Color.yellow));
157         vv.getRenderContext().setEdgeDrawPaintTransformer(new PickableEdgePaintTransformer<Number>(pes, Color.black, Color.cyan));
158         vv.setBackground(Color.white);
159         
160         vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
161         
162         final Function<? super String,Shape> ovals = vv.getRenderContext().getVertexShapeTransformer();
163         final Function<? super String,Shape> squares = 
164         	Functions.<Shape>constant(new Rectangle2D.Float(-10,-10,20,20));
165 
166         // add a listener for ToolTips
167         vv.setVertexToolTipTransformer(new ToStringLabeller());
168         
169         Container content = getContentPane();
170         GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv);
171         content.add(gzsp);
172         
173         /**
174          * the regular graph mouse for the normal view
175          */
176         final DefaultModalGraphMouse<String,Number> graphMouse
177         	= new DefaultModalGraphMouse<String,Number>();
178 
179         vv.setGraphMouse(graphMouse);
180         vv.addKeyListener(graphMouse.getModeKeyListener());
181         
182         hyperbolicViewSupport = 
183             new ViewLensSupport<String,Number>(vv, new HyperbolicShapeTransformer(vv, 
184             		vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)), 
185                     new ModalLensGraphMouse());
186         hyperbolicLayoutSupport = 
187             new LayoutLensSupport<String,Number>(vv, new HyperbolicTransformer(vv, 
188             		vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT)),
189                     new ModalLensGraphMouse());
190         magnifyViewSupport = 
191             new ViewLensSupport<String,Number>(vv, new MagnifyShapeTransformer(vv,
192             		vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.VIEW)),
193                     new ModalLensGraphMouse(new LensMagnificationGraphMousePlugin(1.f, 6.f, .2f)));
194         magnifyLayoutSupport = 
195             new LayoutLensSupport<String,Number>(vv, new MagnifyTransformer(vv, 
196             		vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT)),
197                     new ModalLensGraphMouse(new LensMagnificationGraphMousePlugin(1.f, 6.f, .2f)));
198         hyperbolicLayoutSupport.getLensTransformer().setLensShape(hyperbolicViewSupport.getLensTransformer().getLensShape());
199         magnifyViewSupport.getLensTransformer().setLensShape(hyperbolicLayoutSupport.getLensTransformer().getLensShape());
200         magnifyLayoutSupport.getLensTransformer().setLensShape(magnifyViewSupport.getLensTransformer().getLensShape());
201         
202         final ScalingControl scaler = new CrossoverScalingControl();
203 
204         JButton plus = new JButton("+");
205         plus.addActionListener(new ActionListener() {
206             public void actionPerformed(ActionEvent e) {
207                 scaler.scale(vv, 1.1f, vv.getCenter());
208             }
209         });
210         JButton minus = new JButton("-");
211         minus.addActionListener(new ActionListener() {
212             public void actionPerformed(ActionEvent e) {
213                 scaler.scale(vv, 1/1.1f, vv.getCenter());
214             }
215         });
216         
217         ButtonGroup radio = new ButtonGroup();
218         JRadioButton normal = new JRadioButton("None");
219         normal.addItemListener(new ItemListener() {
220             public void itemStateChanged(ItemEvent e) {
221                 if(e.getStateChange() == ItemEvent.SELECTED) {
222                     if(hyperbolicViewSupport != null) {
223                         hyperbolicViewSupport.deactivate();
224                     }
225                     if(hyperbolicLayoutSupport != null) {
226                         hyperbolicLayoutSupport.deactivate();
227                     }
228                     if(magnifyViewSupport != null) {
229                         magnifyViewSupport.deactivate();
230                     }
231                     if(magnifyLayoutSupport != null) {
232                         magnifyLayoutSupport.deactivate();
233                     }
234                 }
235             }
236         });
237 
238         final JRadioButton hyperView = new JRadioButton("Hyperbolic View");
239         hyperView.addItemListener(new ItemListener(){
240             public void itemStateChanged(ItemEvent e) {
241                 hyperbolicViewSupport.activate(e.getStateChange() == ItemEvent.SELECTED);
242             }
243         });
244         final JRadioButton hyperModel = new JRadioButton("Hyperbolic Layout");
245         hyperModel.addItemListener(new ItemListener(){
246             public void itemStateChanged(ItemEvent e) {
247                 hyperbolicLayoutSupport.activate(e.getStateChange() == ItemEvent.SELECTED);
248             }
249         });
250         final JRadioButton magnifyView = new JRadioButton("Magnified View");
251         magnifyView.addItemListener(new ItemListener(){
252             public void itemStateChanged(ItemEvent e) {
253                 magnifyViewSupport.activate(e.getStateChange() == ItemEvent.SELECTED);
254             }
255         });
256         final JRadioButton magnifyModel = new JRadioButton("Magnified Layout");
257         magnifyModel.addItemListener(new ItemListener(){
258             public void itemStateChanged(ItemEvent e) {
259                 magnifyLayoutSupport.activate(e.getStateChange() == ItemEvent.SELECTED);
260             }
261         });
262         JLabel modeLabel = new JLabel("     Mode Menu >>");
263         modeLabel.setUI(new VerticalLabelUI(false));
264         radio.add(normal);
265         radio.add(hyperModel);
266         radio.add(hyperView);
267         radio.add(magnifyModel);
268         radio.add(magnifyView);
269         normal.setSelected(true);
270         
271         graphMouse.addItemListener(hyperbolicLayoutSupport.getGraphMouse().getModeListener());
272         graphMouse.addItemListener(hyperbolicViewSupport.getGraphMouse().getModeListener());
273         graphMouse.addItemListener(magnifyLayoutSupport.getGraphMouse().getModeListener());
274         graphMouse.addItemListener(magnifyViewSupport.getGraphMouse().getModeListener());
275         
276         ButtonGroup graphRadio = new ButtonGroup();
277         JRadioButton graphButton = new JRadioButton("Graph");
278         graphButton.setSelected(true);
279         graphButton.addItemListener(new ItemListener() {
280 
281             public void itemStateChanged(ItemEvent e) {
282                 if(e.getStateChange() == ItemEvent.SELECTED) {
283                     visualizationModel.setGraphLayout(graphLayout);
284                     vv.getRenderContext().setVertexShapeTransformer(ovals);
285                     vv.getRenderContext().setVertexLabelTransformer(
286                     	new ToStringLabeller());
287                     vv.repaint();
288                 }
289             }});
290         JRadioButton gridButton = new JRadioButton("Grid");
291         gridButton.addItemListener(new ItemListener() {
292 
293             public void itemStateChanged(ItemEvent e) {
294                 if(e.getStateChange() == ItemEvent.SELECTED) {
295                     visualizationModel.setGraphLayout(gridLayout);
296                     vv.getRenderContext().setVertexShapeTransformer(squares);
297                     vv.getRenderContext().setVertexLabelTransformer(Functions.<String>constant(null));
298                     vv.repaint();
299                 }
300             }});
301         graphRadio.add(graphButton);
302         graphRadio.add(gridButton);
303         
304         JPanel modePanel = new JPanel(new GridLayout(3,1));
305         modePanel.setBorder(BorderFactory.createTitledBorder("Display"));
306         modePanel.add(graphButton);
307         modePanel.add(gridButton);
308 
309         JMenuBar menubar = new JMenuBar();
310         menubar.add(graphMouse.getModeMenu());
311         gzsp.setCorner(menubar);
312         
313 
314         Box controls = Box.createHorizontalBox();
315         JPanel zoomControls = new JPanel(new GridLayout(2,1));
316         zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom"));
317         JPanel hyperControls = new JPanel(new GridLayout(3,2));
318         hyperControls.setBorder(BorderFactory.createTitledBorder("Examiner Lens"));
319         zoomControls.add(plus);
320         zoomControls.add(minus);
321         
322         hyperControls.add(normal);
323         hyperControls.add(new JLabel());
324 
325         hyperControls.add(hyperModel);
326         hyperControls.add(magnifyModel);
327         
328         hyperControls.add(hyperView);
329         hyperControls.add(magnifyView);
330         
331         controls.add(zoomControls);
332         controls.add(hyperControls);
333         controls.add(modePanel);
334         controls.add(modeLabel);
335         content.add(controls, BorderLayout.SOUTH);
336     }
337 
338     private Graph<String,Number> generateVertexGrid(Map<String,Point2D> vlf,
339             Dimension d, int interval) {
340         int count = d.width/interval * d.height/interval;
341         Graph<String,Number> graph = new SparseGraph<String,Number>();
342         for(int i=0; i<count; i++) {
343             int x = interval*i;
344             int y = x / d.width * interval;
345             x %= d.width;
346             
347             Point2D location = new Point2D.Float(x, y);
348             String vertex = "v"+i;
349             vlf.put(vertex, location);
350             graph.addVertex(vertex);
351         }
352         return graph;
353     }
354     
355     static class VerticalLabelUI extends BasicLabelUI
356     {
357     	static {
358     		labelUI = new VerticalLabelUI(false);
359     	}
360     	
361     	protected boolean clockwise;
362     	VerticalLabelUI( boolean clockwise )
363     	{
364     		super();
365     		this.clockwise = clockwise;
366     	}
367     	
368 
369         public Dimension getPreferredSize(JComponent c) 
370         {
371         	Dimension dim = super.getPreferredSize(c);
372         	return new Dimension( dim.height, dim.width );
373         }	
374 
375         private static Rectangle paintIconR = new Rectangle();
376         private static Rectangle paintTextR = new Rectangle();
377         private static Rectangle paintViewR = new Rectangle();
378         private static Insets paintViewInsets = new Insets(0, 0, 0, 0);
379 
380     	public void paint(Graphics g, JComponent c) 
381         {
382 
383         	
384             JLabel label = (JLabel)c;
385             String text = label.getText();
386             Icon icon = (label.isEnabled()) ? label.getIcon() : label.getDisabledIcon();
387 
388             if ((icon == null) && (text == null)) {
389                 return;
390             }
391 
392             FontMetrics fm = g.getFontMetrics();
393             paintViewInsets = c.getInsets(paintViewInsets);
394 
395             paintViewR.x = paintViewInsets.left;
396             paintViewR.y = paintViewInsets.top;
397         	
398         	// Use inverted height & width
399             paintViewR.height = c.getWidth() - (paintViewInsets.left + paintViewInsets.right);
400             paintViewR.width = c.getHeight() - (paintViewInsets.top + paintViewInsets.bottom);
401 
402             paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0;
403             paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0;
404 
405             String clippedText = 
406                 layoutCL(label, fm, text, icon, paintViewR, paintIconR, paintTextR);
407 
408         	Graphics2D g2 = (Graphics2D) g;
409         	AffineTransform tr = g2.getTransform();
410         	if( clockwise )
411         	{
412     	    	g2.rotate( Math.PI / 2 ); 
413         		g2.translate( 0, - c.getWidth() );
414         	}
415         	else
416         	{
417     	    	g2.rotate( - Math.PI / 2 ); 
418         		g2.translate( - c.getHeight(), 0 );
419         	}
420 
421         	if (icon != null) {
422                 icon.paintIcon(c, g, paintIconR.x, paintIconR.y);
423             }
424 
425             if (text != null) {
426                 int textX = paintTextR.x;
427                 int textY = paintTextR.y + fm.getAscent();
428 
429                 if (label.isEnabled()) {
430                     paintEnabledText(label, g, clippedText, textX, textY);
431                 }
432                 else {
433                     paintDisabledText(label, g, clippedText, textX, textY);
434                 }
435             }
436         	
437         	
438         	g2.setTransform( tr );
439         }
440     }
441 
442     public static void main(String[] args) {
443         JFrame f = new JFrame();
444         f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
445         f.getContentPane().add(new LensDemo());
446         f.pack();
447         f.setVisible(true);
448     }
449 }