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.List;
23  
24  import org.vectomatic.common.model.Attribute;
25  import org.vectomatic.common.model.FloatAttributeValue;
26  import org.vectomatic.common.model.IShapeVisitor;
27  import org.vectomatic.common.model.Shape;
28  import org.vectomatic.common.model.geometry.BezierSegment;
29  import org.vectomatic.common.model.geometry.BoundingBox;
30  import org.vectomatic.common.model.geometry.Ellipse;
31  import org.vectomatic.common.model.geometry.LineSegment;
32  import org.vectomatic.common.model.geometry.Path;
33  import org.vectomatic.common.model.geometry.Point;
34  import org.vectomatic.common.model.geometry.Polyline;
35  import org.vectomatic.common.model.geometry.Rect;
36  import org.vectomatic.common.model.geometry.Segment;
37  import org.vectomatic.common.model.geometry.ShapeGroup;
38  import org.vectomatic.common.model.geometry.TransformMatrix;
39  import org.vectomatic.common.model.style.IStyle;
40  
41  /**
42   * Shape visitor implementation to perform rendering
43   * of the 2D model
44   */
45  public class RenderVisitor implements IShapeVisitor {
46  	private class Context {
47  		TransformMatrix m;
48  		Shape shape;
49  		public Context() {
50  			m = new TransformMatrix();
51  		}
52  	}
53  	
54  	private DrawingView _view;
55  	private Point _p0, _p1, _p2, _p3;
56  	private Context[] _stack;
57  	private int _index;
58  
59  	public RenderVisitor(DrawingView view) {
60  		_view = view;
61  		_stack = new Context[8];
62  		_p0 = new Point();
63  		_p1 = new Point();
64  		_p2 = new Point();
65  		_p3 = new Point();
66  	}
67  	
68  	public TransformMatrix pushShape(Shape shape) {
69  		// Grow the context stack if too small
70  		if (_index >= _stack.length) {
71  			Context[] stack = new Context[2 * _stack.length];
72  			for (int i = 0; i < _stack.length; i++) {
73  				stack[i] = _stack[i];
74  			}
75  			_stack = stack;
76  		}
77  		
78  		// Allocate a stack slot if not allocated yet. Otherwise reuse it
79  		if (_stack[_index] == null) {
80  			_stack[_index] = new Context();
81  		}
82  		
83  		TransformMatrix m;
84  		if (_index == 0) {
85  			m = _stack[_index].m = shape.getTransform();
86  		} else {
87  			m = shape.getTransform().preMultiply(_stack[_index - 1].m, _stack[_index].m);
88  		}
89  		_stack[_index].shape = shape;
90  		_index++;
91  		return m;
92  	}
93  	
94  	public void popShape() {
95  		_index--;
96  	}
97  	
98  	public void visitEllipse(Ellipse ellipse) {
99  		BoundingBox bbox = ellipse.getBoundingBox();
100 		TransformMatrix m = pushShape(ellipse);
101 		_view.beginPath();
102 		_p0.x = -1f;
103 		_p0.y = 0f;
104 		_p0.transform(m, _p1);
105 		_view.moveTo(_p1.x, _p1.y);
106 
107 		_p0.x = -1f;
108 		_p0.y = -K;
109 		_p0.transform(m, _p1);
110 		_p0.x = -K;
111 		_p0.y = -1f;
112 		_p0.transform(m, _p2);
113 		_p0.x = 0f;
114 		_p0.y = -1f;
115 		_p0.transform(m, _p3);
116 		_view.bezierCurveTo(_p1.x, _p1.y, _p2.x, _p2.y, _p3.x, _p3.y);
117 		
118 		_p0.x = K;
119 		_p0.y = -1f;
120 		_p0.transform(m, _p1);
121 		_p0.x = 1f;
122 		_p0.y = -K;
123 		_p0.transform(m, _p2);
124 		_p0.x = 1f;
125 		_p0.y = 0f;
126 		_p0.transform(m, _p3);
127 		_view.bezierCurveTo(_p1.x, _p1.y, _p2.x, _p2.y, _p3.x, _p3.y);
128 
129 		_p0.x = 1f;
130 		_p0.y = K;
131 		_p0.transform(m, _p1);
132 		_p0.x = K;
133 		_p0.y = 1f;
134 		_p0.transform(m, _p2);
135 		_p0.x = 0f;
136 		_p0.y = 1f;
137 		_p0.transform(m, _p3);
138 		_view.bezierCurveTo(_p1.x, _p1.y, _p2.x, _p2.y, _p3.x, _p3.y);
139 		
140 		_p0.x = -K;
141 		_p0.y = bbox.ymax;
142 		_p0.transform(m, _p1);
143 		_p0.x = -1f;
144 		_p0.y = K;
145 		_p0.transform(m, _p2);
146 		_p0.x = -1f;
147 		_p0.y = 0f;
148 		_p0.transform(m, _p3);
149 		_view.bezierCurveTo(_p1.x, _p1.y, _p2.x, _p2.y, _p3.x, _p3.y);
150 
151 		_view.closePath();
152 		if (applyFillStyle()) {
153 			_view.fill();
154 		}
155 		if (applyStrokeStyle()) {
156 			_view.stroke();
157 		}
158 		popShape();
159 	}
160 
161 	public void visitPolyline(Polyline polyline) {
162 		TransformMatrix m = pushShape(polyline);
163 		Point[] pts = polyline.getVertices();
164 
165 		_view.beginPath();
166 		pts[0].transform(m, _p0);
167 		_view.moveTo(_p0.x, _p0.y);
168 		for (int i = 1; i < pts.length; i++) {
169 			pts[i].transform(m, _p0);
170 			_view.lineTo(_p0.x, _p0.y);
171 		}
172 		if (polyline.isClosed()) {
173 			_view.closePath();
174 			if (applyFillStyle()) {
175 				_view.fill();
176 			}
177 		}
178 		if (applyStrokeStyle()) {
179 			_view.stroke();
180 		}
181 		
182 		popShape();
183 	}
184 
185 	public void visitRect(Rect rect) {
186 		TransformMatrix m = pushShape(rect);
187 
188 		_view.beginPath();
189 		_p0.x = -1f;
190 		_p0.y = -1f;
191 		_p0.transform(m, _p1);
192 		_view.moveTo(_p1.x, _p1.y);
193 
194 		_p0.x = 1f;
195 		_p0.y = -1f;
196 		_p0.transform(m, _p2);
197 		_view.lineTo(_p2.x, _p2.y);
198 		
199 		_p0.x = 1f;
200 		_p0.y = 1f;
201 		_p0.transform(m, _p2);
202 		_view.lineTo(_p2.x, _p2.y);
203 		
204 		_p0.x = -1f;
205 		_p0.y = 1f;
206 		_p0.transform(m, _p2);
207 		_view.lineTo(_p2.x, _p2.y);
208 		
209 		_view.lineTo(_p1.x, _p1.y);
210 		
211 		_view.closePath();
212 	
213 		if (applyFillStyle()) {
214 			_view.fill();
215 		}
216 		if (applyStrokeStyle()) {
217 			_view.stroke();
218 		}
219 		popShape();
220 	}
221 
222 	public void visitShapeGroup(ShapeGroup group) {
223 		pushShape(group);
224 		List<Shape> shapes = group.getShapes();
225 		for (int i = 0, size = shapes.size(); i < size; i++) {
226 			Shape shape = shapes.get(i);
227 			shape.acceptVisitor(this);
228 		}
229 		popShape();
230 	}
231 
232 	public void visitPath(Path path) {
233 		TransformMatrix m = pushShape(path);
234 		_view.beginPath();
235 		List<Segment> segments = path.getSegments();
236 		for (int i = 0, size = segments.size(); i < size; i++) {
237 			Segment segment = segments.get(i);
238 			if (i == 0) {
239 				_view.beginPath();
240 				segment.getStartPoint().transform(m, _p0);
241 				_view.moveTo(_p0.x, _p0.y);
242 			}
243 			if (segment instanceof LineSegment) {
244 				segment.getEndPoint().transform(m, _p0);
245 				_view.lineTo(_p0.x, _p0.y);
246 			} else {
247 				BezierSegment bezierSegment = (BezierSegment)segment;
248 				bezierSegment.getStartControlPoint().transform(m, _p0);
249 				bezierSegment.getEndControlPoint().transform(m, _p1);
250 				bezierSegment.getEndPoint().transform(m, _p2);
251 				_view.bezierCurveTo(_p0.x, _p0.y, _p1.x, _p1.y, _p2.x, _p2.y);
252 			}
253 		}
254 		if (path.isClosed()) {
255 			_view.closePath();
256 			if (applyFillStyle()) {
257 				_view.fill();
258 			}
259 		}
260 		if (applyStrokeStyle()) {
261 			_view.stroke();
262 		}
263 		
264 		popShape();
265 	}
266 
267 	private boolean applyStrokeStyle() {
268 		boolean doStroke = false;
269 		IStyle strokeStyle = (IStyle)getAttribute(Attribute.LINE_STYLE);
270 		if (strokeStyle != null && strokeStyle.getKind() != IStyle.NONE) {
271 			float opacity = ((FloatAttributeValue)getAttribute(Attribute.LINE_OPACITY)).getValue();
272 			if (opacity > 0.0f) {
273 				doStroke = true;
274 				strokeStyle.acceptVisitor(_view.getStrokeStyleVisitor());
275 				float viewOpacity = _view.getGlobalAlpha();
276 				if (viewOpacity != opacity) {
277 					_view.setGlobalAlpha(opacity);
278 				}
279 				float lineWidth = ((FloatAttributeValue)getAttribute(Attribute.LINE_WIDTH)).getValue();
280 				if (_view.getLineWidth() != lineWidth) {
281 					_view.setLineWidth(lineWidth);
282 				}
283 			}
284 		}
285 		return doStroke;
286 	}
287 	
288 	private boolean applyFillStyle() {
289 		boolean doFill = false;
290 		IStyle fillStyle = (IStyle)getAttribute(Attribute.FILL_STYLE);
291 		if (fillStyle != null && fillStyle.getKind() != IStyle.NONE) {
292 			float opacity = ((FloatAttributeValue)getAttribute(Attribute.FILL_OPACITY)).getValue();
293 			if (opacity > 0.0f) {
294 				doFill = true;
295 				fillStyle.acceptVisitor(_view.getFillStyleVisitor());
296 				float viewOpacity = _view.getGlobalAlpha();
297 				if (viewOpacity != opacity) {
298 					_view.setGlobalAlpha(opacity);
299 				}
300 			}
301 		}
302 		return doFill;
303 	}
304 	
305 	private Object getAttribute(Attribute attr) {
306 		Object attrValue = null;
307 		for (int i = 0; i < _index; i++) {
308 			if (_stack[i].shape.definesAttribute(attr)) {
309 				attrValue = _stack[i].shape.getAttribute(attr);
310 				break;
311 			}
312 		}
313 		return attrValue;
314 	}
315 
316 }