View Javadoc

1   /**********************************************
2    * Copyright (C) 2009 Lukas Laag
3    * This file is part of Vectomatic.
4    * 
5    * Vectomatic is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    * 
10   * Vectomatic is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   * 
15   * You should have received a copy of the GNU General Public License
16   * along with Vectomatic.  If not, see http://www.gnu.org/licenses/
17   **********************************************/
18  package org.vectomatic.client.rep.controller;
19  
20  import org.vectomatic.client.rep.view.DrawingView;
21  import org.vectomatic.common.model.geometry.Point;
22  import org.vectomatic.common.model.geometry.TransformMatrix;
23  import org.vectomatic.common.model.style.Color;
24  
25  import com.google.gwt.user.client.Timer;
26  
27  /**
28   * Class to manage the 2D compass which enables rotating and scaling the
29   * drawing view
30   */
31  public class Compass {
32  	public static final int NONE = -1;
33  	public static final int TRANSLATE_N = 0;
34  	public static final int TRANSLATE_S = 1;
35  	public static final int TRANSLATE_W = 2;
36  	public static final int TRANSLATE_E = 3;
37  	public static final int ROTATE = 4;
38  	public static final int SCALE = 5;
39  	private static final float A0 = (float)(Math.PI * 2 / 360 * 7);
40  	private static final float A1 = (float)(Math.PI * 2 / 360 * 12);
41  	private static final float A2 = (float)(Math.PI * 2 / 360 * 4);
42  	private static final float W = 0.15f;
43  	private static final Point[] WHEEL = new Point[10];
44  	static {
45  		WHEEL[0] = new Point(
46  			(float)(Math.cos(Math.PI * 1.5 - A0) * (1 - 0.5* W)),
47  			(float)(Math.sin(Math.PI * 1.5 - A0) * (1 - 0.5* W)));			
48  		WHEEL[1] = new Point(
49  			(float)(Math.cos(Math.PI * 1.5 - A1)),
50  			(float)(Math.sin(Math.PI * 1.5 - A1)));
51  		WHEEL[2] = new Point(
52  			(float)(Math.cos(Math.PI * 1.5 + A1)),
53  			(float)(Math.sin(Math.PI * 1.5 + A1)));
54  		WHEEL[3] = new Point(
55  			(float)(Math.cos(Math.PI * 1.5 + A0) * (1 - 0.5* W)),
56  			(float)(Math.sin(Math.PI * 1.5 + A0) * (1 - 0.5* W)));
57  		WHEEL[4] = new Point(
58  			(float)(Math.cos(Math.PI * 1.5 + A1)) * (1 - W),
59  			(float)(Math.sin(Math.PI * 1.5 + A1)) * (1 - W));
60  		WHEEL[5] = new Point(
61  			(float)(Math.cos(Math.PI * 1.5 - A1)) * (1 - W),
62  			(float)(Math.sin(Math.PI * 1.5 - A1)) * (1 - W));
63  		WHEEL[6] = new Point(
64  			(float)(Math.cos(Math.PI * 1.5 - A0 + A2)) * (1 - W),
65  			(float)(Math.sin(Math.PI * 1.5 - A0 + A2)) * (1 - W));
66  		WHEEL[7] = new Point(
67  			(float)(Math.cos(Math.PI * 1.5 - A0 + A2)) * (1 - W),
68  			-(float)(Math.sqrt(1 - Math.cos(Math.PI * 1.5 - A0 + A2) * (1 - W) * Math.cos(Math.PI * 1.5 - A0 + A2) * (1 - W))));
69  		WHEEL[8] = new Point(
70  			(float)(Math.cos(Math.PI * 1.5 + A0 - A2)) * (1 - W),
71  			(float)(Math.sin(Math.PI * 1.5 + A0 - A2)) * (1 - W));
72  		WHEEL[9] = new Point(
73  			(float)(Math.cos(Math.PI * 1.5 + A0 - A2)) * (1 - W),
74  			-(float)(Math.sqrt(1 - Math.cos(Math.PI * 1.5 - A0 + A2) * (1 - W) * Math.cos(Math.PI * 1.5 - A0 + A2) * (1 - W))));
75  	}
76  	private static final int DELAY = 100;
77  
78  	private DrawingView _view;
79  	private Point[] _wheel;
80  	private TransformMatrix _m, _mTmp;
81  	private Point _p1, _p2, _c;
82  	private float _r, _radius, _r0, _r1, _s;
83  	private float[] _x, _y;
84  	private int _pick;
85  	private boolean _isMouseDown;
86  	private Timer _northTimer = new Timer() {
87  		@Override
88  		public void run() {
89  			_p1.x = 0f;
90  			_p1.y = 10f;
91  			translate(_p1);
92  			if (_pick == TRANSLATE_N && _isMouseDown) {
93  				schedule(DELAY);
94  			}
95  		}		
96  	};
97  	private Timer _southTimer = new Timer() {
98  		@Override
99  		public void run() {
100 			_p1.x = 0f;
101 			_p1.y = -10f;
102 			translate(_p1);
103 			if (_pick == TRANSLATE_S && _isMouseDown) {
104 				schedule(DELAY);
105 			}
106 		}		
107 	};
108 	private Timer _westTimer = new Timer() {
109 		@Override
110 		public void run() {
111 			_p1.x = 10f;
112 			_p1.y = 0f;
113 			translate(_p1);
114 			if (_pick == TRANSLATE_W && _isMouseDown) {
115 				schedule(DELAY);
116 			}
117 		}		
118 	};
119 	private Timer _eastTimer = new Timer() {
120 		@Override
121 		public void run() {
122 			_p1.x = -10f;
123 			_p1.y = 0f;
124 			translate(_p1);
125 			if (_pick == TRANSLATE_E && _isMouseDown) {
126 				schedule(DELAY);
127 			}
128 		}		
129 	};
130 	
131 	public Compass(DrawingView view) {
132 		_c = new Point();
133 		_view = view;
134 		_p1 = new Point();
135 		_p2 = new Point();
136 		_m = new TransformMatrix();
137 		_mTmp = new TransformMatrix();
138 		_wheel = new Point[WHEEL.length];
139 		for (int i = 0; i < _wheel.length;i++) {
140 			_wheel[i] = new Point();
141 		}
142 		_x = new float[29];
143 		_y = new float[29];
144 	}
145 	
146 	public void setPosition(Point c) {
147 		c.copyTo(_c);
148 		update();
149 	}
150 	
151 	public Point getPosition() {
152 		return _c;
153 	}
154 	public float getRadius() {
155 		return _radius;
156 	}
157 	public void setRadius(float radius) {
158 		_radius = radius;
159 		update();
160 	}
161 	
162 	private void update() {
163 		_r = 1f;
164 		_s = 1f;
165 		float D = _radius * 0.3f;
166 		float H = _radius * 0.15f;
167 		float E = _radius * 0.02f;
168 		_x[0] = _c.x - D; _y[0] = _c.y - D - E;	// N
169 		_x[1] = _c.x - D; _y[1] = _c.y - D - H;
170 		_x[2] = _c.x; _y[2] = _c.y -D - 2 * H;
171 		_x[3] = _c.x + D; _y[3] = _c.y - D - H;
172 		_x[4] = _c.x + D; _y[4] = _c.y - D - E;
173 		_x[5] = _c.x - D; _y[5] = _c.y + D + E;	// S
174 		_x[6] = _c.x - D; _y[6] = _c.y + D + H;
175 		_x[7] = _c.x; _y[7] = _c.y + D + 2 * H;
176 		_x[8] = _c.x + D; _y[8] = _c.y + D + H;
177 		_x[9] = _c.x + D; _y[9] = _c.y + D + E;
178 		_x[10] = _c.x - D - E; _y[10] = _c.y - D; // W
179 		_x[11] = _c.x - D - H; _y[11] = _c.y - D;
180 		_x[12] = _c.x - D - 2 * H; _y[12] = _c.y;
181 		_x[13] = _c.x - D - H; _y[13] = _c.y + D;
182 		_x[14] = _c.x - D - E; _y[14] = _c.y + D;
183 		_x[15] = _c.x + D + E; _y[15] = _c.y - D; // E
184 		_x[16] = _c.x + D + H; _y[16] = _c.y - D;
185 		_x[17] = _c.x + D + 2 * H; _y[17] = _c.y;
186 		_x[18] = _c.x + D + H; _y[18] = _c.y + D;
187 		_x[19] = _c.x + D + E; _y[19] = _c.y + D;
188 		_x[20] = _c.x + _radius + H; _y[20] = _c.y - _radius; // slider
189 		_x[21] = _c.x + _radius + 2 * H; _y[21] = _c.y - _radius;
190 		_x[22] = _c.x + _radius + 2 * H; _y[22] = _c.y + _radius;
191 		_x[23] = _c.x + _radius + H; _y[23] = _c.y + _radius;
192 		_x[24] = _c.x + _radius + H - E; // slider knob
193 		_x[25] = _c.x + _radius + 2 * H + E;
194 		_x[26] = _c.x + _radius + 3 * H;
195 		_x[27] = _c.x + _radius + 2 * H + E;
196 		_x[28] = _c.x + _radius + H - E;
197 		setRotation(0f);
198 		setScaling(0.5f);
199 		_pick = NONE;		
200 	}
201 	
202 	public void translate(Point p) {
203 		_view.vectorToRefCoordinates(p);
204 		_view.getTranslation(_p2);
205 		_p2.add(p);
206 		_view.setTranslation(_p2);		
207 	}
208 	
209 	public float getRotation() {
210 		return _r;
211 	}
212 	
213 	public void setRotation(float r) {
214 		if (_r != r) {
215 			_r = r;
216 			_m.rotation(r).preMultiply(_mTmp.scaling(_radius, _radius)).preMultiply(_mTmp.translation(_c.x, _c.y));
217 			for (int i = 0; i < WHEEL.length;i++) {
218 				WHEEL[i].transform(_m, _wheel[i]);
219 			}
220 			_view.setRotation(r);
221 		}
222 	}
223 	
224 	public void setScaling(float s) {
225 		if (_s != s) {
226 			_s = s;
227 			float H = 0.15f;
228 			float y = s * ((2 - H) *_radius);
229 			float h = _radius * H;
230 			_y[24] = _c.y - _radius + y;
231 			_y[25] = _c.y - _radius + y;
232 			_y[26] = _c.y - _radius + y + h * 0.5f;
233 			_y[27] = _c.y - _radius + y + h;
234 			_y[28] = _c.y - _radius + y + h;
235 			_p1.x = (s <= 0.5f) ? (0.125f + 1.75f * s) : ( 1 + (s - 0.5f) * 14f); _p1.y = _p1.x;
236 			_view.setScaling(_p1);
237 		}
238 	}
239 	public float getScaling() {
240 		return _s;
241 	}
242 	
243 	public int pick(Point p) {
244 		_pick = NONE;
245 		float d = p.subtract(_c, _p1).length();
246 		if (p.x > _x[0] && p.x < _x[3] && p.y > _y[2] && p.y < _y[0]) {
247 			_pick = TRANSLATE_N;
248 		} else if (p.x > _x[5] && p.x < _x[8] && p.y < _y[7] && p.y > _y[5]) {
249 			_pick = TRANSLATE_S;
250 		} else if (p.x > _x[12] && p.x < _x[10] && p.y > _y[10] && p.y < _y[13]) {
251 			_pick = TRANSLATE_W;
252 		} else if (p.x < _x[17] && p.x > _x[15] && p.y > _y[15] && p.y < _y[18]) {
253 			_pick = TRANSLATE_E;
254 		} else if (d < _radius && d > (1 - W) * _radius) {
255 			_pick = ROTATE;
256 		} else if (p.x > _x[24] && p.x < _x[26] && p.y > _y[24] && p.y < _y[27]) {
257 			_pick = SCALE;
258 		}
259 		return _pick;
260 	}
261 	
262 	public void render() {
263 		_view.beginPath();
264 		_view.moveTo(_wheel[0].x, _wheel[0].y);
265 		_view.lineTo(_wheel[1].x, _wheel[1].y);
266 		_view.arc(_c.x, _c.y, _radius, (float)(Math.PI * 1.5 - A1) + _r, (float)(Math.PI * 1.5 + A1) + _r, true);
267 		_view.lineTo(_wheel[3].x, _wheel[3].y);
268 		_view.lineTo(_wheel[4].x, _wheel[4].y);
269 		_view.arc(_c.x, _c.y, _radius * (1 - W), (float)(Math.PI * 1.5 + A1) + _r, (float)(Math.PI * 1.5 - A1) + _r, false);
270 		_view.lineTo(_wheel[0].x, _wheel[0].y);
271 		_view.moveTo(_wheel[6].x, _wheel[6].y);
272 		_view.lineTo(_wheel[7].x, _wheel[7].y);
273 		_view.lineTo(_wheel[8].x, _wheel[8].y);
274 		_view.lineTo(_wheel[9].x, _wheel[9].y);
275 		_view.setStrokeStyle(Color.BLACK.toString());
276 		_view.setLineJoin("bevel");
277 		_view.setFillStyle("#C3D9FF");
278 		if (_pick == ROTATE) {
279 			_view.fill();
280 		}
281 		_view.stroke();
282 		_view.beginPath();
283 		_view.moveTo(_x[0], _y[0]);
284 		_view.lineTo(_x[1], _y[1]);
285 		_view.lineTo(_x[2], _y[2]);
286 		_view.lineTo(_x[3], _y[3]);
287 		_view.lineTo(_x[4], _y[4]);
288 		_view.lineTo(_x[0], _y[0]);
289 		if (_pick == TRANSLATE_N) {
290 			_view.fill();
291 		}
292 		_view.stroke();
293 		_view.beginPath();
294 		_view.moveTo(_x[5], _y[5]);
295 		_view.lineTo(_x[6], _y[6]);
296 		_view.lineTo(_x[7], _y[7]);
297 		_view.lineTo(_x[8], _y[8]);
298 		_view.lineTo(_x[9], _y[9]);
299 		_view.lineTo(_x[5], _y[5]);
300 		if (_pick == TRANSLATE_S) {
301 			_view.fill();
302 		}
303 		_view.stroke();
304 		_view.beginPath();
305 		_view.moveTo(_x[10], _y[10]);
306 		_view.lineTo(_x[11], _y[11]);
307 		_view.lineTo(_x[12], _y[12]);
308 		_view.lineTo(_x[13], _y[13]);
309 		_view.lineTo(_x[14], _y[14]);
310 		_view.lineTo(_x[10], _y[10]);
311 		if (_pick == TRANSLATE_W) {
312 			_view.fill();
313 		}
314 		_view.stroke();
315 		_view.beginPath();
316 		_view.moveTo(_x[15], _y[15]);
317 		_view.lineTo(_x[16], _y[16]);
318 		_view.lineTo(_x[17], _y[17]);
319 		_view.lineTo(_x[18], _y[18]);
320 		_view.lineTo(_x[19], _y[19]);
321 		_view.lineTo(_x[15], _y[15]);
322 		if (_pick == TRANSLATE_E) {
323 			_view.fill();
324 		}
325 		_view.stroke();
326 		_view.beginPath();
327 		_view.moveTo(_x[20], _y[20]);
328 		_view.lineTo(_x[21], _y[21]);
329 		_view.lineTo(_x[22], _y[22]);
330 		_view.lineTo(_x[23], _y[23]);
331 		_view.lineTo(_x[20], _y[20]);
332 		_view.stroke();
333 		_view.beginPath();
334 		_view.moveTo(_x[24], _y[24]);
335 		_view.lineTo(_x[25], _y[25]);
336 		_view.lineTo(_x[26], _y[26]);
337 		_view.lineTo(_x[27], _y[27]);
338 		_view.lineTo(_x[28], _y[28]);
339 		_view.lineTo(_x[24], _y[24]);
340 		if (_pick != SCALE) {
341 			_view.setFillStyle("rgb(255,255,255)");
342 		}
343 		_view.fill();
344 		_view.stroke();
345 	}
346 
347 	
348 	public void mouseDown(Point p) {
349 		_isMouseDown = true;
350 		switch(_pick) {
351 			case ROTATE:
352 				_r0 = _r;
353 				p.subtract(_c, _p1);
354 				_r1 = (float)(Math.acos(_p1.x / _p1.length()));
355 				if (p.y > _c.y) {
356 					_r1 = (float)(2 * Math.PI - _r1);
357 				}
358 				break;
359 			case TRANSLATE_N:
360 				_northTimer.run();
361 				break;
362 			case TRANSLATE_S:
363 				_southTimer.run();
364 				break;
365 			case TRANSLATE_W:
366 				_westTimer.run();
367 				break;
368 			case TRANSLATE_E:
369 				_eastTimer.run();
370 				break;
371 		}
372 	}
373 
374 	public void mouseMove(Point p) {
375 		if (_isMouseDown) {
376 			if (_pick == ROTATE) {
377 				p.subtract(_c, _p1);
378 				float r2 = (float)(Math.acos(_p1.x / _p1.length()));
379 				if (p.y > _c.y) {
380 					r2 = (float)(2 * Math.PI - r2);
381 				}
382 				setRotation(_r0 - r2 + _r1);
383 				//_area.setText("r1 = " + _r1 + " r2 = " + r2 + " acos = " + (_p.x / _p.length()) + " p = " + p);
384 			} else if (_pick == SCALE) {
385 				float s = 0f;
386 				if (p.y > _c.y - _radius) {
387 					if (p.y < _c.y + _radius) {
388 						s = 0.5f * (p.y - (_c.y - _radius)) / _radius;
389 					} else {
390 						s = 1f;
391 					}
392 				}
393 				setScaling(s);
394 			}
395 		}
396 	}
397 
398 	public void mouseUp() {
399 		_isMouseDown = false;
400 	}
401 
402 }