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

Paste-bin is for source code and general debugging text.

Login or Register to edit, delete and keep track of your pastes and more.

Raw Paste

Login or Register to edit or fork this paste. It's free.