g.pie.js
[g.raphael] / g.pie.js
1 /*
2  * g.Raphael 0.5 - Charting library, based on RaphaĆ«l
3  *
4  * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
5  * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
6  */
7 Raphael = typeof(Raphael) != 'undefined' ? Raphael :  (imports ? imports.seed.Raphael.Raphael : {});
8 Roo = typeof(Roo) != 'undefined' ? Roo:  (imports ? imports.seed.Roo.Roo: {});
9 //chartinst = typeof(chartinst) != 'undefined' ? chartinst:  (imports ? imports.chartinst.chartinst : {});
10  
11
12
13 (function () {
14
15     /**
16      * @param {Raphael} paper to draw on
17      * @param {int} cx - centre X
18      * @param {int} cy - centre Y
19      * @param {int} r - radius
20      * @param {Array} values
21      * @param {Object} opts options
22      *   cut : after this meany items - do not show a pie element?
23      *   
24      *   
25      * 
26      */
27
28     function Piechart(paper, cx, cy, r, values, opts) {
29         
30         opts = opts || {};
31         Roo.log('in????');
32         Roo.log(opts);
33         var chartinst = this,
34             sectors = [],
35             covers = paper.set(),
36             chart = paper.set(),
37             series = paper.set(),
38             order = [],
39             len = values.length,
40             angle = 0,
41             total = 0,
42             others = 0,
43             cut = opts.cut || 9,
44             defcut = true;
45             
46         function sector(cx, cy, r, startAngle, endAngle, fill) {
47             var rad = Math.PI / 180,
48                 x1 = cx + r * Math.cos(-startAngle * rad),
49                 x2 = cx + r * Math.cos(-endAngle * rad),
50                 xm = cx + r / 2 * Math.cos(-(startAngle + (endAngle - startAngle) / 2) * rad),
51                 y1 = cy + r * Math.sin(-startAngle * rad),
52                 y2 = cy + r * Math.sin(-endAngle * rad),
53                 ym = cy + r / 2 * Math.sin(-(startAngle + (endAngle - startAngle) / 2) * rad),
54                 res = [
55                     "M", cx, cy,
56                     "L", x1, y1,
57                     "A", r, r, 0, +(Math.abs(endAngle - startAngle) > 180), 1, x2, y2,
58                     "z"
59                 ];
60
61             res.middle = { x: xm, y: ym };
62             return res;
63         }
64         
65         chart.covers = covers;
66         Roo.log('show colors');
67         Roo.log(chartinst.colors);
68         if (len == 1) {
69             series.push(paper.circle(cx, cy, r).attr({ fill: opts.colors && opts.colors[0] || chartinst.colors[0] || "#666", stroke: opts.stroke || "#fff", "stroke-width": opts.strokewidth == null ? 1 : opts.strokewidth }));
70             covers.push(paper.circle(cx, cy, r).attr(chartinst.shim));
71             total = values[0] * 1;
72             values[0] = { value: values[0], order: 0, valueOf: function () { return this.value; } };
73             series[0].middle = {x: cx, y: cy};
74             series[0].mangle = 180;
75         } else {
76             for (var i = 0; i < len; i++) {
77                 total += values[i] * 1;
78                 values[i] = {
79                         value: values[i],
80                         order: i,
81                         valueOf: function () { return this.value; }
82                 };
83             }
84             //Roo.log(total);
85             if (!opts.no_sort) {
86                 values.sort(function (a, b) {
87                     return b.value - a.value;
88                 });
89             }
90             for (i = 0; i < len; i++) {
91                 if (defcut && values[i] * 360 / total <= 1.5) {
92                     cut = i;
93                     defcut = false;
94                 }
95
96                 if (i > cut) {
97                     defcut = false;
98                     values[cut].value += values[i];
99                     values[cut].others = true;
100                     others = values[cut].value;
101                 }
102             }
103             
104             len = Math.min(cut + 1, values.length);
105             others && values.splice(len) && (values[cut].others = true);
106
107             for (i = 0; i < len; i++) {
108                 var mangle = angle - 360 * values[i] / total / 2;
109
110                 if (!i) {
111                     // start..
112                     if (typeof(opts.start_angle) == 'undefined') {
113                         angle = 90 - mangle;
114                     } else {
115                         angle = opts.start_angle;
116                     }
117                     
118                     //Roo.log('start angle: ' + angle);
119                     mangle = angle - 360 * values[i] / total / 2;
120                 }
121
122                 if (opts.init) {
123                     var ipath = sector(cx, cy, 1, angle, angle - 360 * values[i] / total).join(",");
124                 }
125
126                 var path = sector(cx, cy, r, angle, angle -= 360 * values[i] / total);
127                 
128                 var p = paper.path(opts.init ? ipath : path).attr({ fill: opts.colors && opts.colors[i] || chartinst.colors[i] || "#666", stroke: opts.stroke || "#fff", "stroke-width": (opts.strokewidth == null ? 1 : opts.strokewidth), "stroke-linejoin": "round" });
129
130                 p.value = values[i];
131                 p.middle = path.middle;
132                 p.mangle = mangle;
133                 sectors.push(p);
134                 series.push(p);
135                 opts.init && p.animate({ path: path.join(",") }, (+opts.init - 1) || 1000, ">");
136             }
137
138             for (i = 0; i < len; i++) {
139                 p = paper.path(sectors[i].attr("path")).attr(chartinst.shim);
140                 opts.href && opts.href[i] && p.attr({ href: opts.href[i] });
141                 p.attr = function () {};
142                 covers.push(p);
143                 series.push(p);
144             }
145         }
146
147         chart.hover = function (fin, fout) {
148             fout = fout || function () {};
149
150             var that = this;
151
152             for (var i = 0; i < len; i++) {
153                 (function (sector, cover, j) {
154                     var o = {
155                         sector: sector,
156                         cover: cover,
157                         cx: cx,
158                         cy: cy,
159                         mx: sector.middle.x,
160                         my: sector.middle.y,
161                         mangle: sector.mangle,
162                         r: r,
163                         value: values[j],
164                         j : j,
165                         total: total,
166                         label: that.labels && that.labels[j]
167                     };
168                     cover.mouseover(function () {
169                         fin.call(o);
170                     }).mouseout(function () {
171                         fout.call(o);
172                     });
173                 })(series[i], covers[i], i);
174             }
175             return this;
176         };
177
178         // x: where label could be put
179         // y: where label could be put
180         // value: value to show
181         // total: total number to count %
182         chart.each = function (f) {
183             var that = this;
184
185             for (var i = 0; i < len; i++) {
186                 (function (sector, cover, j) {
187                     var o = {
188                         sector: sector,
189                         cover: cover,
190                         cx: cx,
191                         cy: cy,
192                         x: sector.middle.x,
193                         y: sector.middle.y,
194                         mangle: sector.mangle,
195                         r: r,
196                          j : j,
197                         value: values[j],
198                         total: total,
199                         label: that.labels && that.labels[j]
200                     };
201                     f.call(o);
202                 })(series[i], covers[i], i);
203             }
204             return this;
205         };
206
207         chart.click = function (f) {
208             var that = this;
209
210             for (var i = 0; i < len; i++) {
211                 (function (sector, cover, j) {
212                     var o = {
213                         sector: sector,
214                         cover: cover,
215                         cx: cx,
216                         cy: cy,
217                         mx: sector.middle.x,
218                         my: sector.middle.y,
219                         mangle: sector.mangle,
220                         r: r,
221                         j : j,
222                         value: values[j],
223                         total: total,
224                         label: that.labels && that.labels[j]
225                     };
226                     cover.click(function () { f.call(o); });
227                 })(series[i], covers[i], i);
228             }
229             return this;
230         };
231
232         chart.inject = function (element) {
233             element.insertBefore(covers[0]);
234         };
235
236         var legend = function (labels, otherslabel, mark, dir) {
237             var x = cx + r + r / 5,
238                 y = cy,
239                 h = y + 10;
240             
241             labels = labels || [];
242             dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "east";
243             mark = paper[mark && mark.toLowerCase()] || "circle";
244             chart.labels = paper.set();
245
246             for (var i = 0; i < len; i++) {
247                 var clr = series[i].attr("fill"),
248                     j = values[i].order,
249                     txt;
250
251                 values[i].others && (labels[j] = otherslabel || "Others");
252                 labels[j] = chartinst.labelise(labels[j], values[i], total);
253                 chart.labels.push(paper.set());
254                 chart.labels[i].push(paper[mark](x + 5, h, 5).attr({ fill: clr, stroke: "none" }));
255                 chart.labels[i].push(
256                     txt = paper.text(x + 20, h, labels[j] || values[j]).attr(chartinst.txtattr).attr({ fill: opts.legendcolor || "#000", "text-anchor": "start"}));
257                 covers[i].label = chart.labels[i];
258                // Roo.log(JSON.stringify(txt.getBBox()));
259                 h += txt.getBBox().height * 1.2;
260             }
261
262             var bb = chart.labels.getBBox(),
263                 tr = {
264                     east: [0, -bb.height / 2],
265                     west: [-bb.width - 2 * r - 20, -bb.height / 2],
266                     north: [-r - bb.width / 2, -r - bb.height - 10],
267                     south: [-r - bb.width / 2, r + 10]
268                 }[dir];
269
270             chart.labels.translate.apply(chart.labels, tr);
271             chart.push(chart.labels);
272         };
273
274         if (opts.legend) {
275             legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos);
276         }
277
278         chart.push(series, covers);
279         chart.series = series;
280         chart.covers = covers;
281         
282         chart.sector = sector;
283         chart.cx = cx;
284         chart.cy = cy;
285         chart.r = r;
286         return chart;
287     };
288     
289     //inheritance
290     var F = function() {};
291     F.prototype = Raphael.g;
292     Piechart.prototype = new F;
293     
294     //public
295     Raphael.fn.piechart = function(cx, cy, r, values, opts) {
296         return new Piechart(this, cx, cy, r, values, opts);
297     }
298     
299 })();