File: charts/js/StackingUtil.js
- /**
- * Provides functionality for creating stacked series.
- *
- * @module charts
- * @submodule series-stacked
- */
- var Y_Lang = Y.Lang;
-
- /**
- * Utility class used for creating stacked series.
- *
- * @module charts
- * @class StackingUtil
- * @constructor
- * @submodule series-stacked
- */
- function StackingUtil(){}
-
- StackingUtil.prototype = {
- /**
- * Indicates whether the series is stacked.
- *
- * @property _stacked
- * @private
- */
- _stacked: true,
-
- /**
- * @protected
- *
- * Adjusts coordinate values for stacked series.
- *
- * @method _stackCoordinates
- */
- _stackCoordinates: function()
- {
- if(this.get("direction") === "vertical")
- {
- this._stackXCoords();
- }
- else
- {
- this._stackYCoords();
- }
- },
-
- /**
- * Stacks coordinates for a stacked vertical series.
- *
- * @method _stackXCoords
- * @protected
- */
- _stackXCoords: function()
- {
- var order = this.get("order"),
- seriesCollection = this.get("seriesTypeCollection"),
- i = 0,
- xcoords = this.get("xcoords"),
- ycoords = this.get("ycoords"),
- len,
- coord,
- prevCoord,
- prevOrder,
- stackedXCoords = xcoords.concat(),
- prevXCoords,
- prevYCoords,
- nullIndices = [],
- nullIndex;
- if(order > 0)
- {
- prevXCoords = seriesCollection[order - 1].get("stackedXCoords");
- prevYCoords = seriesCollection[order - 1].get("stackedYCoords");
- len = prevXCoords.length;
- }
- else
- {
- len = xcoords.length;
- }
- for(; i < len; i = i + 1)
- {
- if(Y_Lang.isNumber(xcoords[i]))
- {
- if(order > 0)
- {
- prevCoord = prevXCoords[i];
- if(!Y_Lang.isNumber(prevCoord))
- {
- prevOrder = order;
- while(prevOrder > - 1 && !Y_Lang.isNumber(prevCoord))
- {
- prevOrder = prevOrder - 1;
- if(prevOrder > -1)
- {
- prevCoord = seriesCollection[prevOrder].get("stackedXCoords")[i];
- }
- else
- {
- prevCoord = this._leftOrigin;
- }
- }
- }
- xcoords[i] = xcoords[i] + prevCoord;
- }
- stackedXCoords[i] = xcoords[i];
- }
- else
- {
- nullIndices.push(i);
- }
- }
- this._cleanXNaN(stackedXCoords, ycoords);
- len = nullIndices.length;
- if(len > 0)
- {
- for(i = 0; i < len; i = i + 1)
- {
- nullIndex = nullIndices[i];
- coord = order > 0 ? prevXCoords[nullIndex] : this._leftOrigin;
- stackedXCoords[nullIndex] = Math.max(stackedXCoords[nullIndex], coord);
- }
- }
- this.set("stackedXCoords", stackedXCoords);
- this.set("stackedYCoords", ycoords);
- },
-
- /**
- * Stacks coordinates for a stacked horizontal series.
- *
- * @method _stackYCoords
- * @protected
- */
- _stackYCoords: function()
- {
- var order = this.get("order"),
- graphic = this.get("graphic"),
- h = graphic.get("height"),
- seriesCollection = this.get("seriesTypeCollection"),
- i = 0,
- xcoords = this.get("xcoords"),
- ycoords = this.get("ycoords"),
- len,
- coord,
- prevCoord,
- prevOrder,
- stackedYCoords = ycoords.concat(),
- prevXCoords,
- prevYCoords,
- nullIndices = [],
- nullIndex;
- if(order > 0)
- {
- prevXCoords = seriesCollection[order - 1].get("stackedXCoords");
- prevYCoords = seriesCollection[order - 1].get("stackedYCoords");
- len = prevYCoords.length;
- }
- else
- {
- len = ycoords.length;
- }
- for(; i < len; i = i + 1)
- {
- if(Y_Lang.isNumber(ycoords[i]))
- {
- if(order > 0)
- {
- prevCoord = prevYCoords[i];
- if(!Y_Lang.isNumber(prevCoord))
- {
- prevOrder = order;
- while(prevOrder > - 1 && !Y_Lang.isNumber(prevCoord))
- {
- prevOrder = prevOrder - 1;
- if(prevOrder > -1)
- {
- prevCoord = seriesCollection[prevOrder].get("stackedYCoords")[i];
- }
- else
- {
- prevCoord = this._bottomOrigin;
- }
- }
- }
- ycoords[i] = prevCoord - (h - ycoords[i]);
- }
- stackedYCoords[i] = ycoords[i];
- }
- else
- {
- nullIndices.push(i);
- }
- }
- this._cleanYNaN(xcoords, stackedYCoords);
- len = nullIndices.length;
- if(len > 0)
- {
- for(i = 0; i < len; i = i + 1)
- {
- nullIndex = nullIndices[i];
- coord = order > 0 ? prevYCoords[nullIndex] : h;
- stackedYCoords[nullIndex] = Math.min(stackedYCoords[nullIndex], coord);
- }
- }
- this.set("stackedXCoords", xcoords);
- this.set("stackedYCoords", stackedYCoords);
- },
-
- /**
- * Cleans invalid x-coordinates by calculating their value based on the corresponding y-coordinate, the
- * previous valid x-coordinate with its corresponding y-coordinate and the next valid x-coordinate with
- * its corresponding y-coordinate. If there is no previous or next valid x-coordinate, the value will not
- * be altered.
- *
- * @method _cleanXNaN
- * @param {Array} xcoords An array of x-coordinate values
- * @param {Array} ycoords An arry of y-coordinate values
- * @private
- */
- _cleanXNaN: function(xcoords, ycoords)
- {
- var previousValidIndex,
- nextValidIndex,
- previousValidX,
- previousValidY,
- x,
- y,
- nextValidX,
- nextValidY,
- isNumber = Y_Lang.isNumber,
- m,
- i = 0,
- len = ycoords.length;
- for(; i < len; ++i)
- {
- x = xcoords[i];
- y = ycoords[i];
- //if x is invalid, calculate where it should be
- if(!isNumber(x) && i > 0 && i < len - 1)
- {
- previousValidY = ycoords[i - 1];
- //check to see if the previous value is valid
- previousValidX = this._getPreviousValidCoordValue(xcoords, i);
- nextValidY = ycoords[i + 1];
- nextValidX = this._getNextValidCoordValue(xcoords, i);
- //check to see if the next value is valid
- if(isNumber(previousValidX) && isNumber(nextValidX))
- {
- //calculate slope and solve for x
- m = (nextValidY - previousValidY) / (nextValidX - previousValidX);
- xcoords[i] = (y + (m * previousValidX) - previousValidY)/m;
- }
- previousValidIndex = NaN;
- nextValidIndex = NaN;
- }
- }
- },
-
- /**
- * Returns the previous valid (numeric) value in an array if available.
- *
- * @method _getPreviousValidCoordValue
- * @param {Array} coords Array of values
- * @param {Number} index The index in the array in which to begin searching.
- * @return Number
- * @private
- */
- _getPreviousValidCoordValue: function(coords, index)
- {
- var coord,
- isNumber = Y_Lang.isNumber,
- limit = -1;
- while(!isNumber(coord) && index > limit)
- {
- index = index - 1;
- coord = coords[index];
- }
- return coord;
- },
-
- /**
- * Returns the next valid (numeric) value in an array if available.
- *
- * @method _getNextValidCoordValue
- * @param {Array} coords Array of values
- * @param {Number} index The index in the array in which to begin searching.
- * @return Number
- * @private
- */
- _getNextValidCoordValue: function(coords, index)
- {
- var coord,
- isNumber = Y_Lang.isNumber,
- limit = coords.length;
- while(!isNumber(coord) && index < limit)
- {
- index = index + 1;
- coord = coords[index];
- }
- return coord;
- },
-
- /**
- * Cleans invalid y-coordinates by calculating their value based on the corresponding x-coordinate, the
- * previous valid y-coordinate with its corresponding x-coordinate and the next valid y-coordinate with
- * its corresponding x-coordinate. If there is no previous or next valid y-coordinate, the value will not
- * be altered.
- *
- * @method _cleanYNaN
- * @param {Array} xcoords An array of x-coordinate values
- * @param {Array} ycoords An arry of y-coordinate values
- * @private
- */
- _cleanYNaN: function(xcoords, ycoords)
- {
- var previousValidIndex,
- nextValidIndex,
- previousValidX,
- previousValidY,
- x,
- y,
- nextValidX,
- nextValidY,
- isNumber = Y_Lang.isNumber,
- m,
- i = 0,
- len = xcoords.length;
- for(; i < len; ++i)
- {
- x = xcoords[i];
- y = ycoords[i];
- //if y is invalid, calculate where it should be
- if(!isNumber(y) && i > 0 && i < len - 1)
- {
- //check to see if the previous value is valid
- previousValidX = xcoords[i - 1];
- previousValidY = this._getPreviousValidCoordValue(ycoords, i);
- //check to see if the next value is valid
- nextValidX = xcoords[i + 1];
- nextValidY = this._getNextValidCoordValue(ycoords, i);
- if(isNumber(previousValidY) && isNumber(nextValidY))
- {
- //calculate slope and solve for y
- m = (nextValidY - previousValidY) / (nextValidX - previousValidX);
- ycoords[i] = previousValidY + ((m * x) - (m * previousValidX));
- }
- previousValidIndex = NaN;
- nextValidIndex = NaN;
- }
- }
- }
- };
- Y.StackingUtil = StackingUtil;
-
-