API Docs for: 3.8.0
Show:

File: charts/js/Fills.js

  1. /**
  2.  * Utility class used for drawing area fills.
  3.  *
  4.  * @module charts
  5.  * @class Fills
  6.  * @constructor
  7.  */
  8. function Fills(cfg)
  9. {
  10.     var attrs = {
  11.         area: {
  12.             getter: function()
  13.             {
  14.                 return this._defaults || this._getAreaDefaults();
  15.             },

  16.             setter: function(val)
  17.             {
  18.                 var defaults = this._defaults || this._getAreaDefaults();
  19.                 this._defaults = Y.merge(defaults, val);
  20.             }
  21.         }
  22.     };
  23.     this.addAttrs(attrs, cfg);
  24.     this.get("styles");
  25. }

  26. Fills.prototype = {
  27.     /**
  28.      * Returns a path shape used for drawing fills.
  29.      *
  30.      * @method _getPath
  31.      * @return Path
  32.      * @private
  33.      */
  34.     _getPath: function()
  35.     {
  36.         var path = this._path;
  37.         if(!path)
  38.         {
  39.             path = this.get("graph").get("graphic").addShape({type:"path"});
  40.             this._path = path;
  41.         }
  42.         return path;
  43.     },

  44.     /**
  45.      * Toggles visibility
  46.      *
  47.      * @method _toggleVisible
  48.      * @param {Boolean} visible indicates visibilitye
  49.      * @private
  50.      */
  51.     _toggleVisible: function(visible)
  52.     {
  53.         if(this._path)
  54.         {
  55.             this._path.set("visible", visible);
  56.         }
  57.     },

  58.     /**
  59.      * Draws fill
  60.      *
  61.      * @method drawFill
  62.      * @param {Array} xcoords The x-coordinates for the series.
  63.      * @param {Array} ycoords The y-coordinates for the series.
  64.      * @protected
  65.      */
  66.     drawFill: function(xcoords, ycoords)
  67.     {
  68.         if(xcoords.length < 1)
  69.         {
  70.             return;
  71.         }
  72.         var isNumber = Y_Lang.isNumber,
  73.             len = xcoords.length,
  74.             firstX = xcoords[0],
  75.             firstY = ycoords[0],
  76.             lastValidX = firstX,
  77.             lastValidY = firstY,
  78.             nextX,
  79.             nextY,
  80.             pointValid,
  81.             noPointsRendered = true,
  82.             i = 0,
  83.             styles = this.get("styles").area,
  84.             path = this._getPath(),
  85.             color = styles.color || this._getDefaultColor(this.get("graphOrder"), "slice");
  86.         path.clear();
  87.         path.set("fill", {
  88.             color: color,
  89.             opacity: styles.alpha
  90.         });
  91.         path.set("stroke", {weight: 0});
  92.         for(; i < len; i = ++i)
  93.         {
  94.             nextX = xcoords[i];
  95.             nextY = ycoords[i];
  96.             pointValid = isNumber(nextX) && isNumber(nextY);
  97.             if(!pointValid)
  98.             {
  99.                 continue;
  100.             }
  101.             if(noPointsRendered)
  102.             {
  103.                 this._firstValidX = nextX;
  104.                 this._firstValidY = nextY;
  105.                 noPointsRendered = false;
  106.                 path.moveTo(nextX, nextY);
  107.             }
  108.             else
  109.             {
  110.                 path.lineTo(nextX, nextY);
  111.             }
  112.             lastValidX = nextX;
  113.             lastValidY = nextY;
  114.         }
  115.         this._lastValidX = lastValidX;
  116.         this._lastValidY = lastValidY;
  117.         path.end();
  118.     },

  119.     /**
  120.      * Draws a fill for a spline
  121.      *
  122.      * @method drawAreaSpline
  123.      * @protected
  124.      */
  125.     drawAreaSpline: function()
  126.     {
  127.         if(this.get("xcoords").length < 1)
  128.         {
  129.             return;
  130.         }
  131.         var xcoords = this.get("xcoords"),
  132.             ycoords = this.get("ycoords"),
  133.             curvecoords = this.getCurveControlPoints(xcoords, ycoords),
  134.             len = curvecoords.length,
  135.             cx1,
  136.             cx2,
  137.             cy1,
  138.             cy2,
  139.             x,
  140.             y,
  141.             i = 0,
  142.             firstX = xcoords[0],
  143.             firstY = ycoords[0],
  144.             styles = this.get("styles").area,
  145.             path = this._getPath(),
  146.             color = styles.color || this._getDefaultColor(this.get("graphOrder"), "slice");
  147.         path.set("fill", {
  148.             color: color,
  149.             opacity: styles.alpha
  150.         });
  151.         path.set("stroke", {weight: 0});
  152.         path.moveTo(firstX, firstY);
  153.         for(; i < len; i = ++i)
  154.         {
  155.             x = curvecoords[i].endx;
  156.             y = curvecoords[i].endy;
  157.             cx1 = curvecoords[i].ctrlx1;
  158.             cx2 = curvecoords[i].ctrlx2;
  159.             cy1 = curvecoords[i].ctrly1;
  160.             cy2 = curvecoords[i].ctrly2;
  161.             path.curveTo(cx1, cy1, cx2, cy2, x, y);
  162.         }
  163.         if(this.get("direction") === "vertical")
  164.         {
  165.             path.lineTo(this._leftOrigin, y);
  166.             path.lineTo(this._leftOrigin, firstY);
  167.         }
  168.         else
  169.         {
  170.             path.lineTo(x, this._bottomOrigin);
  171.             path.lineTo(firstX, this._bottomOrigin);
  172.         }
  173.         path.lineTo(firstX, firstY);
  174.         path.end();
  175.     },

  176.     /**
  177.      * Draws a a stacked area spline
  178.      *
  179.      * @method drawStackedAreaSpline
  180.      * @protected
  181.      */
  182.     drawStackedAreaSpline: function()
  183.     {
  184.         if(this.get("xcoords").length < 1)
  185.         {
  186.             return;
  187.         }
  188.         var xcoords = this.get("xcoords"),
  189.             ycoords = this.get("ycoords"),
  190.             curvecoords,
  191.             order = this.get("order"),
  192.             type = this.get("type"),
  193.             graph = this.get("graph"),
  194.             seriesCollection = graph.seriesTypes[type],
  195.             prevXCoords,
  196.             prevYCoords,
  197.             len,
  198.             cx1,
  199.             cx2,
  200.             cy1,
  201.             cy2,
  202.             x,
  203.             y,
  204.             i = 0,
  205.             firstX,
  206.             firstY,
  207.             styles = this.get("styles").area,
  208.             path = this._getPath(),
  209.             color = styles.color || this._getDefaultColor(this.get("graphOrder"), "slice");
  210.         firstX = xcoords[0];
  211.         firstY = ycoords[0];
  212.         curvecoords = this.getCurveControlPoints(xcoords, ycoords);
  213.         len = curvecoords.length;
  214.         path.set("fill", {
  215.             color: color,
  216.             opacity: styles.alpha
  217.         });
  218.         path.set("stroke", {weight: 0});
  219.         path.moveTo(firstX, firstY);
  220.         for(; i < len; i = ++i)
  221.         {
  222.             x = curvecoords[i].endx;
  223.             y = curvecoords[i].endy;
  224.             cx1 = curvecoords[i].ctrlx1;
  225.             cx2 = curvecoords[i].ctrlx2;
  226.             cy1 = curvecoords[i].ctrly1;
  227.             cy2 = curvecoords[i].ctrly2;
  228.             path.curveTo(cx1, cy1, cx2, cy2, x, y);
  229.         }
  230.         if(order > 0)
  231.         {
  232.             prevXCoords = seriesCollection[order - 1].get("xcoords").concat().reverse();
  233.             prevYCoords = seriesCollection[order - 1].get("ycoords").concat().reverse();
  234.             curvecoords = this.getCurveControlPoints(prevXCoords, prevYCoords);
  235.             i = 0;
  236.             len = curvecoords.length;
  237.             path.lineTo(prevXCoords[0], prevYCoords[0]);
  238.             for(; i < len; i = ++i)
  239.             {
  240.                 x = curvecoords[i].endx;
  241.                 y = curvecoords[i].endy;
  242.                 cx1 = curvecoords[i].ctrlx1;
  243.                 cx2 = curvecoords[i].ctrlx2;
  244.                 cy1 = curvecoords[i].ctrly1;
  245.                 cy2 = curvecoords[i].ctrly2;
  246.                 path.curveTo(cx1, cy1, cx2, cy2, x, y);
  247.             }
  248.         }
  249.         else
  250.         {
  251.             if(this.get("direction") === "vertical")
  252.             {
  253.                 path.lineTo(this._leftOrigin, ycoords[ycoords.length-1]);
  254.                 path.lineTo(this._leftOrigin, firstY);
  255.             }
  256.             else
  257.             {
  258.                 path.lineTo(xcoords[xcoords.length-1], this._bottomOrigin);
  259.                 path.lineTo(firstX, this._bottomOrigin);
  260.             }

  261.         }
  262.         path.lineTo(firstX, firstY);
  263.         path.end();
  264.     },

  265.     /**
  266.      * Storage for default area styles.
  267.      *
  268.      * @property _defaults
  269.      * @type Object
  270.      * @private
  271.      */
  272.     _defaults: null,

  273.     /**
  274.      * Concatenates coordinate array with correct coordinates for closing an area fill.
  275.      *
  276.      * @method _getClosingPoints
  277.      * @return Array
  278.      * @protected
  279.      */
  280.     _getClosingPoints: function()
  281.     {
  282.         var xcoords = this.get("xcoords").concat(),
  283.             ycoords = this.get("ycoords").concat(),
  284.             firstValidIndex,
  285.             lastValidIndex;
  286.         if(this.get("direction") === "vertical")
  287.         {
  288.             lastValidIndex = this._getLastValidIndex(xcoords);
  289.             firstValidIndex = this._getFirstValidIndex(xcoords);
  290.             ycoords.push(ycoords[lastValidIndex]);
  291.             ycoords.push(ycoords[firstValidIndex]);
  292.             xcoords.push(this._leftOrigin);
  293.             xcoords.push(this._leftOrigin);
  294.         }
  295.         else
  296.         {
  297.             lastValidIndex = this._getLastValidIndex(ycoords);
  298.             firstValidIndex = this._getFirstValidIndex(ycoords);
  299.             xcoords.push(xcoords[lastValidIndex]);
  300.             xcoords.push(xcoords[firstValidIndex]);
  301.             ycoords.push(this._bottomOrigin);
  302.             ycoords.push(this._bottomOrigin);
  303.         }
  304.         xcoords.push(xcoords[0]);
  305.         ycoords.push(ycoords[0]);
  306.         return [xcoords, ycoords];
  307.     },

  308.     /**
  309.      * Returns the order of the series closest to the current series that has a valid value for the current index.
  310.      *
  311.      * @method _getHighestValidOrder
  312.      * @param {Array} seriesCollection Array of series of a given type.
  313.      * @param {Number} index Index of the series item.
  314.      * @param {Number} order Index of the the series in the seriesCollection
  315.      * @param {String} direction Indicates the direction of the series
  316.      * @return Number
  317.      * @private
  318.      */
  319.     _getHighestValidOrder: function(seriesCollection, index, order, direction)
  320.     {
  321.         var coords = direction == "vertical" ? "stackedXCoords" : "stackedYCoords",
  322.             coord;
  323.         while(isNaN(coord) && order > -1)
  324.         {
  325.           order = order - 1;
  326.           if(order > -1)
  327.           {
  328.             coord = seriesCollection[order].get(coords)[index];
  329.           }
  330.         }
  331.         return order;
  332.     },

  333.     /**
  334.      * Returns an array containing the x and y coordinates for a given series and index.
  335.      *
  336.      * @method _getCoordsByOrderAndIndex
  337.      * @param {Array} seriesCollection Array of series of a given type.
  338.      * @param {Number} index Index of the series item.
  339.      * @param {Number} order Index of the the series in the seriesCollection
  340.      * @param {String} direction Indicates the direction of the series
  341.      * @return Array
  342.      * @private
  343.      */
  344.     _getCoordsByOrderAndIndex: function(seriesCollection, index, order, direction)
  345.     {
  346.         var xcoord,
  347.             ycoord;
  348.         if(direction == "vertical")
  349.         {
  350.             xcoord = order < 0 ? this._leftOrigin : seriesCollection[order].get("stackedXCoords")[index];
  351.             ycoord = this.get("stackedYCoords")[index];
  352.         }
  353.         else
  354.         {
  355.             xcoord = this.get("stackedXCoords")[index];
  356.             ycoord = order < 0 ? this._bottomOrigin : seriesCollection[order].get("stackedYCoords")[index];
  357.         }
  358.         return [xcoord, ycoord];
  359.     },

  360.     /**
  361.      * Concatenates coordinate array with the correct coordinates for closing an area stack.
  362.      *
  363.      * @method _getStackedClosingPoints
  364.      * @return Array
  365.      * @protected
  366.      */
  367.     _getStackedClosingPoints: function()
  368.     {
  369.         var order = this.get("order"),
  370.             type = this.get("type"),
  371.             graph = this.get("graph"),
  372.             direction = this.get("direction"),
  373.             seriesCollection = graph.seriesTypes[type],
  374.             firstValidIndex,
  375.             lastValidIndex,
  376.             xcoords = this.get("stackedXCoords"),
  377.             ycoords = this.get("stackedYCoords"),
  378.             limit,
  379.             previousSeries,
  380.             previousSeriesFirstValidIndex,
  381.             previousSeriesLastValidIndex,
  382.             previousXCoords,
  383.             previousYCoords,
  384.             coords,
  385.             closingXCoords,
  386.             closingYCoords,
  387.             currentIndex,
  388.             highestValidOrder,
  389.             oldOrder;
  390.         if(order < 1)
  391.         {
  392.           return this._getClosingPoints();
  393.         }

  394.         previousSeries = seriesCollection[order - 1];
  395.         previousXCoords = previousSeries.get("stackedXCoords").concat();
  396.         previousYCoords = previousSeries.get("stackedYCoords").concat();
  397.         if(direction == "vertical")
  398.         {
  399.             firstValidIndex = this._getFirstValidIndex(xcoords);
  400.             lastValidIndex = this._getLastValidIndex(xcoords);
  401.             previousSeriesFirstValidIndex = previousSeries._getFirstValidIndex(previousXCoords);
  402.             previousSeriesLastValidIndex = previousSeries._getLastValidIndex(previousXCoords);
  403.         }
  404.         else
  405.         {
  406.             firstValidIndex = this._getFirstValidIndex(ycoords);
  407.             lastValidIndex = this._getLastValidIndex(ycoords);
  408.             previousSeriesFirstValidIndex = previousSeries._getFirstValidIndex(previousYCoords);
  409.             previousSeriesLastValidIndex = previousSeries._getLastValidIndex(previousYCoords);
  410.         }
  411.         if(previousSeriesLastValidIndex >= firstValidIndex && previousSeriesFirstValidIndex <= lastValidIndex)
  412.         {
  413.             previousSeriesFirstValidIndex = Math.max(firstValidIndex, previousSeriesFirstValidIndex);
  414.             previousSeriesLastValidIndex = Math.min(lastValidIndex, previousSeriesLastValidIndex);
  415.             previousXCoords = previousXCoords.slice(previousSeriesFirstValidIndex, previousSeriesLastValidIndex + 1);
  416.             previousYCoords = previousYCoords.slice(previousSeriesFirstValidIndex, previousSeriesLastValidIndex + 1);
  417.             limit = previousSeriesFirstValidIndex;
  418.         }
  419.         else
  420.         {
  421.             limit = lastValidIndex;
  422.         }

  423.         closingXCoords = [xcoords[firstValidIndex]];
  424.         closingYCoords = [ycoords[firstValidIndex]];
  425.         currentIndex = firstValidIndex;
  426.         while((isNaN(highestValidOrder) || highestValidOrder < order - 1) && currentIndex <= limit)
  427.         {
  428.             oldOrder = highestValidOrder;
  429.             highestValidOrder = this._getHighestValidOrder(seriesCollection, currentIndex, order, direction);
  430.             if(!isNaN(oldOrder) && highestValidOrder > oldOrder)
  431.             {
  432.                 coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex, oldOrder, direction);
  433.                 closingXCoords.push(coords[0]);
  434.                 closingYCoords.push(coords[1]);
  435.             }
  436.             coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex, highestValidOrder, direction);
  437.             closingXCoords.push(coords[0]);
  438.             closingYCoords.push(coords[1]);
  439.             currentIndex = currentIndex + 1;
  440.         }
  441.         if(previousXCoords && previousXCoords.length > 0 && previousSeriesLastValidIndex > firstValidIndex && previousSeriesFirstValidIndex < lastValidIndex)
  442.         {
  443.             closingXCoords = closingXCoords.concat(previousXCoords);
  444.             closingYCoords = closingYCoords.concat(previousYCoords);
  445.             highestValidOrder = order -1;
  446.         }
  447.         currentIndex = Math.max(firstValidIndex, previousSeriesLastValidIndex);
  448.         order = order - 1;
  449.         highestValidOrder = NaN;
  450.         while(currentIndex <= lastValidIndex)
  451.         {
  452.             oldOrder = highestValidOrder;
  453.             highestValidOrder = this._getHighestValidOrder(seriesCollection, currentIndex, order, direction);
  454.             if(!isNaN(oldOrder))
  455.             {
  456.                 if(highestValidOrder > oldOrder)
  457.                 {
  458.                     coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex, oldOrder, direction);
  459.                     closingXCoords.push(coords[0]);
  460.                     closingYCoords.push(coords[1]);
  461.                 }
  462.                 else if(highestValidOrder < oldOrder)
  463.                 {
  464.                     coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex - 1, highestValidOrder, direction);
  465.                     closingXCoords.push(coords[0]);
  466.                     closingYCoords.push(coords[1]);
  467.                 }
  468.             }
  469.             coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex, highestValidOrder, direction);
  470.             closingXCoords.push(coords[0]);
  471.             closingYCoords.push(coords[1]);
  472.             currentIndex = currentIndex + 1;
  473.         }

  474.         closingXCoords.reverse();
  475.         closingYCoords.reverse();
  476.         return [xcoords.concat(closingXCoords), ycoords.concat(closingYCoords)];
  477.     },

  478.     /**
  479.      * Returns default values for area styles.
  480.      *
  481.      * @method _getAreaDefaults
  482.      * @return Object
  483.      * @private
  484.      */
  485.     _getAreaDefaults: function()
  486.     {
  487.         return {
  488.         };
  489.     }
  490. };
  491. Y.augment(Fills, Y.Attribute);
  492. Y.Fills = Fills;

  493.