View Javadoc

1   /**********************************************
2    * Copyright (C) 2010 Lukas Laag
3    * This file is part of lib-gwt-svg-edu.
4    * 
5    * libgwtsvg-edu 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   * libgwtsvg-edu 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 libgwtsvg-edu.  If not, see http://www.gnu.org/licenses/
17   **********************************************/
18  package org.vectomatic.svg.edu.client.maze;
19  
20  import org.vectomatic.dom.svg.OMSVGDocument;
21  import org.vectomatic.dom.svg.OMSVGGElement;
22  import org.vectomatic.dom.svg.OMSVGPathElement;
23  import org.vectomatic.dom.svg.OMSVGPathSeg;
24  import org.vectomatic.dom.svg.OMSVGPathSegCurvetoCubicAbs;
25  import org.vectomatic.dom.svg.OMSVGPathSegCurvetoCubicRel;
26  import org.vectomatic.dom.svg.OMSVGPathSegCurvetoCubicSmoothAbs;
27  import org.vectomatic.dom.svg.OMSVGPathSegCurvetoCubicSmoothRel;
28  import org.vectomatic.dom.svg.OMSVGPathSegLinetoAbs;
29  import org.vectomatic.dom.svg.OMSVGPathSegLinetoHorizontalAbs;
30  import org.vectomatic.dom.svg.OMSVGPathSegLinetoHorizontalRel;
31  import org.vectomatic.dom.svg.OMSVGPathSegLinetoRel;
32  import org.vectomatic.dom.svg.OMSVGPathSegLinetoVerticalAbs;
33  import org.vectomatic.dom.svg.OMSVGPathSegLinetoVerticalRel;
34  import org.vectomatic.dom.svg.OMSVGPathSegList;
35  import org.vectomatic.dom.svg.OMSVGPathSegMovetoAbs;
36  import org.vectomatic.dom.svg.OMSVGPathSegMovetoRel;
37  import org.vectomatic.dom.svg.OMSVGRect;
38  import org.vectomatic.dom.svg.OMSVGRectElement;
39  import org.vectomatic.svg.edu.client.maze.RectangularMaze.RectangularCell;
40  
41  import com.google.gwt.core.client.GWT;
42  
43  /**
44   * Class to rasterize an arbitrary SVG path into a grid of maze cells
45   * @author laaglu
46   */
47  public class Rasterizer {
48  	static class RasterizationResult {
49  		RectangularCell[][] grid;
50  		int srcX, srcY;
51  		int destX, destY;
52  		public RasterizationResult(int colCount, int rowCount) {
53  			grid = new RectangularCell[colCount][rowCount];
54  			for (int i = 0; i < colCount; i++) {
55  				grid[i] = new RectangularCell[rowCount];
56  			}
57  		}
58  	}
59  	
60  	public static RasterizationResult rasterize(OMSVGPathElement path, OMSVGGElement cellGroup, int colCount, int rowCount) {
61  		long t1 = System.currentTimeMillis();
62  		OMSVGRect bbox = path.getBBox();
63  		float minx = bbox.getX();
64  		float miny = bbox.getY();
65  		float width = bbox.getWidth();
66  		float height = bbox.getHeight();
67  		Canvas canvas = Canvas.createCanvas((int)width, (int)height);
68  
69  		// Map the SVG path to a canvas path
70  		OMSVGPathSegList segs = path.getPathSegList();
71  		canvas.beginPath();
72  		float x = 0f, y = 0f, prevx2 = 0, prevy2 = 0, x1, y1;
73  		boolean prevIsCubic = false;
74  		for (int i = 0, size = segs.getNumberOfItems(); i < size; i++) {
75  			OMSVGPathSeg seg = segs.getItem(i);
76  			switch (seg.getPathSegType()) {
77  				case OMSVGPathSeg.PATHSEG_CLOSEPATH:
78  					canvas.closePath();
79  					prevIsCubic = false;
80  					break;
81  				case OMSVGPathSeg.PATHSEG_MOVETO_ABS:
82  					OMSVGPathSegMovetoAbs moveToAbs = (OMSVGPathSegMovetoAbs)seg;
83  					prevx2 = x = moveToAbs.getX();
84  					prevy2 = y = moveToAbs.getY();
85  					canvas.moveTo(x, y);
86  					prevIsCubic = false;
87  					break;
88  				case OMSVGPathSeg.PATHSEG_MOVETO_REL:
89  					OMSVGPathSegMovetoRel moveToRel = (OMSVGPathSegMovetoRel)seg;
90  					x += moveToRel.getX();
91  					y += moveToRel.getY();
92  					prevx2 = x;
93  					prevy2 = y;
94  					canvas.moveTo(x, y);
95  					prevIsCubic = false;
96  					break;
97  				case OMSVGPathSeg.PATHSEG_LINETO_ABS:
98  					OMSVGPathSegLinetoAbs lineToAbs = (OMSVGPathSegLinetoAbs)seg;
99  					prevx2 = x = lineToAbs.getX();
100 					prevy2 = y = lineToAbs.getY();
101 					canvas.lineTo(x, y);
102 					prevIsCubic = false;
103 					break;
104 				case OMSVGPathSeg.PATHSEG_LINETO_REL:
105 					OMSVGPathSegLinetoRel lineToRel = (OMSVGPathSegLinetoRel)seg;
106 					x += lineToRel.getX();
107 					y += lineToRel.getY();
108 					prevx2 = x;
109 					prevy2 = y;
110 					canvas.lineTo(x, y);
111 					prevIsCubic = false;
112 					break;
113 				case OMSVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
114 					OMSVGPathSegCurvetoCubicAbs curveToCubicAbs = (OMSVGPathSegCurvetoCubicAbs)seg;
115 					x = curveToCubicAbs.getX();
116 					y = curveToCubicAbs.getY();
117 					prevx2 = curveToCubicAbs.getX2();
118 					prevy2 = curveToCubicAbs.getY2();
119 					canvas.bezierCurveTo(curveToCubicAbs.getX1(), curveToCubicAbs.getY1(), prevx2, prevy2, x, y);
120 					prevIsCubic = true;
121 					break;
122 				case OMSVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
123 					OMSVGPathSegCurvetoCubicRel curveToCubicRel = (OMSVGPathSegCurvetoCubicRel)seg;
124 					prevx2 = (curveToCubicRel.getX2() + x);
125 					prevy2 = (curveToCubicRel.getY2() + y);
126 					canvas.bezierCurveTo(curveToCubicRel.getX1() + x, curveToCubicRel.getY1() + y, prevx2, prevy2, curveToCubicRel.getX() + x, curveToCubicRel.getY() + y);
127 					x += curveToCubicRel.getX();
128 					y += curveToCubicRel.getY();
129 					prevIsCubic = true;
130 					break;
131 				case OMSVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
132 					OMSVGPathSegCurvetoCubicSmoothAbs curveToSmoothAbs = (OMSVGPathSegCurvetoCubicSmoothAbs)seg;
133 					if (prevIsCubic) {
134 						x1 = 2 * x - prevx2;
135 						y1 = 2 * y - prevy2;
136 					} else {
137 						x1 = x;
138 						y1 = y;
139 					}
140 					x = curveToSmoothAbs.getX();
141 					y = curveToSmoothAbs.getY();
142 					prevx2 = curveToSmoothAbs.getX2();
143 					prevy2 = curveToSmoothAbs.getY2();
144 					canvas.bezierCurveTo(x1, y1, prevx2, prevy2, x, y);
145 					prevIsCubic = true;
146 					break;
147 				case OMSVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
148 					OMSVGPathSegCurvetoCubicSmoothRel curveToSmoothRel = (OMSVGPathSegCurvetoCubicSmoothRel)seg;
149 					if (prevIsCubic) {
150 						x1 = 2 * x - prevx2;
151 						y1 = 2 * y - prevy2;
152 					} else {
153 						x1 = x;
154 						y1 = y;
155 					}
156 					prevx2 = (x + curveToSmoothRel.getX2());
157 					prevy2 = (y + curveToSmoothRel.getY2());
158 					x += curveToSmoothRel.getX();
159 					y += curveToSmoothRel.getY();
160 					canvas.bezierCurveTo(x1, y1, prevx2, prevy2, x, y);
161 					prevIsCubic = true;
162 					break;
163 				case OMSVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
164 					OMSVGPathSegLinetoHorizontalAbs lineToHorizAbs = (OMSVGPathSegLinetoHorizontalAbs)seg;
165 					prevx2 = x = lineToHorizAbs.getX();
166 					canvas.lineTo(x, y);
167 					prevIsCubic = false;
168 					break;
169 				case OMSVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
170 					OMSVGPathSegLinetoHorizontalRel lineToHorizRel = (OMSVGPathSegLinetoHorizontalRel)seg;
171 					x += lineToHorizRel.getX();
172 					prevx2 = x;
173 					canvas.lineTo(x, y);
174 					prevIsCubic = false;
175 					break;
176 				case OMSVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
177 					OMSVGPathSegLinetoVerticalAbs lineToVertAbs = (OMSVGPathSegLinetoVerticalAbs)seg;
178 					prevy2 = y = lineToVertAbs.getY();
179 					canvas.lineTo(x, y);
180 					prevIsCubic = false;
181 					break;
182 				case OMSVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
183 					OMSVGPathSegLinetoVerticalRel lineToVertRel = (OMSVGPathSegLinetoVerticalRel)seg;
184 					y += lineToVertRel.getY();
185 					prevy2 = y;
186 					canvas.lineTo(x, y);
187 					prevIsCubic = false;
188 					break;
189 				case OMSVGPathSeg.PATHSEG_ARC_ABS:
190 				case OMSVGPathSeg.PATHSEG_ARC_REL:
191 				default:
192 					throw new IllegalStateException("Unsupported seg type:" + seg.getPathSegType());
193 			}
194 		}
195 		
196 		// Determine the start and end cell by computing the
197 		// cell with the smaller distance to the specified points
198 		String start = path.getAttributeNS(RectangularMaze.VECTOMATIC_NS, "start");
199 		String[] startArray = start.split("x");
200 		float xstart = Float.parseFloat(startArray[0]);
201 		float ystart = Float.parseFloat(startArray[1]);
202 		String end = path.getAttributeNS(RectangularMaze.VECTOMATIC_NS, "end");
203 		String[] endArray = end.split("x");
204 		float xend = Float.parseFloat(endArray[0]);
205 		float yend = Float.parseFloat(endArray[1]);
206 		float dstart = Float.MAX_VALUE, dend = Float.MAX_VALUE;
207 		
208 
209 		
210 		// Render the canvas path as a cell grid using isPointInPath
211 		OMSVGDocument doc = (OMSVGDocument) cellGroup.getOwnerDocument();
212 		RasterizationResult result = new RasterizationResult(colCount, rowCount);
213 		
214 		float cellWidth = width / colCount;
215 		float cellHeight = height / rowCount;
216 		for (int i = 0; i < colCount; i++) {
217 			for (int j = 0; j < rowCount; j++) {
218 				// Is the cell center in the path
219 				float px = minx + (i + 0.5f) * cellWidth;
220 				float py = miny + (j + 0.5f) * cellHeight;
221 				if (canvas.isPointInPath(px, py)) {
222 					RectangularCell cell = new RectangularCell(i, j);
223 					OMSVGRectElement rect = doc.createSVGRectElement(minx + i * cellWidth, miny + j * cellHeight, cellWidth, cellHeight, 0f, 0f);
224 					cell.setRect(rect);
225 					cellGroup.appendChild(rect);
226 					float d = (px - xstart) * (px - xstart) + (py - ystart) * (py - ystart);
227 					if (d < dstart) {
228 						dstart = d;
229 						result.srcX = i;
230 						result.srcY = j;
231 					}
232 					d = (px - xend) * (px - xend) + (py - yend) * (py - yend);
233 					if (d < dend) {
234 						dend = d;
235 						result.destX = i;
236 						result.destY = j;
237 					}
238 					result.grid[i][j] = cell;
239 				}
240 			}
241 		}
242 		long t2 = System.currentTimeMillis();
243 
244 		GWT.log("cells rasterization = " + (t2 - t1));
245 		return result;
246 	}
247 }