/**
* Drawing Polyline
* If you want to see a polyline using DOM Rendering, you need the [Raphael.js](http://raphaeljs.com) 2.1.0 or above.
*
* @class
* @extends collie.DisplayObject
* @requires http://raphaeljs.com
* @param {Object} [htOption] Options
* @param {String} [htOption.strokeColor]
* @param {Number} [htOption.strokeWidth=0] When this option set as 0, The stroke disappears.
* @param {String} [htOption.fillColor]
* @param {String} [htOption.fillImage] Fill the image inside a polyline
* @param {String} [htOption.closePath=false] Closing a Path
* @param {String} [htOption.dashArray] ["", "-", ".", "-.", "-..", ". ", "- ", "--", "- .", "--.", "--.."]
* @param {String} [htOption.lineCap="butt"] ["butt", "square", "round"]
* @param {String} [htOption.lineJoin="miter"] ["bevel", "round", "miter"]
* @param {Number} [htOption.miterLimit=10]
* @example
* // Draw a Rectangle
* var line = new collie.Polyline({
* closePath : true
* }).addTo(layer);
* line.setPointData([
* [0, 0],
* [100, 0],
* [100, 100],
* [0, 100]
* ]);
*
* // using moveTo
* line.moveTo(200, 0);
* line.lineTo(300, 0);
* line.lineTo(300, 100);
* line.lineTo(200, 100);
* line.lineTo(200, 0); // expand boundary and set change status.
*/
collie.Polyline = collie.Class(/** @lends collie.Polyline.prototype */{
$init : function (htOption) {
this.option({
strokeColor : '#000000',
strokeWidth : 1,
fillColor : '',
fillImage : '',
lineCap : "butt",
lineJoin : "miter",
miterLimit : 10,
dashArray : "",
closePath : false // 마지막을 자동으로 연결해 줌
}, null, true);
this._aPointData = [];
this._oPaper = null;
this._htPointBoundary = {
right : null,
bottom : null
};
},
/**
* Set points for drawing
*
* @param {Array} aPointData [[x1, y1, bMoveTo], [x2, y2, bMoveTo], ...]
* @param {Boolean} bSkipExpandSize You can this option set as true if you don't want to expand size
*/
setPointData : function (aPointData, bSkipExpandSize) {
this._aPointData = aPointData;
this.setChanged();
if (!bSkipExpandSize) {
this._expandBoundary(aPointData);
}
},
/**
* Return points
*
* @return {Array}
*/
getPointData : function () {
return this._aPointData;
},
/**
* Add a point
* @param {Number} nX
* @param {Number} nY
* @param {Boolean} bSkipExpandSize You can this option set as true if you don't want to expand size
*/
addPoint : function (nX, nY, bSkipExpandSize) {
this._aPointData.push([nX, nY]);
this.setChanged();
if (!bSkipExpandSize) {
this._expandBoundary(nX, nY);
}
},
/**
* Move a cursor position
*
* @param {Number} nX
* @param {Number} nY
*/
moveTo : function (nX, nY) {
this._aPointData.push([nX, nY, true]);
},
/**
* Alias for the addPoint method
* @see collie.Polyline#addPoint
*/
lineTo : function (nX, nY, bSkipExpandSize) {
this.addPoint(nX, nY, bSkipExpandSize);
},
/**
* Reset points
*/
resetPointData : function () {
this._aPointData = [];
this._htPointBoundary = {
right : null,
bottom : null
};
this.setChanged();
},
/**
* 포인트 영역을 늘린다
* @private
*
* @param {Array|Number} nX 배열로 들어오면 배열을 돌면서 확장 한다
* @param {Number} nY
* @param {Boolean} bSkipAdoptSize 크기를 객체에 적용하는 것을 생략한다
*/
_expandBoundary : function (nX, nY, bSkipAdoptSize) {
if (nX instanceof Array) {
for (var i = 0, len = nX.length; i < len; i++) {
this._expandBoundary(nX[i][0], nX[i][1], true);
}
} else {
this._htPointBoundary.right = this._htPointBoundary.right === null ? nX : Math.max(nX, this._htPointBoundary.right);
this._htPointBoundary.bottom = this._htPointBoundary.bottom === null ? nY : Math.max(nY, this._htPointBoundary.bottom);
}
// 크기 적용
if (!bSkipAdoptSize) {
var nStrokeWidth = this._htOption.strokeWidth * (collie.Renderer.isRetinaDisplay() ? 2 : 1);
var nWidth = this._htPointBoundary.right + nStrokeWidth * 2;
var nHeight = this._htPointBoundary.bottom + nStrokeWidth * 2;
this.set({
width : nWidth,
height : nHeight
});
if (this._oPaper !== null) {
this._oPaper.setSize(nWidth, nHeight);
}
}
},
/**
* Delegate
* @private
*/
onDOMDraw : function (oEvent) {
var el = oEvent.element;
// 점이 2개 미만이면 그리지 않는다
if (this._aPointData.length < 2) {
return;
}
if (typeof Raphael === "undefined") {
return;
}
var htInfo = this.get();
var nStrokeWidth = htInfo.strokeWidth;
var htDirty = this.getDirty();
if (this._oPaper === null) {
this._oPaper = Raphael(el, htInfo.width, htInfo.height);
this._oPaper.canvas.style.zIndex = 10;
} else if (htDirty && (htDirty.width || htDirty.height)) {
this._oPaper.setSize(htInfo.width, htInfo.height);
}
el.style.left = -(nStrokeWidth / 2) + "px";
el.style.top = -(nStrokeWidth / 2) + "px";
this._oPaper.clear();
var str = "M" + this._aPointData[0][0] + "," + this._aPointData[0][1];
for (var i = 1, len = this._aPointData.length; i < len; i++) {
str += (this._aPointData[i][2] ? "M" : "L") + this._aPointData[i][0] + "," + this._aPointData[i][1];
}
// 마지막이 연결되어 있지 않다면
if (
htInfo.closePath && (
this._aPointData[0][0] !== this._aPointData[this._aPointData.length - 1][0] ||
this._aPointData[0][1] !== this._aPointData[this._aPointData.length - 1][1]
)
) {
str += "L" + this._aPointData[0][0] + "," + this._aPointData[0][1];
}
var line = this._oPaper.path(str + (htInfo.closePath ? "Z" : ""));
line.transform("t" + (nStrokeWidth / 2) + "," + (nStrokeWidth / 2));
if (htInfo.fillImage) {
collie.ImageManager.getImage(htInfo.fillImage, function (el) {
line.attr("fill", "url('" + el.src + "')");
});
} else if (htInfo.fillColor) {
line.attr("fill", htInfo.fillColor);
}
if (htInfo.lineCap) {
line.attr("stroke-linecap", htInfo.lineCap);
}
if (htInfo.lineJoin) {
line.attr("stroke-linejoin", htInfo.lineJoin);
}
if (htInfo.miterLimit !== null) {
line.attr("stroke-miterlimit", htInfo.miterLimit);
}
if (htInfo.strokeColor) {
line.attr("stroke", htInfo.strokeColor);
}
line.attr("stroke-width", nStrokeWidth);
if (htInfo.dashArray) {
line.attr("stroke-dasharray", htInfo.dashArray);
}
},
/**
* Delegate
* @private
*/
onCanvasDraw : function (oEvent) {
// 점이 2개 미만이면 그리지 않는다
if (this._aPointData.length < 2) {
return;
}
var htInfo = this.get();
var oContext = oEvent.context;
var bIsRetinaDisplay = collie.Renderer.isRetinaDisplay();
var nStrokeWidth = htInfo.strokeWidth;
var nRatio = (bIsRetinaDisplay ? 2 : 1);
oContext.save();
oContext.translate(oEvent.x, oEvent.y);
// 레티나 디스플레이 대응
if (bIsRetinaDisplay) {
nStrokeWidth *= 2;
}
if (htInfo.fillImage) {
var el = collie.ImageManager.getImage(htInfo.fillImage);
if (!el) {
collie.ImageManager.getImage(htInfo.fillImage, function () {
this.setChanged();
}.bind(this));
} else {
var pattern = oContext.createPattern(el, "repeat");
oContext.fillStyle = pattern;
}
} else if (htInfo.fillColor) {
oContext.fillStyle = htInfo.fillColor;
}
if (htInfo.strokeColor) {
oContext.strokeStyle = htInfo.strokeColor;
}
oContext.lineWidth = nStrokeWidth;
if (htInfo.lineCap) {
oContext.lineCap = htInfo.lineCap;
}
if (htInfo.lineJoin) {
oContext.lineJoin = htInfo.lineJoin;
}
if (htInfo.miterLimit) {
oContext.miterLimit = htInfo.miterLimit;
}
if (htInfo.dashArray) {
// moveTo를 하면 fill이 안된다 연결된 선을 하나 더 그려서 해결한다
if (htInfo.fillColor || htInfo.fillImage) {
oContext.beginPath();
oContext.moveTo(this._aPointData[0][0] * nRatio, this._aPointData[0][1] * nRatio);
for (var i = 1, len = this._aPointData.length; i < len; i++) {
if (this._aPointData[i][2]) {
oContext.moveTo(this._aPointData[i][0] * nRatio, this._aPointData[i][1] * nRatio);
continue;
}
oContext.lineTo(this._aPointData[i][0] * nRatio, this._aPointData[i][1] * nRatio);
}
if (htInfo.closePath) {
oContext.closePath();
}
oContext.fill();
}
oContext.resetDashedLine();
}
// 앞에 그린 선은 지워진다
oContext.beginPath();
oContext.moveTo(this._aPointData[0][0] * nRatio, this._aPointData[0][1] * nRatio);
for (var i = 1, len = this._aPointData.length; i < len; i++) {
// moveTo
if (this._aPointData[i][2]) {
oContext.moveTo(this._aPointData[i][0] * nRatio, this._aPointData[i][1] * nRatio);
continue;
}
if (htInfo.dashArray) {
oContext.dashedLine(this._aPointData[i - 1][0] * nRatio, this._aPointData[i - 1][1] * nRatio, this._aPointData[i][0] * nRatio, this._aPointData[i][1] * nRatio, collie.raphaelDashArray[htInfo.dashArray], nStrokeWidth);
} else {
oContext.lineTo(this._aPointData[i][0] * nRatio, this._aPointData[i][1] * nRatio);
}
}
// 마지막이 연결되어 있지 않다면
if (
htInfo.dashArray && htInfo.closePath && (
this._aPointData[0][0] !== this._aPointData[this._aPointData.length - 1][0] ||
this._aPointData[0][1] !== this._aPointData[this._aPointData.length - 1][1]
)) {
oContext.dashedLine(this._aPointData[i - 1][0] * nRatio, this._aPointData[i - 1][1] * nRatio, this._aPointData[0][0] * nRatio, this._aPointData[0][1] * nRatio, collie.raphaelDashArray[htInfo.dashArray], nStrokeWidth);
}
if (htInfo.closePath) {
oContext.closePath();
}
if (!htInfo.dashArray && (htInfo.fillColor || htInfo.fillImage)) {
oContext.fill();
}
if (htInfo.strokeWidth) {
oContext.stroke();
}
oContext.restore();
},
/**
* Returns information of The Class as String
*
* @return {String}
*/
toString : function () {
return "Polyline" + (this._htOption.name ? " " + this._htOption.name : "")+ " #" + this.getId() + (this.getImage() ? "(image:" + this.getImage().src + ")" : "");
}
}, collie.DisplayObject);