]> git.proxmox.com Git - sencha-touch.git/blob - src/src/chart/series/Pie.js
import Sencha Touch 2.4.2 source
[sencha-touch.git] / src / src / chart / series / Pie.js
1 /**
2 * @class Ext.chart.series.Pie
3 * @extends Ext.chart.series.Polar
4 *
5 * Creates a Pie Chart. A Pie Chart is a useful visualization technique to display quantitative information for different
6 * categories that also have a meaning as a whole.
7 * As with all other series, the Pie Series must be appended in the *series* Chart array configuration. See the Chart
8 * documentation for more information. A typical configuration object for the pie series could be:
9 *
10 * @example preview
11 * var chart = new Ext.chart.PolarChart({
12 * animate: true,
13 * interactions: ['rotate'],
14 * colors: ['#115fa6', '#94ae0a', '#a61120', '#ff8809', '#ffd13e'],
15 * store: {
16 * fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
17 * data: [
18 * {name: 'metric one', data1: 10, data2: 12, data3: 14, data4: 8, data5: 13},
19 * {name: 'metric two', data1: 7, data2: 8, data3: 16, data4: 10, data5: 3},
20 * {name: 'metric three', data1: 5, data2: 2, data3: 14, data4: 12, data5: 7},
21 * {name: 'metric four', data1: 2, data2: 14, data3: 6, data4: 1, data5: 23},
22 * {name: 'metric five', data1: 27, data2: 38, data3: 36, data4: 13, data5: 33}
23 * ]
24 * },
25 * series: [{
26 * type: 'pie',
27 * label: {
28 * field: 'name',
29 * display: 'rotate'
30 * },
31 * xField: 'data3',
32 * donut: 30
33 * }]
34 * });
35 * Ext.Viewport.setLayout('fit');
36 * Ext.Viewport.add(chart);
37 *
38 * In this configuration we set `pie` as the type for the series, set an object with specific style properties for highlighting options
39 * (triggered when hovering elements). We also set true to `showInLegend` so all the pie slices can be represented by a legend item.
40 * We set `data1` as the value of the field to determine the angle span for each pie slice. We also set a label configuration object
41 * where we set the field name of the store field to be rendered as text for the label. The labels will also be displayed rotated.
42 * We set `contrast` to `true` to flip the color of the label if it is to similar to the background color. Finally, we set the font family
43 * and size through the `font` parameter.
44 *
45 */
46 Ext.define('Ext.chart.series.Pie', {
47 extend: 'Ext.chart.series.Polar',
48 requires: [
49 'Ext.chart.series.sprite.PieSlice'
50 ],
51 type: 'pie',
52 alias: 'series.pie',
53 seriesType: 'pieslice',
54
55 config: {
56 /**
57 * @cfg {String} labelField
58 * @deprecated Use {@link Ext.chart.series.Pie#label} instead.
59 * The store record field name to be used for the pie slice labels.
60 */
61 labelField: false,
62
63 /**
64 * @cfg {Number} donut Specifies the radius of the donut hole, as a percentage of the chart's radius.
65 * Defaults to 0 (no donut hole).
66 */
67 donut: 0,
68
69 /**
70 * @cfg {String} field
71 * @deprecated Use xField directly
72 */
73 field: null,
74
75 /**
76 * @cfg {Number} rotation The starting angle of the pie slices.
77 */
78 rotation: 0,
79
80 /**
81 * @cfg {Number} [totalAngle=2*PI] The total angle of the pie series.
82 */
83 totalAngle: Math.PI * 2,
84
85 /**
86 * @cfg {Array} hidden Determines which pie slices are hidden.
87 */
88 hidden: [],
89
90 /**
91 * @cfg {Number} Allows adjustment of the radius by a spefic perfentage.
92 */
93 radiusFactor: 100,
94
95 style: {
96
97 }
98 },
99
100 directions: ['X'],
101
102 setField: function (f) {
103 return this.setXField(f);
104 },
105
106 getField: function () {
107 return this.getXField();
108 },
109
110 applyRadius : function (radius) {
111 return radius * this.getRadiusFactor() * 0.01;
112 },
113
114 updateLabelData: function () {
115 var me = this,
116 store = me.getStore(),
117 items = store.getData().items,
118 sprites = me.getSprites(),
119 labelField = me.getLabel().getTemplate().getField(),
120 hidden = me.getHidden(),
121 i, ln, labels, sprite;
122 if (sprites.length > 0 && labelField) {
123 labels = [];
124 for (i = 0, ln = items.length; i < ln; i++) {
125 labels.push(items[i].get(labelField));
126 }
127 for (i = 0, ln = sprites.length; i < ln; i++) {
128 sprite = sprites[i];
129 sprite.setAttributes({label: labels[i]});
130 sprite.putMarker('labels', {hidden: hidden[i]}, sprite.attr.attributeId);
131 }
132 }
133 },
134
135 coordinateX: function () {
136 var me = this,
137 store = me.getStore(),
138 items = store.getData().items,
139 length = items.length,
140 field = me.getXField(),
141 value, sum = 0,
142 hidden = me.getHidden(),
143 summation = [], i,
144 lastAngle = 0,
145 totalAngle = me.getTotalAngle(),
146 sprites = me.getSprites();
147
148 if (!sprites) {
149 return;
150 }
151
152 for (i = 0; i < length; i++) {
153 value = Math.abs(Number(items[i].get(field))) || 0;
154 if (!hidden[i]) {
155 sum += value;
156 }
157 summation[i] = sum;
158 if (i >= hidden.length) {
159 hidden[i] = false;
160 }
161 }
162
163 if (sum !== 0) {
164 sum = totalAngle / sum;
165 }
166 for (i = 0; i < length; i++) {
167 sprites[i].setAttributes({
168 startAngle: lastAngle,
169 endAngle: lastAngle = (sum ? summation[i] * sum : 0),
170 globalAlpha: 1
171 });
172 }
173 for (; i < me.sprites.length; i++) {
174 sprites[i].setAttributes({
175 startAngle: totalAngle,
176 endAngle: totalAngle,
177 globalAlpha: 0
178 });
179 }
180 me.getChart().refreshLegendStore();
181 },
182
183 updateCenter: function (center) {
184 this.setStyle({
185 translationX: center[0] + this.getOffsetX(),
186 translationY: center[1] + this.getOffsetY()
187 });
188 this.doUpdateStyles();
189 },
190
191 updateRadius: function (radius) {
192 this.setStyle({
193 startRho: radius * this.getDonut() * 0.01, // Percentage
194 endRho: radius
195 });
196 this.doUpdateStyles();
197 },
198
199 updateDonut: function (donut) {
200 var radius = this.getRadius();
201 this.setStyle({
202 startRho: radius * donut * 0.01, // Percentage
203 endRho: radius
204 });
205 this.doUpdateStyles();
206 },
207
208 updateRotation: function (rotation) {
209 this.setStyle({
210 rotationRads: rotation
211 });
212 this.doUpdateStyles();
213 },
214
215 updateTotalAngle: function (totalAngle) {
216 this.processData();
217 },
218
219 getSprites: function () {
220 var me = this,
221 chart = me.getChart(),
222 store = me.getStore();
223 if (!chart || !store) {
224 return [];
225 }
226 me.getColors();
227 me.getSubStyle();
228 var items = store.getData().items,
229 length = items.length,
230 animation = chart && chart.getAnimate(),
231 sprites = me.sprites, sprite,
232 spriteIndex = 0, rendererData,
233 i, spriteCreated = false,
234 label = me.getLabel(),
235 labelTpl = label.getTemplate();
236
237 rendererData = {
238 store: store,
239 field: me.getField(),
240 series: me
241 };
242
243 for (i = 0; i < length; i++) {
244 sprite = sprites[i];
245 if (!sprite) {
246 sprite = me.createSprite();
247 if (me.getHighlightCfg()) {
248 sprite.config.highlightCfg = me.getHighlightCfg();
249 sprite.addModifier('highlight', true);
250 }
251 if (labelTpl.getField()) {
252 labelTpl.setAttributes({
253 labelOverflowPadding: me.getLabelOverflowPadding()
254 });
255 labelTpl.fx.setCustomDuration({'callout': 200});
256 sprite.bindMarker('labels', label);
257 }
258 sprite.setAttributes(me.getStyleByIndex(i));
259 sprite.rendererData = rendererData;
260 sprite.rendererIndex = spriteIndex++;
261 spriteCreated = true;
262 }
263 sprite.fx.setConfig(animation);
264 }
265 if (spriteCreated) {
266 me.doUpdateStyles();
267 }
268 return me.sprites;
269 },
270
271 normalizeAngle: function (angle) {
272 var pi2 = Math.PI * 2;
273 if (angle >= 0) {
274 return angle % pi2;
275 }
276 return (angle % pi2 + pi2) % pi2;
277 },
278
279 betweenAngle: function (x, a, b) {
280 var normalize = this.normalizeAngle;
281 a = normalize(a);
282 b = normalize(b);
283 x = normalize(x);
284 if (b === 0) {
285 b = Math.PI * 2;
286 }
287 return x >= a && x < b;
288 },
289
290 /**
291 * Returns the pie slice for a given angle
292 * @param {Number} angle The angle to search for the slice
293 * @return {Object} An object containing the reocord, sprite, scope etc.
294 */
295 getItemForAngle: function (angle) {
296 var me = this,
297 sprites = me.getSprites(),
298 attr;
299
300 angle %= Math.PI * 2;
301
302 while (angle < 0) {
303 angle += Math.PI * 2;
304 }
305
306 if (sprites) {
307 var store = me.getStore(),
308 items = store.getData().items,
309 hidden = me.getHidden(),
310 i = 0,
311 ln = store.getCount();
312
313 for (; i < ln; i++) {
314 if(!hidden[i]) {
315 // Fortunately, the id of items equals the index of it in instances list.
316 attr = sprites[i].attr;
317
318 if (attr.startAngle <= angle && attr.endAngle >= angle) {
319 return {
320 series: me,
321 sprite: sprites[i],
322 index: i,
323 record: items[i],
324 field: me.getXField()
325 };
326 }
327 }
328 }
329 }
330
331 return null;
332 },
333
334 getItemForPoint: function (x, y) {
335 var me = this,
336 sprites = me.getSprites();
337 if (sprites) {
338 var center = me.getCenter(),
339 offsetX = me.getOffsetX(),
340 offsetY = me.getOffsetY(),
341 originalX = x - center[0] + offsetX,
342 originalY = y - center[1] + offsetY,
343 store = me.getStore(),
344 donut = me.getDonut(),
345 items = store.getData().items,
346 direction = Math.atan2(originalY, originalX) - me.getRotation(),
347 donutLimit = Math.sqrt(originalX * originalX + originalY * originalY),
348 endRadius = me.getRadius(),
349 startRadius = donut / 100 * endRadius,
350 hidden = me.getHidden(),
351 i, ln, attr;
352
353 for (i = 0, ln = items.length; i < ln; i++) {
354 if(!hidden[i]) {
355 // Fortunately, the id of items equals the index of it in instances list.
356 attr = sprites[i].attr;
357 if (startRadius + attr.margin <= donutLimit && donutLimit + attr.margin <= endRadius) {
358 if (this.betweenAngle(direction, attr.startAngle, attr.endAngle)) {
359 return {
360 series: this,
361 sprite: sprites[i],
362 index: i,
363 record: items[i],
364 field: this.getXField()
365 };
366 }
367 }
368 }
369 }
370 return null;
371 }
372 },
373
374 provideLegendInfo: function (target) {
375 var store = this.getStore();
376 if (store) {
377 var items = store.getData().items,
378 labelField = this.getLabel().getTemplate().getField(),
379 field = this.getField(),
380 hidden = this.getHidden();
381 for (var i = 0; i < items.length; i++) {
382 target.push({
383 name: labelField ? String(items[i].get(labelField)) : field + ' ' + i,
384 mark: this.getStyleByIndex(i).fillStyle || this.getStyleByIndex(i).strokeStyle || 'black',
385 disabled: hidden[i],
386 series: this.getId(),
387 index: i
388 });
389 }
390 }
391 }
392 });
393