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 instanciating 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
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.
73 * This parameter is required.
78 * @cfg {Boolean} needle
79 * Use the Gauge Series as an area series or add a needle to it. Default's false.
84 * @cfg {Boolean|Number} donut
85 * Use the entire disk or just a fraction of it for the gauge. Default's false.
90 * @cfg {Boolean} showInLegend
91 * Whether to add the pie chart elements as legend items. Default's false.
97 * An object containing styles for overriding series styles from Theming.
101 constructor: function(config) {
102 this.callParent(arguments);
105 surface = chart.surface,
107 shadow = chart.shadow, i, l, cfg;
108 Ext.apply(me, config, {
112 stroke: 'rgb(200, 200, 200)',
121 stroke: 'rgb(150, 150, 150)',
130 stroke: 'rgb(100, 100, 100)',
137 me.group = surface.getGroup(me.seriesId);
139 for (i = 0, l = me.shadowAttributes.length; i < l; i++) {
140 me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
143 surface.customAttributes.segment = function(opt) {
144 return me.getSegment(opt);
148 //@private updates some onbefore render parameters.
149 initialize: function() {
151 store = me.chart.substore || me.chart.store;
152 //Add yFields to be used in Legend.js
154 if (me.label.field) {
155 store.each(function(rec) {
156 me.yField.push(rec.get(me.label.field));
161 // @private returns an object with properties for a Slice
162 getSegment: function(opt) {
170 x1 = 0, x2 = 0, x3 = 0, x4 = 0,
171 y1 = 0, y2 = 0, y3 = 0, y4 = 0,
173 r = opt.endRho - opt.startRho,
174 startAngle = opt.startAngle,
175 endAngle = opt.endAngle,
176 midAngle = (startAngle + endAngle) / 2 * rad,
177 margin = opt.margin || 0,
178 flag = abs(endAngle - startAngle) > 180,
179 a1 = Math.min(startAngle, endAngle) * rad,
180 a2 = Math.max(startAngle, endAngle) * rad,
183 x += margin * cos(midAngle);
184 y += margin * sin(midAngle);
186 x1 = x + opt.startRho * cos(a1);
187 y1 = y + opt.startRho * sin(a1);
189 x2 = x + opt.endRho * cos(a1);
190 y2 = y + opt.endRho * sin(a1);
192 x3 = x + opt.startRho * cos(a2);
193 y3 = y + opt.startRho * sin(a2);
195 x4 = x + opt.endRho * cos(a2);
196 y4 = y + opt.endRho * sin(a2);
198 if (abs(x1 - x3) <= delta && abs(y1 - y3) <= delta) {
201 //Solves mysterious clipping bug with IE
207 ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
215 ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
217 ["A", opt.startRho, opt.startRho, 0, +flag, 0, x1, y1],
223 // @private utility function to calculate the middle point of a pie slice.
224 calcMiddle: function(item) {
230 startAngle = slice.startAngle,
231 endAngle = slice.endAngle,
232 radius = Math.max(('rho' in slice) ? slice.rho: me.radius, me.label.minMargin),
234 a1 = Math.min(startAngle, endAngle) * rad,
235 a2 = Math.max(startAngle, endAngle) * rad,
236 midAngle = -(a1 + (a2 - a1) / 2),
237 xm = x + (item.endRho + item.startRho) / 2 * Math.cos(midAngle),
238 ym = y - (item.endRho + item.startRho) / 2 * Math.sin(midAngle);
247 * Draws the series for the current chart.
249 drawSeries: function() {
252 store = chart.substore || chart.store,
254 animate = me.chart.animate,
255 axis = me.chart.axes.get(0),
256 minimum = axis && axis.minimum || me.minimum || 0,
257 maximum = axis && axis.maximum || me.maximum || 0,
258 field = me.angleField || me.field || me.xField,
259 surface = chart.surface,
260 chartBBox = chart.chartBBox,
265 seriesStyle = me.seriesStyle,
266 seriesLabelStyle = me.seriesLabelStyle,
267 colorArrayStyle = me.colorArrayStyle,
268 colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0,
269 gutterX = chart.maxGutter[0],
270 gutterY = chart.maxGutter[1],
273 rendererAttributes, centerX, centerY, slice, slices, sprite, value,
274 item, ln, record, i, j, startAngle, endAngle, middleAngle, sliceLength, path,
275 p, spriteOptions, bbox, splitAngle, sliceA, sliceB;
277 Ext.apply(seriesStyle, me.style || {});
282 //override theme colors
284 colorArrayStyle = me.colorSet;
285 colorArrayLength = colorArrayStyle.length;
288 //if not store or store is empty then there's nothing to draw
289 if (!store || !store.getCount()) {
293 centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
294 centerY = me.centerY = chartBBox.y + chartBBox.height;
295 me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y);
296 me.slices = slices = [];
297 me.items = items = [];
300 record = store.getAt(0);
301 me.value = record.get(field);
313 splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum));
316 splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum));
321 endAngle: splitAngle,
326 value: me.maximum - value,
327 startAngle: splitAngle,
331 slices.push(sliceA, sliceB);
334 //do pie slices after.
335 for (i = 0, ln = slices.length; i < ln; i++) {
337 sprite = group.getAt(i);
338 //set pie slice properties
339 rendererAttributes = Ext.apply({
341 startAngle: slice.startAngle,
342 endAngle: slice.endAngle,
345 startRho: slice.rho * +donut / 100,
348 }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {}));
351 rendererAttributes.segment, {
358 // Create a new sprite if needed (no height)
360 spriteOptions = Ext.apply({
363 }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {}));
364 sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes));
366 slice.sprite = slice.sprite || [];
367 item.sprite = sprite;
368 slice.sprite.push(sprite);
370 rendererAttributes = me.renderer(sprite, record, rendererAttributes, i, store);
371 sprite._to = rendererAttributes;
372 me.onAnimate(sprite, {
373 to: rendererAttributes
376 rendererAttributes = me.renderer(sprite, record, Ext.apply(rendererAttributes, {
379 sprite.setAttributes(rendererAttributes, true);
384 splitAngle = splitAngle * Math.PI / 180;
386 if (!me.needleSprite) {
387 me.needleSprite = me.chart.surface.add({
389 path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
390 centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
391 'L', centerX + me.radius * cos(splitAngle),
392 centerY + -Math.abs(me.radius * sin(splitAngle))],
398 me.onAnimate(me.needleSprite, {
400 path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
401 centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
402 'L', centerX + me.radius * cos(splitAngle),
403 centerY + -Math.abs(me.radius * sin(splitAngle))]
407 me.needleSprite.setAttributes({
409 path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
410 centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
411 'L', centerX + me.radius * cos(splitAngle),
412 centerY + -Math.abs(me.radius * sin(splitAngle))]
416 me.needleSprite.setAttributes({
425 * Sets the Gauge chart to the current specified value.
427 setValue: function (value) {
432 // @private callback for when creating a label sprite.
433 onCreateLabel: function(storeItem, item, i, display) {},
435 // @private callback for when placing a label sprite.
436 onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {},
438 // @private callback for when placing a callout.
439 onPlaceCallout: function() {},
441 // @private handles sprite animation for the series.
442 onAnimate: function(sprite, attr) {
444 return this.callParent(arguments);
447 isItemInPoint: function(x, y, item, i) {
451 // @private shows all elements in the series.
452 showAll: function() {
453 if (!isNaN(this._index)) {
454 this.__excludes[this._index] = false;
460 * Returns the color of the series (to be displayed as color for the series legend item).
461 * @param item {Object} Info about the item; same format as returned by #getItemForPoint
463 getLegendColor: function(index) {
465 return me.colorArrayStyle[index % me.colorArrayStyle.length];