| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240 | <!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title>The source code</title>  <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 type="text/javascript">    function highlight() {      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";    }  </script></head><body onload="prettyPrint(); highlight();">  <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.draw.Draw', {    /* Begin Definitions */    singleton: true,    requires: ['Ext.draw.Color'],    /* End Definitions */    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: "along",        blur: null,        "clip-rect": "csv",        cx: null,        cy: null,        fill: "color",        "fill-opacity": null,        "font-size": null,        height: null,        opacity: null,        path: "path",        r: null,        rotation: "csv",        rx: null,        ry: null,        scale: "csv",        stroke: "color",        "stroke-opacity": null,        "stroke-width": null,        translation: "csv",        width: null,        x: null,        y: null    },    is: function(o, type) {        type = String(type).toLowerCase();        return (type == "object" && o === Object(o)) ||            (type == "undefined" && typeof o == type) ||            (type == "null" && o === null) ||            (type == "array" && Array.isArray && Array.isArray(o)) ||            (Object.prototype.toString.call(o).toLowerCase().slice(8, -1)) == type;    },    ellipsePath: function(sprite) {        var attr = sprite.attr;        return Ext.String.format("M{0},{1}A{2},{3},0,1,1,{0},{4}A{2},{3},0,1,1,{0},{1}z", 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("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", 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("M{0},{1}L{2},{1},{2},{3},{0},{3}z", attr.x, attr.y, attr.width + attr.x, attr.height + attr.y);        }    },    // To be deprecated, converts itself (an arrayPath) to a proper SVG path string    path2string: function () {        return this.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1");    },    // Convert the passed arrayPath to a proper SVG path string (d attribute)    pathToString: function(arrayPath) {        return arrayPath.join(",").replace(Ext.draw.Draw.pathToStringRE, "$1");    },    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, "array") && me.is(pathString[0], "array")) { // 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 && params.push(+b);                });                if (name == "m" && params.length > 2) {                    data.push([b].concat(Ext.Array.splice(params, 0, 2)));                    name = "l";                    b = (b == "m") ? "l" : "L";                }                while (params.length >= 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 < ii; i++) {            pathi = path[i];            for (j = 1, jj = pathi.length; j < 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, "array") || !this.is(pathArray && pathArray[0], "array")) { // rough assumption            pathArray = this.parsePathString(pathArray);        }        for (i = 0, ii = pathArray.length; i < ii; i++) {            res[i] = [];            for (j = 0, jj = pathArray[i].length; j < jj; j++) {                res[i][j] = pathArray[i][j];            }        }        res.toString = this.path2string;        return res;    },    pathToAbsolute: function (pathArray) {        if (!this.is(pathArray, "array") || !this.is(pathArray && pathArray[0], "array")) { // 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 && pathArray[0][0] == "M") {            x = +pathArray[0][1];            y = +pathArray[0][2];            mx = x;            my = y;            i++;            res[0] = ["M", x, y];        }        for (; i < 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 "A":                        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 "V":                        r[1] = +pathSegment[1] + y;                        break;                    // Horizontal LineTo                    case "H":                        r[1] = +pathSegment[1] + x;                        break;                    case "M":                    // MoveTo                        mx = +pathSegment[1] + x;                        my = +pathSegment[2] + y;                    default:                        j = 1;                        ln2 = pathSegment.length;                        for (; j < ln2; j++) {                            r[j] = +pathSegment[j] + ((j % 2) ? x : y);                        }                }            }            else {                j = 0;                ln2 = pathSegment.length;                for (; j < ln2; j++) {                    res[i][j] = pathSegment[j];                }            }            switch (r[0]) {                // ClosePath                case "Z":                    x = mx;                    y = my;                    break;                // Horizontal LineTo                case "H":                    x = r[1];                    break;                // Vertical LineTo                case "V":                    y = r[1];                    break;                // MoveTo                case "M":                    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, "array") || !this.is(pathArray && pathArray[0], "array")) {            pathArray = this.parsePathString(pathArray);        }        var res = [],            x = 0,            y = 0,            mx = 0,            my = 0,            start = 0,            r,            pa,            i,            j,            k,            len,            ii,            jj,            kk;                if (pathArray[0][0] == "M") {            x = pathArray[0][1];            y = pathArray[0][2];            mx = x;            my = y;            start++;            res.push(["M", x, y]);        }        for (i = start, ii = pathArray.length; i < ii; i++) {            r = res[i] = [];            pa = pathArray[i];            if (pa[0] != pa[0].toLowerCase()) {                r[0] = pa[0].toLowerCase();                switch (r[0]) {                    case "a":                        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 "v":                        r[1] = +(pa[1] - y).toFixed(3);                        break;                    case "m":                        mx = pa[1];                        my = pa[2];                    default:                        for (j = 1, jj = pa.length; j < jj; j++) {                            r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);                        }                }            } else {                r = res[i] = [];                if (pa[0] == "m") {                    mx = pa[1] + x;                    my = pa[2] + y;                }                for (k = 0, kk = pa.length; k < kk; k++) {                    res[i][k] = pa[k];                }            }            len = res[i].length;            switch (res[i][0]) {                case "z":                    x = mx;                    y = my;                    break;                case "h":                    x += +res[i][len - 1];                    break;                case "v":                    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 < ln; i++) {            points[i] = me.command2curve(points[i], attrs);            if (points[i].length > 7) {                    points[i].shift();                    point = points[i];                    while (point.length) {                        Ext.Array.splice(points, i++, 0, ["C"].concat(Ext.Array.splice(point, 0, 6)));                    }                    Ext.Array.erase(points, i, 1);                    ln = points.length;                    i--;                }            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 > 7) {                    pp[i].shift();                    var pi = pp[i];                    while (pi.length) {                        Ext.Array.splice(pp, i++, 0, ["C"].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 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {                    Ext.Array.splice(path2, i, 0, ["M", 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);                }            },            i, ii,            seg, seg2, seglen, seg2len;        for (i = 0, ii = Math.max(p.length, p2.length || 0); i < 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);            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 ["C", d.x, d.y, d.x, d.y, d.x, d.y];        }        if (pathCommand[0] != "T" && pathCommand[0] != "Q") {            d.qx = d.qy = null;        }        switch (pathCommand[0]) {            case "M":                d.X = pathCommand[1];                d.Y = pathCommand[2];                break;            case "A":                pathCommand = ["C"].concat(me.arc2curve.apply(me, [d.x, d.y].concat(pathCommand.slice(1))));                break;            case "S":                pathCommand = ["C", d.x + (d.x - (d.bx || d.x)), d.y + (d.y - (d.by || d.y))].concat(pathCommand.slice(1));                break;            case "T":                d.qx = d.x + (d.x - (d.qx || d.x));                d.qy = d.y + (d.y - (d.qy || d.y));                pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, d.qx, d.qy, pathCommand[1], pathCommand[2]));                break;            case "Q":                d.qx = pathCommand[1];                d.qy = pathCommand[2];                pathCommand = ["C"].concat(me.quadratic2curve(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[3], pathCommand[4]));                break;            case "L":                pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], pathCommand[2], pathCommand[1], pathCommand[2]);                break;            case "H":                pathCommand = ["C"].concat(d.x, d.y, pathCommand[1], d.y, pathCommand[1], d.y);                break;            case "V":                pathCommand = ["C"].concat(d.x, d.y, d.x, pathCommand[1], d.x, pathCommand[1]);                break;            case "Z":                pathCommand = ["C"].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 > 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 < cx ? PI - f1 : f1;            f2 = x2 < cx ? PI - f2 : f2;            if (f1 < 0) {                f1 = PI * 2 + f1;            }            if (f2 < 0) {                f2 = PI * 2 + f2;            }            if (sweep_flag && f1 > f2) {                f1 = f1 - PI * 2;            }            if (!sweep_flag && f2 > f1) {                f2 = f2 - PI * 2;            }        }        else {            f1 = recursive[0];            f2 = recursive[1];            cx = recursive[2];            cy = recursive[3];        }        df = f2 - f1;        if (mabs(df) > _120) {            f2old = f2;            x2old = x2;            y2old = y2;            f2 = f1 + _120 * (sweep_flag && f2 > 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(",");            newres = [];            ln = res.length;            for (i = 0;  i < 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 && !dx && !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] == "A") {                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 + "")) {            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 < ln; i++) {            p = path[i];            if (p[0] == "M") {                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]) > (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 < ln; ++i) {            cp2 = clipPolygon[i];            inputList = outputList;            outputList = [];            s = inputList[inputList.length - 1];            j = 0;            ln2 = inputList.length;            for (; j < 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;    },        bezier : function (a, b, c, d, x) {        if (x === 0) {            return a;        }         else if (x === 1) {            return d;        }        var du = 1 - x,            d3 = du * du * du,            r = x / du;        return d3 * (a + r * (3 * b + r * (3 * c + d * r)));    },        bezierDim : function (a, b, c, d) {        var points = [], r,            A, top, C, delta, bottom, s,            min, max, i;        // The min and max happens on boundary or b' == 0        if (a + 3 * c == d + 3 * b) {               r = a - b;            r /= 2 * (a - b - b + c);            if ( r < 1 && r > 0) {                points.push(r);            }        } else {            // b'(x) / -3 = (a-3b+3c-d)x^2+ (-2a+4b-2c)x + (a-b)            // delta = -4 (-b^2+a c+b c-c^2-a d+b d)            A = a - 3 * b + 3 * c - d;            top = 2 * (a - b - b + c);            C = a - b;            delta = top * top - 4 * A * C;            bottom = A + A;            if (delta === 0) {                r = top / bottom;                if (r < 1 && r > 0) {                    points.push(r);                }            } else if (delta > 0) {                s = Math.sqrt(delta);                r = (s + top) / bottom;                                if (r < 1 && r > 0) {                    points.push(r);                }                                r = (top - s) / bottom;                                if (r < 1 && r > 0) {                    points.push(r);                }            }        }        min = Math.min(a, d);        max = Math.max(a, d);        for (i = 0; i < points.length; i++) {            min = Math.min(min, this.bezier(a, b, c, d, points[i]));            max = Math.max(max, this.bezier(a, b, c, d, points[i]));        }        return [min, max];    },        curveDim: function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {        var x = this.bezierDim(p1x, c1x, c2x, p2x),            y = this.bezierDim(p1y, c1y, c2y, p2y);        return {            min: {                x: x[0],                y: y[0]            },            max: {                x: x[1],                y: y[1]            }        };    },<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 >= prevY && curY >= nextY) || (curY <= prevY && curY <= nextY)) {            control1Angle = control2Angle = halfPI;        } else {            control1Angle = atan((curX - prevX) / abs(curY - prevY));            if (prevY < curY) {                control1Angle = PI - control1Angle;            }            control2Angle = atan((nextX - curX) / abs(curY - nextY));            if (nextY < 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 > 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 > prevY && control1Y < prevY) || (curY < prevY && control1Y > prevY)) {            control1X += abs(prevY - control1Y) * (control1X - curX) / (control1Y - curY);            control1Y = prevY;        }        if ((curY > nextY && control2Y < nextY) || (curY < nextY && control2Y > 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,            pathi,            pathil,            pathim,            pathiml,            pathip,            pathipl,            begl;                for (; i < ii; i++) {            pathi = path[i];            pathil = pathi.length;            pathim = path[i - 1];            pathiml = pathim.length;            pathip = path[i + 1];            pathipl = pathip && pathip.length;            if (pathi[0] == "M") {                mx = pathi[1];                my = pathi[2];                j = i + 1;                while (path[j][0] != "C") {                    j++;                }                cx = path[j][5];                cy = path[j][6];                newp.push(["M", mx, my]);                beg = newp.length;                x = mx;                y = my;                continue;            }            if (pathi[pathil - 2] == mx && pathi[pathil - 1] == my && (!pathip || pathip[0] == "M")) {                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] == "M") {                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(["C", 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>     * @private     */    snapEnds: function (from, to, stepsMax, prettyNumbers) {        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;                if(prettyNumbers){            for (i = 0; i < ln; i++) {                value = interval[i][0];                weight = (value - modulo) < 0 ? 1e6 : (value - modulo) / interval[i][1];                if (weight < 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 < to) {                cur += step;                stepCount++;            }            to = +cur.toFixed(10);        }else{            stepCount = stepsMax;        }                return {            from: from,            to: to,            power: level,            step: step,            steps: stepCount        };    },<span id='Ext-draw-Draw-method-snapEndsByDate'>    /**</span>     * snapEndsByDate is a utility method to deduce an appropriate tick configuration for the data set of given     * feature. Refer to {@link #snapEnds}.     *     * @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:     *     - from: The result start value, which may be lower than the original start value     *     - to: The result end value, which may be higher than the original end value     *     - step: The value size of each step     *     - 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]]            ],            sLen         = scales.length,            stop         = false,            scale, j, yearDiff, s;        // Find the most desirable scale        for (s = 0; s < sLen; s++) {            scale = scales[s];            if (!stop) {                for (j = 0; j < scale[1].length; j++) {                    if (to < Ext.Date.add(from, scale[0], scale[1][j] * stepsMax)) {                        selectedStep = [scale[0], scale[1][j]];                        stop         = true;                        break;                    }                }            }        }        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>     * snapEndsByDateAndStep is 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     */    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 < 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 >= bbox.x && x <= (bbox.x + bbox.width) && y >= bbox.y && y <= (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] < 0) {                vector[0] = -vector[2];                vector[2] = 0;            }            if (vector[3] < 0) {                vector[1] = -vector[3];                vector[3] = 0;            }        }        for (stop in stops) {            if (stops.hasOwnProperty(stop) && 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></body></html>
 |