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