#

API Docs for: 3.10.3
Show: Inherited Protected Private Deprecated

# File: charts/js/Fills.js

    /**
     * Provides functionality for drawing fills in a series.
     *
     * @module charts
     * @submodule series-fill-util
     */
    var Y_Lang = Y.Lang;

    /**
     * Utility class used for drawing area fills.
     *
     * @class Fills
     * @constructor
     * @submodule series-fill-util
     */
    function Fills() {}

    Fills.ATTRS = {
        area: {
            getter: function()
            {
                return this._defaults || this._getAreaDefaults();
            },

            setter: function(val)
            {
                var defaults = this._defaults || this._getAreaDefaults();
                this._defaults = Y.merge(defaults, val);
            }
        }
    };

    Fills.prototype = {
        /**
         * Returns a path shape used for drawing fills.
         *
         * @method _getPath
         * @return Path
         * @private
         */
        _getPath: function()
        {
            var path = this._path;
            if(!path)
            {
                path = this.get("graphic").addShape({type:"path"});
                this._path = path;
            }
            return path;
        },

        /**
         * Toggles visibility
         *
         * @method _toggleVisible
         * @param {Boolean} visible indicates visibilitye
         * @private
         */
        _toggleVisible: function(visible)
        {
            if(this._path)
            {
                this._path.set("visible", visible);
            }
        },

        /**
         * Draws fill
         *
         * @method drawFill
         * @param {Array} xcoords The x-coordinates for the series.
         * @param {Array} ycoords The y-coordinates for the series.
         * @protected
         */
        drawFill: function(xcoords, ycoords)
        {
            if(xcoords.length < 1)
            {
                return;
            }
            var isNumber = Y_Lang.isNumber,
                len = xcoords.length,
                firstX = xcoords[0],
                firstY = ycoords[0],
                lastValidX = firstX,
                lastValidY = firstY,
                nextX,
                nextY,
                pointValid,
                noPointsRendered = true,
                i = 0,
                styles = this.get("styles").area,
                path = this._getPath(),
                color = styles.color || this._getDefaultColor(this.get("graphOrder"), "slice");
            path.clear();
            path.set("fill", {
                color: color,
                opacity: styles.alpha
            });
            path.set("stroke", {weight: 0});
            for(; i < len; i = ++i)
            {
                nextX = xcoords[i];
                nextY = ycoords[i];
                pointValid = isNumber(nextX) && isNumber(nextY);
                if(!pointValid)
                {
                    continue;
                }
                if(noPointsRendered)
                {
                    this._firstValidX = nextX;
                    this._firstValidY = nextY;
                    noPointsRendered = false;
                    path.moveTo(nextX, nextY);
                }
                else
                {
                    path.lineTo(nextX, nextY);
                }
                lastValidX = nextX;
                lastValidY = nextY;
            }
            this._lastValidX = lastValidX;
            this._lastValidY = lastValidY;
            path.end();
        },

        /**
         * Draws a fill for a spline
         *
         * @method drawAreaSpline
         * @protected
         */
        drawAreaSpline: function()
        {
            if(this.get("xcoords").length < 1)
            {
                return;
            }
            var xcoords = this.get("xcoords"),
                ycoords = this.get("ycoords"),
                curvecoords = this.getCurveControlPoints(xcoords, ycoords),
                len = curvecoords.length,
                cx1,
                cx2,
                cy1,
                cy2,
                x,
                y,
                i = 0,
                firstX = xcoords[0],
                firstY = ycoords[0],
                styles = this.get("styles").area,
                path = this._getPath(),
                color = styles.color || this._getDefaultColor(this.get("graphOrder"), "slice");
            path.set("fill", {
                color: color,
                opacity: styles.alpha
            });
            path.set("stroke", {weight: 0});
            path.moveTo(firstX, firstY);
            for(; i < len; i = ++i)
            {
                x = curvecoords[i].endx;
                y = curvecoords[i].endy;
                cx1 = curvecoords[i].ctrlx1;
                cx2 = curvecoords[i].ctrlx2;
                cy1 = curvecoords[i].ctrly1;
                cy2 = curvecoords[i].ctrly2;
                path.curveTo(cx1, cy1, cx2, cy2, x, y);
            }
            if(this.get("direction") === "vertical")
            {
                path.lineTo(this._leftOrigin, y);
                path.lineTo(this._leftOrigin, firstY);
            }
            else
            {
                path.lineTo(x, this._bottomOrigin);
                path.lineTo(firstX, this._bottomOrigin);
            }
            path.lineTo(firstX, firstY);
            path.end();
        },

        /**
         * Draws a a stacked area spline
         *
         * @method drawStackedAreaSpline
         * @protected
         */
        drawStackedAreaSpline: function()
        {
            if(this.get("xcoords").length < 1)
            {
                return;
            }
            var xcoords = this.get("xcoords"),
                ycoords = this.get("ycoords"),
                curvecoords,
                order = this.get("order"),
                seriesCollection = this.get("seriesTypeCollection"),
                prevXCoords,
                prevYCoords,
                len,
                cx1,
                cx2,
                cy1,
                cy2,
                x,
                y,
                i = 0,
                firstX,
                firstY,
                styles = this.get("styles").area,
                path = this._getPath(),
                color = styles.color || this._getDefaultColor(this.get("graphOrder"), "slice");
            firstX = xcoords[0];
            firstY = ycoords[0];
            curvecoords = this.getCurveControlPoints(xcoords, ycoords);
            len = curvecoords.length;
            path.set("fill", {
                color: color,
                opacity: styles.alpha
            });
            path.set("stroke", {weight: 0});
            path.moveTo(firstX, firstY);
            for(; i < len; i = ++i)
            {
                x = curvecoords[i].endx;
                y = curvecoords[i].endy;
                cx1 = curvecoords[i].ctrlx1;
                cx2 = curvecoords[i].ctrlx2;
                cy1 = curvecoords[i].ctrly1;
                cy2 = curvecoords[i].ctrly2;
                path.curveTo(cx1, cy1, cx2, cy2, x, y);
            }
            if(order > 0)
            {
                prevXCoords = seriesCollection[order - 1].get("xcoords").concat().reverse();
                prevYCoords = seriesCollection[order - 1].get("ycoords").concat().reverse();
                curvecoords = this.getCurveControlPoints(prevXCoords, prevYCoords);
                i = 0;
                len = curvecoords.length;
                path.lineTo(prevXCoords[0], prevYCoords[0]);
                for(; i < len; i = ++i)
                {
                    x = curvecoords[i].endx;
                    y = curvecoords[i].endy;
                    cx1 = curvecoords[i].ctrlx1;
                    cx2 = curvecoords[i].ctrlx2;
                    cy1 = curvecoords[i].ctrly1;
                    cy2 = curvecoords[i].ctrly2;
                    path.curveTo(cx1, cy1, cx2, cy2, x, y);
                }
            }
            else
            {
                if(this.get("direction") === "vertical")
                {
                    path.lineTo(this._leftOrigin, ycoords[ycoords.length-1]);
                    path.lineTo(this._leftOrigin, firstY);
                }
                else
                {
                    path.lineTo(xcoords[xcoords.length-1], this._bottomOrigin);
                    path.lineTo(firstX, this._bottomOrigin);
                }

            }
            path.lineTo(firstX, firstY);
            path.end();
        },

        /**
         * Storage for default area styles.
         *
         * @property _defaults
         * @type Object
         * @private
         */
        _defaults: null,

        /**
         * Concatenates coordinate array with correct coordinates for closing an area fill.
         *
         * @method _getClosingPoints
         * @return Array
         * @protected
         */
        _getClosingPoints: function()
        {
            var xcoords = this.get("xcoords").concat(),
                ycoords = this.get("ycoords").concat(),
                firstValidIndex,
                lastValidIndex;
            if(this.get("direction") === "vertical")
            {
                lastValidIndex = this._getLastValidIndex(xcoords);
                firstValidIndex = this._getFirstValidIndex(xcoords);
                ycoords.push(ycoords[lastValidIndex]);
                ycoords.push(ycoords[firstValidIndex]);
                xcoords.push(this._leftOrigin);
                xcoords.push(this._leftOrigin);
            }
            else
            {
                lastValidIndex = this._getLastValidIndex(ycoords);
                firstValidIndex = this._getFirstValidIndex(ycoords);
                xcoords.push(xcoords[lastValidIndex]);
                xcoords.push(xcoords[firstValidIndex]);
                ycoords.push(this._bottomOrigin);
                ycoords.push(this._bottomOrigin);
            }
            xcoords.push(xcoords[0]);
            ycoords.push(ycoords[0]);
            return [xcoords, ycoords];
        },

        /**
         * Returns the order of the series closest to the current series that has a valid value for the current index.
         *
         * @method _getHighestValidOrder
         * @param {Array} seriesCollection Array of series of a given type.
         * @param {Number} index Index of the series item.
         * @param {Number} order Index of the the series in the seriesCollection
         * @param {String} direction Indicates the direction of the series
         * @return Number
         * @private
         */
        _getHighestValidOrder: function(seriesCollection, index, order, direction)
        {
            var coords = direction === "vertical" ? "stackedXCoords" : "stackedYCoords",
                coord;
            while(isNaN(coord) && order > -1)
            {
              order = order - 1;
              if(order > -1)
              {
                coord = seriesCollection[order].get(coords)[index];
              }
            }
            return order;
        },

        /**
         * Returns an array containing the x and y coordinates for a given series and index.
         *
         * @method _getCoordsByOrderAndIndex
         * @param {Array} seriesCollection Array of series of a given type.
         * @param {Number} index Index of the series item.
         * @param {Number} order Index of the the series in the seriesCollection
         * @param {String} direction Indicates the direction of the series
         * @return Array
         * @private
         */
        _getCoordsByOrderAndIndex: function(seriesCollection, index, order, direction)
        {
            var xcoord,
                ycoord;
            if(direction === "vertical")
            {
                xcoord = order < 0 ? this._leftOrigin : seriesCollection[order].get("stackedXCoords")[index];
                ycoord = this.get("stackedYCoords")[index];
            }
            else
            {
                xcoord = this.get("stackedXCoords")[index];
                ycoord = order < 0 ? this._bottomOrigin : seriesCollection[order].get("stackedYCoords")[index];
            }
            return [xcoord, ycoord];
        },

        /**
         * Concatenates coordinate array with the correct coordinates for closing an area stack.
         *
         * @method _getStackedClosingPoints
         * @return Array
         * @protected
         */
        _getStackedClosingPoints: function()
        {
            var order = this.get("order"),
                direction = this.get("direction"),
                seriesCollection = this.get("seriesTypeCollection"),
                firstValidIndex,
                lastValidIndex,
                xcoords = this.get("stackedXCoords"),
                ycoords = this.get("stackedYCoords"),
                limit,
                previousSeries,
                previousSeriesFirstValidIndex,
                previousSeriesLastValidIndex,
                previousXCoords,
                previousYCoords,
                coords,
                closingXCoords,
                closingYCoords,
                currentIndex,
                highestValidOrder,
                oldOrder;
            if(order < 1)
            {
              return this._getClosingPoints();
            }

            previousSeries = seriesCollection[order - 1];
            previousXCoords = previousSeries.get("stackedXCoords").concat();
            previousYCoords = previousSeries.get("stackedYCoords").concat();
            if(direction === "vertical")
            {
                firstValidIndex = this._getFirstValidIndex(xcoords);
                lastValidIndex = this._getLastValidIndex(xcoords);
                previousSeriesFirstValidIndex = previousSeries._getFirstValidIndex(previousXCoords);
                previousSeriesLastValidIndex = previousSeries._getLastValidIndex(previousXCoords);
            }
            else
            {
                firstValidIndex = this._getFirstValidIndex(ycoords);
                lastValidIndex = this._getLastValidIndex(ycoords);
                previousSeriesFirstValidIndex = previousSeries._getFirstValidIndex(previousYCoords);
                previousSeriesLastValidIndex = previousSeries._getLastValidIndex(previousYCoords);
            }
            if(previousSeriesLastValidIndex >= firstValidIndex && previousSeriesFirstValidIndex <= lastValidIndex)
            {
                previousSeriesFirstValidIndex = Math.max(firstValidIndex, previousSeriesFirstValidIndex);
                previousSeriesLastValidIndex = Math.min(lastValidIndex, previousSeriesLastValidIndex);
                previousXCoords = previousXCoords.slice(previousSeriesFirstValidIndex, previousSeriesLastValidIndex + 1);
                previousYCoords = previousYCoords.slice(previousSeriesFirstValidIndex, previousSeriesLastValidIndex + 1);
                limit = previousSeriesFirstValidIndex;
            }
            else
            {
                limit = lastValidIndex;
            }

            closingXCoords = [xcoords[firstValidIndex]];
            closingYCoords = [ycoords[firstValidIndex]];
            currentIndex = firstValidIndex;
            while((isNaN(highestValidOrder) || highestValidOrder < order - 1) && currentIndex <= limit)
            {
                oldOrder = highestValidOrder;
                highestValidOrder = this._getHighestValidOrder(seriesCollection, currentIndex, order, direction);
                if(!isNaN(oldOrder) && highestValidOrder > oldOrder)
                {
                    coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex, oldOrder, direction);
                    closingXCoords.push(coords[0]);
                    closingYCoords.push(coords[1]);
                }
                coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex, highestValidOrder, direction);
                closingXCoords.push(coords[0]);
                closingYCoords.push(coords[1]);
                currentIndex = currentIndex + 1;
            }
            if(previousXCoords &&
                previousXCoords.length > 0 &&
                previousSeriesLastValidIndex > firstValidIndex &&
                previousSeriesFirstValidIndex < lastValidIndex)
            {
                closingXCoords = closingXCoords.concat(previousXCoords);
                closingYCoords = closingYCoords.concat(previousYCoords);
                highestValidOrder = order -1;
            }
            currentIndex = Math.max(firstValidIndex, previousSeriesLastValidIndex);
            order = order - 1;
            highestValidOrder = NaN;
            while(currentIndex <= lastValidIndex)
            {
                oldOrder = highestValidOrder;
                highestValidOrder = this._getHighestValidOrder(seriesCollection, currentIndex, order, direction);
                if(!isNaN(oldOrder))
                {
                    if(highestValidOrder > oldOrder)
                    {
                        coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex, oldOrder, direction);
                        closingXCoords.push(coords[0]);
                        closingYCoords.push(coords[1]);
                    }
                    else if(highestValidOrder < oldOrder)
                    {
                        coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex - 1, highestValidOrder, direction);
                        closingXCoords.push(coords[0]);
                        closingYCoords.push(coords[1]);
                    }
                }
                coords = this._getCoordsByOrderAndIndex(seriesCollection, currentIndex, highestValidOrder, direction);
                closingXCoords.push(coords[0]);
                closingYCoords.push(coords[1]);
                currentIndex = currentIndex + 1;
            }

            closingXCoords.reverse();
            closingYCoords.reverse();
            return [xcoords.concat(closingXCoords), ycoords.concat(closingYCoords)];
        },

        /**
         * Returns default values for area styles.
         *
         * @method _getAreaDefaults
         * @return Object
         * @private
         */
        _getAreaDefaults: function()
        {
            return {
            };
        }
    };
    Y.augment(Fills, Y.Attribute);
    Y.Fills = Fills;