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");