View Javadoc

1   /*
2    * Copyright (c) 2005, the JUNG Project and the Regents of the University 
3    * of California
4    * All rights reserved.
5    *
6    * This software is open-source under the BSD license; see either
7    * "license.txt" or
8    * http://jung.sourceforge.net/license.txt for a description.
9    * Created on Mar 8, 2005
10   *
11   */
12  package edu.uci.ics.jung.visualization.control;
13  
14  import java.awt.BasicStroke;
15  import java.awt.Color;
16  import java.awt.Cursor;
17  import java.awt.Dimension;
18  import java.awt.Graphics2D;
19  import java.awt.Point;
20  import java.awt.RenderingHints;
21  import java.awt.Toolkit;
22  import java.awt.event.MouseEvent;
23  import java.awt.event.MouseListener;
24  import java.awt.event.MouseMotionListener;
25  import java.awt.geom.Point2D;
26  import java.awt.image.BufferedImage;
27  import java.util.Collections;
28  
29  import edu.uci.ics.jung.visualization.Layer;
30  import edu.uci.ics.jung.visualization.VisualizationViewer;
31  import edu.uci.ics.jung.visualization.transform.MutableTransformer;
32  
33  /** 
34   * RotatingGraphMouse provides the abiity to rotate the graph using
35   * the mouse. By default, it is activated by mouse button one drag
36   * with the shift key pressed. The modifiers can be overridden so that
37   * a different mouse/key combination activates the rotation
38   * 
39   * @author Tom Nelson
40   */
41  public class RotatingGraphMousePlugin extends AbstractGraphMousePlugin
42      implements MouseListener, MouseMotionListener {
43  
44  	/**
45  	 * create an instance with default modifier values
46  	 */
47  	public RotatingGraphMousePlugin() {
48  	    this(MouseEvent.BUTTON1_MASK | MouseEvent.SHIFT_MASK);
49  	}
50  
51  	/**
52  	 * create an instance with passed zoom in/out values
53  	 * @param modifiers the event modifiers to trigger rotation
54  	 */
55  	public RotatingGraphMousePlugin(int modifiers) {
56  	    super(modifiers);
57  	    Dimension cd = Toolkit.getDefaultToolkit().getBestCursorSize(16,16);
58          BufferedImage cursorImage = 
59          		new BufferedImage(cd.width,cd.height,BufferedImage.TYPE_INT_ARGB);
60          Graphics2D g = cursorImage.createGraphics();
61          g.addRenderingHints(Collections.singletonMap(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
62          g.setColor(new Color(0,0,0,0));
63          g.fillRect(0,0,16,16);
64  
65          int left = 0;
66          int top = 0;
67          int right = 15;
68          int bottom = 15;
69          
70          g.setColor(Color.white);
71          g.setStroke(new BasicStroke(3));
72          // top bent line
73          g.drawLine(left+2,top+6,right/2+1,top);
74          g.drawLine(right/2+1,top,right-2,top+5);
75          // bottom bent line
76          g.drawLine(left+2,bottom-6,right/2,bottom);
77          g.drawLine(right/2,bottom,right-2,bottom-6);
78          // top arrow
79          g.drawLine(left+2,top+6,left+5,top+6);
80          g.drawLine(left+2,top+6,left+2,top+3);
81          // bottom arrow
82          g.drawLine(right-2,bottom-6,right-6,bottom-6);
83          g.drawLine(right-2, bottom-6,right-2,bottom-3);
84  
85          
86          g.setColor(Color.black);
87          g.setStroke(new BasicStroke(1));
88          // top bent line
89          g.drawLine(left+2,top+6,right/2+1,top);
90          g.drawLine(right/2+1,top,right-2,top+5);
91          // bottom bent line
92          g.drawLine(left+2,bottom-6,right/2,bottom);
93          g.drawLine(right/2,bottom,right-2,bottom-6);
94          // top arrow
95          g.drawLine(left+2,top+6,left+5,top+6);
96          g.drawLine(left+2,top+6,left+2,top+3);
97          // bottom arrow
98          g.drawLine(right-2,bottom-6,right-6,bottom-6);
99          g.drawLine(right-2, bottom-6,right-2,bottom-3);
100 
101         g.dispose();
102         
103         cursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(), "RotateCursor");
104 	}
105 
106     /**
107      * save the 'down' point and check the modifiers. If the
108      * modifiers are accepted, set the cursor to the 'hand' cursor
109 	 * @param e the event
110 	 */
111     public void mousePressed(MouseEvent e) {
112         VisualizationViewer vv = (VisualizationViewer)e.getSource();
113            boolean accepted = checkModifiers(e);
114            down = e.getPoint();
115           if(accepted) {
116                vv.setCursor(cursor);
117            }
118     }
119     
120 	/**
121      * unset the down point and change the cursor back to the default
122 	 */
123     public void mouseReleased(MouseEvent e) {
124         VisualizationViewer vv = (VisualizationViewer)e.getSource();
125         down = null;
126         vv.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
127     }
128     
129     /**
130      * check the modifiers. If accepted, use the mouse drag motion
131      * to rotate the graph
132 	 */
133     public void mouseDragged(MouseEvent e) {
134         if(down == null) return;
135         VisualizationViewer vv = (VisualizationViewer)e.getSource();
136         boolean accepted = checkModifiers(e);
137         if(accepted) {
138             MutableTransformer modelTransformer =
139                 vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT);
140             // rotate
141             vv.setCursor(cursor);
142             
143             Point2D center = vv.getCenter();
144             Point2D q = down;
145             Point2D p = e.getPoint();
146             Point2D v1 = new Point2D.Double(center.getX()-p.getX(), center.getY()-p.getY());
147             Point2D v2 = new Point2D.Double(center.getX()-q.getX(), center.getY()-q.getY());
148             double theta = angleBetween(v1, v2);
149             modelTransformer.rotate(theta, vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, center));
150             down.x = e.getX();
151             down.y = e.getY();
152         
153             e.consume();
154         }
155     }
156     
157     /**
158      * Returns the angle between two vectors from the origin
159      * to points v1 and v2.
160      * @param v1
161      * @param v2
162      * @return
163      */
164     protected double angleBetween(Point2D v1, Point2D v2) {
165         double x1 = v1.getX();
166         double y1 = v1.getY();
167         double x2 = v2.getX();
168         double y2 = v2.getY();
169         // cross product for direction
170         double cross = x1*y2 - x2*y1;
171         int cw = 1;
172         if(cross > 0) {
173             cw = -1;
174         } 
175         // dot product for angle
176         double angle = 
177             cw*Math.acos( ( x1*x2 + y1*y2 ) / 
178                 ( Math.sqrt( x1*x1 + y1*y1 ) * 
179                         Math.sqrt( x2*x2 + y2*y2 ) ) );
180         if(Double.isNaN(angle)) {
181             angle = 0;
182         }
183         return angle;
184     }
185 
186     public void mouseClicked(MouseEvent e) {
187     }
188 
189     public void mouseEntered(MouseEvent e) {
190     }
191 
192     public void mouseExited(MouseEvent e) {
193     }
194 
195     public void mouseMoved(MouseEvent e) {
196     }
197 }