Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / docs / source / Gauge2.html
1 <!DOCTYPE html><html><head><title>Sencha Documentation Project</title><link rel="stylesheet" href="../reset.css" type="text/css"><link rel="stylesheet" href="../prettify.css" type="text/css"><link rel="stylesheet" href="../prettify_sa.css" type="text/css"><script type="text/javascript" src="../prettify.js"></script></head><body onload="prettyPrint()"><pre class="prettyprint"><pre><span id='Ext-chart.series.Gauge'>/**
2 </span> * @class Ext.chart.series.Gauge
3  * @extends Ext.chart.series.Series
4  * 
5  * Creates a Gauge Chart. Gauge Charts are used to show progress in a certain variable. There are two ways of using the Gauge chart.
6  * One is setting a store element into the Gauge and selecting the field to be used from that store. Another one is instanciating the
7  * visualization and using the `setValue` method to adjust the value you want.
8  *
9  * A chart/series configuration for the Gauge visualization could look like this:
10  * 
11  *     {
12  *         xtype: 'chart',
13  *         store: store,
14  *         axes: [{
15  *             type: 'gauge',
16  *             position: 'gauge',
17  *             minimum: 0,
18  *             maximum: 100,
19  *             steps: 10,
20  *             margin: -10
21  *         }],
22  *         series: [{
23  *             type: 'gauge',
24  *             field: 'data1',
25  *             donut: false,
26  *             colorSet: ['#F49D10', '#ddd']
27  *         }]
28  *     }
29  * 
30  * In this configuration we create a special Gauge axis to be used with the gauge visualization (describing half-circle markers), and also we're
31  * setting a maximum, minimum and steps configuration options into the axis. The Gauge series configuration contains the store field to be bound to
32  * the visual display and the color set to be used with the visualization.
33  * 
34  * @xtype gauge
35  */
36 Ext.define('Ext.chart.series.Gauge', {
37
38     /* Begin Definitions */
39
40     extend: 'Ext.chart.series.Series',
41
42     /* End Definitions */
43
44     type: &quot;gauge&quot;,
45     alias: 'series.gauge',
46
47     rad: Math.PI / 180,
48
49 <span id='Ext-chart.series.Gauge-cfg-highlightDuration'>    /**
50 </span>     * @cfg {Number} highlightDuration
51      * The duration for the pie slice highlight effect.
52      */
53     highlightDuration: 150,
54
55 <span id='Ext-chart.series.Gauge-cfg-angleField'>    /**
56 </span>     * @cfg {String} angleField
57      * The store record field name to be used for the pie angles.
58      * The values bound to this field name must be positive real numbers.
59      * This parameter is required.
60      */
61     angleField: false,
62
63 <span id='Ext-chart.series.Gauge-cfg-needle'>    /**
64 </span>     * @cfg {Boolean} needle
65      * Use the Gauge Series as an area series or add a needle to it. Default's false.
66      */
67     needle: false,
68     
69 <span id='Ext-chart.series.Gauge-cfg-donut'>    /**
70 </span>     * @cfg {Boolean|Number} donut
71      * Use the entire disk or just a fraction of it for the gauge. Default's false.
72      */
73     donut: false,
74
75 <span id='Ext-chart.series.Gauge-cfg-showInLegend'>    /**
76 </span>     * @cfg {Boolean} showInLegend
77      * Whether to add the pie chart elements as legend items. Default's false.
78      */
79     showInLegend: false,
80
81 <span id='Ext-chart.series.Gauge-cfg-style'>    /**
82 </span>     * @cfg {Object} style
83      * An object containing styles for overriding series styles from Theming.
84      */
85     style: {},
86     
87     constructor: function(config) {
88         this.callParent(arguments);
89         var me = this,
90             chart = me.chart,
91             surface = chart.surface,
92             store = chart.store,
93             shadow = chart.shadow, i, l, cfg;
94         Ext.apply(me, config, {
95             shadowAttributes: [{
96                 &quot;stroke-width&quot;: 6,
97                 &quot;stroke-opacity&quot;: 1,
98                 stroke: 'rgb(200, 200, 200)',
99                 translate: {
100                     x: 1.2,
101                     y: 2
102                 }
103             },
104             {
105                 &quot;stroke-width&quot;: 4,
106                 &quot;stroke-opacity&quot;: 1,
107                 stroke: 'rgb(150, 150, 150)',
108                 translate: {
109                     x: 0.9,
110                     y: 1.5
111                 }
112             },
113             {
114                 &quot;stroke-width&quot;: 2,
115                 &quot;stroke-opacity&quot;: 1,
116                 stroke: 'rgb(100, 100, 100)',
117                 translate: {
118                     x: 0.6,
119                     y: 1
120                 }
121             }]
122         });
123         me.group = surface.getGroup(me.seriesId);
124         if (shadow) {
125             for (i = 0, l = me.shadowAttributes.length; i &lt; l; i++) {
126                 me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
127             }
128         }
129         surface.customAttributes.segment = function(opt) {
130             return me.getSegment(opt);
131         };
132     },
133     
134     //@private updates some onbefore render parameters.
135     initialize: function() {
136         var me = this,
137             store = me.chart.substore || me.chart.store;
138         //Add yFields to be used in Legend.js
139         me.yField = [];
140         if (me.label.field) {
141             store.each(function(rec) {
142                 me.yField.push(rec.get(me.label.field));
143             });
144         }
145     },
146
147     // @private returns an object with properties for a Slice
148     getSegment: function(opt) {
149         var me = this,
150             rad = me.rad,
151             cos = Math.cos,
152             sin = Math.sin,
153             abs = Math.abs,
154             x = me.centerX,
155             y = me.centerY,
156             x1 = 0, x2 = 0, x3 = 0, x4 = 0,
157             y1 = 0, y2 = 0, y3 = 0, y4 = 0,
158             delta = 1e-2,
159             r = opt.endRho - opt.startRho,
160             startAngle = opt.startAngle,
161             endAngle = opt.endAngle,
162             midAngle = (startAngle + endAngle) / 2 * rad,
163             margin = opt.margin || 0,
164             flag = abs(endAngle - startAngle) &gt; 180,
165             a1 = Math.min(startAngle, endAngle) * rad,
166             a2 = Math.max(startAngle, endAngle) * rad,
167             singleSlice = false;
168
169         x += margin * cos(midAngle);
170         y += margin * sin(midAngle);
171
172         x1 = x + opt.startRho * cos(a1);
173         y1 = y + opt.startRho * sin(a1);
174
175         x2 = x + opt.endRho * cos(a1);
176         y2 = y + opt.endRho * sin(a1);
177
178         x3 = x + opt.startRho * cos(a2);
179         y3 = y + opt.startRho * sin(a2);
180
181         x4 = x + opt.endRho * cos(a2);
182         y4 = y + opt.endRho * sin(a2);
183
184         if (abs(x1 - x3) &lt;= delta &amp;&amp; abs(y1 - y3) &lt;= delta) {
185             singleSlice = true;
186         }
187         //Solves mysterious clipping bug with IE
188         if (singleSlice) {
189             return {
190                 path: [
191                 [&quot;M&quot;, x1, y1],
192                 [&quot;L&quot;, x2, y2],
193                 [&quot;A&quot;, opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
194                 [&quot;Z&quot;]]
195             };
196         } else {
197             return {
198                 path: [
199                 [&quot;M&quot;, x1, y1],
200                 [&quot;L&quot;, x2, y2],
201                 [&quot;A&quot;, opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
202                 [&quot;L&quot;, x3, y3],
203                 [&quot;A&quot;, opt.startRho, opt.startRho, 0, +flag, 0, x1, y1],
204                 [&quot;Z&quot;]]
205             };
206         }
207     },
208
209     // @private utility function to calculate the middle point of a pie slice.
210     calcMiddle: function(item) {
211         var me = this,
212             rad = me.rad,
213             slice = item.slice,
214             x = me.centerX,
215             y = me.centerY,
216             startAngle = slice.startAngle,
217             endAngle = slice.endAngle,
218             radius = Math.max(('rho' in slice) ? slice.rho: me.radius, me.label.minMargin),
219             donut = +me.donut,
220             a1 = Math.min(startAngle, endAngle) * rad,
221             a2 = Math.max(startAngle, endAngle) * rad,
222             midAngle = -(a1 + (a2 - a1) / 2),
223             xm = x + (item.endRho + item.startRho) / 2 * Math.cos(midAngle),
224             ym = y - (item.endRho + item.startRho) / 2 * Math.sin(midAngle);
225
226         item.middle = {
227             x: xm,
228             y: ym
229         };
230     },
231
232 <span id='Ext-chart.series.Gauge-method-drawSeries'>    /**
233 </span>     * Draws the series for the current chart.
234      */
235     drawSeries: function() {
236         var me = this,
237             chart = me.chart,
238             store = chart.substore || chart.store,
239             group = me.group,
240             animate = me.chart.animate,
241             axis = me.chart.axes.get(0),
242             minimum = axis &amp;&amp; axis.minimum || me.minimum || 0,
243             maximum = axis &amp;&amp; axis.maximum || me.maximum || 0,
244             field = me.angleField || me.field || me.xField,
245             surface = chart.surface,
246             chartBBox = chart.chartBBox,
247             rad = me.rad,
248             donut = +me.donut,
249             values = {},
250             items = [],
251             seriesStyle = me.seriesStyle,
252             seriesLabelStyle = me.seriesLabelStyle,
253             colorArrayStyle = me.colorArrayStyle,
254             colorArrayLength = colorArrayStyle &amp;&amp; colorArrayStyle.length || 0,
255             gutterX = chart.maxGutter[0],
256             gutterY = chart.maxGutter[1],
257             cos = Math.cos,
258             sin = Math.sin,
259             rendererAttributes, centerX, centerY, slice, slices, sprite, value,
260             item, ln, record, i, j, startAngle, endAngle, middleAngle, sliceLength, path,
261             p, spriteOptions, bbox, splitAngle, sliceA, sliceB;
262         
263         Ext.apply(seriesStyle, me.style || {});
264
265         me.setBBox();
266         bbox = me.bbox;
267
268         //override theme colors
269         if (me.colorSet) {
270             colorArrayStyle = me.colorSet;
271             colorArrayLength = colorArrayStyle.length;
272         }
273         
274         //if not store or store is empty then there's nothing to draw
275         if (!store || !store.getCount()) {
276             return;
277         }
278         
279         centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
280         centerY = me.centerY = chartBBox.y + chartBBox.height;
281         me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y);
282         me.slices = slices = [];
283         me.items = items = [];
284         
285         if (!me.value) {
286             record = store.getAt(0);
287             me.value = record.get(field);
288         }
289         
290         value = me.value;
291         if (me.needle) {
292             sliceA = {
293                 series: me,
294                 value: value,
295                 startAngle: -180,
296                 endAngle: 0,
297                 rho: me.radius
298             };
299             splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum));
300             slices.push(sliceA);
301         } else {
302             splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum));
303             sliceA = {
304                 series: me,
305                 value: value,
306                 startAngle: -180,
307                 endAngle: splitAngle,
308                 rho: me.radius
309             };
310             sliceB = {
311                 series: me,
312                 value: me.maximum - value,
313                 startAngle: splitAngle,
314                 endAngle: 0,
315                 rho: me.radius
316             };
317             slices.push(sliceA, sliceB);
318         }
319         
320         //do pie slices after.
321         for (i = 0, ln = slices.length; i &lt; ln; i++) {
322             slice = slices[i];
323             sprite = group.getAt(i);
324             //set pie slice properties
325             rendererAttributes = Ext.apply({
326                 segment: {
327                     startAngle: slice.startAngle,
328                     endAngle: slice.endAngle,
329                     margin: 0,
330                     rho: slice.rho,
331                     startRho: slice.rho * +donut / 100,
332                     endRho: slice.rho
333                 } 
334             }, Ext.apply(seriesStyle, colorArrayStyle &amp;&amp; { fill: colorArrayStyle[i % colorArrayLength] } || {}));
335
336             item = Ext.apply({},
337             rendererAttributes.segment, {
338                 slice: slice,
339                 series: me,
340                 storeItem: record,
341                 index: i
342             });
343             items[i] = item;
344             // Create a new sprite if needed (no height)
345             if (!sprite) {
346                 spriteOptions = Ext.apply({
347                     type: &quot;path&quot;,
348                     group: group
349                 }, Ext.apply(seriesStyle, colorArrayStyle &amp;&amp; { fill: colorArrayStyle[i % colorArrayLength] } || {}));
350                 sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes));
351             }
352             slice.sprite = slice.sprite || [];
353             item.sprite = sprite;
354             slice.sprite.push(sprite);
355             if (animate) {
356                 rendererAttributes = me.renderer(sprite, record, rendererAttributes, i, store);
357                 sprite._to = rendererAttributes;
358                 me.onAnimate(sprite, {
359                     to: rendererAttributes
360                 });
361             } else {
362                 rendererAttributes = me.renderer(sprite, record, Ext.apply(rendererAttributes, {
363                     hidden: false
364                 }), i, store);
365                 sprite.setAttributes(rendererAttributes, true);
366             }
367         }
368         
369         if (me.needle) {
370             splitAngle = splitAngle * Math.PI / 180;
371             
372             if (!me.needleSprite) {
373                 me.needleSprite = me.chart.surface.add({
374                     type: 'path',
375                     path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
376                                 centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
377                            'L', centerX + me.radius * cos(splitAngle),
378                                 centerY + -Math.abs(me.radius * sin(splitAngle))],
379                     'stroke-width': 4,
380                     'stroke': '#222'
381                 });
382             } else {
383                 if (animate) {
384                     me.onAnimate(me.needleSprite, {
385                         to: {
386                         path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
387                                     centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
388                                'L', centerX + me.radius * cos(splitAngle),
389                                     centerY + -Math.abs(me.radius * sin(splitAngle))]
390                         }
391                     });
392                 } else {
393                     me.needleSprite.setAttributes({
394                         type: 'path',
395                         path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
396                                     centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
397                                'L', centerX + me.radius * cos(splitAngle),
398                                     centerY + -Math.abs(me.radius * sin(splitAngle))]
399                     });
400                 }
401             }
402             me.needleSprite.setAttributes({
403                 hidden: false    
404             }, true);
405         }
406         
407         delete me.value;
408     },
409     
410 <span id='Ext-chart.series.Gauge-method-setValue'>    /**
411 </span>     * Sets the Gauge chart to the current specified value.
412     */
413     setValue: function (value) {
414         this.value = value;
415         this.drawSeries();
416     },
417
418     // @private callback for when creating a label sprite.
419     onCreateLabel: function(storeItem, item, i, display) {},
420
421     // @private callback for when placing a label sprite.
422     onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {},
423
424     // @private callback for when placing a callout.
425     onPlaceCallout: function() {},
426
427     // @private handles sprite animation for the series.
428     onAnimate: function(sprite, attr) {
429         sprite.show();
430         return this.callParent(arguments);
431     },
432
433     isItemInPoint: function(x, y, item, i) {
434         return false;
435     },
436     
437     // @private shows all elements in the series.
438     showAll: function() {
439         if (!isNaN(this._index)) {
440             this.__excludes[this._index] = false;
441             this.drawSeries();
442         }
443     },
444     
445 <span id='Ext-chart.series.Gauge-method-getLegendColor'>    /**
446 </span>     * Returns the color of the series (to be displayed as color for the series legend item).
447      * @param item {Object} Info about the item; same format as returned by #getItemForPoint
448      */
449     getLegendColor: function(index) {
450         var me = this;
451         return me.colorArrayStyle[index % me.colorArrayStyle.length];
452     }
453 });
454
455 </pre></pre></body></html>