UNPKG

9.77 kBJavaScriptView Raw
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);