]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * @class Ext.fx.DrawPath\r | |
3 | * Provides SVG Paths handling functions. Copied from Ext.draw.Draw in ExtJs 4.2 in order\r | |
4 | * to break the dependencies on parsePathString() and interpolatePaths() in PropertyHandler.js\r | |
5 | * @private\r | |
6 | */\r | |
7 | Ext.define('Ext.fx.DrawPath', {\r | |
8 | /* Begin Definitions */\r | |
9 | \r | |
10 | singleton: true,\r | |
11 | \r | |
12 | /* End Definitions */\r | |
13 | \r | |
14 | pathToStringRE: /,?([achlmqrstvxz]),?/gi,\r | |
15 | pathCommandRE: /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,\r | |
16 | pathValuesRE: /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,\r | |
17 | stopsRE: /^(\d+%?)$/,\r | |
18 | radian: Math.PI / 180,\r | |
19 | \r | |
20 | is: function(o, type) {\r | |
21 | type = String(type).toLowerCase();\r | |
22 | return (type == "object" && o === Object(o)) ||\r | |
23 | (type == "undefined" && typeof o == type) ||\r | |
24 | (type == "null" && o === null) ||\r | |
25 | (type == "array" && Array.isArray && Array.isArray(o)) ||\r | |
26 | (Object.prototype.toString.call(o).toLowerCase().slice(8, -1)) == type;\r | |
27 | },\r | |
28 | \r | |
29 | // To be deprecated, converts itself (an arrayPath) to a proper SVG path string\r | |
30 | path2string: function () {\r | |
31 | return this.join(",").replace(Ext.fx.DrawPath.pathToStringRE, "$1");\r | |
32 | },\r | |
33 | \r | |
34 | // Convert the passed arrayPath to a proper SVG path string (d attribute)\r | |
35 | pathToString: function(arrayPath) {\r | |
36 | return arrayPath.join(",").replace(Ext.fx.DrawPath.pathToStringRE, "$1");\r | |
37 | },\r | |
38 | \r | |
39 | parsePathString: function (pathString) {\r | |
40 | if (!pathString) {\r | |
41 | return null;\r | |
42 | }\r | |
43 | var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},\r | |
44 | data = [],\r | |
45 | me = this;\r | |
46 | if (me.is(pathString, "array") && me.is(pathString[0], "array")) { // rough assumption\r | |
47 | data = me.pathClone(pathString);\r | |
48 | }\r | |
49 | if (!data.length) {\r | |
50 | String(pathString).replace(me.pathCommandRE, function (a, b, c) {\r | |
51 | var params = [],\r | |
52 | name = b.toLowerCase();\r | |
53 | c.replace(me.pathValuesRE, function (a, b) {\r | |
54 | if (b) {\r | |
55 | params.push(+b);\r | |
56 | }\r | |
57 | });\r | |
58 | if (name == "m" && params.length > 2) {\r | |
59 | data.push([b].concat(Ext.Array.splice(params, 0, 2)));\r | |
60 | name = "l";\r | |
61 | b = (b == "m") ? "l" : "L";\r | |
62 | }\r | |
63 | while (params.length >= paramCounts[name]) {\r | |
64 | data.push([b].concat(Ext.Array.splice(params, 0, paramCounts[name])));\r | |
65 | if (!paramCounts[name]) {\r | |
66 | break;\r | |
67 | }\r | |
68 | }\r | |
69 | });\r | |
70 | }\r | |
71 | data.toString = me.path2string;\r | |
72 | return data;\r | |
73 | },\r | |
74 | \r | |
75 | pathClone: function(pathArray) {\r | |
76 | var res = [],\r | |
77 | j, jj, i, ii;\r | |
78 | if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { // rough assumption\r | |
79 | pathArray = this.parsePathString(pathArray);\r | |
80 | }\r | |
81 | for (i = 0, ii = pathArray.length; i < ii; i++) {\r | |
82 | res[i] = [];\r | |
83 | for (j = 0, jj = pathArray[i].length; j < jj; j++) {\r | |
84 | res[i][j] = pathArray[i][j];\r | |
85 | }\r | |
86 | }\r | |
87 | res.toString = this.path2string;\r | |
88 | return res;\r | |
89 | },\r | |
90 | \r | |
91 | pathToAbsolute: function (pathArray) {\r | |
92 | if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { // rough assumption\r | |
93 | pathArray = this.parsePathString(pathArray);\r | |
94 | }\r | |
95 | var res = [],\r | |
96 | x = 0,\r | |
97 | y = 0,\r | |
98 | mx = 0,\r | |
99 | my = 0,\r | |
100 | i = 0,\r | |
101 | ln = pathArray.length,\r | |
102 | r, pathSegment, j, ln2;\r | |
103 | // MoveTo initial x/y position\r | |
104 | if (ln && pathArray[0][0] == "M") {\r | |
105 | x = +pathArray[0][1];\r | |
106 | y = +pathArray[0][2];\r | |
107 | mx = x;\r | |
108 | my = y;\r | |
109 | i++;\r | |
110 | res[0] = ["M", x, y];\r | |
111 | }\r | |
112 | for (; i < ln; i++) {\r | |
113 | r = res[i] = [];\r | |
114 | pathSegment = pathArray[i];\r | |
115 | if (pathSegment[0] != pathSegment[0].toUpperCase()) {\r | |
116 | r[0] = pathSegment[0].toUpperCase();\r | |
117 | switch (r[0]) {\r | |
118 | // Elliptical Arc\r | |
119 | case "A":\r | |
120 | r[1] = pathSegment[1];\r | |
121 | r[2] = pathSegment[2];\r | |
122 | r[3] = pathSegment[3];\r | |
123 | r[4] = pathSegment[4];\r | |
124 | r[5] = pathSegment[5];\r | |
125 | r[6] = +(pathSegment[6] + x);\r | |
126 | r[7] = +(pathSegment[7] + y);\r | |
127 | break;\r | |
128 | // Vertical LineTo\r | |
129 | case "V":\r | |
130 | r[1] = +pathSegment[1] + y;\r | |
131 | break;\r | |
132 | // Horizontal LineTo\r | |
133 | case "H":\r | |
134 | r[1] = +pathSegment[1] + x;\r | |
135 | break;\r | |
136 | case "M":\r | |
137 | // MoveTo\r | |
138 | mx = +pathSegment[1] + x;\r | |
139 | my = +pathSegment[2] + y;\r | |
140 | // fall;\r | |
141 | default:\r | |
142 | j = 1;\r | |
143 | ln2 = pathSegment.length;\r | |
144 | for (; j < ln2; j++) {\r | |
145 | r[j] = +pathSegment[j] + ((j % 2) ? x : y);\r | |
146 | }\r | |
147 | }\r | |
148 | }\r | |
149 | else {\r | |
150 | j = 0;\r | |
151 | ln2 = pathSegment.length;\r | |
152 | for (; j < ln2; j++) {\r | |
153 | res[i][j] = pathSegment[j];\r | |
154 | }\r | |
155 | }\r | |
156 | switch (r[0]) {\r | |
157 | // ClosePath\r | |
158 | case "Z":\r | |
159 | x = mx;\r | |
160 | y = my;\r | |
161 | break;\r | |
162 | // Horizontal LineTo\r | |
163 | case "H":\r | |
164 | x = r[1];\r | |
165 | break;\r | |
166 | // Vertical LineTo\r | |
167 | case "V":\r | |
168 | y = r[1];\r | |
169 | break;\r | |
170 | // MoveTo\r | |
171 | case "M":\r | |
172 | pathSegment = res[i];\r | |
173 | ln2 = pathSegment.length;\r | |
174 | mx = pathSegment[ln2 - 2];\r | |
175 | my = pathSegment[ln2 - 1];\r | |
176 | // fall;\r | |
177 | default:\r | |
178 | pathSegment = res[i];\r | |
179 | ln2 = pathSegment.length;\r | |
180 | x = pathSegment[ln2 - 2];\r | |
181 | y = pathSegment[ln2 - 1];\r | |
182 | }\r | |
183 | }\r | |
184 | res.toString = this.path2string;\r | |
185 | return res;\r | |
186 | },\r | |
187 | \r | |
188 | interpolatePaths: function (path, path2) {\r | |
189 | var me = this,\r | |
190 | p = me.pathToAbsolute(path),\r | |
191 | p2 = me.pathToAbsolute(path2),\r | |
192 | attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},\r | |
193 | attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},\r | |
194 | fixArc = function (pp, i) {\r | |
195 | if (pp[i].length > 7) {\r | |
196 | pp[i].shift();\r | |
197 | var pi = pp[i];\r | |
198 | while (pi.length) {\r | |
199 | Ext.Array.splice(pp, i++, 0, ["C"].concat(Ext.Array.splice(pi, 0, 6)));\r | |
200 | }\r | |
201 | Ext.Array.erase(pp, i, 1);\r | |
202 | ii = Math.max(p.length, p2.length || 0);\r | |
203 | }\r | |
204 | },\r | |
205 | fixM = function (path1, path2, a1, a2, i) {\r | |
206 | if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {\r | |
207 | Ext.Array.splice(path2, i, 0, ["M", a2.x, a2.y]);\r | |
208 | a1.bx = 0;\r | |
209 | a1.by = 0;\r | |
210 | a1.x = path1[i][1];\r | |
211 | a1.y = path1[i][2];\r | |
212 | ii = Math.max(p.length, p2.length || 0);\r | |
213 | }\r | |
214 | },\r | |
215 | i, ii,\r | |
216 | seg, seg2, seglen, seg2len;\r | |
217 | for (i = 0, ii = Math.max(p.length, p2.length || 0); i < ii; i++) {\r | |
218 | p[i] = me.command2curve(p[i], attrs);\r | |
219 | fixArc(p, i);\r | |
220 | (p2[i] = me.command2curve(p2[i], attrs2));\r | |
221 | fixArc(p2, i);\r | |
222 | fixM(p, p2, attrs, attrs2, i);\r | |
223 | fixM(p2, p, attrs2, attrs, i);\r | |
224 | seg = p[i];\r | |
225 | seg2 = p2[i];\r | |
226 | seglen = seg.length;\r | |
227 | seg2len = seg2.length;\r | |
228 | attrs.x = seg[seglen - 2];\r | |
229 | attrs.y = seg[seglen - 1];\r | |
230 | attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;\r | |
231 | attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;\r | |
232 | attrs2.bx = (parseFloat(seg2[seg2len - 4]) || attrs2.x);\r | |
233 | attrs2.by = (parseFloat(seg2[seg2len - 3]) || attrs2.y);\r | |
234 | attrs2.x = seg2[seg2len - 2];\r | |
235 | attrs2.y = seg2[seg2len - 1];\r | |
236 | }\r | |
237 | return [p, p2];\r | |
238 | },\r | |
239 | \r | |
240 | //Returns any path command as a curveto command based on the attrs passed\r | |
241 | command2curve: function (pathCommand, d) {\r | |
242 | var me = this;\r | |
243 | if (!pathCommand) {\r | |
244 | return ["C", d.x, d.y, d.x, d.y, d.x, d.y];\r | |
245 | }\r | |
246 | if (pathCommand[0] != "T" && pathCommand[0] != "Q") {\r | |
247 | d.qx = d.qy = null;\r | |
248 | }\r | |
249 | switch (pathCommand[0]) {\r | |
250 | case "M":\r | |
251 | d.X = pathCommand[1];\r | |
252 | d.Y = pathCommand[2];\r | |
253 | break;\r | |
254 | case "A":\r | |
255 | pathCommand = ["C"].concat(me.arc2curve.apply(me, [d.x, d.y].concat(pathCommand.slice(1))));\r | |
256 | break;\r | |
257 | case "S":\r | |
258 | pathCommand = ["C", d.x + (d.x - (d.bx || d.x)), d.y + (d.y - (d.by || d.y))].concat(pathCommand.slice(1));\r | |
259 | break;\r | |
260 | case "T":\r | |
261 | d.qx = d.x + (d.x - (d.qx || d.x));\r | |
262 | d.qy = d.y + (d.y - (d.qy || d.y));\r | |
263 | pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, d.qx, d.qy, pathCommand[1], pathCommand[2]));\r | |
264 | break;\r | |
265 | case "Q":\r | |
266 | d.qx = pathCommand[1];\r | |
267 | d.qy = pathCommand[2];\r | |
268 | pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[3], pathCommand[4]));\r | |
269 | break;\r | |
270 | case "L":\r | |
271 | pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[1], pathCommand[2]);\r | |
272 | break;\r | |
273 | case "H":\r | |
274 | pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], d.y, pathCommand[1], d.y);\r | |
275 | break;\r | |
276 | case "V":\r | |
277 | pathCommand = ["C"].concat(d.x, d.y, d.x, pathCommand[1], d.x, pathCommand[1]);\r | |
278 | break;\r | |
279 | case "Z":\r | |
280 | pathCommand = ["C"].concat(d.x, d.y, d.X, d.Y, d.X, d.Y);\r | |
281 | break;\r | |
282 | }\r | |
283 | return pathCommand;\r | |
284 | },\r | |
285 | \r | |
286 | quadratic2curve: function (x1, y1, ax, ay, x2, y2) {\r | |
287 | var _13 = 1 / 3,\r | |
288 | _23 = 2 / 3;\r | |
289 | return [\r | |
290 | _13 * x1 + _23 * ax,\r | |
291 | _13 * y1 + _23 * ay,\r | |
292 | _13 * x2 + _23 * ax,\r | |
293 | _13 * y2 + _23 * ay,\r | |
294 | x2,\r | |
295 | y2\r | |
296 | ];\r | |
297 | },\r | |
298 | \r | |
299 | rotate: function (x, y, rad) {\r | |
300 | var cos = Math.cos(rad),\r | |
301 | sin = Math.sin(rad),\r | |
302 | X = x * cos - y * sin,\r | |
303 | Y = x * sin + y * cos;\r | |
304 | return {x: X, y: Y};\r | |
305 | },\r | |
306 | \r | |
307 | arc2curve: function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {\r | |
308 | // for more information of where this Math came from visit:\r | |
309 | // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes\r | |
310 | var me = this,\r | |
311 | PI = Math.PI,\r | |
312 | radian = me.radian,\r | |
313 | _120 = PI * 120 / 180,\r | |
314 | rad = radian * (+angle || 0),\r | |
315 | res = [],\r | |
316 | math = Math,\r | |
317 | mcos = math.cos,\r | |
318 | msin = math.sin,\r | |
319 | msqrt = math.sqrt,\r | |
320 | mabs = math.abs,\r | |
321 | masin = math.asin,\r | |
322 | xy, x, y, h, rx2, ry2, k, cx, cy, f1, f2, df, c1, s1, c2, s2,\r | |
323 | t, hx, hy, m1, m2, m3, m4, newres, i, ln, f2old, x2old, y2old;\r | |
324 | if (!recursive) {\r | |
325 | xy = me.rotate(x1, y1, -rad);\r | |
326 | x1 = xy.x;\r | |
327 | y1 = xy.y;\r | |
328 | xy = me.rotate(x2, y2, -rad);\r | |
329 | x2 = xy.x;\r | |
330 | y2 = xy.y;\r | |
331 | x = (x1 - x2) / 2;\r | |
332 | y = (y1 - y2) / 2;\r | |
333 | h = (x * x) / (rx * rx) + (y * y) / (ry * ry);\r | |
334 | if (h > 1) {\r | |
335 | h = msqrt(h);\r | |
336 | rx = h * rx;\r | |
337 | ry = h * ry;\r | |
338 | }\r | |
339 | rx2 = rx * rx;\r | |
340 | ry2 = ry * ry;\r | |
341 | k = (large_arc_flag == sweep_flag ? -1 : 1) *\r | |
342 | msqrt(mabs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)));\r | |
343 | cx = k * rx * y / ry + (x1 + x2) / 2;\r | |
344 | cy = k * -ry * x / rx + (y1 + y2) / 2;\r | |
345 | f1 = masin(((y1 - cy) / ry).toFixed(7));\r | |
346 | f2 = masin(((y2 - cy) / ry).toFixed(7));\r | |
347 | \r | |
348 | f1 = x1 < cx ? PI - f1 : f1;\r | |
349 | f2 = x2 < cx ? PI - f2 : f2;\r | |
350 | if (f1 < 0) {\r | |
351 | f1 = PI * 2 + f1;\r | |
352 | }\r | |
353 | if (f2 < 0) {\r | |
354 | f2 = PI * 2 + f2;\r | |
355 | }\r | |
356 | if (sweep_flag && f1 > f2) {\r | |
357 | f1 = f1 - PI * 2;\r | |
358 | }\r | |
359 | if (!sweep_flag && f2 > f1) {\r | |
360 | f2 = f2 - PI * 2;\r | |
361 | }\r | |
362 | }\r | |
363 | else {\r | |
364 | f1 = recursive[0];\r | |
365 | f2 = recursive[1];\r | |
366 | cx = recursive[2];\r | |
367 | cy = recursive[3];\r | |
368 | }\r | |
369 | df = f2 - f1;\r | |
370 | if (mabs(df) > _120) {\r | |
371 | f2old = f2;\r | |
372 | x2old = x2;\r | |
373 | y2old = y2;\r | |
374 | f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);\r | |
375 | x2 = cx + rx * mcos(f2);\r | |
376 | y2 = cy + ry * msin(f2);\r | |
377 | res = me.arc2curve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);\r | |
378 | }\r | |
379 | df = f2 - f1;\r | |
380 | c1 = mcos(f1);\r | |
381 | s1 = msin(f1);\r | |
382 | c2 = mcos(f2);\r | |
383 | s2 = msin(f2);\r | |
384 | t = math.tan(df / 4);\r | |
385 | hx = 4 / 3 * rx * t;\r | |
386 | hy = 4 / 3 * ry * t;\r | |
387 | m1 = [x1, y1];\r | |
388 | m2 = [x1 + hx * s1, y1 - hy * c1];\r | |
389 | m3 = [x2 + hx * s2, y2 - hy * c2];\r | |
390 | m4 = [x2, y2];\r | |
391 | m2[0] = 2 * m1[0] - m2[0];\r | |
392 | m2[1] = 2 * m1[1] - m2[1];\r | |
393 | if (recursive) {\r | |
394 | return [m2, m3, m4].concat(res);\r | |
395 | }\r | |
396 | else {\r | |
397 | res = [m2, m3, m4].concat(res).join().split(",");\r | |
398 | newres = [];\r | |
399 | ln = res.length;\r | |
400 | for (i = 0; i < ln; i++) {\r | |
401 | newres[i] = i % 2 ? me.rotate(res[i - 1], res[i], rad).y : me.rotate(res[i], res[i + 1], rad).x;\r | |
402 | }\r | |
403 | return newres;\r | |
404 | }\r | |
405 | }\r | |
406 | \r | |
407 | });\r |