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 static org.vectomatic.common.model.geometry.Ellipse.K;
21  
22  import java.util.Iterator;
23  import java.util.List;
24  
25  import org.vectomatic.common.model.IShapeVisitor;
26  import org.vectomatic.common.model.Shape;
27  import org.vectomatic.common.model.geometry.BezierSegment;
28  import org.vectomatic.common.model.geometry.BoundingBox;
29  import org.vectomatic.common.model.geometry.Ellipse;
30  import org.vectomatic.common.model.geometry.LineSegment;
31  import org.vectomatic.common.model.geometry.Path;
32  import org.vectomatic.common.model.geometry.Point;
33  import org.vectomatic.common.model.geometry.Polyline;
34  import org.vectomatic.common.model.geometry.Rect;
35  import org.vectomatic.common.model.geometry.Segment;
36  import org.vectomatic.common.model.geometry.ShapeGroup;
37  import org.vectomatic.common.model.geometry.TransformMatrix;
38  
39  /**
40   * Shape visitor implementation to perform pick correlation
41   * on the 2D model
42   */
43  public class PickVisitor implements IShapeVisitor {
44  	private DrawingView _view;
45  	private Point _p, _p0, _p1, _p2, _p3;
46  	private TransformMatrix _m;
47  	private TransformMatrix[] _stack;
48  	private int _index;
49  	private boolean _picked;
50  	
51  	public PickVisitor(DrawingView view) {
52  		_view = view;
53  		_m = new TransformMatrix();
54  		_stack = new TransformMatrix[8];
55  		_p0 = new Point();
56  		_p1 = new Point();
57  		_p2 = new Point();
58  		_p3 = new Point();
59  	}
60  
61  	/**
62  	 * Returns the picked shape
63  	 * @param p
64  	 * A picking point, in model coordinates
65  	 * @param shapes
66  	 * A list of shapes to pick from
67  	 * @return
68  	 * The picked shape, or null if none is picked.
69  	 */
70  	public Shape pick(Point p, Iterator<Shape> shapes) {
71  		_p = p;
72  		_picked = false;
73  		while(shapes.hasNext()) {
74  			Shape shape = shapes.next();
75  			shape.acceptVisitor(this);
76  			if (_picked) {
77  				return shape;
78  			}
79  		}
80  		return null;		
81  	}
82  
83  
84  	private TransformMatrix pushTransform(TransformMatrix m) {
85  		TransformMatrix currentTransform;
86  		if (_index == 0) {
87  			_stack[_index] = m;
88  			currentTransform = m;
89  		} else {
90  			// Grow the stack if too small
91  			if (_index >= _stack.length) {
92  				TransformMatrix[] stack = new TransformMatrix[2 * _stack.length];
93  				for (int i = 0; i < _stack.length; i++) {
94  					stack[i] = _stack[i];
95  				}
96  				_stack = stack;
97  			}
98  			// Allocate a stack slot if not allocated yet. Otherwise reuse it
99  			if (_stack[_index] == null) {
100 				_stack[_index] = new TransformMatrix();
101 			}
102 			currentTransform = m.preMultiply(_stack[_index - 1], _stack[_index]);
103 		}
104 		_index++;
105 		return currentTransform;
106 	}
107 	
108 	private void popTransform() {
109 		_index--;
110 	}
111 	
112 	public void visitEllipse(Ellipse ellipse) {
113 		BoundingBox bbox = ellipse.getBoundingBox();
114 		TransformMatrix m = pushTransform(ellipse.getTransform());
115 		_view.beginPath();
116 		_p0.x = -1f;
117 		_p0.y = 0f;
118 		_p0.transform(m, _p1);
119 		_view.moveTo(_p1.x, _p1.y);
120 
121 		_p0.x = -1f;
122 		_p0.y = -K;
123 		_p0.transform(m, _p1);
124 		_p0.x = -K;
125 		_p0.y = -1f;
126 		_p0.transform(m, _p2);
127 		_p0.x = 0f;
128 		_p0.y = -1f;
129 		_p0.transform(m, _p3);
130 		_view.bezierCurveTo(_p1.x, _p1.y, _p2.x, _p2.y, _p3.x, _p3.y);
131 		
132 		_p0.x = K;
133 		_p0.y = -1f;
134 		_p0.transform(m, _p1);
135 		_p0.x = 1f;
136 		_p0.y = -K;
137 		_p0.transform(m, _p2);
138 		_p0.x = 1f;
139 		_p0.y = 0f;
140 		_p0.transform(m, _p3);
141 		_view.bezierCurveTo(_p1.x, _p1.y, _p2.x, _p2.y, _p3.x, _p3.y);
142 
143 		_p0.x = 1f;
144 		_p0.y = K;
145 		_p0.transform(m, _p1);
146 		_p0.x = K;
147 		_p0.y = 1f;
148 		_p0.transform(m, _p2);
149 		_p0.x = 0f;
150 		_p0.y = 1f;
151 		_p0.transform(m, _p3);
152 		_view.bezierCurveTo(_p1.x, _p1.y, _p2.x, _p2.y, _p3.x, _p3.y);
153 		
154 		_p0.x = -K;
155 		_p0.y = bbox.ymax;
156 		_p0.transform(m, _p1);
157 		_p0.x = -1f;
158 		_p0.y = K;
159 		_p0.transform(m, _p2);
160 		_p0.x = -1f;
161 		_p0.y = 0f;
162 		_p0.transform(m, _p3);
163 		_view.bezierCurveTo(_p1.x, _p1.y, _p2.x, _p2.y, _p3.x, _p3.y);
164 
165 		_view.closePath();
166 		_picked = _view.isPointInPath(_p.x, _p.y);
167 		popTransform();
168 	}
169 
170 	public void visitPolyline(Polyline polyline) {
171 		TransformMatrix m = pushTransform(polyline.getTransform());
172 		Point[] pts = polyline.getVertices();
173 
174 		_view.beginPath();
175 		pts[0].transform(m, _p0);
176 		_view.moveTo(_p0.x, _p0.y);
177 		for (int i = 1; i < pts.length; i++) {
178 			pts[i].transform(m, _p0);
179 			_view.lineTo(_p0.x, _p0.y);
180 		}
181 		if (!polyline.isClosed()) {
182 			float d = _view.convertToReferenceLength(3);
183 			_m.translation(d, d);
184 			m.preMultiply(_m, _m);
185 			for (int i = pts.length - 1; i >= 0; i--) {
186 				pts[i].transform(_m, _p0);
187 				_view.lineTo(_p0.x, _p0.y);
188 			}
189 			pts[0].transform(_m, _p0);
190 			_view.lineTo(_p0.x, _p0.y);
191 		}
192 		_view.closePath();
193 		_picked = _view.isPointInPath(_p.x, _p.y);
194 		
195 		popTransform();
196 	}
197 
198 	public void visitRect(Rect rect) {
199 		TransformMatrix m = pushTransform(rect.getTransform());
200 
201 		_view.beginPath();
202 		_p0.x = -1f;
203 		_p0.y = -1f;
204 		_p0.transform(m, _p1);
205 		_view.moveTo(_p1.x, _p1.y);
206 
207 		_p0.x = 1f;
208 		_p0.y = -1f;
209 		_p0.transform(m, _p2);
210 		_view.lineTo(_p2.x, _p2.y);
211 		
212 		_p0.x = 1f;
213 		_p0.y = 1f;
214 		_p0.transform(m, _p2);
215 		_view.lineTo(_p2.x, _p2.y);
216 		
217 		_p0.x = -1f;
218 		_p0.y = 1f;
219 		_p0.transform(m, _p2);
220 		_view.lineTo(_p2.x, _p2.y);
221 		
222 		_view.lineTo(_p1.x, _p1.y);
223 		
224 		_view.closePath();
225 		_picked = _view.isPointInPath(_p.x, _p.y);
226 		popTransform();
227 	}
228 
229 	public void visitShapeGroup(ShapeGroup group) {
230 		pushTransform(group.getTransform());
231 		List<Shape> shapes = group.getShapes();
232 		for (int i = 0, size = shapes.size(); i < size && !_picked; i++) {
233 			Shape shape = shapes.get(i);
234 			shape.acceptVisitor(this);
235 		}
236 		popTransform();
237 	}	
238 	
239 	public void visitPath(Path path) {
240 		List<Segment> segments = path.getSegments();
241 		if (path.isClosed()) {
242 			TransformMatrix m = pushTransform(path.getTransform());
243 			_view.beginPath();
244 			for (int i = 0, size = segments.size(); i < size; i++) {
245 				Segment segment = segments.get(i);
246 				if (i == 0) {
247 					_view.beginPath();
248 					segment.getStartPoint().transform(m, _p0);
249 					_view.moveTo(_p0.x, _p0.y);
250 				}
251 				if (segment instanceof LineSegment) {
252 					segment.getEndPoint().transform(m, _p0);
253 					_view.lineTo(_p0.x, _p0.y);
254 				} else {
255 					BezierSegment bezierSegment = (BezierSegment)segment;
256 					bezierSegment.getStartControlPoint().transform(m, _p0);
257 					bezierSegment.getEndControlPoint().transform(m, _p1);
258 					bezierSegment.getEndPoint().transform(m, _p2);
259 					_view.bezierCurveTo(_p0.x, _p0.y, _p1.x, _p1.y, _p2.x, _p2.y);
260 				}
261 			}
262 			_view.closePath();
263 			_picked = _view.isPointInPath(_p.x, _p.y);
264 			popTransform();	
265 			
266 		} else {
267 			TransformMatrix m = pushTransform(path.getTransform().invert(new TransformMatrix()));
268 			float dmax = _view.convertToReferenceLength(3);
269 			dmax *= dmax;
270 			_p.transform(m, _p0);
271 			for (int i = 0, size = segments.size(); i < size; i++) {
272 				Segment segment = segments.get(i);
273 				if (segment.squaredDistanceToPoint(_p0) < dmax) {
274 					_picked = true;
275 					break;
276 				}
277 			}			
278 			popTransform();	
279 		}
280 	}
281 }