Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Draw.html
index 3da68f8..845fc69 100644 (file)
@@ -3,8 +3,8 @@
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
   <title>The source code</title>
-  <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
-  <script type="text/javascript" src="../prettify/prettify.js"></script>
+  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
+  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
   <style type="text/css">
     .highlight { display: block; background-color: #ddd; }
   </style>
   </script>
 </head>
 <body onload="prettyPrint(); highlight();">
-  <pre class="prettyprint lang-js"><span id='Ext-layout-component-Draw'>/**
-</span> * @class Ext.layout.component.Draw
- * @extends Ext.layout.component.Component
+  <pre class="prettyprint lang-js"><span id='Ext-draw-Draw'>/**
+</span> * @class Ext.draw.Draw
+ * Base Drawing class.  Provides base drawing functions.
  * @private
- *
  */
-
-Ext.define('Ext.layout.component.Draw', {
-
+Ext.define('Ext.draw.Draw', {
     /* Begin Definitions */
 
-    alias: 'layout.draw',
+    singleton: true,
 
-    extend: 'Ext.layout.component.Auto',
+    requires: ['Ext.draw.Color'],
 
     /* End Definitions */
 
-    type: 'draw',
+    pathToStringRE: /,?([achlmqrstvxz]),?/gi,
+    pathCommandRE: /([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,
+    pathValuesRE: /(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,
+    stopsRE: /^(\d+%?)$/,
+    radian: Math.PI / 180,
+
+    availableAnimAttrs: {
+        along: &quot;along&quot;,
+        blur: null,
+        &quot;clip-rect&quot;: &quot;csv&quot;,
+        cx: null,
+        cy: null,
+        fill: &quot;color&quot;,
+        &quot;fill-opacity&quot;: null,
+        &quot;font-size&quot;: null,
+        height: null,
+        opacity: null,
+        path: &quot;path&quot;,
+        r: null,
+        rotation: &quot;csv&quot;,
+        rx: null,
+        ry: null,
+        scale: &quot;csv&quot;,
+        stroke: &quot;color&quot;,
+        &quot;stroke-opacity&quot;: null,
+        &quot;stroke-width&quot;: null,
+        translation: &quot;csv&quot;,
+        width: null,
+        x: null,
+        y: null
+    },
+
+    is: function(o, type) {
+        type = String(type).toLowerCase();
+        return (type == &quot;object&quot; &amp;&amp; o === Object(o)) ||
+            (type == &quot;undefined&quot; &amp;&amp; typeof o == type) ||
+            (type == &quot;null&quot; &amp;&amp; o === null) ||
+            (type == &quot;array&quot; &amp;&amp; Array.isArray &amp;&amp; Array.isArray(o)) ||
+            (Object.prototype.toString.call(o).toLowerCase().slice(8, -1)) == type;
+    },
+
+    ellipsePath: function(sprite) {
+        var attr = sprite.attr;
+        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);
+    },
+
+    rectPath: function(sprite) {
+        var attr = sprite.attr;
+        if (attr.radius) {
+            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);
+        }
+        else {
+            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);
+        }
+    },
+
+    // To be deprecated, converts itself (an arrayPath) to a proper SVG path string
+    path2string: function () {
+        return this.join(&quot;,&quot;).replace(Ext.draw.Draw.pathToStringRE, &quot;$1&quot;);
+    },
+
+    // Convert the passed arrayPath to a proper SVG path string (d attribute)
+    pathToString: function(arrayPath) {
+        return arrayPath.join(&quot;,&quot;).replace(Ext.draw.Draw.pathToStringRE, &quot;$1&quot;);
+    },
+
+    parsePathString: function (pathString) {
+        if (!pathString) {
+            return null;
+        }
+        var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0},
+            data = [],
+            me = this;
+        if (me.is(pathString, &quot;array&quot;) &amp;&amp; me.is(pathString[0], &quot;array&quot;)) { // rough assumption
+            data = me.pathClone(pathString);
+        }
+        if (!data.length) {
+            String(pathString).replace(me.pathCommandRE, function (a, b, c) {
+                var params = [],
+                    name = b.toLowerCase();
+                c.replace(me.pathValuesRE, function (a, b) {
+                    b &amp;&amp; params.push(+b);
+                });
+                if (name == &quot;m&quot; &amp;&amp; params.length &gt; 2) {
+                    data.push([b].concat(Ext.Array.splice(params, 0, 2)));
+                    name = &quot;l&quot;;
+                    b = (b == &quot;m&quot;) ? &quot;l&quot; : &quot;L&quot;;
+                }
+                while (params.length &gt;= paramCounts[name]) {
+                    data.push([b].concat(Ext.Array.splice(params, 0, paramCounts[name])));
+                    if (!paramCounts[name]) {
+                        break;
+                    }
+                }
+            });
+        }
+        data.toString = me.path2string;
+        return data;
+    },
+
+    mapPath: function (path, matrix) {
+        if (!matrix) {
+            return path;
+        }
+        var x, y, i, ii, j, jj, pathi;
+        path = this.path2curve(path);
+        for (i = 0, ii = path.length; i &lt; ii; i++) {
+            pathi = path[i];
+            for (j = 1, jj = pathi.length; j &lt; jj-1; j += 2) {
+                x = matrix.x(pathi[j], pathi[j + 1]);
+                y = matrix.y(pathi[j], pathi[j + 1]);
+                pathi[j] = x;
+                pathi[j + 1] = y;
+            }
+        }
+        return path;
+    },
+
+    pathClone: function(pathArray) {
+        var res = [],
+            j, jj, i, ii;
+        if (!this.is(pathArray, &quot;array&quot;) || !this.is(pathArray &amp;&amp; pathArray[0], &quot;array&quot;)) { // rough assumption
+            pathArray = this.parsePathString(pathArray);
+        }
+        for (i = 0, ii = pathArray.length; i &lt; ii; i++) {
+            res[i] = [];
+            for (j = 0, jj = pathArray[i].length; j &lt; jj; j++) {
+                res[i][j] = pathArray[i][j];
+            }
+        }
+        res.toString = this.path2string;
+        return res;
+    },
+
+    pathToAbsolute: function (pathArray) {
+        if (!this.is(pathArray, &quot;array&quot;) || !this.is(pathArray &amp;&amp; pathArray[0], &quot;array&quot;)) { // rough assumption
+            pathArray = this.parsePathString(pathArray);
+        }
+        var res = [],
+            x = 0,
+            y = 0,
+            mx = 0,
+            my = 0,
+            i = 0,
+            ln = pathArray.length,
+            r, pathSegment, j, ln2;
+        // MoveTo initial x/y position
+        if (ln &amp;&amp; pathArray[0][0] == &quot;M&quot;) {
+            x = +pathArray[0][1];
+            y = +pathArray[0][2];
+            mx = x;
+            my = y;
+            i++;
+            res[0] = [&quot;M&quot;, x, y];
+        }
+        for (; i &lt; ln; i++) {
+            r = res[i] = [];
+            pathSegment = pathArray[i];
+            if (pathSegment[0] != pathSegment[0].toUpperCase()) {
+                r[0] = pathSegment[0].toUpperCase();
+                switch (r[0]) {
+                    // Elliptical Arc
+                    case &quot;A&quot;:
+                        r[1] = pathSegment[1];
+                        r[2] = pathSegment[2];
+                        r[3] = pathSegment[3];
+                        r[4] = pathSegment[4];
+                        r[5] = pathSegment[5];
+                        r[6] = +(pathSegment[6] + x);
+                        r[7] = +(pathSegment[7] + y);
+                        break;
+                    // Vertical LineTo
+                    case &quot;V&quot;:
+                        r[1] = +pathSegment[1] + y;
+                        break;
+                    // Horizontal LineTo
+                    case &quot;H&quot;:
+                        r[1] = +pathSegment[1] + x;
+                        break;
+                    case &quot;M&quot;:
+                    // MoveTo
+                        mx = +pathSegment[1] + x;
+                        my = +pathSegment[2] + y;
+                    default:
+                        j = 1;
+                        ln2 = pathSegment.length;
+                        for (; j &lt; ln2; j++) {
+                            r[j] = +pathSegment[j] + ((j % 2) ? x : y);
+                        }
+                }
+            }
+            else {
+                j = 0;
+                ln2 = pathSegment.length;
+                for (; j &lt; ln2; j++) {
+                    res[i][j] = pathSegment[j];
+                }
+            }
+            switch (r[0]) {
+                // ClosePath
+                case &quot;Z&quot;:
+                    x = mx;
+                    y = my;
+                    break;
+                // Horizontal LineTo
+                case &quot;H&quot;:
+                    x = r[1];
+                    break;
+                // Vertical LineTo
+                case &quot;V&quot;:
+                    y = r[1];
+                    break;
+                // MoveTo
+                case &quot;M&quot;:
+                    pathSegment = res[i];
+                    ln2 = pathSegment.length;
+                    mx = pathSegment[ln2 - 2];
+                    my = pathSegment[ln2 - 1];
+                default:
+                    pathSegment = res[i];
+                    ln2 = pathSegment.length;
+                    x = pathSegment[ln2 - 2];
+                    y = pathSegment[ln2 - 1];
+            }
+        }
+        res.toString = this.path2string;
+        return res;
+    },
+
+    // TO BE DEPRECATED
+    pathToRelative: function (pathArray) {
+        if (!this.is(pathArray, &quot;array&quot;) || !this.is(pathArray &amp;&amp; pathArray[0], &quot;array&quot;)) {
+            pathArray = this.parsePathString(pathArray);
+        }
+        var res = [],
+            x = 0,
+            y = 0,
+            mx = 0,
+            my = 0,
+            start = 0;
+        if (pathArray[0][0] == &quot;M&quot;) {
+            x = pathArray[0][1];
+            y = pathArray[0][2];
+            mx = x;
+            my = y;
+            start++;
+            res.push([&quot;M&quot;, x, y]);
+        }
+        for (var i = start, ii = pathArray.length; i &lt; ii; i++) {
+            var r = res[i] = [],
+                pa = pathArray[i];
+            if (pa[0] != pa[0].toLowerCase()) {
+                r[0] = pa[0].toLowerCase();
+                switch (r[0]) {
+                    case &quot;a&quot;:
+                        r[1] = pa[1];
+                        r[2] = pa[2];
+                        r[3] = pa[3];
+                        r[4] = pa[4];
+                        r[5] = pa[5];
+                        r[6] = +(pa[6] - x).toFixed(3);
+                        r[7] = +(pa[7] - y).toFixed(3);
+                        break;
+                    case &quot;v&quot;:
+                        r[1] = +(pa[1] - y).toFixed(3);
+                        break;
+                    case &quot;m&quot;:
+                        mx = pa[1];
+                        my = pa[2];
+                    default:
+                        for (var j = 1, jj = pa.length; j &lt; jj; j++) {
+                            r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
+                        }
+                }
+            } else {
+                r = res[i] = [];
+                if (pa[0] == &quot;m&quot;) {
+                    mx = pa[1] + x;
+                    my = pa[2] + y;
+                }
+                for (var k = 0, kk = pa.length; k &lt; kk; k++) {
+                    res[i][k] = pa[k];
+                }
+            }
+            var len = res[i].length;
+            switch (res[i][0]) {
+                case &quot;z&quot;:
+                    x = mx;
+                    y = my;
+                    break;
+                case &quot;h&quot;:
+                    x += +res[i][len - 1];
+                    break;
+                case &quot;v&quot;:
+                    y += +res[i][len - 1];
+                    break;
+                default:
+                    x += +res[i][len - 2];
+                    y += +res[i][len - 1];
+            }
+        }
+        res.toString = this.path2string;
+        return res;
+    },
+
+    // Returns a path converted to a set of curveto commands
+    path2curve: function (path) {
+        var me = this,
+            points = me.pathToAbsolute(path),
+            ln = points.length,
+            attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
+            i, seg, segLn, point;
+            
+        for (i = 0; i &lt; ln; i++) {
+            points[i] = me.command2curve(points[i], attrs);
+            if (points[i].length &gt; 7) {
+                    points[i].shift();
+                    point = points[i];
+                    while (point.length) {
+                        Ext.Array.splice(points, i++, 0, [&quot;C&quot;].concat(Ext.Array.splice(point, 0, 6)));
+                    }
+                    Ext.Array.erase(points, i, 1);
+                    ln = points.length;
+                }
+            seg = points[i];
+            segLn = seg.length;
+            attrs.x = seg[segLn - 2];
+            attrs.y = seg[segLn - 1];
+            attrs.bx = parseFloat(seg[segLn - 4]) || attrs.x;
+            attrs.by = parseFloat(seg[segLn - 3]) || attrs.y;
+        }
+        return points;
+    },
+    
+    interpolatePaths: function (path, path2) {
+        var me = this,
+            p = me.pathToAbsolute(path),
+            p2 = me.pathToAbsolute(path2),
+            attrs = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
+            attrs2 = {x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null},
+            fixArc = function (pp, i) {
+                if (pp[i].length &gt; 7) {
+                    pp[i].shift();
+                    var pi = pp[i];
+                    while (pi.length) {
+                        Ext.Array.splice(pp, i++, 0, [&quot;C&quot;].concat(Ext.Array.splice(pi, 0, 6)));
+                    }
+                    Ext.Array.erase(pp, i, 1);
+                    ii = Math.max(p.length, p2.length || 0);
+                }
+            },
+            fixM = function (path1, path2, a1, a2, i) {
+                if (path1 &amp;&amp; path2 &amp;&amp; path1[i][0] == &quot;M&quot; &amp;&amp; path2[i][0] != &quot;M&quot;) {
+                    Ext.Array.splice(path2, i, 0, [&quot;M&quot;, a2.x, a2.y]);
+                    a1.bx = 0;
+                    a1.by = 0;
+                    a1.x = path1[i][1];
+                    a1.y = path1[i][2];
+                    ii = Math.max(p.length, p2.length || 0);
+                }
+            };
+        for (var i = 0, ii = Math.max(p.length, p2.length || 0); i &lt; ii; i++) {
+            p[i] = me.command2curve(p[i], attrs);
+            fixArc(p, i);
+            (p2[i] = me.command2curve(p2[i], attrs2));
+            fixArc(p2, i);
+            fixM(p, p2, attrs, attrs2, i);
+            fixM(p2, p, attrs2, attrs, i);
+            var seg = p[i],
+                seg2 = p2[i],
+                seglen = seg.length,
+                seg2len = seg2.length;
+            attrs.x = seg[seglen - 2];
+            attrs.y = seg[seglen - 1];
+            attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
+            attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
+            attrs2.bx = (parseFloat(seg2[seg2len - 4]) || attrs2.x);
+            attrs2.by = (parseFloat(seg2[seg2len - 3]) || attrs2.y);
+            attrs2.x = seg2[seg2len - 2];
+            attrs2.y = seg2[seg2len - 1];
+        }
+        return [p, p2];
+    },
+    
+    //Returns any path command as a curveto command based on the attrs passed
+    command2curve: function (pathCommand, d) {
+        var me = this;
+        if (!pathCommand) {
+            return [&quot;C&quot;, d.x, d.y, d.x, d.y, d.x, d.y];
+        }
+        if (pathCommand[0] != &quot;T&quot; &amp;&amp; pathCommand[0] != &quot;Q&quot;) {
+            d.qx = d.qy = null;
+        }
+        switch (pathCommand[0]) {
+            case &quot;M&quot;:
+                d.X = pathCommand[1];
+                d.Y = pathCommand[2];
+                break;
+            case &quot;A&quot;:
+                pathCommand = [&quot;C&quot;].concat(me.arc2curve.apply(me, [d.x, d.y].concat(pathCommand.slice(1))));
+                break;
+            case &quot;S&quot;:
+                pathCommand = [&quot;C&quot;, d.x + (d.x - (d.bx || d.x)), d.y + (d.y - (d.by || d.y))].concat(pathCommand.slice(1));
+                break;
+            case &quot;T&quot;:
+                d.qx = d.x + (d.x - (d.qx || d.x));
+                d.qy = d.y + (d.y - (d.qy || d.y));
+                pathCommand = [&quot;C&quot;].concat(me.quadratic2curve(d.x, d.y, d.qx, d.qy, pathCommand[1], pathCommand[2]));
+                break;
+            case &quot;Q&quot;:
+                d.qx = pathCommand[1];
+                d.qy = pathCommand[2];
+                pathCommand = [&quot;C&quot;].concat(me.quadratic2curve(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[3], pathCommand[4]));
+                break;
+            case &quot;L&quot;:
+                pathCommand = [&quot;C&quot;].concat(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[1], pathCommand[2]);
+                break;
+            case &quot;H&quot;:
+                pathCommand = [&quot;C&quot;].concat(d.x, d.y, pathCommand[1], d.y, pathCommand[1], d.y);
+                break;
+            case &quot;V&quot;:
+                pathCommand = [&quot;C&quot;].concat(d.x, d.y, d.x, pathCommand[1], d.x, pathCommand[1]);
+                break;
+            case &quot;Z&quot;:
+                pathCommand = [&quot;C&quot;].concat(d.x, d.y, d.X, d.Y, d.X, d.Y);
+                break;
+        }
+        return pathCommand;
+    },
+
+    quadratic2curve: function (x1, y1, ax, ay, x2, y2) {
+        var _13 = 1 / 3,
+            _23 = 2 / 3;
+        return [
+                _13 * x1 + _23 * ax,
+                _13 * y1 + _23 * ay,
+                _13 * x2 + _23 * ax,
+                _13 * y2 + _23 * ay,
+                x2,
+                y2
+            ];
+    },
+    
+    rotate: function (x, y, rad) {
+        var cos = Math.cos(rad),
+            sin = Math.sin(rad),
+            X = x * cos - y * sin,
+            Y = x * sin + y * cos;
+        return {x: X, y: Y};
+    },
+
+    arc2curve: function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
+        // for more information of where this Math came from visit:
+        // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+        var me = this,
+            PI = Math.PI,
+            radian = me.radian,
+            _120 = PI * 120 / 180,
+            rad = radian * (+angle || 0),
+            res = [],
+            math = Math,
+            mcos = math.cos,
+            msin = math.sin,
+            msqrt = math.sqrt,
+            mabs = math.abs,
+            masin = math.asin,
+            xy, cos, sin, x, y, h, rx2, ry2, k, cx, cy, f1, f2, df, c1, s1, c2, s2,
+            t, hx, hy, m1, m2, m3, m4, newres, i, ln, f2old, x2old, y2old;
+        if (!recursive) {
+            xy = me.rotate(x1, y1, -rad);
+            x1 = xy.x;
+            y1 = xy.y;
+            xy = me.rotate(x2, y2, -rad);
+            x2 = xy.x;
+            y2 = xy.y;
+            cos = mcos(radian * angle);
+            sin = msin(radian * angle);
+            x = (x1 - x2) / 2;
+            y = (y1 - y2) / 2;
+            h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
+            if (h &gt; 1) {
+                h = msqrt(h);
+                rx = h * rx;
+                ry = h * ry;
+            }
+            rx2 = rx * rx;
+            ry2 = ry * ry;
+            k = (large_arc_flag == sweep_flag ? -1 : 1) *
+                    msqrt(mabs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)));
+            cx = k * rx * y / ry + (x1 + x2) / 2;
+            cy = k * -ry * x / rx + (y1 + y2) / 2;
+            f1 = masin(((y1 - cy) / ry).toFixed(7));
+            f2 = masin(((y2 - cy) / ry).toFixed(7));
+
+            f1 = x1 &lt; cx ? PI - f1 : f1;
+            f2 = x2 &lt; cx ? PI - f2 : f2;
+            if (f1 &lt; 0) {
+                f1 = PI * 2 + f1;
+            }
+            if (f2 &lt; 0) {
+                f2 = PI * 2 + f2;
+            }
+            if (sweep_flag &amp;&amp; f1 &gt; f2) {
+                f1 = f1 - PI * 2;
+            }
+            if (!sweep_flag &amp;&amp; f2 &gt; f1) {
+                f2 = f2 - PI * 2;
+            }
+        }
+        else {
+            f1 = recursive[0];
+            f2 = recursive[1];
+            cx = recursive[2];
+            cy = recursive[3];
+        }
+        df = f2 - f1;
+        if (mabs(df) &gt; _120) {
+            f2old = f2;
+            x2old = x2;
+            y2old = y2;
+            f2 = f1 + _120 * (sweep_flag &amp;&amp; f2 &gt; f1 ? 1 : -1);
+            x2 = cx + rx * mcos(f2);
+            y2 = cy + ry * msin(f2);
+            res = me.arc2curve(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
+        }
+        df = f2 - f1;
+        c1 = mcos(f1);
+        s1 = msin(f1);
+        c2 = mcos(f2);
+        s2 = msin(f2);
+        t = math.tan(df / 4);
+        hx = 4 / 3 * rx * t;
+        hy = 4 / 3 * ry * t;
+        m1 = [x1, y1];
+        m2 = [x1 + hx * s1, y1 - hy * c1];
+        m3 = [x2 + hx * s2, y2 - hy * c2];
+        m4 = [x2, y2];
+        m2[0] = 2 * m1[0] - m2[0];
+        m2[1] = 2 * m1[1] - m2[1];
+        if (recursive) {
+            return [m2, m3, m4].concat(res);
+        }
+        else {
+            res = [m2, m3, m4].concat(res).join().split(&quot;,&quot;);
+            newres = [];
+            ln = res.length;
+            for (i = 0;  i &lt; ln; i++) {
+                newres[i] = i % 2 ? me.rotate(res[i - 1], res[i], rad).y : me.rotate(res[i], res[i + 1], rad).x;
+            }
+            return newres;
+        }
+    },
+
+    // TO BE DEPRECATED
+    rotateAndTranslatePath: function (sprite) {
+        var alpha = sprite.rotation.degrees,
+            cx = sprite.rotation.x,
+            cy = sprite.rotation.y,
+            dx = sprite.translation.x,
+            dy = sprite.translation.y,
+            path,
+            i,
+            p,
+            xy,
+            j,
+            res = [];
+        if (!alpha &amp;&amp; !dx &amp;&amp; !dy) {
+            return this.pathToAbsolute(sprite.attr.path);
+        }
+        dx = dx || 0;
+        dy = dy || 0;
+        path = this.pathToAbsolute(sprite.attr.path);
+        for (i = path.length; i--;) {
+            p = res[i] = path[i].slice();
+            if (p[0] == &quot;A&quot;) {
+                xy = this.rotatePoint(p[6], p[7], alpha, cx, cy);
+                p[6] = xy.x + dx;
+                p[7] = xy.y + dy;
+            } else {
+                j = 1;
+                while (p[j + 1] != null) {
+                    xy = this.rotatePoint(p[j], p[j + 1], alpha, cx, cy);
+                    p[j] = xy.x + dx;
+                    p[j + 1] = xy.y + dy;
+                    j += 2;
+                }
+            }
+        }
+        return res;
+    },
+
+    // TO BE DEPRECATED
+    rotatePoint: function (x, y, alpha, cx, cy) {
+        if (!alpha) {
+            return {
+                x: x,
+                y: y
+            };
+        }
+        cx = cx || 0;
+        cy = cy || 0;
+        x = x - cx;
+        y = y - cy;
+        alpha = alpha * this.radian;
+        var cos = Math.cos(alpha),
+            sin = Math.sin(alpha);
+        return {
+            x: x * cos - y * sin + cx,
+            y: x * sin + y * cos + cy
+        };
+    },
+
+    pathDimensions: function (path) {
+        if (!path || !(path + &quot;&quot;)) {
+            return {x: 0, y: 0, width: 0, height: 0};
+        }
+        path = this.path2curve(path);
+        var x = 0, 
+            y = 0,
+            X = [],
+            Y = [],
+            i = 0,
+            ln = path.length,
+            p, xmin, ymin, dim;
+        for (; i &lt; ln; i++) {
+            p = path[i];
+            if (p[0] == &quot;M&quot;) {
+                x = p[1];
+                y = p[2];
+                X.push(x);
+                Y.push(y);
+            }
+            else {
+                dim = this.curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
+                X = X.concat(dim.min.x, dim.max.x);
+                Y = Y.concat(dim.min.y, dim.max.y);
+                x = p[5];
+                y = p[6];
+            }
+        }
+        xmin = Math.min.apply(0, X);
+        ymin = Math.min.apply(0, Y);
+        return {
+            x: xmin,
+            y: ymin,
+            path: path,
+            width: Math.max.apply(0, X) - xmin,
+            height: Math.max.apply(0, Y) - ymin
+        };
+    },
+
+    intersectInside: function(path, cp1, cp2) {
+        return (cp2[0] - cp1[0]) * (path[1] - cp1[1]) &gt; (cp2[1] - cp1[1]) * (path[0] - cp1[0]);
+    },
+
+    intersectIntersection: function(s, e, cp1, cp2) {
+        var p = [],
+            dcx = cp1[0] - cp2[0],
+            dcy = cp1[1] - cp2[1],
+            dpx = s[0] - e[0],
+            dpy = s[1] - e[1],
+            n1 = cp1[0] * cp2[1] - cp1[1] * cp2[0],
+            n2 = s[0] * e[1] - s[1] * e[0],
+            n3 = 1 / (dcx * dpy - dcy * dpx);
+
+        p[0] = (n1 * dpx - n2 * dcx) * n3;
+        p[1] = (n1 * dpy - n2 * dcy) * n3;
+        return p;
+    },
+
+    intersect: function(subjectPolygon, clipPolygon) {
+        var me = this,
+            i = 0,
+            ln = clipPolygon.length,
+            cp1 = clipPolygon[ln - 1],
+            outputList = subjectPolygon,
+            cp2, s, e, point, ln2, inputList, j;
+        for (; i &lt; ln; ++i) {
+            cp2 = clipPolygon[i];
+            inputList = outputList;
+            outputList = [];
+            s = inputList[inputList.length - 1];
+            j = 0;
+            ln2 = inputList.length;
+            for (; j &lt; ln2; j++) {
+                e = inputList[j];
+                if (me.intersectInside(e, cp1, cp2)) {
+                    if (!me.intersectInside(s, cp1, cp2)) {
+                        outputList.push(me.intersectIntersection(s, e, cp1, cp2));
+                    }
+                    outputList.push(e);
+                }
+                else if (me.intersectInside(s, cp1, cp2)) {
+                    outputList.push(me.intersectIntersection(s, e, cp1, cp2));
+                }
+                s = e;
+            }
+            cp1 = cp2;
+        }
+        return outputList;
+    },
 
-    onLayout : function(width, height) {
-        this.owner.surface.setSize(width, height);
-        this.callParent(arguments);
+    curveDim: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
+        var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
+            b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
+            c = p1x - c1x,
+            t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a,
+            t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a,
+            y = [p1y, p2y],
+            x = [p1x, p2x],
+            dot;
+        if (Math.abs(t1) &gt; 1e12) {
+            t1 = 0.5;
+        }
+        if (Math.abs(t2) &gt; 1e12) {
+            t2 = 0.5;
+        }
+        if (t1 &gt; 0 &amp;&amp; t1 &lt; 1) {
+            dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
+            x.push(dot.x);
+            y.push(dot.y);
+        }
+        if (t2 &gt; 0 &amp;&amp; t2 &lt; 1) {
+            dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
+            x.push(dot.x);
+            y.push(dot.y);
+        }
+        a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
+        b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
+        c = p1y - c1y;
+        t1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 / a;
+        t2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 / a;
+        if (Math.abs(t1) &gt; 1e12) {
+            t1 = 0.5;
+        }
+        if (Math.abs(t2) &gt; 1e12) {
+            t2 = 0.5;
+        }
+        if (t1 &gt; 0 &amp;&amp; t1 &lt; 1) {
+            dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
+            x.push(dot.x);
+            y.push(dot.y);
+        }
+        if (t2 &gt; 0 &amp;&amp; t2 &lt; 1) {
+            dot = this.findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
+            x.push(dot.x);
+            y.push(dot.y);
+        }
+        return {
+            min: {x: Math.min.apply(0, x), y: Math.min.apply(0, y)},
+            max: {x: Math.max.apply(0, x), y: Math.max.apply(0, y)}
+        };
+    },
+
+<span id='Ext-draw-Draw-method-getAnchors'>    /**
+</span>     * @private
+     *
+     * Calculates bezier curve control anchor points for a particular point in a path, with a
+     * smoothing curve applied. The smoothness of the curve is controlled by the 'value' parameter.
+     * Note that this algorithm assumes that the line being smoothed is normalized going from left
+     * to right; it makes special adjustments assuming this orientation.
+     *
+     * @param {Number} prevX X coordinate of the previous point in the path
+     * @param {Number} prevY Y coordinate of the previous point in the path
+     * @param {Number} curX X coordinate of the current point in the path
+     * @param {Number} curY Y coordinate of the current point in the path
+     * @param {Number} nextX X coordinate of the next point in the path
+     * @param {Number} nextY Y coordinate of the next point in the path
+     * @param {Number} value A value to control the smoothness of the curve; this is used to
+     * divide the distance between points, so a value of 2 corresponds to
+     * half the distance between points (a very smooth line) while higher values
+     * result in less smooth curves. Defaults to 4.
+     * @return {Object} Object containing x1, y1, x2, y2 bezier control anchor points; x1 and y1
+     * are the control point for the curve toward the previous path point, and
+     * x2 and y2 are the control point for the curve toward the next path point.
+     */
+    getAnchors: function (prevX, prevY, curX, curY, nextX, nextY, value) {
+        value = value || 4;
+        var M = Math,
+            PI = M.PI,
+            halfPI = PI / 2,
+            abs = M.abs,
+            sin = M.sin,
+            cos = M.cos,
+            atan = M.atan,
+            control1Length, control2Length, control1Angle, control2Angle,
+            control1X, control1Y, control2X, control2Y, alpha;
+
+        // Find the length of each control anchor line, by dividing the horizontal distance
+        // between points by the value parameter.
+        control1Length = (curX - prevX) / value;
+        control2Length = (nextX - curX) / value;
+
+        // Determine the angle of each control anchor line. If the middle point is a vertical
+        // turnaround then we force it to a flat horizontal angle to prevent the curve from
+        // dipping above or below the middle point. Otherwise we use an angle that points
+        // toward the previous/next target point.
+        if ((curY &gt;= prevY &amp;&amp; curY &gt;= nextY) || (curY &lt;= prevY &amp;&amp; curY &lt;= nextY)) {
+            control1Angle = control2Angle = halfPI;
+        } else {
+            control1Angle = atan((curX - prevX) / abs(curY - prevY));
+            if (prevY &lt; curY) {
+                control1Angle = PI - control1Angle;
+            }
+            control2Angle = atan((nextX - curX) / abs(curY - nextY));
+            if (nextY &lt; curY) {
+                control2Angle = PI - control2Angle;
+            }
+        }
+
+        // Adjust the calculated angles so they point away from each other on the same line
+        alpha = halfPI - ((control1Angle + control2Angle) % (PI * 2)) / 2;
+        if (alpha &gt; halfPI) {
+            alpha -= PI;
+        }
+        control1Angle += alpha;
+        control2Angle += alpha;
+
+        // Find the control anchor points from the angles and length
+        control1X = curX - control1Length * sin(control1Angle);
+        control1Y = curY + control1Length * cos(control1Angle);
+        control2X = curX + control2Length * sin(control2Angle);
+        control2Y = curY + control2Length * cos(control2Angle);
+
+        // One last adjustment, make sure that no control anchor point extends vertically past
+        // its target prev/next point, as that results in curves dipping above or below and
+        // bending back strangely. If we find this happening we keep the control angle but
+        // reduce the length of the control line so it stays within bounds.
+        if ((curY &gt; prevY &amp;&amp; control1Y &lt; prevY) || (curY &lt; prevY &amp;&amp; control1Y &gt; prevY)) {
+            control1X += abs(prevY - control1Y) * (control1X - curX) / (control1Y - curY);
+            control1Y = prevY;
+        }
+        if ((curY &gt; nextY &amp;&amp; control2Y &lt; nextY) || (curY &lt; nextY &amp;&amp; control2Y &gt; nextY)) {
+            control2X -= abs(nextY - control2Y) * (control2X - curX) / (control2Y - curY);
+            control2Y = nextY;
+        }
+        
+        return {
+            x1: control1X,
+            y1: control1Y,
+            x2: control2X,
+            y2: control2Y
+        };
+    },
+
+    /* Smoothing function for a path.  Converts a path into cubic beziers.  Value defines the divider of the distance between points.
+     * Defaults to a value of 4.
+     */
+    smooth: function (originalPath, value) {
+        var path = this.path2curve(originalPath),
+            newp = [path[0]],
+            x = path[0][1],
+            y = path[0][2],
+            j,
+            points,
+            i = 1,
+            ii = path.length,
+            beg = 1,
+            mx = x,
+            my = y,
+            cx = 0,
+            cy = 0;
+        for (; i &lt; ii; i++) {
+            var pathi = path[i],
+                pathil = pathi.length,
+                pathim = path[i - 1],
+                pathiml = pathim.length,
+                pathip = path[i + 1],
+                pathipl = pathip &amp;&amp; pathip.length;
+            if (pathi[0] == &quot;M&quot;) {
+                mx = pathi[1];
+                my = pathi[2];
+                j = i + 1;
+                while (path[j][0] != &quot;C&quot;) {
+                    j++;
+                }
+                cx = path[j][5];
+                cy = path[j][6];
+                newp.push([&quot;M&quot;, mx, my]);
+                beg = newp.length;
+                x = mx;
+                y = my;
+                continue;
+            }
+            if (pathi[pathil - 2] == mx &amp;&amp; pathi[pathil - 1] == my &amp;&amp; (!pathip || pathip[0] == &quot;M&quot;)) {
+                var begl = newp[beg].length;
+                points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], mx, my, newp[beg][begl - 2], newp[beg][begl - 1], value);
+                newp[beg][1] = points.x2;
+                newp[beg][2] = points.y2;
+            }
+            else if (!pathip || pathip[0] == &quot;M&quot;) {
+                points = {
+                    x1: pathi[pathil - 2],
+                    y1: pathi[pathil - 1]
+                };
+            } else {
+                points = this.getAnchors(pathim[pathiml - 2], pathim[pathiml - 1], pathi[pathil - 2], pathi[pathil - 1], pathip[pathipl - 2], pathip[pathipl - 1], value);
+            }
+            newp.push([&quot;C&quot;, x, y, points.x1, points.y1, pathi[pathil - 2], pathi[pathil - 1]]);
+            x = points.x2;
+            y = points.y2;
+        }
+        return newp;
+    },
+
+    findDotAtSegment: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
+        var t1 = 1 - t;
+        return {
+            x: Math.pow(t1, 3) * p1x + Math.pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + Math.pow(t, 3) * p2x,
+            y: Math.pow(t1, 3) * p1y + Math.pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + Math.pow(t, 3) * p2y
+        };
+    },
+
+<span id='Ext-draw-Draw-method-snapEnds'>    /**
+</span>     * A utility method to deduce an appropriate tick configuration for the data set of given
+     * feature.
+     * 
+     * @param {Number/Date} from The minimum value in the data
+     * @param {Number/Date} to The maximum value in the data
+     * @param {Number} stepsMax The maximum number of ticks
+     * @return {Object} The calculated step and ends info; When `from` and `to` are Dates, refer to the
+     * return value of {@link #snapEndsByDate}. For numerical `from` and `to` the return value contains:
+     * @return {Number} return.from The result start value, which may be lower than the original start value
+     * @return {Number} return.to The result end value, which may be higher than the original end value
+     * @return {Number} return.power The calculate power.
+     * @return {Number} return.step The value size of each step
+     * @return {Number} return.steps The number of steps.
+     */
+    snapEnds: function (from, to, stepsMax) {
+        if (Ext.isDate(from)) {
+            return this.snapEndsByDate(from, to, stepsMax);
+        }
+        var step = (to - from) / stepsMax,
+            level = Math.floor(Math.log(step) / Math.LN10) + 1,
+            m = Math.pow(10, level),
+            cur,
+            modulo = Math.round((step % m) * Math.pow(10, 2 - level)),
+            interval = [[0, 15], [20, 4], [30, 2], [40, 4], [50, 9], [60, 4], [70, 2], [80, 4], [100, 15]],
+            stepCount = 0,
+            value,
+            weight,
+            i,
+            topValue,
+            topWeight = 1e9,
+            ln = interval.length;
+        cur = from = Math.floor(from / m) * m;
+        for (i = 0; i &lt; ln; i++) {
+            value = interval[i][0];
+            weight = (value - modulo) &lt; 0 ? 1e6 : (value - modulo) / interval[i][1];
+            if (weight &lt; topWeight) {
+                topValue = value;
+                topWeight = weight;
+            }
+        }
+        step = Math.floor(step * Math.pow(10, -level)) * Math.pow(10, level) + topValue * Math.pow(10, level - 2);
+        while (cur &lt; to) {
+            cur += step;
+            stepCount++;
+        }
+        to = +cur.toFixed(10);
+        return {
+            from: from,
+            to: to,
+            power: level,
+            step: step,
+            steps: stepCount
+        };
+    },
+
+<span id='Ext-draw-Draw-method-snapEndsByDate'>    /**
+</span>     * A utility method to deduce an appropriate tick configuration for the data set of given
+     * feature when data is Dates. Refer to {@link #snapEnds} for numeric data.
+     *
+     * @param {Date} from The minimum value in the data
+     * @param {Date} to The maximum value in the data
+     * @param {Number} stepsMax The maximum number of ticks
+     * @param {Boolean} lockEnds If true, the 'from' and 'to' parameters will be used as fixed end values
+     * and will not be adjusted
+     * @return {Object} The calculated step and ends info; properties are:
+     * @return {Date} return.from The result start value, which may be lower than the original start value
+     * @return {Date} return.to The result end value, which may be higher than the original end value
+     * @return {Number} return.step The value size of each step
+     * @return {Number} return.steps The number of steps.
+     * NOTE: the steps may not divide the from/to range perfectly evenly;
+     * there may be a smaller distance between the last step and the end value than between prior
+     * steps, particularly when the `endsLocked` param is true. Therefore it is best to not use
+     * the `steps` result when finding the axis tick points, instead use the `step`, `to`, and
+     * `from` to find the correct point for each tick.
+     */
+    snapEndsByDate: function (from, to, stepsMax, lockEnds) {
+        var selectedStep = false, scales = [
+                [Ext.Date.MILLI, [1, 2, 3, 5, 10, 20, 30, 50, 100, 200, 300, 500]],
+                [Ext.Date.SECOND, [1, 2, 3, 5, 10, 15, 30]],
+                [Ext.Date.MINUTE, [1, 2, 3, 5, 10, 20, 30]],
+                [Ext.Date.HOUR, [1, 2, 3, 4, 6, 12]],
+                [Ext.Date.DAY, [1, 2, 3, 7, 14]],
+                [Ext.Date.MONTH, [1, 2, 3, 4, 6]]
+            ], j, yearDiff;
+
+        // Find the most desirable scale
+        Ext.each(scales, function(scale, i) {
+            for (j = 0; j &lt; scale[1].length; j++) {
+                if (to &lt; Ext.Date.add(from, scale[0], scale[1][j] * stepsMax)) {
+                    selectedStep = [scale[0], scale[1][j]];
+                    return false;
+                }
+            }
+        });
+        if (!selectedStep) {
+            yearDiff = this.snapEnds(from.getFullYear(), to.getFullYear() + 1, stepsMax, lockEnds);
+            selectedStep = [Date.YEAR, Math.round(yearDiff.step)];
+        }
+        return this.snapEndsByDateAndStep(from, to, selectedStep, lockEnds);
+    },
+
+
+<span id='Ext-draw-Draw-method-snapEndsByDateAndStep'>    /**
+</span>     * A utility method to deduce an appropriate tick configuration for the data set of given
+     * feature and specific step size.
+     * @param {Date} from The minimum value in the data
+     * @param {Date} to The maximum value in the data
+     * @param {Array} step An array with two components: The first is the unit of the step (day, month, year, etc).
+     * The second one is the number of units for the step (1, 2, etc.).
+     * @param {Boolean} lockEnds If true, the 'from' and 'to' parameters will be used as fixed end values
+     * and will not be adjusted
+     * @return {Object} See the return value of {@link #snapEndsByDate}.
+     */
+    snapEndsByDateAndStep: function(from, to, step, lockEnds) {
+        var fromStat = [from.getFullYear(), from.getMonth(), from.getDate(),
+                from.getHours(), from.getMinutes(), from.getSeconds(), from.getMilliseconds()],
+            steps = 0, testFrom, testTo;
+        if (lockEnds) {
+            testFrom = from;
+        } else {
+            switch (step[0]) {
+                case Ext.Date.MILLI:
+                    testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
+                            fromStat[4], fromStat[5], Math.floor(fromStat[6] / step[1]) * step[1]);
+                    break;
+                case Ext.Date.SECOND:
+                    testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
+                            fromStat[4], Math.floor(fromStat[5] / step[1]) * step[1], 0);
+                    break;
+                case Ext.Date.MINUTE:
+                    testFrom = new Date(fromStat[0], fromStat[1], fromStat[2], fromStat[3],
+                            Math.floor(fromStat[4] / step[1]) * step[1], 0, 0);
+                    break;
+                case Ext.Date.HOUR:
+                    testFrom = new Date(fromStat[0], fromStat[1], fromStat[2],
+                            Math.floor(fromStat[3] / step[1]) * step[1], 0, 0, 0);
+                    break;
+                case Ext.Date.DAY:
+                    testFrom = new Date(fromStat[0], fromStat[1],
+                            Math.floor(fromStat[2] - 1 / step[1]) * step[1] + 1, 0, 0, 0, 0);
+                    break;
+                case Ext.Date.MONTH:
+                    testFrom = new Date(fromStat[0], Math.floor(fromStat[1] / step[1]) * step[1], 1, 0, 0, 0, 0);
+                    break;
+                default: // Ext.Date.YEAR
+                    testFrom = new Date(Math.floor(fromStat[0] / step[1]) * step[1], 0, 1, 0, 0, 0, 0);
+                    break;
+            }
+        }
+
+        testTo = testFrom;
+        // TODO(zhangbei) : We can do it better somehow...
+        while (testTo &lt; to) {
+            testTo = Ext.Date.add(testTo, step[0], step[1]);
+            steps++;
+        }
+
+        if (lockEnds) {
+            testTo = to;
+        }
+        return {
+            from : +testFrom,
+            to : +testTo,
+            step : (testTo - testFrom) / steps,
+            steps : steps
+        };
+    },
+
+    sorter: function (a, b) {
+        return a.offset - b.offset;
+    },
+
+    rad: function(degrees) {
+        return degrees % 360 * Math.PI / 180;
+    },
+
+    degrees: function(radian) {
+        return radian * 180 / Math.PI % 360;
+    },
+
+    withinBox: function(x, y, bbox) {
+        bbox = bbox || {};
+        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));
+    },
+
+    parseGradient: function(gradient) {
+        var me = this,
+            type = gradient.type || 'linear',
+            angle = gradient.angle || 0,
+            radian = me.radian,
+            stops = gradient.stops,
+            stopsArr = [],
+            stop,
+            vector,
+            max,
+            stopObj;
+
+        if (type == 'linear') {
+            vector = [0, 0, Math.cos(angle * radian), Math.sin(angle * radian)];
+            max = 1 / (Math.max(Math.abs(vector[2]), Math.abs(vector[3])) || 1);
+            vector[2] *= max;
+            vector[3] *= max;
+            if (vector[2] &lt; 0) {
+                vector[0] = -vector[2];
+                vector[2] = 0;
+            }
+            if (vector[3] &lt; 0) {
+                vector[1] = -vector[3];
+                vector[3] = 0;
+            }
+        }
+
+        for (stop in stops) {
+            if (stops.hasOwnProperty(stop) &amp;&amp; me.stopsRE.test(stop)) {
+                stopObj = {
+                    offset: parseInt(stop, 10),
+                    color: Ext.draw.Color.toHex(stops[stop].color) || '#ffffff',
+                    opacity: stops[stop].opacity || 1
+                };
+                stopsArr.push(stopObj);
+            }
+        }
+        // Sort by pct property
+        Ext.Array.sort(stopsArr, me.sorter);
+        if (type == 'linear') {
+            return {
+                id: gradient.id,
+                type: type,
+                vector: vector,
+                stops: stopsArr
+            };
+        }
+        else {
+            return {
+                id: gradient.id,
+                type: type,
+                centerX: gradient.centerX,
+                centerY: gradient.centerY,
+                focalX: gradient.focalX,
+                focalY: gradient.focalY,
+                radius: gradient.radius,
+                vector: vector,
+                stops: stopsArr
+            };
+        }
     }
-});</pre>
+});
+
+</pre>
 </body>
 </html>