Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / docs / source / Draw2.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-draw-Draw'>/**
19 </span> * @class Ext.draw.Draw
20  * Base Drawing class.  Provides base drawing functions.
21  * @private
22  */
23 Ext.define('Ext.draw.Draw', {
24     /* Begin Definitions */
25
26     singleton: true,
27
28     requires: ['Ext.draw.Color'],
29
30     /* End Definitions */
31
32     pathToStringRE: /,?([achlmqrstvxz]),?/gi,
33     pathCommandRE: /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,
34     pathValuesRE: /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,
35     stopsRE: /^(\d+%?)$/,
36     radian: Math.PI / 180,
37
38     availableAnimAttrs: {
39         along: &quot;along&quot;,
40         blur: null,
41         &quot;clip-rect&quot;: &quot;csv&quot;,
42         cx: null,
43         cy: null,
44         fill: &quot;color&quot;,
45         &quot;fill-opacity&quot;: null,
46         &quot;font-size&quot;: null,
47         height: null,
48         opacity: null,
49         path: &quot;path&quot;,
50         r: null,
51         rotation: &quot;csv&quot;,
52         rx: null,
53         ry: null,
54         scale: &quot;csv&quot;,
55         stroke: &quot;color&quot;,
56         &quot;stroke-opacity&quot;: null,
57         &quot;stroke-width&quot;: null,
58         translation: &quot;csv&quot;,
59         width: null,
60         x: null,
61         y: null
62     },
63
64     is: function(o, type) {
65         type = String(type).toLowerCase();
66         return (type == &quot;object&quot; &amp;&amp; o === Object(o)) ||
67             (type == &quot;undefined&quot; &amp;&amp; typeof o == type) ||
68             (type == &quot;null&quot; &amp;&amp; o === null) ||
69             (type == &quot;array&quot; &amp;&amp; Array.isArray &amp;&amp; Array.isArray(o)) ||
70             (Object.prototype.toString.call(o).toLowerCase().slice(8, -1)) == type;
71     },
72
73     ellipsePath: function(sprite) {
74         var attr = sprite.attr;
75         return Ext.String.format(&quot;M{0},{1}A{2},{3},0,1,1,{0},{4}A{2},{3},0,1,1,{0},{1}z&quot;, attr.x, attr.y - attr.ry, attr.rx, attr.ry, attr.y + attr.ry);
76     },
77
78     rectPath: function(sprite) {
79         var attr = sprite.attr;
80         if (attr.radius) {
81             return Ext.String.format(&quot;M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z&quot;, attr.x + attr.radius, attr.y, attr.width - attr.radius * 2, attr.radius, -attr.radius, attr.height - attr.radius * 2, attr.radius * 2 - attr.width, attr.radius * 2 - attr.height);
82         }
83         else {
84             return Ext.String.format(&quot;M{0},{1}l{2},0,0,{3},{4},0z&quot;, attr.x, attr.y, attr.width, attr.height, -attr.width);
85         }
86     },
87
88     // To be deprecated, converts itself (an arrayPath) to a proper SVG path string
89     path2string: function () {
90         return this.join(&quot;,&quot;).replace(Ext.draw.Draw.pathToStringRE, &quot;$1&quot;);
91     },
92
93     // Convert the passed arrayPath to a proper SVG path string (d attribute)
94     pathToString: function(arrayPath) {
95         return arrayPath.join(&quot;,&quot;).replace(Ext.draw.Draw.pathToStringRE, &quot;$1&quot;);
96     },
97
98     parsePathString: function (pathString) {
99         if (!pathString) {
100             return null;
101         }
102         var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},
103             data = [],
104             me = this;
105         if (me.is(pathString, &quot;array&quot;) &amp;&amp; me.is(pathString[0], &quot;array&quot;)) { // rough assumption
106             data = me.pathClone(pathString);
107         }
108         if (!data.length) {
109             String(pathString).replace(me.pathCommandRE, function (a, b, c) {
110                 var params = [],
111                     name = b.toLowerCase();
112                 c.replace(me.pathValuesRE, function (a, b) {
113                     b &amp;&amp; params.push(+b);
114                 });
115                 if (name == &quot;m&quot; &amp;&amp; params.length &gt; 2) {
116                     data.push([b].concat(Ext.Array.splice(params, 0, 2)));
117                     name = &quot;l&quot;;
118                     b = (b == &quot;m&quot;) ? &quot;l&quot; : &quot;L&quot;;
119                 }
120                 while (params.length &gt;= paramCounts[name]) {
121                     data.push([b].concat(Ext.Array.splice(params, 0, paramCounts[name])));
122                     if (!paramCounts[name]) {
123                         break;
124                     }
125                 }
126             });
127         }
128         data.toString = me.path2string;
129         return data;
130     },
131
132     mapPath: function (path, matrix) {
133         if (!matrix) {
134             return path;
135         }
136         var x, y, i, ii, j, jj, pathi;
137         path = this.path2curve(path);
138         for (i = 0, ii = path.length; i &lt; ii; i++) {
139             pathi = path[i];
140             for (j = 1, jj = pathi.length; j &lt; jj-1; j += 2) {
141                 x = matrix.x(pathi[j], pathi[j + 1]);
142                 y = matrix.y(pathi[j], pathi[j + 1]);
143                 pathi[j] = x;
144                 pathi[j + 1] = y;
145             }
146         }
147         return path;
148     },
149
150     pathClone: function(pathArray) {
151         var res = [],
152             j, jj, i, ii;
153         if (!this.is(pathArray, &quot;array&quot;) || !this.is(pathArray &amp;&amp; pathArray[0], &quot;array&quot;)) { // rough assumption
154             pathArray = this.parsePathString(pathArray);
155         }
156         for (i = 0, ii = pathArray.length; i &lt; ii; i++) {
157             res[i] = [];
158             for (j = 0, jj = pathArray[i].length; j &lt; jj; j++) {
159                 res[i][j] = pathArray[i][j];
160             }
161         }
162         res.toString = this.path2string;
163         return res;
164     },
165
166     pathToAbsolute: function (pathArray) {
167         if (!this.is(pathArray, &quot;array&quot;) || !this.is(pathArray &amp;&amp; pathArray[0], &quot;array&quot;)) { // rough assumption
168             pathArray = this.parsePathString(pathArray);
169         }
170         var res = [],
171             x = 0,
172             y = 0,
173             mx = 0,
174             my = 0,
175             i = 0,
176             ln = pathArray.length,
177             r, pathSegment, j, ln2;
178         // MoveTo initial x/y position
179         if (ln &amp;&amp; pathArray[0][0] == &quot;M&quot;) {
180             x = +pathArray[0][1];
181             y = +pathArray[0][2];
182             mx = x;
183             my = y;
184             i++;
185             res[0] = [&quot;M&quot;, x, y];
186         }
187         for (; i &lt; ln; i++) {
188             r = res[i] = [];
189             pathSegment = pathArray[i];
190             if (pathSegment[0] != pathSegment[0].toUpperCase()) {
191                 r[0] = pathSegment[0].toUpperCase();
192                 switch (r[0]) {
193                     // Elliptical Arc
194                     case &quot;A&quot;:
195                         r[1] = pathSegment[1];
196                         r[2] = pathSegment[2];
197                         r[3] = pathSegment[3];
198                         r[4] = pathSegment[4];
199                         r[5] = pathSegment[5];
200                         r[6] = +(pathSegment[6] + x);
201                         r[7] = +(pathSegment[7] + y);
202                         break;
203                     // Vertical LineTo
204                     case &quot;V&quot;:
205                         r[1] = +pathSegment[1] + y;
206                         break;
207                     // Horizontal LineTo
208                     case &quot;H&quot;:
209                         r[1] = +pathSegment[1] + x;
210                         break;
211                     case &quot;M&quot;:
212                     // MoveTo
213                         mx = +pathSegment[1] + x;
214                         my = +pathSegment[2] + y;
215                     default:
216                         j = 1;
217                         ln2 = pathSegment.length;
218                         for (; j &lt; ln2; j++) {
219                             r[j] = +pathSegment[j] + ((j % 2) ? x : y);
220                         }
221                 }
222             }
223             else {
224                 j = 0;
225                 ln2 = pathSegment.length;
226                 for (; j &lt; ln2; j++) {
227                     res[i][j] = pathSegment[j];
228                 }
229             }
230             switch (r[0]) {
231                 // ClosePath
232                 case &quot;Z&quot;:
233                     x = mx;
234                     y = my;
235                     break;
236                 // Horizontal LineTo
237                 case &quot;H&quot;:
238                     x = r[1];
239                     break;
240                 // Vertical LineTo
241                 case &quot;V&quot;:
242                     y = r[1];
243                     break;
244                 // MoveTo
245                 case &quot;M&quot;:
246                     pathSegment = res[i];
247                     ln2 = pathSegment.length;
248                     mx = pathSegment[ln2 - 2];
249                     my = pathSegment[ln2 - 1];
250                 default:
251                     pathSegment = res[i];
252                     ln2 = pathSegment.length;
253                     x = pathSegment[ln2 - 2];
254                     y = pathSegment[ln2 - 1];
255             }
256         }
257         res.toString = this.path2string;
258         return res;
259     },
260
261     // TO BE DEPRECATED
262     pathToRelative: function (pathArray) {
263         if (!this.is(pathArray, &quot;array&quot;) || !this.is(pathArray &amp;&amp; pathArray[0], &quot;array&quot;)) {
264             pathArray = this.parsePathString(pathArray);
265         }
266         var res = [],
267             x = 0,
268             y = 0,
269             mx = 0,
270             my = 0,
271             start = 0;
272         if (pathArray[0][0] == &quot;M&quot;) {
273             x = pathArray[0][1];
274             y = pathArray[0][2];
275             mx = x;
276             my = y;
277             start++;
278             res.push([&quot;M&quot;, x, y]);
279         }
280         for (var i = start, ii = pathArray.length; i &lt; ii; i++) {
281             var r = res[i] = [],
282                 pa = pathArray[i];
283             if (pa[0] != pa[0].toLowerCase()) {
284                 r[0] = pa[0].toLowerCase();
285                 switch (r[0]) {
286                     case &quot;a&quot;:
287                         r[1] = pa[1];
288                         r[2] = pa[2];
289                         r[3] = pa[3];
290                         r[4] = pa[4];
291                         r[5] = pa[5];
292                         r[6] = +(pa[6] - x).toFixed(3);
293                         r[7] = +(pa[7] - y).toFixed(3);
294                         break;
295                     case &quot;v&quot;:
296                         r[1] = +(pa[1] - y).toFixed(3);
297                         break;
298                     case &quot;m&quot;:
299                         mx = pa[1];
300                         my = pa[2];
301                     default:
302                         for (var j = 1, jj = pa.length; j &lt; jj; j++) {
303                             r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
304                         }
305                 }
306             } else {
307                 r = res[i] = [];
308                 if (pa[0] == &quot;m&quot;) {
309                     mx = pa[1] + x;
310                     my = pa[2] + y;
311                 }
312                 for (var k = 0, kk = pa.length; k &lt; kk; k++) {
313                     res[i][k] = pa[k];
314                 }
315             }
316             var len = res[i].length;
317             switch (res[i][0]) {
318                 case &quot;z&quot;:
319                     x = mx;
320                     y = my;
321                     break;
322                 case &quot;h&quot;:
323                     x += +res[i][len - 1];
324                     break;
325                 case &quot;v&quot;:
326                     y += +res[i][len - 1];
327                     break;
328                 default:
329                     x += +res[i][len - 2];
330                     y += +res[i][len - 1];
331             }
332         }
333         res.toString = this.path2string;
334         return res;
335     },
336
337     // Returns a path converted to a set of curveto commands
338     path2curve: function (path) {
339         var me = this,
340             points = me.pathToAbsolute(path),
341             ln = points.length,
342             attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
343             i, seg, segLn, point;
344             
345         for (i = 0; i &lt; ln; i++) {
346             points[i] = me.command2curve(points[i], attrs);
347             if (points[i].length &gt; 7) {
348                     points[i].shift();
349                     point = points[i];
350                     while (point.length) {
351                         Ext.Array.splice(points, i++, 0, [&quot;C&quot;].concat(Ext.Array.splice(point, 0, 6)));
352                     }
353                     Ext.Array.erase(points, i, 1);
354                     ln = points.length;
355                 }
356             seg = points[i];
357             segLn = seg.length;
358             attrs.x = seg[segLn - 2];
359             attrs.y = seg[segLn - 1];
360             attrs.bx = parseFloat(seg[segLn - 4]) || attrs.x;
361             attrs.by = parseFloat(seg[segLn - 3]) || attrs.y;
362         }
363         return points;
364     },
365     
366     interpolatePaths: function (path, path2) {
367         var me = this,
368             p = me.pathToAbsolute(path),
369             p2 = me.pathToAbsolute(path2),
370             attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
371             attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
372             fixArc = function (pp, i) {
373                 if (pp[i].length &gt; 7) {
374                     pp[i].shift();
375                     var pi = pp[i];
376                     while (pi.length) {
377                         Ext.Array.splice(pp, i++, 0, [&quot;C&quot;].concat(Ext.Array.splice(pi, 0, 6)));
378                     }
379                     Ext.Array.erase(pp, i, 1);
380                     ii = Math.max(p.length, p2.length || 0);
381                 }
382             },
383             fixM = function (path1, path2, a1, a2, i) {
384                 if (path1 &amp;&amp; path2 &amp;&amp; path1[i][0] == &quot;M&quot; &amp;&amp; path2[i][0] != &quot;M&quot;) {
385                     Ext.Array.splice(path2, i, 0, [&quot;M&quot;, a2.x, a2.y]);
386                     a1.bx = 0;
387                     a1.by = 0;
388                     a1.x = path1[i][1];
389                     a1.y = path1[i][2];
390                     ii = Math.max(p.length, p2.length || 0);
391                 }
392             };
393         for (var i = 0, ii = Math.max(p.length, p2.length || 0); i &lt; ii; i++) {
394             p[i] = me.command2curve(p[i], attrs);
395             fixArc(p, i);
396             (p2[i] = me.command2curve(p2[i], attrs2));
397             fixArc(p2, i);
398             fixM(p, p2, attrs, attrs2, i);
399             fixM(p2, p, attrs2, attrs, i);
400             var seg = p[i],
401                 seg2 = p2[i],
402                 seglen = seg.length,
403                 seg2len = seg2.length;
404             attrs.x = seg[seglen - 2];
405             attrs.y = seg[seglen - 1];
406             attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
407             attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
408             attrs2.bx = (parseFloat(seg2[seg2len - 4]) || attrs2.x);
409             attrs2.by = (parseFloat(seg2[seg2len - 3]) || attrs2.y);
410             attrs2.x = seg2[seg2len - 2];
411             attrs2.y = seg2[seg2len - 1];
412         }
413         return [p, p2];
414     },
415     
416     //Returns any path command as a curveto command based on the attrs passed
417     command2curve: function (pathCommand, d) {
418         var me = this;
419         if (!pathCommand) {
420             return [&quot;C&quot;, d.x, d.y, d.x, d.y, d.x, d.y];
421         }
422         if (pathCommand[0] != &quot;T&quot; &amp;&amp; pathCommand[0] != &quot;Q&quot;) {
423             d.qx = d.qy = null;
424         }
425         switch (pathCommand[0]) {
426             case &quot;M&quot;:
427                 d.X = pathCommand[1];
428                 d.Y = pathCommand[2];
429                 break;
430             case &quot;A&quot;:
431                 pathCommand = [&quot;C&quot;].concat(me.arc2curve.apply(me, [d.x, d.y].concat(pathCommand.slice(1))));
432                 break;
433             case &quot;S&quot;:
434                 pathCommand = [&quot;C&quot;, d.x + (d.x - (d.bx || d.x)), d.y + (d.y - (d.by || d.y))].concat(pathCommand.slice(1));
435                 break;
436             case &quot;T&quot;:
437                 d.qx = d.x + (d.x - (d.qx || d.x));
438                 d.qy = d.y + (d.y - (d.qy || d.y));
439                 pathCommand = [&quot;C&quot;].concat(me.quadratic2curve(d.x, d.y, d.qx, d.qy, pathCommand[1], pathCommand[2]));
440                 break;
441             case &quot;Q&quot;:
442                 d.qx = pathCommand[1];
443                 d.qy = pathCommand[2];
444                 pathCommand = [&quot;C&quot;].concat(me.quadratic2curve(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[3], pathCommand[4]));
445                 break;
446             case &quot;L&quot;:
447                 pathCommand = [&quot;C&quot;].concat(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[1], pathCommand[2]);
448                 break;
449             case &quot;H&quot;:
450                 pathCommand = [&quot;C&quot;].concat(d.x, d.y, pathCommand[1], d.y, pathCommand[1], d.y);
451                 break;
452             case &quot;V&quot;:
453                 pathCommand = [&quot;C&quot;].concat(d.x, d.y, d.x, pathCommand[1], d.x, pathCommand[1]);
454                 break;
455             case &quot;Z&quot;:
456                 pathCommand = [&quot;C&quot;].concat(d.x, d.y, d.X, d.Y, d.X, d.Y);
457                 break;
458         }
459         return pathCommand;
460     },
461
462     quadratic2curve: function (x1, y1, ax, ay, x2, y2) {
463         var _13 = 1 / 3,
464             _23 = 2 / 3;
465         return [
466                 _13 * x1 + _23 * ax,
467                 _13 * y1 + _23 * ay,
468                 _13 * x2 + _23 * ax,
469                 _13 * y2 + _23 * ay,
470                 x2,
471                 y2
472             ];
473     },
474     
475     rotate: function (x, y, rad) {
476         var cos = Math.cos(rad),
477             sin = Math.sin(rad),
478             X = x * cos - y * sin,
479             Y = x * sin + y * cos;
480         return {x: X, y: Y};
481     },
482
483     arc2curve: function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
484         // for more information of where this Math came from visit:
485         // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
486         var me = this,
487             PI = Math.PI,
488             radian = me.radian,
489             _120 = PI * 120 / 180,
490             rad = radian * (+angle || 0),
491             res = [],
492             math = Math,
493             mcos = math.cos,
494             msin = math.sin,
495             msqrt = math.sqrt,
496             mabs = math.abs,
497             masin = math.asin,
498             xy, cos, sin, x, y, h, rx2, ry2, k, cx, cy, f1, f2, df, c1, s1, c2, s2,
499             t, hx, hy, m1, m2, m3, m4, newres, i, ln, f2old, x2old, y2old;
500         if (!recursive) {
501             xy = me.rotate(x1, y1, -rad);
502             x1 = xy.x;
503             y1 = xy.y;
504             xy = me.rotate(x2, y2, -rad);
505             x2 = xy.x;
506             y2 = xy.y;
507             cos = mcos(radian * angle);
508             sin = msin(radian * angle);
509             x = (x1 - x2) / 2;
510             y = (y1 - y2) / 2;
511             h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
512             if (h &gt; 1) {
513                 h = msqrt(h);
514                 rx = h * rx;
515                 ry = h * ry;
516             }
517             rx2 = rx * rx;
518             ry2 = ry * ry;
519             k = (large_arc_flag == sweep_flag ? -1 : 1) *
520                     msqrt(mabs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)));
521             cx = k * rx * y / ry + (x1 + x2) / 2;
522             cy = k * -ry * x / rx + (y1 + y2) / 2;
523             f1 = masin(((y1 - cy) / ry).toFixed(7));
524             f2 = masin(((y2 - cy) / ry).toFixed(7));
525
526             f1 = x1 &lt; cx ? PI - f1 : f1;
527             f2 = x2 &lt; cx ? PI - f2 : f2;
528             if (f1 &lt; 0) {
529                 f1 = PI * 2 + f1;
530             }
531             if (f2 &lt; 0) {
532                 f2 = PI * 2 + f2;
533             }
534             if (sweep_flag &amp;&amp; f1 &gt; f2) {
535                 f1 = f1 - PI * 2;
536             }
537             if (!sweep_flag &amp;&amp; f2 &gt; f1) {
538                 f2 = f2 - PI * 2;
539             }
540         }
541         else {
542             f1 = recursive[0];
543             f2 = recursive[1];
544             cx = recursive[2];
545             cy = recursive[3];
546         }
547         df = f2 - f1;
548         if (mabs(df) &gt; _120) {
549             f2old = f2;
550             x2old = x2;
551             y2old = y2;
552             f2 = f1 + _120 * (sweep_flag &amp;&amp; f2 &gt; f1 ? 1 : -1);
553             x2 = cx + rx * mcos(f2);
554             y2 = cy + ry * msin(f2);
555             res = me.arc2curve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
556         }
557         df = f2 - f1;
558         c1 = mcos(f1);
559         s1 = msin(f1);
560         c2 = mcos(f2);
561         s2 = msin(f2);
562         t = math.tan(df / 4);
563         hx = 4 / 3 * rx * t;
564         hy = 4 / 3 * ry * t;
565         m1 = [x1, y1];
566         m2 = [x1 + hx * s1, y1 - hy * c1];
567         m3 = [x2 + hx * s2, y2 - hy * c2];
568         m4 = [x2, y2];
569         m2[0] = 2 * m1[0] - m2[0];
570         m2[1] = 2 * m1[1] - m2[1];
571         if (recursive) {
572             return [m2, m3, m4].concat(res);
573         }
574         else {
575             res = [m2, m3, m4].concat(res).join().split(&quot;,&quot;);
576             newres = [];
577             ln = res.length;
578             for (i = 0;  i &lt; ln; i++) {
579                 newres[i] = i % 2 ? me.rotate(res[i - 1], res[i], rad).y : me.rotate(res[i], res[i + 1], rad).x;
580             }
581             return newres;
582         }
583     },
584
585     // TO BE DEPRECATED
586     rotateAndTranslatePath: function (sprite) {
587         var alpha = sprite.rotation.degrees,
588             cx = sprite.rotation.x,
589             cy = sprite.rotation.y,
590             dx = sprite.translation.x,
591             dy = sprite.translation.y,
592             path,
593             i,
594             p,
595             xy,
596             j,
597             res = [];
598         if (!alpha &amp;&amp; !dx &amp;&amp; !dy) {
599             return this.pathToAbsolute(sprite.attr.path);
600         }
601         dx = dx || 0;
602         dy = dy || 0;
603         path = this.pathToAbsolute(sprite.attr.path);
604         for (i = path.length; i--;) {
605             p = res[i] = path[i].slice();
606             if (p[0] == &quot;A&quot;) {
607                 xy = this.rotatePoint(p[6], p[7], alpha, cx, cy);
608                 p[6] = xy.x + dx;
609                 p[7] = xy.y + dy;
610             } else {
611                 j = 1;
612                 while (p[j + 1] != null) {
613                     xy = this.rotatePoint(p[j], p[j + 1], alpha, cx, cy);
614                     p[j] = xy.x + dx;
615                     p[j + 1] = xy.y + dy;
616                     j += 2;
617                 }
618             }
619         }
620         return res;
621     },
622
623     // TO BE DEPRECATED
624     rotatePoint: function (x, y, alpha, cx, cy) {
625         if (!alpha) {
626             return {
627                 x: x,
628                 y: y
629             };
630         }
631         cx = cx || 0;
632         cy = cy || 0;
633         x = x - cx;
634         y = y - cy;
635         alpha = alpha * this.radian;
636         var cos = Math.cos(alpha),
637             sin = Math.sin(alpha);
638         return {
639             x: x * cos - y * sin + cx,
640             y: x * sin + y * cos + cy
641         };
642     },
643
644     pathDimensions: function (path) {
645         if (!path || !(path + &quot;&quot;)) {
646             return {x: 0, y: 0, width: 0, height: 0};
647         }
648         path = this.path2curve(path);
649         var x = 0, 
650             y = 0,
651             X = [],
652             Y = [],
653             i = 0,
654             ln = path.length,
655             p, xmin, ymin, dim;
656         for (; i &lt; ln; i++) {
657             p = path[i];
658             if (p[0] == &quot;M&quot;) {
659                 x = p[1];
660                 y = p[2];
661                 X.push(x);
662                 Y.push(y);
663             }
664             else {
665                 dim = this.curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
666                 X = X.concat(dim.min.x, dim.max.x);
667                 Y = Y.concat(dim.min.y, dim.max.y);
668                 x = p[5];
669                 y = p[6];
670             }
671         }
672         xmin = Math.min.apply(0, X);
673         ymin = Math.min.apply(0, Y);
674         return {
675             x: xmin,
676             y: ymin,
677             path: path,
678             width: Math.max.apply(0, X) - xmin,
679             height: Math.max.apply(0, Y) - ymin
680         };
681     },
682
683     intersectInside: function(path, cp1, cp2) {
684         return (cp2[0] - cp1[0]) * (path[1] - cp1[1]) &gt; (cp2[1] - cp1[1]) * (path[0] - cp1[0]);
685     },
686
687     intersectIntersection: function(s, e, cp1, cp2) {
688         var p = [],
689             dcx = cp1[0] - cp2[0],
690             dcy = cp1[1] - cp2[1],
691             dpx = s[0] - e[0],
692             dpy = s[1] - e[1],
693             n1 = cp1[0] * cp2[1] - cp1[1] * cp2[0],
694             n2 = s[0] * e[1] - s[1] * e[0],
695             n3 = 1 / (dcx * dpy - dcy * dpx);
696
697         p[0] = (n1 * dpx - n2 * dcx) * n3;
698         p[1] = (n1 * dpy - n2 * dcy) * n3;
699         return p;
700     },
701
702     intersect: function(subjectPolygon, clipPolygon) {
703         var me = this,
704             i = 0,
705             ln = clipPolygon.length,
706             cp1 = clipPolygon[ln - 1],
707             outputList = subjectPolygon,
708             cp2, s, e, point, ln2, inputList, j;
709         for (; i &lt; ln; ++i) {
710             cp2 = clipPolygon[i];
711             inputList = outputList;
712             outputList = [];
713             s = inputList[inputList.length - 1];
714             j = 0;
715             ln2 = inputList.length;
716             for (; j &lt; ln2; j++) {
717                 e = inputList[j];
718                 if (me.intersectInside(e, cp1, cp2)) {
719                     if (!me.intersectInside(s, cp1, cp2)) {
720                         outputList.push(me.intersectIntersection(s, e, cp1, cp2));
721                     }
722                     outputList.push(e);
723                 }
724                 else if (me.intersectInside(s, cp1, cp2)) {
725                     outputList.push(me.intersectIntersection(s, e, cp1, cp2));
726                 }
727                 s = e;
728             }
729             cp1 = cp2;
730         }
731         return outputList;
732     },
733
734     curveDim: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
735         var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
736             b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
737             c = p1x - c1x,
738             t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a,
739             t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a,
740             y = [p1y, p2y],
741             x = [p1x, p2x],
742             dot;
743         if (Math.abs(t1) &gt; 1e12) {
744             t1 = 0.5;
745         }
746         if (Math.abs(t2) &gt; 1e12) {
747             t2 = 0.5;
748         }
749         if (t1 &gt; 0 &amp;&amp; t1 &lt; 1) {
750             dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
751             x.push(dot.x);
752             y.push(dot.y);
753         }
754         if (t2 &gt; 0 &amp;&amp; t2 &lt; 1) {
755             dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
756             x.push(dot.x);
757             y.push(dot.y);
758         }
759         a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
760         b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
761         c = p1y - c1y;
762         t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
763         t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
764         if (Math.abs(t1) &gt; 1e12) {
765             t1 = 0.5;
766         }
767         if (Math.abs(t2) &gt; 1e12) {
768             t2 = 0.5;
769         }
770         if (t1 &gt; 0 &amp;&amp; t1 &lt; 1) {
771             dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
772             x.push(dot.x);
773             y.push(dot.y);
774         }
775         if (t2 &gt; 0 &amp;&amp; t2 &lt; 1) {
776             dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
777             x.push(dot.x);
778             y.push(dot.y);
779         }
780         return {
781             min: {x: Math.min.apply(0, x), y: Math.min.apply(0, y)},
782             max: {x: Math.max.apply(0, x), y: Math.max.apply(0, y)}
783         };
784     },
785
786 <span id='Ext-draw-Draw-method-getAnchors'>    /**
787 </span>     * @private
788      *
789      * Calculates bezier curve control anchor points for a particular point in a path, with a
790      * smoothing curve applied. The smoothness of the curve is controlled by the 'value' parameter.
791      * Note that this algorithm assumes that the line being smoothed is normalized going from left
792      * to right; it makes special adjustments assuming this orientation.
793      *
794      * @param {Number} prevX X coordinate of the previous point in the path
795      * @param {Number} prevY Y coordinate of the previous point in the path
796      * @param {Number} curX X coordinate of the current point in the path
797      * @param {Number} curY Y coordinate of the current point in the path
798      * @param {Number} nextX X coordinate of the next point in the path
799      * @param {Number} nextY Y coordinate of the next point in the path
800      * @param {Number} value A value to control the smoothness of the curve; this is used to
801      *                 divide the distance between points, so a value of 2 corresponds to
802      *                 half the distance between points (a very smooth line) while higher values
803      *                 result in less smooth curves. Defaults to 4.
804      * @return {Object} Object containing x1, y1, x2, y2 bezier control anchor points; x1 and y1
805      *                  are the control point for the curve toward the previous path point, and
806      *                  x2 and y2 are the control point for the curve toward the next path point.
807      */
808     getAnchors: function (prevX, prevY, curX, curY, nextX, nextY, value) {
809         value = value || 4;
810         var M = Math,
811             PI = M.PI,
812             halfPI = PI / 2,
813             abs = M.abs,
814             sin = M.sin,
815             cos = M.cos,
816             atan = M.atan,
817             control1Length, control2Length, control1Angle, control2Angle,
818             control1X, control1Y, control2X, control2Y, alpha;
819
820         // Find the length of each control anchor line, by dividing the horizontal distance
821         // between points by the value parameter.
822         control1Length = (curX - prevX) / value;
823         control2Length = (nextX - curX) / value;
824
825         // Determine the angle of each control anchor line. If the middle point is a vertical
826         // turnaround then we force it to a flat horizontal angle to prevent the curve from
827         // dipping above or below the middle point. Otherwise we use an angle that points
828         // toward the previous/next target point.
829         if ((curY &gt;= prevY &amp;&amp; curY &gt;= nextY) || (curY &lt;= prevY &amp;&amp; curY &lt;= nextY)) {
830             control1Angle = control2Angle = halfPI;
831         } else {
832             control1Angle = atan((curX - prevX) / abs(curY - prevY));
833             if (prevY &lt; curY) {
834                 control1Angle = PI - control1Angle;
835             }
836             control2Angle = atan((nextX - curX) / abs(curY - nextY));
837             if (nextY &lt; curY) {
838                 control2Angle = PI - control2Angle;
839             }
840         }
841
842         // Adjust the calculated angles so they point away from each other on the same line
843         alpha = halfPI - ((control1Angle + control2Angle) % (PI * 2)) / 2;
844         if (alpha &gt; halfPI) {
845             alpha -= PI;
846         }
847         control1Angle += alpha;
848         control2Angle += alpha;
849
850         // Find the control anchor points from the angles and length
851         control1X = curX - control1Length * sin(control1Angle);
852         control1Y = curY + control1Length * cos(control1Angle);
853         control2X = curX + control2Length * sin(control2Angle);
854         control2Y = curY + control2Length * cos(control2Angle);
855
856         // One last adjustment, make sure that no control anchor point extends vertically past
857         // its target prev/next point, as that results in curves dipping above or below and
858         // bending back strangely. If we find this happening we keep the control angle but
859         // reduce the length of the control line so it stays within bounds.
860         if ((curY &gt; prevY &amp;&amp; control1Y &lt; prevY) || (curY &lt; prevY &amp;&amp; control1Y &gt; prevY)) {
861             control1X += abs(prevY - control1Y) * (control1X - curX) / (control1Y - curY);
862             control1Y = prevY;
863         }
864         if ((curY &gt; nextY &amp;&amp; control2Y &lt; nextY) || (curY &lt; nextY &amp;&amp; control2Y &gt; nextY)) {
865             control2X -= abs(nextY - control2Y) * (control2X - curX) / (control2Y - curY);
866             control2Y = nextY;
867         }
868         
869         return {
870             x1: control1X,
871             y1: control1Y,
872             x2: control2X,
873             y2: control2Y
874         };
875     },
876
877     /* Smoothing function for a path.  Converts a path into cubic beziers.  Value defines the divider of the distance between points.
878      * Defaults to a value of 4.
879      */
880     smooth: function (originalPath, value) {
881         var path = this.path2curve(originalPath),
882             newp = [path[0]],
883             x = path[0][1],
884             y = path[0][2],
885             j,
886             points,
887             i = 1,
888             ii = path.length,
889             beg = 1,
890             mx = x,
891             my = y,
892             cx = 0,
893             cy = 0;
894         for (; i &lt; ii; i++) {
895             var pathi = path[i],
896                 pathil = pathi.length,
897                 pathim = path[i - 1],
898                 pathiml = pathim.length,
899                 pathip = path[i + 1],
900                 pathipl = pathip &amp;&amp; pathip.length;
901             if (pathi[0] == &quot;M&quot;) {
902                 mx = pathi[1];
903                 my = pathi[2];
904                 j = i + 1;
905                 while (path[j][0] != &quot;C&quot;) {
906                     j++;
907                 }
908                 cx = path[j][5];
909                 cy = path[j][6];
910                 newp.push([&quot;M&quot;, mx, my]);
911                 beg = newp.length;
912                 x = mx;
913                 y = my;
914                 continue;
915             }
916             if (pathi[pathil - 2] == mx &amp;&amp; pathi[pathil - 1] == my &amp;&amp; (!pathip || pathip[0] == &quot;M&quot;)) {
917                 var begl = newp[beg].length;
918                 points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], mx, my, newp[beg][begl - 2], newp[beg][begl - 1], value);
919                 newp[beg][1] = points.x2;
920                 newp[beg][2] = points.y2;
921             }
922             else if (!pathip || pathip[0] == &quot;M&quot;) {
923                 points = {
924                     x1: pathi[pathil - 2],
925                     y1: pathi[pathil - 1]
926                 };
927             } else {
928                 points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], pathi[pathil - 2], pathi[pathil - 1], pathip[pathipl - 2], pathip[pathipl - 1], value);
929             }
930             newp.push([&quot;C&quot;, x, y, points.x1, points.y1, pathi[pathil - 2], pathi[pathil - 1]]);
931             x = points.x2;
932             y = points.y2;
933         }
934         return newp;
935     },
936
937     findDotAtSegment: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
938         var t1 = 1 - t;
939         return {
940             x: Math.pow(t1, 3) * p1x + Math.pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + Math.pow(t, 3) * p2x,
941             y: Math.pow(t1, 3) * p1y + Math.pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + Math.pow(t, 3) * p2y
942         };
943     },
944
945     snapEnds: function (from, to, stepsMax) {
946         var step = (to - from) / stepsMax,
947             level = Math.floor(Math.log(step) / Math.LN10) + 1,
948             m = Math.pow(10, level),
949             cur,
950             modulo = Math.round((step % m) * Math.pow(10, 2 - level)),
951             interval = [[0, 15], [20, 4], [30, 2], [40, 4], [50, 9], [60, 4], [70, 2], [80, 4], [100, 15]],
952             stepCount = 0,
953             value,
954             weight,
955             i,
956             topValue,
957             topWeight = 1e9,
958             ln = interval.length;
959         cur = from = Math.floor(from / m) * m;
960         for (i = 0; i &lt; ln; i++) {
961             value = interval[i][0];
962             weight = (value - modulo) &lt; 0 ? 1e6 : (value - modulo) / interval[i][1];
963             if (weight &lt; topWeight) {
964                 topValue = value;
965                 topWeight = weight;
966             }
967         }
968         step = Math.floor(step * Math.pow(10, -level)) * Math.pow(10, level) + topValue * Math.pow(10, level - 2);
969         while (cur &lt; to) {
970             cur += step;
971             stepCount++;
972         }
973         to = +cur.toFixed(10);
974         return {
975             from: from,
976             to: to,
977             power: level,
978             step: step,
979             steps: stepCount
980         };
981     },
982
983     sorter: function (a, b) {
984         return a.offset - b.offset;
985     },
986
987     rad: function(degrees) {
988         return degrees % 360 * Math.PI / 180;
989     },
990
991     degrees: function(radian) {
992         return radian * 180 / Math.PI % 360;
993     },
994
995     withinBox: function(x, y, bbox) {
996         bbox = bbox || {};
997         return (x &gt;= bbox.x &amp;&amp; x &lt;= (bbox.x + bbox.width) &amp;&amp; y &gt;= bbox.y &amp;&amp; y &lt;= (bbox.y + bbox.height));
998     },
999
1000     parseGradient: function(gradient) {
1001         var me = this,
1002             type = gradient.type || 'linear',
1003             angle = gradient.angle || 0,
1004             radian = me.radian,
1005             stops = gradient.stops,
1006             stopsArr = [],
1007             stop,
1008             vector,
1009             max,
1010             stopObj;
1011
1012         if (type == 'linear') {
1013             vector = [0, 0, Math.cos(angle * radian), Math.sin(angle * radian)];
1014             max = 1 / (Math.max(Math.abs(vector[2]), Math.abs(vector[3])) || 1);
1015             vector[2] *= max;
1016             vector[3] *= max;
1017             if (vector[2] &lt; 0) {
1018                 vector[0] = -vector[2];
1019                 vector[2] = 0;
1020             }
1021             if (vector[3] &lt; 0) {
1022                 vector[1] = -vector[3];
1023                 vector[3] = 0;
1024             }
1025         }
1026
1027         for (stop in stops) {
1028             if (stops.hasOwnProperty(stop) &amp;&amp; me.stopsRE.test(stop)) {
1029                 stopObj = {
1030                     offset: parseInt(stop, 10),
1031                     color: Ext.draw.Color.toHex(stops[stop].color) || '#ffffff',
1032                     opacity: stops[stop].opacity || 1
1033                 };
1034                 stopsArr.push(stopObj);
1035             }
1036         }
1037         // Sort by pct property
1038         Ext.Array.sort(stopsArr, me.sorter);
1039         if (type == 'linear') {
1040             return {
1041                 id: gradient.id,
1042                 type: type,
1043                 vector: vector,
1044                 stops: stopsArr
1045             };
1046         }
1047         else {
1048             return {
1049                 id: gradient.id,
1050                 type: type,
1051                 centerX: gradient.centerX,
1052                 centerY: gradient.centerY,
1053                 focalX: gradient.focalX,
1054                 focalY: gradient.focalY,
1055                 radius: gradient.radius,
1056                 vector: vector,
1057                 stops: stopsArr
1058             };
1059         }
1060     }
1061 });
1062 </pre>
1063 </body>
1064 </html>