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.view;
19  
20  import java.util.Iterator;
21  
22  import org.vectomatic.client.rep.RepApplication;
23  import org.vectomatic.client.rep.controller.Compass;
24  import org.vectomatic.client.rep.controller.IController;
25  import org.vectomatic.client.rep.controller.RepresentationController;
26  import org.vectomatic.common.model.DrawingModel;
27  import org.vectomatic.common.model.IStyleVisitor;
28  import org.vectomatic.common.model.Shape;
29  import org.vectomatic.common.model.geometry.Point;
30  import org.vectomatic.common.model.geometry.TransformMatrix;
31  import org.vectomatic.common.model.style.Color;
32  import org.vectomatic.common.model.style.NoneStyle;
33  
34  import com.google.gwt.user.client.ui.KeyboardListener;
35  import com.google.gwt.user.client.ui.MouseListenerAdapter;
36  import com.google.gwt.user.client.ui.MouseWheelListener;
37  import com.google.gwt.user.client.ui.MouseWheelVelocity;
38  import com.google.gwt.user.client.ui.Widget;
39  import com.gwt.components.client.Canvas;
40  import com.gwtext.client.widgets.Resizable;
41  import com.gwtext.client.widgets.ResizableConfig;
42  import com.gwtext.client.widgets.event.ResizableListenerAdapter;
43  
44  /**
45   * Class to render the geometry. This is where most
46   * of the action occurs. Events are caught and routed
47   * the to controllers. Rendering is done.
48   */
49  public class DrawingView extends Canvas {
50  	private static final int MARGIN = 5;
51  	private float _r;
52  	private Point _s;
53  	private Point _t;
54  	private transient boolean _dirty;
55  	private transient TransformMatrix _m;
56  	private TransformMatrix _mInv;
57  	
58  	private RepresentationController _repController;
59  	private IController _controller;
60  	private RenderVisitor _renderer;
61  	private PickVisitor _picker;
62  	private Cursor _cursor;
63  	private DrawingModel _model;
64  	private Compass _compass;
65  	private boolean _compassMode;
66  	private IStyleVisitor _strokeStyleVisitor = new IStyleVisitor() {
67  
68  		public void visitColor(Color color) {
69  			String style = color.toString();
70  			if (!style.equals(getStrokeStyle())) {
71  				setStrokeStyle(style);
72  			}
73  		}
74  
75  		public void visitNoneStyle(NoneStyle none) {
76  			// TODO Auto-generated method stub
77  			
78  		}
79  		
80  	};
81  	private IStyleVisitor _fillStyleVisitor = new IStyleVisitor() {
82  
83  		public void visitColor(Color color) {
84  			String style = color.toString();
85  			if (!style.equals(getFillStyle())) {
86  				setFillStyle(style);
87  			}
88  		}
89  
90  		public void visitNoneStyle(NoneStyle none) {
91  			// TODO Auto-generated method stub
92  			
93  		}
94  		
95  	};
96  		
97  	public DrawingView(RepresentationController repController, DrawingModel model, int width, int height) {
98  		super(width, height);
99  		saveContext();
100 		_t = new Point();
101 		_s = new Point(Point.UNIT);
102 		_m = new TransformMatrix();
103 
104 		_repController = repController;
105 		_model = model;
106 		_mInv = new TransformMatrix();
107 		_compass = new Compass(this);
108 		_compass.setPosition(new Point(width - 70, 50));
109 		_compass.setRadius(45);
110 		_renderer = new RenderVisitor(this);
111 		_picker = new PickVisitor(this);
112 		addMouseListener(new MouseListenerAdapter() {
113 			Point p = new Point();
114 			@Override
115 			public void onMouseDown(Widget sender, int x, int y) {
116 				p.x = x;
117 				p.y = y;
118 				//RepApplication.app.debugArea.setText(toModelCoordinates(p, new Point()).toString());
119 				if (_compass.pick(p) != Compass.NONE) {
120 					_compassMode = true;
121 					_compass.mouseDown(p);
122 				} else {
123 					_compassMode = false;
124 					//p.transform(_mInv);
125 					RepApplication.app.debugPrint("x=" + p.x + " y=" + p.y);
126 					_controller.mouseDown(DrawingView.this, p, modifiers);
127 				}
128 			}
129 			@Override
130 			public void onMouseMove(Widget sender, int x, int y) {
131 				p.x = x;
132 				p.y = y;
133 				if (_compassMode) {
134 					_compass.mouseMove(p);
135 				} else {
136 					_compass.pick(p);
137 					//p.transform(_mInv);
138 					_controller.mouseMove(DrawingView.this, p, modifiers);					
139 				}
140 			}
141 			@Override
142 			public void onMouseUp(Widget sender, int x, int y) {
143 				p.x = x;
144 				p.y = y;
145 				if (_compassMode) {
146 					_compass.mouseUp();
147 					_compassMode = false;
148 				} else {
149 					//p.transform(_mInv);
150 					_controller.mouseUp(DrawingView.this, p, modifiers);
151 				}
152 			}
153 		});
154 		addKeyboardListener(new KeyboardListener() {
155 			public void onKeyDown(Widget sender, char keyCode, int modifiers) {
156 				_controller.keyDown(DrawingView.this,keyCode, modifiers);
157 			}
158 			public void onKeyPress(Widget sender, char keyCode, int modifiers) {
159 				_controller.keyPress(DrawingView.this,keyCode, modifiers);
160 			}
161 			public void onKeyUp(Widget sender, char keyCode, int modifiers) {
162 				_controller.keyUp(DrawingView.this,keyCode, modifiers);
163 			}
164 		});
165 		addMouseWheelListener(new MouseWheelListener() {
166 			Point p = new Point();
167 			public void onMouseWheel(Widget sender, MouseWheelVelocity velocity) {
168 				int delta = 10 * velocity.getDeltaY();
169 				if ((modifiers & KeyboardListener.MODIFIER_CTRL) != 0) {
170 					if (delta > 0) {
171 						float s = _compass.getScaling();
172 						_compass.setScaling(s + 0.1f * (1 - s));
173 					} else {
174 						float s = _compass.getScaling();
175 						_compass.setScaling(s - 0.1f * s);
176 					}
177 				} else {
178 					if ((modifiers & KeyboardListener.MODIFIER_ALT) != 0) {
179 						p.x = delta;
180 						p.y = 0f;
181 					} else {
182 						p.x = 0f;
183 						p.y = delta;
184 					}
185 					_compass.translate(p);
186 				}
187 			}
188 		});
189 	}
190 
191 	public float getRotation() {
192 		return _r;
193 	}
194 	
195 	public void setRotation(float r) {
196 		_r = r;
197 		_dirty = true;
198 	}
199 	
200 	public Point getScaling(Point s) {
201 		_s.copyTo(s);
202 		return s;
203 	}
204 	
205 	public void setScaling(Point s) {
206 		s.copyTo(_s);
207 		_dirty = true;
208 	}
209 	
210 	public Point getTranslation(Point t) {
211 		_t.copyTo(t);
212 		return t;
213 	}
214 	
215 	public void setTranslation(Point t) {
216 		t.copyTo(_t);
217 		_dirty = true;
218 	}
219 	
220 	public TransformMatrix getTransform() {
221 		if (_dirty) {
222 			updateTransform();
223 		}
224 		return _m;
225 	}
226 	public TransformMatrix getInverseTransform() {
227 		return _mInv;
228 	}
229 	
230 	protected void updateTransform() {
231 		float cos = (float)Math.cos(_r);
232 		float sin = (float)Math.sin(_r);
233 		float x1 = _t.x - 0.5f * getWidth();
234 		float y1 = _t.y - 0.5f * getHeight();
235 		_m.m11 = _s.x * cos; _m.m12 = -_s.y * sin; _m.m13 = _s.x * cos  * x1 - _s.y * sin * y1 + 0.5f * getWidth();
236 		_m.m21 = _s.x * sin; _m.m22 =  _s.y * cos; _m.m23 = _s.x * sin  * x1 + _s.y * cos * y1 + 0.5f * getHeight();
237 		_dirty = false;
238 	}
239 	
240 	public void reset() {
241 		setTranslation(Point.ZERO);
242 		_compass.setRotation(0f);
243 		_compass.setScaling(0.5f);
244 	}
245 
246 	public void render() {
247 		// Restore a clean context with no transformation of any kind set
248 		// Clear the screen area
249 		restoreContext();
250 		clearRect(0, 0, getWidth(), getHeight());
251 		
252 		// Set-up a coordinate system transformed according to
253 		// the specifications defined by the compass
254 		saveContext();
255 		translate(0.5f * getWidth(), 0.5f * getHeight());
256 		rotate(_r);
257 		scale(_s.x, _s.y);
258 		translate(-0.5f * getWidth() + _t.x, -0.5f * getHeight() + _t.y);
259 		getTransform().invert(_mInv);
260 		
261 		// Draw the shaded canvas outline
262 		int width = _repController.getCurrentRep().getWidth();
263 		int height = _repController.getCurrentRep().getHeight();
264 		setFillStyle(Color.DARK_GRAY.toString());
265 		fillRect(MARGIN, height, width, MARGIN);
266 		fillRect(width, MARGIN, MARGIN, height);
267 		setStrokeStyle(Color.LIGHT_GRAY.toString());
268 		strokeRect(0, 0, width, height);
269 
270 		// Draw the model geometry
271 		Iterator<Shape> iterator = _model.iterator();
272 		while (iterator.hasNext()) {
273 			Shape shape = iterator.next();
274 			shape.acceptVisitor(_renderer);
275 		}
276 		// Draw extra transient geometry currently owned by the current controller
277 		getController().render(this);
278 		
279 		// Restore the clean context with no transformation of any kind set
280 		// Use this context to draw the compass
281 		restoreContext();
282 		_compass.render();
283 		
284 		// Save the clean context for the next frame
285 		saveContext();
286 	}
287 	
288 	/**
289 	 * Converts a distance in the screen coordinate system
290 	 * into a distance in the reference coordinate system
291 	 * @param pixels
292 	 * @return
293 	 */
294 	public float convertToReferenceLength(int screenLength) {
295 		Point p0 = new Point(0, 0);
296 		p0.transform(_mInv);
297 		Point p1 = new Point(screenLength, 0);
298 		p1.transform(_mInv);
299 		return p1.subtract(p0).length();
300 	}
301 	
302 	public Point vectorToRefCoordinates(Point v) {
303 		return vectorToRefCoordinates(v, v);
304 	}
305 	
306 	public Point vectorToRefCoordinates(Point v, Point dest) {
307 		v.transform(_mInv, dest);
308 		dest.x -= _mInv.m13;
309 		dest.y -= _mInv.m23;
310 		return dest;
311 	}
312 	
313 	/**
314 	 * Converts a point from screen coordinates to model coordinates
315 	 * @param p
316 	 * The point to transfom (in screen coordinates)
317 	 * @return
318 	 * The transformed point
319 	 */
320 	public Point toModelCoordinates(Point p) {
321 		return toModelCoordinates(p, p);
322 	}
323 	
324 	public Point toModelCoordinates(Point p, Point dest) {
325 		return p.transform(_mInv, dest);
326 	}
327 	
328 	public Point toScreenCoordinates(Point p) {
329 		return toScreenCoordinates(p, p);
330 	}
331 
332 	/**
333 	 * Converts a point from model coordinates to screen coordinates
334 	 * @param p
335 	 * The point to transfom (in model coordinates)
336 	 * @return
337 	 * The transformed point
338 	 */
339 	public Point toScreenCoordinates(Point p, Point dest) {
340 		return p.transform(_m, dest);
341 	}
342 
343 	public void setCursor(Cursor cursor) {
344 		if (_cursor != cursor) {
345 			if (_cursor != null) {
346 				removeStyleName(_cursor.getCssText());
347 			}
348 			if (cursor != null) {
349 				addStyleName(cursor.getCssText());
350 			}
351 			_cursor = cursor;
352 		}
353 	}
354 
355 	public void setController(IController controller) {
356 		_controller = controller;
357 	}
358 
359 	public IController getController() {
360 		return _controller;
361 	}
362 	
363 	public RenderVisitor getRenderer() {
364 		return _renderer;
365 	}
366 	
367 	public PickVisitor getPicker() {
368 		return _picker;
369 	}
370 	
371 	public IStyleVisitor getStrokeStyleVisitor() {
372 		return _strokeStyleVisitor;
373 	}
374 	public IStyleVisitor getFillStyleVisitor() {
375 		return _fillStyleVisitor;
376 	}
377 	
378     @Override
379 	protected void onLoad() {
380 		ResizableConfig config = new ResizableConfig();
381 		config.setPinned(true);
382 		config.setHandles(Resizable.SOUTH_EAST);
383 		config.setMinWidth(320);
384 		config.setMinHeight(200);
385 		final Resizable resizable = new Resizable(getElement(), config);
386 		resizable.addListener(new ResizableListenerAdapter() {
387 			public void onResize(Resizable self, int width, int height) {
388 				setWidth(width);
389 				setHeight(height);
390 				// Add an extra saveContext here. This is because resizing causes
391 				// all the push contexts to be lost (and DrawingView expects to have
392 				// a context pushed when render is invoked)
393 				saveContext();
394 				_compass.setPosition(new Point(width - 70, 50));
395 				_compass.setRadius(45);
396 			}
397 		});    	
398     }
399 }