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