1 | (function() {
|
2 | var SVGPath;
|
3 |
|
4 | SVGPath = (function() {
|
5 | var apply, arcToSegments, cx, cy, parameters, parse, px, py, runners, segmentToBezier, solveArc, sx, sy;
|
6 |
|
7 | function SVGPath() {}
|
8 |
|
9 | SVGPath.apply = function(doc, path) {
|
10 | var commands;
|
11 | commands = parse(path);
|
12 | return apply(commands, doc);
|
13 | };
|
14 |
|
15 | parameters = {
|
16 | A: 7,
|
17 | a: 7,
|
18 | C: 6,
|
19 | c: 6,
|
20 | H: 1,
|
21 | h: 1,
|
22 | L: 2,
|
23 | l: 2,
|
24 | M: 2,
|
25 | m: 2,
|
26 | Q: 4,
|
27 | q: 4,
|
28 | S: 4,
|
29 | s: 4,
|
30 | T: 2,
|
31 | t: 2,
|
32 | V: 1,
|
33 | v: 1,
|
34 | Z: 0,
|
35 | z: 0
|
36 | };
|
37 |
|
38 | parse = function(path) {
|
39 | var args, c, cmd, curArg, foundDecimal, j, len, params, ret;
|
40 | ret = [];
|
41 | args = [];
|
42 | curArg = "";
|
43 | foundDecimal = false;
|
44 | params = 0;
|
45 | for (j = 0, len = path.length; j < len; j++) {
|
46 | c = path[j];
|
47 | if (parameters[c] != null) {
|
48 | params = parameters[c];
|
49 | if (cmd) {
|
50 | if (curArg.length > 0) {
|
51 | args[args.length] = +curArg;
|
52 | }
|
53 | ret[ret.length] = {
|
54 | cmd: cmd,
|
55 | args: args
|
56 | };
|
57 | args = [];
|
58 | curArg = "";
|
59 | foundDecimal = false;
|
60 | }
|
61 | cmd = c;
|
62 | } else if ((c === " " || c === ",") || (c === "-" && curArg.length > 0 && curArg[curArg.length - 1] !== 'e') || (c === "." && foundDecimal)) {
|
63 | if (curArg.length === 0) {
|
64 | continue;
|
65 | }
|
66 | if (args.length === params) {
|
67 | ret[ret.length] = {
|
68 | cmd: cmd,
|
69 | args: args
|
70 | };
|
71 | args = [+curArg];
|
72 | if (cmd === "M") {
|
73 | cmd = "L";
|
74 | }
|
75 | if (cmd === "m") {
|
76 | cmd = "l";
|
77 | }
|
78 | } else {
|
79 | args[args.length] = +curArg;
|
80 | }
|
81 | foundDecimal = c === ".";
|
82 | curArg = c === '-' || c === '.' ? c : '';
|
83 | } else {
|
84 | curArg += c;
|
85 | if (c === '.') {
|
86 | foundDecimal = true;
|
87 | }
|
88 | }
|
89 | }
|
90 | if (curArg.length > 0) {
|
91 | if (args.length === params) {
|
92 | ret[ret.length] = {
|
93 | cmd: cmd,
|
94 | args: args
|
95 | };
|
96 | args = [+curArg];
|
97 | if (cmd === "M") {
|
98 | cmd = "L";
|
99 | }
|
100 | if (cmd === "m") {
|
101 | cmd = "l";
|
102 | }
|
103 | } else {
|
104 | args[args.length] = +curArg;
|
105 | }
|
106 | }
|
107 | ret[ret.length] = {
|
108 | cmd: cmd,
|
109 | args: args
|
110 | };
|
111 | return ret;
|
112 | };
|
113 |
|
114 | cx = cy = px = py = sx = sy = 0;
|
115 |
|
116 | apply = function(commands, doc) {
|
117 | var c, i, j, len, name;
|
118 | cx = cy = px = py = sx = sy = 0;
|
119 | for (i = j = 0, len = commands.length; j < len; i = ++j) {
|
120 | c = commands[i];
|
121 | if (typeof runners[name = c.cmd] === "function") {
|
122 | runners[name](doc, c.args);
|
123 | }
|
124 | }
|
125 | return cx = cy = px = py = 0;
|
126 | };
|
127 |
|
128 | runners = {
|
129 | M: function(doc, a) {
|
130 | cx = a[0];
|
131 | cy = a[1];
|
132 | px = py = null;
|
133 | sx = cx;
|
134 | sy = cy;
|
135 | return doc.moveTo(cx, cy);
|
136 | },
|
137 | m: function(doc, a) {
|
138 | cx += a[0];
|
139 | cy += a[1];
|
140 | px = py = null;
|
141 | sx = cx;
|
142 | sy = cy;
|
143 | return doc.moveTo(cx, cy);
|
144 | },
|
145 | C: function(doc, a) {
|
146 | cx = a[4];
|
147 | cy = a[5];
|
148 | px = a[2];
|
149 | py = a[3];
|
150 | return doc.bezierCurveTo.apply(doc, a);
|
151 | },
|
152 | c: function(doc, a) {
|
153 | doc.bezierCurveTo(a[0] + cx, a[1] + cy, a[2] + cx, a[3] + cy, a[4] + cx, a[5] + cy);
|
154 | px = cx + a[2];
|
155 | py = cy + a[3];
|
156 | cx += a[4];
|
157 | return cy += a[5];
|
158 | },
|
159 | S: function(doc, a) {
|
160 | if (px === null) {
|
161 | px = cx;
|
162 | py = cy;
|
163 | }
|
164 | doc.bezierCurveTo(cx - (px - cx), cy - (py - cy), a[0], a[1], a[2], a[3]);
|
165 | px = a[0];
|
166 | py = a[1];
|
167 | cx = a[2];
|
168 | return cy = a[3];
|
169 | },
|
170 | s: function(doc, a) {
|
171 | if (px === null) {
|
172 | px = cx;
|
173 | py = cy;
|
174 | }
|
175 | doc.bezierCurveTo(cx - (px - cx), cy - (py - cy), cx + a[0], cy + a[1], cx + a[2], cy + a[3]);
|
176 | px = cx + a[0];
|
177 | py = cy + a[1];
|
178 | cx += a[2];
|
179 | return cy += a[3];
|
180 | },
|
181 | Q: function(doc, a) {
|
182 | px = a[0];
|
183 | py = a[1];
|
184 | cx = a[2];
|
185 | cy = a[3];
|
186 | return doc.quadraticCurveTo(a[0], a[1], cx, cy);
|
187 | },
|
188 | q: function(doc, a) {
|
189 | doc.quadraticCurveTo(a[0] + cx, a[1] + cy, a[2] + cx, a[3] + cy);
|
190 | px = cx + a[0];
|
191 | py = cy + a[1];
|
192 | cx += a[2];
|
193 | return cy += a[3];
|
194 | },
|
195 | T: function(doc, a) {
|
196 | if (px === null) {
|
197 | px = cx;
|
198 | py = cy;
|
199 | } else {
|
200 | px = cx - (px - cx);
|
201 | py = cy - (py - cy);
|
202 | }
|
203 | doc.quadraticCurveTo(px, py, a[0], a[1]);
|
204 | px = cx - (px - cx);
|
205 | py = cy - (py - cy);
|
206 | cx = a[0];
|
207 | return cy = a[1];
|
208 | },
|
209 | t: function(doc, a) {
|
210 | if (px === null) {
|
211 | px = cx;
|
212 | py = cy;
|
213 | } else {
|
214 | px = cx - (px - cx);
|
215 | py = cy - (py - cy);
|
216 | }
|
217 | doc.quadraticCurveTo(px, py, cx + a[0], cy + a[1]);
|
218 | cx += a[0];
|
219 | return cy += a[1];
|
220 | },
|
221 | A: function(doc, a) {
|
222 | solveArc(doc, cx, cy, a);
|
223 | cx = a[5];
|
224 | return cy = a[6];
|
225 | },
|
226 | a: function(doc, a) {
|
227 | a[5] += cx;
|
228 | a[6] += cy;
|
229 | solveArc(doc, cx, cy, a);
|
230 | cx = a[5];
|
231 | return cy = a[6];
|
232 | },
|
233 | L: function(doc, a) {
|
234 | cx = a[0];
|
235 | cy = a[1];
|
236 | px = py = null;
|
237 | return doc.lineTo(cx, cy);
|
238 | },
|
239 | l: function(doc, a) {
|
240 | cx += a[0];
|
241 | cy += a[1];
|
242 | px = py = null;
|
243 | return doc.lineTo(cx, cy);
|
244 | },
|
245 | H: function(doc, a) {
|
246 | cx = a[0];
|
247 | px = py = null;
|
248 | return doc.lineTo(cx, cy);
|
249 | },
|
250 | h: function(doc, a) {
|
251 | cx += a[0];
|
252 | px = py = null;
|
253 | return doc.lineTo(cx, cy);
|
254 | },
|
255 | V: function(doc, a) {
|
256 | cy = a[0];
|
257 | px = py = null;
|
258 | return doc.lineTo(cx, cy);
|
259 | },
|
260 | v: function(doc, a) {
|
261 | cy += a[0];
|
262 | px = py = null;
|
263 | return doc.lineTo(cx, cy);
|
264 | },
|
265 | Z: function(doc) {
|
266 | doc.closePath();
|
267 | cx = sx;
|
268 | return cy = sy;
|
269 | },
|
270 | z: function(doc) {
|
271 | doc.closePath();
|
272 | cx = sx;
|
273 | return cy = sy;
|
274 | }
|
275 | };
|
276 |
|
277 | solveArc = function(doc, x, y, coords) {
|
278 | var bez, ex, ey, j, large, len, results, rot, rx, ry, seg, segs, sweep;
|
279 | rx = coords[0], ry = coords[1], rot = coords[2], large = coords[3], sweep = coords[4], ex = coords[5], ey = coords[6];
|
280 | segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y);
|
281 | results = [];
|
282 | for (j = 0, len = segs.length; j < len; j++) {
|
283 | seg = segs[j];
|
284 | bez = segmentToBezier.apply(null, seg);
|
285 | results.push(doc.bezierCurveTo.apply(doc, bez));
|
286 | }
|
287 | return results;
|
288 | };
|
289 |
|
290 | arcToSegments = function(x, y, rx, ry, large, sweep, rotateX, ox, oy) {
|
291 | var a00, a01, a10, a11, cos_th, d, i, j, pl, ref, result, segments, sfactor, sfactor_sq, sin_th, th, th0, th1, th2, th3, th_arc, x0, x1, xc, y0, y1, yc;
|
292 | th = rotateX * (Math.PI / 180);
|
293 | sin_th = Math.sin(th);
|
294 | cos_th = Math.cos(th);
|
295 | rx = Math.abs(rx);
|
296 | ry = Math.abs(ry);
|
297 | px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5;
|
298 | py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5;
|
299 | pl = (px * px) / (rx * rx) + (py * py) / (ry * ry);
|
300 | if (pl > 1) {
|
301 | pl = Math.sqrt(pl);
|
302 | rx *= pl;
|
303 | ry *= pl;
|
304 | }
|
305 | a00 = cos_th / rx;
|
306 | a01 = sin_th / rx;
|
307 | a10 = (-sin_th) / ry;
|
308 | a11 = cos_th / ry;
|
309 | x0 = a00 * ox + a01 * oy;
|
310 | y0 = a10 * ox + a11 * oy;
|
311 | x1 = a00 * x + a01 * y;
|
312 | y1 = a10 * x + a11 * y;
|
313 | d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
|
314 | sfactor_sq = 1 / d - 0.25;
|
315 | if (sfactor_sq < 0) {
|
316 | sfactor_sq = 0;
|
317 | }
|
318 | sfactor = Math.sqrt(sfactor_sq);
|
319 | if (sweep === large) {
|
320 | sfactor = -sfactor;
|
321 | }
|
322 | xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
|
323 | yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
|
324 | th0 = Math.atan2(y0 - yc, x0 - xc);
|
325 | th1 = Math.atan2(y1 - yc, x1 - xc);
|
326 | th_arc = th1 - th0;
|
327 | if (th_arc < 0 && sweep === 1) {
|
328 | th_arc += 2 * Math.PI;
|
329 | } else if (th_arc > 0 && sweep === 0) {
|
330 | th_arc -= 2 * Math.PI;
|
331 | }
|
332 | segments = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001)));
|
333 | result = [];
|
334 | for (i = j = 0, ref = segments; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
|
335 | th2 = th0 + i * th_arc / segments;
|
336 | th3 = th0 + (i + 1) * th_arc / segments;
|
337 | result[i] = [xc, yc, th2, th3, rx, ry, sin_th, cos_th];
|
338 | }
|
339 | return result;
|
340 | };
|
341 |
|
342 | segmentToBezier = function(cx, cy, th0, th1, rx, ry, sin_th, cos_th) {
|
343 | var a00, a01, a10, a11, t, th_half, x1, x2, x3, y1, y2, y3;
|
344 | a00 = cos_th * rx;
|
345 | a01 = -sin_th * ry;
|
346 | a10 = sin_th * rx;
|
347 | a11 = cos_th * ry;
|
348 | th_half = 0.5 * (th1 - th0);
|
349 | t = (8 / 3) * Math.sin(th_half * 0.5) * Math.sin(th_half * 0.5) / Math.sin(th_half);
|
350 | x1 = cx + Math.cos(th0) - t * Math.sin(th0);
|
351 | y1 = cy + Math.sin(th0) + t * Math.cos(th0);
|
352 | x3 = cx + Math.cos(th1);
|
353 | y3 = cy + Math.sin(th1);
|
354 | x2 = x3 + t * Math.sin(th1);
|
355 | y2 = y3 - t * Math.cos(th1);
|
356 | return [a00 * x1 + a01 * y1, a10 * x1 + a11 * y1, a00 * x2 + a01 * y2, a10 * x2 + a11 * y2, a00 * x3 + a01 * y3, a10 * x3 + a11 * y3];
|
357 | };
|
358 |
|
359 | return SVGPath;
|
360 |
|
361 | })();
|
362 |
|
363 | module.exports = SVGPath;
|
364 |
|
365 | }).call(this);
|