jui.define("chart.brush.pie", [ "util.base", "util.math", "util.color" ], function(_, math, ColorUtil) {
/**
* @class chart.brush.pie
* @extends chart.brush.core
*/
var PieBrush = function() {
var self = this, textY = 3;
var preAngle = 0, preRate = 0, preOpacity = 1;
var g, cache_active = {};
this.setActiveEvent = function(pie, centerX, centerY, centerAngle) {
var dist = this.chart.theme("pieActiveDistance"),
tx = Math.cos(math.radian(centerAngle)) * dist,
ty = Math.sin(math.radian(centerAngle)) * dist;
pie.translate(centerX + tx, centerY + ty);
}
this.setActiveTextEvent = function(pie, centerX, centerY, centerAngle, outerRadius, isActive) {
var dist = (isActive) ? this.chart.theme("pieActiveDistance") : 0,
cx = centerX + (Math.cos(math.radian(centerAngle)) * ((outerRadius + dist) / 2)),
cy = centerY + (Math.sin(math.radian(centerAngle)) * ((outerRadius + dist) / 2));
pie.translate(cx, cy);
}
this.getFormatText = function(target, value, max) {
var key = target;
if(typeof(this.brush.format) == "function") {
return this.format(key, value, max);
} else {
if (!value) {
return key;
}
return key + ": " + this.format(value);
}
}
this.drawPie = function(centerX, centerY, outerRadius, startAngle, endAngle, color) {
var pie = this.chart.svg.group();
if (endAngle == 360) { // if pie is full size, draw a circle as pie brush
var circle = this.chart.svg.circle({
cx : centerX,
cy : centerY,
r : outerRadius,
fill : color,
stroke : this.chart.theme("pieBorderColor") || color,
"stroke-width" : this.chart.theme("pieBorderWidth")
});
pie.append(circle);
return pie;
}
var path = this.chart.svg.path({
fill : color,
stroke : this.chart.theme("pieBorderColor") || color,
"stroke-width" : this.chart.theme("pieBorderWidth")
});
// 바깥 지름 부터 그림
var obj = math.rotate(0, -outerRadius, math.radian(startAngle)),
startX = obj.x,
startY = obj.y;
// 시작 하는 위치로 옮김
path.MoveTo(startX, startY);
// outer arc 에 대한 지점 설정
obj = math.rotate(startX, startY, math.radian(endAngle));
pie.translate(centerX, centerY);
// arc 그림
path.Arc(outerRadius, outerRadius, 0, (endAngle > 180) ? 1 : 0, 1, obj.x, obj.y)
.LineTo(0, 0)
.ClosePath();
pie.append(path);
pie.order = 1;
return pie;
}
this.drawPie3d = function(centerX, centerY, outerRadius, startAngle, endAngle, color) {
var pie = this.chart.svg.group(),
path = this.chart.svg.path({
fill : color,
stroke : this.chart.theme("pieBorderColor") || color,
"stroke-width" : this.chart.theme("pieBorderWidth")
});
// 바깥 지름 부터 그림
var obj = math.rotate(0, -outerRadius, math.radian(startAngle)),
startX = obj.x,
startY = obj.y;
// 시작 하는 위치로 옮김
path.MoveTo(startX, startY);
// outer arc 에 대한 지점 설정
obj = math.rotate(startX, startY, math.radian(endAngle));
pie.translate(centerX, centerY);
// arc 그림
path.Arc(outerRadius, outerRadius, 0, (endAngle > 180) ? 1 : 0, 1, obj.x, obj.y)
var y = obj.y + 10,
x = obj.x + 5,
targetX = startX + 5,
targetY = startY + 10;
path.LineTo(x, y);
path.Arc(outerRadius, outerRadius, 0, (endAngle > 180) ? 1 : 0, 0, targetX, targetY)
path.ClosePath();
pie.append(path);
pie.order = 1;
return pie;
}
this.drawText = function(centerX, centerY, centerAngle, outerRadius, text) {
var g = this.svg.group({
visibility: !this.brush.showText ? "hidden" : "visible"
}),
isLeft = (centerAngle + 90 > 180) ? true : false;
if(text === "" || !text) {
return g;
}
if(this.brush.showText == "inside") {
var cx = centerX + (Math.cos(math.radian(centerAngle)) * (outerRadius / 2)),
cy = centerY + (Math.sin(math.radian(centerAngle)) * (outerRadius / 2));
var text = this.chart.text({
"font-size": this.chart.theme("pieInnerFontSize"),
fill: this.chart.theme("pieInnerFontColor"),
"text-anchor": "middle",
y: textY
}, text);
text.translate(cx, cy);
g.append(text);
g.order = 2;
} else {
// TODO: 각도가 좁을 때, 텍스트와 라인을 보정하는 코드 개선 필요
var rate = this.chart.theme("pieOuterLineRate"),
diffAngle = Math.abs(centerAngle - preAngle);
if(diffAngle < 2) {
if(preRate == 0) {
preRate = rate;
}
var tick = rate * 0.05;
preRate -= tick;
preOpacity -= 0.25;
} else {
preRate = rate;
preOpacity = 1;
}
if(preRate > 1.2) {
var dist = this.chart.theme("pieOuterLineSize"),
r = outerRadius * preRate,
cx = centerX + (Math.cos(math.radian(centerAngle)) * outerRadius),
cy = centerY + (Math.sin(math.radian(centerAngle)) * outerRadius),
tx = centerX + (Math.cos(math.radian(centerAngle)) * r),
ty = centerY + (Math.sin(math.radian(centerAngle)) * r),
ex = (isLeft) ? tx - dist : tx + dist;
var path = this.svg.path({
fill: "transparent",
stroke: this.chart.theme("pieOuterLineColor"),
"stroke-width": this.chart.theme("pieOuterLineWidth"),
"stroke-opacity": preOpacity
});
path.MoveTo(cx, cy)
.LineTo(tx, ty)
.LineTo(ex, ty);
var text = this.chart.text({
"font-size": this.chart.theme("pieOuterFontSize"),
"fill": this.chart.theme("pieOuterFontColor"),
"fill-opacity": preOpacity,
"text-anchor": (isLeft) ? "end" : "start",
y: textY
}, text);
text.translate(ex + (isLeft ? -3 : 3), ty);
g.append(text);
g.append(path);
g.order = 0;
preAngle = centerAngle;
}
}
return g;
}
this.drawUnit = function (index, data, g) {
var props = this.getProperty(index),
centerX = props.centerX,
centerY = props.centerY,
outerRadius = props.outerRadius;
var target = this.brush.target,
active = this.brush.active,
all = 360,
startAngle = 0,
max = 0;
for (var i = 0; i < target.length; i++) {
max += data[target[i]];
}
for (var i = 0; i < target.length; i++) {
if(data[target[i]] == 0) continue;
var value = data[target[i]],
endAngle = all * (value / max);
if (this.brush['3d']) {
var pie3d = this.drawPie3d(centerX, centerY, outerRadius, startAngle, endAngle, ColorUtil.darken(this.color(i), 0.5));
g.append(pie3d);
}
startAngle += endAngle;
}
startAngle = 0;
for (var i = 0; i < target.length; i++) {
var value = data[target[i]],
endAngle = all * (value / max),
centerAngle = startAngle + (endAngle / 2) - 90,
pie = this.drawPie(centerX, centerY, outerRadius, startAngle, endAngle, this.color(i)),
text = this.drawText(centerX, centerY, centerAngle, outerRadius, this.getFormatText(target[i], value, max));
// 설정된 키 활성화
if (active == target[i] || _.inArray(target[i], active) != -1) {
if(this.brush.showText == "inside") {
this.setActiveTextEvent(text.get(0), centerX, centerY, centerAngle, outerRadius, true);
}
this.setActiveEvent(pie, centerX, centerY, centerAngle);
cache_active[centerAngle] = true;
}
// 활성화 이벤트 설정
if (this.brush.activeEvent != null) {
(function(p, t, cx, cy, ca, r) {
p.on(self.brush.activeEvent, function(e) {
if(!cache_active[ca]) {
if(self.brush.showText == "inside") {
self.setActiveTextEvent(t, cx, cy, ca, r, true);
}
self.setActiveEvent(p, cx, cy, ca);
cache_active[ca] = true;
} else {
if(self.brush.showText == "inside") {
self.setActiveTextEvent(t, cx, cy, ca, r, false);
}
p.translate(cx, cy);
cache_active[ca] = false;
}
});
p.attr({ cursor: "pointer" });
})(pie, text.get(0), centerX, centerY, centerAngle, outerRadius);
}
self.addEvent(pie, index, i);
g.append(pie);
g.append(text);
startAngle += endAngle;
}
}
this.drawNoData = function(g) {
var props = this.getProperty(0);
g.append(this.drawPie(props.centerX, props.centerY, props.outerRadius, 0, 360, this.chart.theme("pieNoDataBackgroundColor")));
}
this.drawBefore = function() {
g = this.chart.svg.group();
}
this.draw = function() {
if(this.listData().length == 0) {
this.drawNoData(g);
} else {
this.eachData(function(data, i) {
this.drawUnit(i, data, g);
});
}
return g;
}
this.getProperty = function(index) {
var obj = this.axis.c(index);
var width = obj.width,
height = obj.height,
x = obj.x,
y = obj.y,
min = width;
if (height < min) {
min = height;
}
return {
centerX: width / 2 + x,
centerY: height / 2 + y,
outerRadius: min / 2
}
}
}
PieBrush.setup = function() {
return {
/** @cfg {Boolean} [clip=false] If the brush is drawn outside of the chart, cut the area. */
clip: false,
/** @cfg {String} [showText=null] Set the text appear. (outside or inside) */
showText: null,
/** @cfg {Function} [format=null] Returns a value from the format callback function of a defined option. */
format: null,
/** @cfg {Boolean} [3d=false] check 3d support */
"3d": false,
/** @cfg {String|Array} [active=null] Activates the pie of an applicable property's name. */
active: null,
/** @cfg {String} [activeEvent=null] Activates the pie in question when a configured event occurs (click, mouseover, etc). */
activeEvent: null
}
}
return PieBrush;
}, "chart.brush.core");