3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.chart.series.Gauge
17 * @extends Ext.chart.series.Series
19 * Creates a Gauge Chart. Gauge Charts are used to show progress in a certain variable. There are two ways of using the Gauge chart.
20 * One is setting a store element into the Gauge and selecting the field to be used from that store. Another one is instantiating the
21 * visualization and using the `setValue` method to adjust the value you want.
23 * A chart/series configuration for the Gauge visualization could look like this:
40 * colorSet: ['#F49D10', '#ddd']
44 * In this configuration we create a special Gauge axis to be used with the gauge visualization (describing half-circle markers), and also we're
45 * setting a maximum, minimum and steps configuration options into the axis. The Gauge series configuration contains the store field to be bound to
46 * the visual display and the color set to be used with the visualization.
50 Ext.define('Ext.chart.series.Gauge', {
52 /* Begin Definitions */
54 extend: 'Ext.chart.series.Series',
59 alias: 'series.gauge',
64 * @cfg {Number} highlightDuration
65 * The duration for the pie slice highlight effect.
67 highlightDuration: 150,
70 * @cfg {String} angleField (required)
71 * The store record field name to be used for the pie angles.
72 * The values bound to this field name must be positive real numbers.
77 * @cfg {Boolean} needle
78 * Use the Gauge Series as an area series or add a needle to it. Default's false.
83 * @cfg {Boolean/Number} donut
84 * Use the entire disk or just a fraction of it for the gauge. Default's false.
89 * @cfg {Boolean} showInLegend
90 * Whether to add the pie chart elements as legend items. Default's false.
96 * An object containing styles for overriding series styles from Theming.
100 constructor: function(config) {
101 this.callParent(arguments);
104 surface = chart.surface,
106 shadow = chart.shadow, i, l, cfg;
107 Ext.apply(me, config, {
111 stroke: 'rgb(200, 200, 200)',
120 stroke: 'rgb(150, 150, 150)',
129 stroke: 'rgb(100, 100, 100)',
136 me.group = surface.getGroup(me.seriesId);
138 for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
139 me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
142 surface.customAttributes.segment = function(opt) {
143 return me.getSegment(opt);
147 //@private updates some onbefore render parameters.
148 initialize: function() {
150 store = me.chart.getChartStore();
151 //Add yFields to be used in Legend.js
153 if (me.label.field) {
154 store.each(function(rec) {
155 me.yField.push(rec.get(me.label.field));
160 // @private returns an object with properties for a Slice
161 getSegment: function(opt) {
169 x1 = 0, x2 = 0, x3 = 0, x4 = 0,
170 y1 = 0, y2 = 0, y3 = 0, y4 = 0,
172 r = opt.endRho - opt.startRho,
173 startAngle = opt.startAngle,
174 endAngle = opt.endAngle,
175 midAngle = (startAngle + endAngle) / 2 * rad,
176 margin = opt.margin || 0,
177 flag = abs(endAngle - startAngle) > 180,
178 a1 = Math.min(startAngle, endAngle) * rad,
179 a2 = Math.max(startAngle, endAngle) * rad,
182 x += margin * cos(midAngle);
183 y += margin * sin(midAngle);
185 x1 = x + opt.startRho * cos(a1);
186 y1 = y + opt.startRho * sin(a1);
188 x2 = x + opt.endRho * cos(a1);
189 y2 = y + opt.endRho * sin(a1);
191 x3 = x + opt.startRho * cos(a2);
192 y3 = y + opt.startRho * sin(a2);
194 x4 = x + opt.endRho * cos(a2);
195 y4 = y + opt.endRho * sin(a2);
197 if (abs(x1 - x3) <= delta && abs(y1 - y3) <= delta) {
200 //Solves mysterious clipping bug with IE
206 ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
214 ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
216 ["A", opt.startRho, opt.startRho, 0, +flag, 0, x1, y1],
222 // @private utility function to calculate the middle point of a pie slice.
223 calcMiddle: function(item) {
229 startAngle = slice.startAngle,
230 endAngle = slice.endAngle,
231 radius = Math.max(('rho' in slice) ? slice.rho: me.radius, me.label.minMargin),
233 a1 = Math.min(startAngle, endAngle) * rad,
234 a2 = Math.max(startAngle, endAngle) * rad,
235 midAngle = -(a1 + (a2 - a1) / 2),
236 xm = x + (item.endRho + item.startRho) / 2 * Math.cos(midAngle),
237 ym = y - (item.endRho + item.startRho) / 2 * Math.sin(midAngle);
246 * Draws the series for the current chart.
248 drawSeries: function() {
251 store = chart.getChartStore(),
253 animate = me.chart.animate,
254 axis = me.chart.axes.get(0),
255 minimum = axis && axis.minimum || me.minimum || 0,
256 maximum = axis && axis.maximum || me.maximum || 0,
257 field = me.angleField || me.field || me.xField,
258 surface = chart.surface,
259 chartBBox = chart.chartBBox,
264 seriesStyle = me.seriesStyle,
265 seriesLabelStyle = me.seriesLabelStyle,
266 colorArrayStyle = me.colorArrayStyle,
267 colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
268 gutterX = chart.maxGutter[0],
269 gutterY = chart.maxGutter[1],
272 rendererAttributes, centerX, centerY, slice, slices, sprite, value,
273 item, ln, record, i, j, startAngle, endAngle, middleAngle, sliceLength, path,
274 p, spriteOptions, bbox, splitAngle, sliceA, sliceB;
276 Ext.apply(seriesStyle, me.style || {});
281 //override theme colors
283 colorArrayStyle = me.colorSet;
284 colorArrayLength = colorArrayStyle.length;
287 //if not store or store is empty then there's nothing to draw
288 if (!store || !store.getCount()) {
292 centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
293 centerY = me.centerY = chartBBox.y + chartBBox.height;
294 me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y);
295 me.slices = slices = [];
296 me.items = items = [];
299 record = store.getAt(0);
300 me.value = record.get(field);
312 splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum));
315 splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum));
320 endAngle: splitAngle,
325 value: me.maximum - value,
326 startAngle: splitAngle,
330 slices.push(sliceA, sliceB);
333 //do pie slices after.
334 for (i = 0, ln = slices.length; i < ln; i++) {
336 sprite = group.getAt(i);
337 //set pie slice properties
338 rendererAttributes = Ext.apply({
340 startAngle: slice.startAngle,
341 endAngle: slice.endAngle,
344 startRho: slice.rho * +donut / 100,
347 }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {}));
350 rendererAttributes.segment, {
357 // Create a new sprite if needed (no height)
359 spriteOptions = Ext.apply({
362 }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {}));
363 sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes));
365 slice.sprite = slice.sprite || [];
366 item.sprite = sprite;
367 slice.sprite.push(sprite);
369 rendererAttributes = me.renderer(sprite, record, rendererAttributes, i, store);
370 sprite._to = rendererAttributes;
371 me.onAnimate(sprite, {
372 to: rendererAttributes
375 rendererAttributes = me.renderer(sprite, record, Ext.apply(rendererAttributes, {
378 sprite.setAttributes(rendererAttributes, true);
383 splitAngle = splitAngle * Math.PI / 180;
385 if (!me.needleSprite) {
386 me.needleSprite = me.chart.surface.add({
388 path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
389 centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
390 'L', centerX + me.radius * cos(splitAngle),
391 centerY + -Math.abs(me.radius * sin(splitAngle))],
397 me.onAnimate(me.needleSprite, {
399 path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
400 centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
401 'L', centerX + me.radius * cos(splitAngle),
402 centerY + -Math.abs(me.radius * sin(splitAngle))]
406 me.needleSprite.setAttributes({
408 path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
409 centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
410 'L', centerX + me.radius * cos(splitAngle),
411 centerY + -Math.abs(me.radius * sin(splitAngle))]
415 me.needleSprite.setAttributes({
424 * Sets the Gauge chart to the current specified value.
426 setValue: function (value) {
431 // @private callback for when creating a label sprite.
432 onCreateLabel: function(storeItem, item, i, display) {},
434 // @private callback for when placing a label sprite.
435 onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {},
437 // @private callback for when placing a callout.
438 onPlaceCallout: function() {},
440 // @private handles sprite animation for the series.
441 onAnimate: function(sprite, attr) {
443 return this.callParent(arguments);
446 isItemInPoint: function(x, y, item, i) {
450 // @private shows all elements in the series.
451 showAll: function() {
452 if (!isNaN(this._index)) {
453 this.__excludes[this._index] = false;
459 * Returns the color of the series (to be displayed as color for the series legend item).
460 * @param item {Object} Info about the item; same format as returned by #getItemForPoint
462 getLegendColor: function(index) {
464 return me.colorArrayStyle[index % me.colorArrayStyle.length];