1 nv.models.pie = function() {
3 //============================================================
4 // Public Variables with Default Settings
5 //------------------------------------------------------------
7 var margin = {top: 0, right: 0, bottom: 0, left: 0}
10 , getX = function(d) { return d.x }
11 , getY = function(d) { return d.y }
12 , getDescription = function(d) { return d.description }
13 , id = Math.floor(Math.random() * 10000) //Create semi-unique ID in case user doesn't select one
14 , color = nv.utils.defaultColor()
15 , valueFormat = d3.format(',.2f')
17 , pieLabelsOutside = true
18 , donutLabelsOutside = false
20 , labelThreshold = .02 //if slice percentage is under this, don't show label
22 , labelSunbeamLayout = false
26 , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout')
29 //============================================================
32 function chart(selection) {
33 selection.each(function(data) {
34 var availableWidth = width - margin.left - margin.right,
35 availableHeight = height - margin.top - margin.bottom,
36 radius = Math.min(availableWidth, availableHeight) / 2,
37 arcRadius = radius-(radius / 5),
38 container = d3.select(this);
41 //------------------------------------------------------------
42 // Setup containers and skeleton of chart
44 //var wrap = container.selectAll('.nv-wrap.nv-pie').data([data]);
45 var wrap = container.selectAll('.nv-wrap.nv-pie').data(data);
46 var wrapEnter = wrap.enter().append('g').attr('class','nvd3 nv-wrap nv-pie nv-chart-' + id);
47 var gEnter = wrapEnter.append('g');
48 var g = wrap.select('g');
50 gEnter.append('g').attr('class', 'nv-pie');
52 wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
53 g.select('.nv-pie').attr('transform', 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')');
55 //------------------------------------------------------------
59 .on('click', function(d,i) {
69 var arc = d3.svg.arc()
70 .outerRadius(arcRadius);
72 if (startAngle) arc.startAngle(startAngle)
73 if (endAngle) arc.endAngle(endAngle);
74 if (donut) arc.innerRadius(radius * donutRatio);
76 // Setup the Pie chart and choose the data element
77 var pie = d3.layout.pie()
79 .value(function(d) { return d.disabled ? 0 : getY(d) });
81 var slices = wrap.select('.nv-pie').selectAll('.nv-slice')
84 slices.exit().remove();
86 var ae = slices.enter().append('g')
87 .attr('class', 'nv-slice')
88 .on('mouseover', function(d,i){
89 d3.select(this).classed('hover', true);
90 dispatch.elementMouseover({
95 pos: [d3.event.pageX, d3.event.pageY],
99 .on('mouseout', function(d,i){
100 d3.select(this).classed('hover', false);
101 dispatch.elementMouseout({
109 .on('click', function(d,i) {
110 dispatch.elementClick({
118 d3.event.stopPropagation();
120 .on('dblclick', function(d,i) {
121 dispatch.elementDblClick({
129 d3.event.stopPropagation();
133 .attr('fill', function(d,i) { return color(d, i); })
134 .attr('stroke', function(d,i) { return color(d, i); });
136 var paths = ae.append('path')
137 .each(function(d) { this._current = d; });
140 d3.transition(slices.select('path'))
142 //.attrTween('d', arcTween);
145 // This does the normal label
146 var labelsArc = d3.svg.arc().innerRadius(0);
148 if (pieLabelsOutside){ labelsArc = arc; }
150 if (donutLabelsOutside) { labelsArc = d3.svg.arc().outerRadius(arc.outerRadius()); }
152 ae.append("g").classed("nv-label", true)
153 .each(function(d, i) {
154 var group = d3.select(this);
157 .attr('transform', function(d) {
158 if (labelSunbeamLayout) {
159 d.outerRadius = arcRadius + 10; // Set Outer Coordinate
160 d.innerRadius = arcRadius + 15; // Set Inner Coordinate
161 var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI);
162 if ((d.startAngle+d.endAngle)/2 < Math.PI) {
167 return 'translate(' + labelsArc.centroid(d) + ') rotate(' + rotateAngle + ')';
169 d.outerRadius = radius + 10; // Set Outer Coordinate
170 d.innerRadius = radius + 15; // Set Inner Coordinate
171 return 'translate(' + labelsArc.centroid(d) + ')'
176 .style('stroke', '#fff')
177 .style('fill', '#fff')
182 .style('text-anchor', labelSunbeamLayout ? ((d.startAngle + d.endAngle) / 2 < Math.PI ? 'start' : 'end') : 'middle') //center the text on it's origin or begin/end if orthogonal aligned
183 .style('fill', '#000')
188 slices.select(".nv-label").transition()
189 .attr('transform', function(d) {
190 if (labelSunbeamLayout) {
191 d.outerRadius = arcRadius + 10; // Set Outer Coordinate
192 d.innerRadius = arcRadius + 15; // Set Inner Coordinate
193 var rotateAngle = (d.startAngle + d.endAngle) / 2 * (180 / Math.PI);
194 if ((d.startAngle+d.endAngle)/2 < Math.PI) {
199 return 'translate(' + labelsArc.centroid(d) + ') rotate(' + rotateAngle + ')';
201 d.outerRadius = radius + 10; // Set Outer Coordinate
202 d.innerRadius = radius + 15; // Set Inner Coordinate
203 return 'translate(' + labelsArc.centroid(d) + ')'
207 slices.each(function(d, i) {
208 var slice = d3.select(this);
211 .select(".nv-label text")
212 .style('text-anchor', labelSunbeamLayout ? ((d.startAngle + d.endAngle) / 2 < Math.PI ? 'start' : 'end') : 'middle') //center the text on it's origin or begin/end if orthogonal aligned
213 .text(function(d, i) {
214 var percent = (d.endAngle - d.startAngle) / (2 * Math.PI);
215 return Math.round(percent*1001/10)+"%";
217 "key" : getX(d.data),
218 "value": getY(d.data),
219 "percent": d3.format('%')(percent)
221 return (d.value && percent > labelThreshold) ? labelTypes[labelType] : '';
225 var textBox = slice.select('text').node().getBBox();
226 slice.select(".nv-label rect")
227 .attr("width", textBox.width + 10)
228 .attr("height", textBox.height + 10)
229 .attr("transform", function() {
230 return "translate(" + [textBox.x - 5, textBox.y - 5] + ")";
236 // Computes the angle of an arc, converting from radians to degrees.
238 var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
239 return a > 90 ? a - 180 : a;
242 function arcTween(a) {
243 a.endAngle = isNaN(a.endAngle) ? 0 : a.endAngle;
244 a.startAngle = isNaN(a.startAngle) ? 0 : a.startAngle;
245 if (!donut) a.innerRadius = 0;
246 var i = d3.interpolate(this._current, a);
247 this._current = i(0);
253 function tweenPie(b) {
255 var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
267 //============================================================
268 // Expose Public Variables
269 //------------------------------------------------------------
271 chart.dispatch = dispatch;
272 chart.options = nv.utils.optionsFunc.bind(chart);
274 chart.margin = function(_) {
275 if (!arguments.length) return margin;
276 margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
277 margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
278 margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
279 margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
283 chart.width = function(_) {
284 if (!arguments.length) return width;
289 chart.height = function(_) {
290 if (!arguments.length) return height;
295 chart.values = function(_) {
296 nv.log("pie.values() is no longer supported.");
300 chart.x = function(_) {
301 if (!arguments.length) return getX;
306 chart.y = function(_) {
307 if (!arguments.length) return getY;
308 getY = d3.functor(_);
312 chart.description = function(_) {
313 if (!arguments.length) return getDescription;
318 chart.showLabels = function(_) {
319 if (!arguments.length) return showLabels;
324 chart.labelSunbeamLayout = function(_) {
325 if (!arguments.length) return labelSunbeamLayout;
326 labelSunbeamLayout = _;
330 chart.donutLabelsOutside = function(_) {
331 if (!arguments.length) return donutLabelsOutside;
332 donutLabelsOutside = _;
336 chart.pieLabelsOutside = function(_) {
337 if (!arguments.length) return pieLabelsOutside;
338 pieLabelsOutside = _;
342 chart.labelType = function(_) {
343 if (!arguments.length) return labelType;
345 labelType = labelType || "key";
349 chart.donut = function(_) {
350 if (!arguments.length) return donut;
355 chart.donutRatio = function(_) {
356 if (!arguments.length) return donutRatio;
361 chart.startAngle = function(_) {
362 if (!arguments.length) return startAngle;
367 chart.endAngle = function(_) {
368 if (!arguments.length) return endAngle;
373 chart.id = function(_) {
374 if (!arguments.length) return id;
379 chart.color = function(_) {
380 if (!arguments.length) return color;
381 color = nv.utils.getColor(_);
385 chart.valueFormat = function(_) {
386 if (!arguments.length) return valueFormat;
391 chart.labelThreshold = function(_) {
392 if (!arguments.length) return labelThreshold;
396 //============================================================