View Javadoc

1   // Copyright 2003-2005, FreeHEP.
2   package hep.wired.interaction;
3   
4   import java.awt.*;
5   import java.awt.event.*;
6   import java.awt.geom.*;
7   import javax.swing.*;
8   
9   import org.freehep.application.Application;
10  import org.freehep.util.DiscreteAngle;
11  
12  import hep.wired.edit.WiredCompoundEdit;
13  import hep.wired.edit.Scale;
14  import hep.wired.edit.Transform2D;
15  import hep.wired.edit.Translate;
16  import hep.wired.edit.Rotate;
17  import hep.wired.feature.Translateable;
18  import hep.wired.services.GraphicsPanel;
19  import hep.wired.services.RecordPlot;
20  import hep.wired.image.WiredBaseImage;
21  
22  /***
23   * Drag/click mouse to define an (angled) rectangle to scale, rotate and translate plot.
24   *
25   * Implements:
26   * 1. CMCMC - Click, Move, Click, Move, Click
27   * 2. CMD - Click, Move, Drag
28   * 3. DMC - Drag, Move, Click
29   * 4. DMD - Drag, Move, Drag
30   *
31   * @author Mark Donszelmann
32   * @version $Id: DragAngledRectangleToScale.java 682 2005-03-14 02:24:52Z duns $
33   */
34  public class DragAngledRectangleToScale extends DefaultInteractionHandler {
35  
36      private static final String name = "Combined Transformation";
37      private static final int cursorSize = 32;
38      private boolean fixedRatio = true;
39  
40      private int n;
41      private int[] x = new int[3];
42      private int[] y = new int[3];
43      private int xp, yp;
44  
45      private DiscreteAngle angle;
46      private Double perpAngle1;
47      private Double perpAngle2;
48  
49      private GeneralPath path;
50  
51      /***
52       * Create a angled rectangle handler to generate scal edits.
53       */
54      public DragAngledRectangleToScale() {
55          super("Drag Angled Rectangle to Scale");
56      }
57  
58      public Icon getIcon(int size) {
59          return WiredBaseImage.getIcon("AngledRectangle%w", size);
60      }    
61  
62      public String getDescription() {
63          return "Click to define first point of angled rectangle.";
64      }
65  
66      public boolean isSupportedBy(GraphicsPanel panel) {
67          return new Scale().isSupportedBy(panel) &&
68                 new Transform2D().isSupportedBy(panel) &&
69                 new Translate().isSupportedBy(panel) &&
70                 new Rotate().isSupportedBy(panel);
71      }
72  
73      public void changeCursor(RecordPlot plot, InputEvent event) {
74          if ((n == 2) && !event.isControlDown()) {
75              // calculate cursor
76              if (x[2] == x[1]) {
77                  plot.setCursor(WiredBaseImage.getBestCursor("YSkewCursor%w", cursorSize, cursorSize));
78              } else if (y[2] == y[1]) {
79                  plot.setCursor(WiredBaseImage.getBestCursor("XSkewCursor%w", cursorSize, cursorSize));
80              } else {
81                  plot.setCursor(WiredBaseImage.getBestCursor("AngledRectangleCursor%w", cursorSize, cursorSize));
82              }
83          } else {
84              plot.setCursor(WiredBaseImage.getBestCursor("AngledRectangleCursor%w", cursorSize, cursorSize));
85          }
86      }
87  
88      public void reset(RecordPlot plot, InputEvent event) {
89          n = 0;
90          plot.drawShape(null);
91          changeCursor(plot, event);
92          Application.getApplication().setStatusMessage(getDescription());
93      }
94  
95      public void mouseEntered(RecordPlot plot, MouseEvent event) {
96          plot.requestFocusInWindow();
97          changeCursor(plot, event);
98      }
99      
100     public void mouseButton1Clicked(RecordPlot plot, MouseEvent event) {
101         definePoint(plot, event);
102         pinPoint(plot, event);
103     }
104 
105     public void mouseButton1DragStarted(RecordPlot plot, MouseEvent event) {
106         definePoint(plot, event);
107         // last point is defined by end of the drag
108         if (n < 2) {
109             pinPoint(plot, event);
110         }
111     }
112 
113     public void mouseButton1Dragged(RecordPlot plot, MouseEvent event) {
114         // keep point moving
115         if (n > 0) {
116             definePoint(plot, event);
117         }
118     }
119 
120     public void mouseButton1DragEnded(RecordPlot plot, MouseEvent event) {
121         definePoint(plot, event);
122         pinPoint(plot, event);
123     }
124 
125     public void mouseMoved(RecordPlot plot, MouseEvent event) {
126         mouseButton1Dragged(plot, event);
127     }
128 
129     public boolean shiftKeyPressed(RecordPlot plot, KeyEvent event) {
130         modifyPoint(plot, event);
131         return true;
132     }
133 
134     public boolean shiftKeyReleased(RecordPlot plot, KeyEvent event) {
135         modifyPoint(plot, event);
136         return true;
137     }
138 
139     public boolean ctrlKeyPressed(RecordPlot plot, KeyEvent event) {
140         modifyPoint(plot, event);
141         return true;
142     }
143 
144     public boolean ctrlKeyReleased(RecordPlot plot, KeyEvent event) {
145         modifyPoint(plot, event);
146         return true;
147     }
148 
149     private void definePoint(RecordPlot plot, MouseEvent event) {
150         xp = event.getX();
151         yp = event.getY();
152         switch(n) {
153             case 0: x[0] = x[1] = x[2] = xp;
154                     y[0] = y[1] = y[2] = yp;
155                     break;
156             case 1: x[1] = x[2] = xp;
157                     y[1] = y[2] = yp;
158                     plot.drawShape(new Line2D.Double(x[0], y[0], x[1], y[1]));
159                     break;
160             case 2: x[2] = xp;
161                     y[2] = yp;
162                     modifyPoint(plot, event);
163                     break;
164             default:finished(plot, event);
165                     break;
166         }
167     }
168 
169     private void modifyPoint(RecordPlot plot, InputEvent event) {
170         if (n != 2) return;
171         if (!event.isControlDown()) {
172             int dx = xp - x[1];
173             int dy = yp - y[1];
174             // calculate drag angle
175             double alpha = (Math.atan2(dy, dx) + 2*Math.PI) % (2*Math.PI);
176 
177             // calculate discrete angle
178             double beta = angle.getAngle(alpha);
179 
180             // calculate length of vector along discrete angle
181             double cosBeta = Math.cos(beta);
182             double sinBeta = Math.sin(beta);
183             double h1 = Math.abs((cosBeta < 0.1) ? dx : dx / cosBeta);
184             double h2 = Math.abs((sinBeta < 0.1) ? dy : dy / sinBeta);
185             double height2 = Math.max(h1, h2);
186 
187             // restrict ratio
188             if (!event.isShiftDown()) {
189                 int wx = x[1] - x[0];
190                 int wy = y[1] - y[0];
191                 double width = Math.sqrt(wx*wx + wy*wy);
192                 double ratio = (double)plot.getHeight() / (double)plot.getWidth();
193                 height2 = ratio*width/2;
194                 fixedRatio = true;
195             } else {
196                 fixedRatio = false;
197             }
198 
199             // calculate point
200             x[2] = (int)(height2 * cosBeta + x[1]);
201             y[2] = (int)(height2 * sinBeta + y[1]);
202         } else {
203             x[2] = xp;
204             y[2] = yp;
205         }
206 
207         // define shape
208         int dx = x[2] - x[1];
209         int dy = y[2] - y[1];
210         path = new GeneralPath();
211         path.moveTo(x[0],    y[0]);
212         path.lineTo(x[1],    y[1]);
213         path.moveTo(x[0]+dx, y[0]+dy);
214         path.lineTo(x[1]+dx, y[1]+dy);
215         path.lineTo(x[1]-dx, y[1]-dy);
216         path.lineTo(x[0]-dx, y[0]-dy);
217         path.closePath();
218         plot.drawShape(path);
219         changeCursor(plot, event);
220     }
221 
222     private void pinPoint(RecordPlot plot, InputEvent event) {
223         switch (n) {
224             case 0:
225                 Application.getApplication().setStatusMessage("Click to define angle of angled rectangle.");
226                 angle = new DiscreteAngle();
227                 angle.addAngle(0);
228                 angle.addAngle(Math.PI/2);
229                 angle.addAngle(Math.PI);
230                 angle.addAngle(3*Math.PI/2);
231                 angle.addAngle(2*Math.PI);
232                 n++;
233                 break;
234             case 1:
235                 Application.getApplication().setStatusMessage("Click to define rectangle, Shift removes ratio constraint, Ctrl removes angle and ratio constraint.");
236                 double perp = (Math.atan2(y[1]-y[0], x[1]-x[0]) + 3*Math.PI/2.0) % (2*Math.PI);
237                 perpAngle1 = angle.addAngle(perp);
238                 perpAngle2 = angle.addAngle((perp+Math.PI) % (2*Math.PI));
239                 n++;
240                 break;
241             case 2:
242                 finished(plot, event);
243                 n = 0;
244                 break;
245             default:
246                 // Should never happen.
247                 break;
248         }
249     }
250 
251     private void finished(RecordPlot plot, InputEvent event) {
252         WiredCompoundEdit edit = new WiredCompoundEdit(name);
253 
254         int dx1 = x[1] - x[0];
255         int dy1 = y[1] - y[0];
256         int dx2 = x[2] - x[1];
257         int dy2 = y[2] - y[1];
258         double w  = Math.sqrt(dx1*dx1 + dy1*dy1);
259         double h  = Math.sqrt(dx2*dx2 + dy2*dy2) * 2;
260 
261         int w2 = plot.getWidth()/2;
262         int h2 = plot.getHeight()/2;
263 
264         if ((w != 0) && (h != 0)) {
265 
266             // center
267             int dx = dx1/2 + x[0] - w2;
268             int dy = dy1/2 + y[0] - h2;
269             Translateable translateable = (Translateable)plot.getGraphicsPanel().getFeature(Translateable.class);
270             double t[] = translateable.getModelTranslation(new double[] { -dx, dy, 0 }, plot.getGraphicsPanel().getViewPort());
271             Translate translate = new Translate(t[0], t[1], t[2], path, 5);
272             Shape shape = translate.createTransformedShape((Component)plot, path);
273             edit.addEdit(translate);
274 
275             // Rotate
276             double alpha = Math.atan2(dy1, dx1);
277             if (alpha > Math.PI/2)   alpha -= Math.PI;
278             if (alpha <= -Math.PI/2) alpha += Math.PI;
279             Rotate rotate = new Rotate(alpha, 0, 0, 1, shape, 5);
280             shape = rotate.createTransformedShape((Component)plot, shape);
281             edit.addEdit(rotate);
282 
283             // Shear
284             double beta = Math.atan2(dy2, dx2);
285             double delta = Math.PI/2.0-beta+alpha;
286             if (delta > Math.PI/2)   delta -= Math.PI;
287             if (delta <= -Math.PI/2) delta += Math.PI;
288             double shx = Math.tan(delta);
289             double esy = h / (h * Math.cos(delta));
290             Transform2D transform = new Transform2D(1, 0, shx, esy, 0, 0, shape, 5);
291             shape = transform.createTransformedShape((Component)plot, shape);
292             edit.addEdit(transform);
293 
294             // Zoom
295             double sx = plot.getWidth() / (w+1);
296             double sy = fixedRatio ? sx : plot.getHeight() / (h+1);
297             double sz = fixedRatio ? sx : Math.min(sx, sy);
298             edit.addEdit(new Scale(sx, sy, sz, shape, 5));
299 
300             // send
301             edit.end();
302             plot.postEdit(edit);
303         }
304 
305         reset(plot, event);
306     }
307 
308     public String toString() {
309         return "Drag angled rectangle to Scale";
310     }
311 }