1
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
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
108 if (n < 2) {
109 pinPoint(plot, event);
110 }
111 }
112
113 public void mouseButton1Dragged(RecordPlot plot, MouseEvent event) {
114
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
175 double alpha = (Math.atan2(dy, dx) + 2*Math.PI) % (2*Math.PI);
176
177
178 double beta = angle.getAngle(alpha);
179
180
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
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
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
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
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
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
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
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
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
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 }