/**
* Provides functionality for drawing lines in a series.
*
* @module charts
* @submodule series-line-util
*/
/**
* Utility class used for drawing lines.
*
* @class Lines
* @constructor
* @submodule series-line-util
*/
var Y_Lang = Y.Lang;
function Lines(){}
Lines.prototype = {
/**
* @property _lineDefaults
* @type Object
* @private
*/
_lineDefaults: null,
/**
* Creates a graphic in which to draw a series.
*
* @method _getGraphic
* @return Graphic
* @private
*/
_getGraphic: function()
{
var graphic = this.get("graphic") || this.get("graph").get("graphic");
if(!this._lineGraphic)
{
this._lineGraphic = graphic.addShape({type: "path"});
}
this._lineGraphic.clear();
return this._lineGraphic;
},
/**
* Toggles visibility
*
* @method _toggleVisible
* @param {Boolean} visible indicates visibilitye
* @private
*/
_toggleVisible: function(visible)
{
if(this._lineGraphic)
{
this._lineGraphic.set("visible", visible);
}
},
/**
* Draws lines for the series.
*
* @method drawLines
* @protected
*/
drawLines: function()
{
if(this.get("xcoords").length < 1)
{
return;
}
var isNumber = Y_Lang.isNumber,
xcoords,
ycoords,
direction = this.get("direction"),
len,
lastPointValid,
pointValid,
noPointsRendered = true,
lastValidX,
lastValidY,
nextX,
nextY,
i,
styles = this.get("styles").line,
lineType = styles.lineType,
lc = styles.color || this._getDefaultColor(this.get("graphOrder"), "line"),
lineAlpha = styles.alpha,
dashLength = styles.dashLength,
gapSpace = styles.gapSpace,
connectDiscontinuousPoints = styles.connectDiscontinuousPoints,
discontinuousType = styles.discontinuousType,
discontinuousDashLength = styles.discontinuousDashLength,
discontinuousGapSpace = styles.discontinuousGapSpace,
path = this._getGraphic();
if(this._stacked)
{
xcoords = this.get("stackedXCoords");
ycoords = this.get("stackedYCoords");
}
else
{
xcoords = this.get("xcoords");
ycoords = this.get("ycoords");
}
len = direction === "vertical" ? ycoords.length : xcoords.length;
path.set("stroke", {
weight: styles.weight,
color: lc,
opacity: lineAlpha
});
for(i = 0; i < len; i = ++i)
{
nextX = xcoords[i];
nextY = ycoords[i];
pointValid = isNumber(nextX) && isNumber(nextY);
if(!pointValid)
{
lastPointValid = pointValid;
continue;
}
if(noPointsRendered)
{
noPointsRendered = false;
path.moveTo(nextX, nextY);
}
else if(lastPointValid)
{
if(lineType !== "dashed")
{
path.lineTo(nextX, nextY);
}
else
{
this.drawDashedLine(path, lastValidX, lastValidY, nextX, nextY,
dashLength,
gapSpace);
}
}
else if(!connectDiscontinuousPoints)
{
path.moveTo(nextX, nextY);
}
else
{
if(discontinuousType !== "solid")
{
this.drawDashedLine(path, lastValidX, lastValidY, nextX, nextY,
discontinuousDashLength,
discontinuousGapSpace);
}
else
{
path.lineTo(nextX, nextY);
}
}
lastValidX = nextX;
lastValidY = nextY;
lastPointValid = true;
}
path.end();
},
/**
* Connects data points with a consistent curve for a series.
*
* @method drawSpline
* @protected
*/
drawSpline: 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,
styles = this.get("styles").line,
path = this._getGraphic(),
lineAlpha = styles.alpha,
color = styles.color || this._getDefaultColor(this.get("graphOrder"), "line");
path.set("stroke", {
weight: styles.weight,
color: color,
opacity: lineAlpha
});
path.moveTo(xcoords[0], ycoords[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);
}
path.end();
},
/**
* Draws a dashed line between two points.
*
* @method drawDashedLine
* @param {Number} xStart The x position of the start of the line
* @param {Number} yStart The y position of the start of the line
* @param {Number} xEnd The x position of the end of the line
* @param {Number} yEnd The y position of the end of the line
* @param {Number} dashSize the size of dashes, in pixels
* @param {Number} gapSize the size of gaps between dashes, in pixels
* @private
*/
drawDashedLine: function(path, xStart, yStart, xEnd, yEnd, dashSize, gapSize)
{
dashSize = dashSize || 10;
gapSize = gapSize || 10;
var segmentLength = dashSize + gapSize,
xDelta = xEnd - xStart,
yDelta = yEnd - yStart,
delta = Math.sqrt(Math.pow(xDelta, 2) + Math.pow(yDelta, 2)),
segmentCount = Math.floor(Math.abs(delta / segmentLength)),
radians = Math.atan2(yDelta, xDelta),
xCurrent = xStart,
yCurrent = yStart,
i;
xDelta = Math.cos(radians) * segmentLength;
yDelta = Math.sin(radians) * segmentLength;
for(i = 0; i < segmentCount; ++i)
{
path.moveTo(xCurrent, yCurrent);
path.lineTo(xCurrent + Math.cos(radians) * dashSize, yCurrent + Math.sin(radians) * dashSize);
xCurrent += xDelta;
yCurrent += yDelta;
}
path.moveTo(xCurrent, yCurrent);
delta = Math.sqrt((xEnd - xCurrent) * (xEnd - xCurrent) + (yEnd - yCurrent) * (yEnd - yCurrent));
if(delta > dashSize)
{
path.lineTo(xCurrent + Math.cos(radians) * dashSize, yCurrent + Math.sin(radians) * dashSize);
}
else if(delta > 0)
{
path.lineTo(xCurrent + Math.cos(radians) * delta, yCurrent + Math.sin(radians) * delta);
}
path.moveTo(xEnd, yEnd);
},
/**
* Default values for `styles` attribute.
*
* @method _getLineDefaults
* @return Object
* @protected
*/
_getLineDefaults: function()
{
return {
alpha: 1,
weight: 6,
lineType:"solid",
dashLength:10,
gapSpace:10,
connectDiscontinuousPoints:true,
discontinuousType:"solid",
discontinuousDashLength:10,
discontinuousGapSpace:10
};
}
};
Y.augment(Lines, Y.Attribute);
Y.Lines = Lines;