X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/7a654f8d43fdb43d78b63d90528bed6e86b608cc..6746dc89c47ed01b165cc1152533605f97eb8e8d:/docs/source/Gauge.html diff --git a/docs/source/Gauge.html b/docs/source/Gauge.html index a2024613..e3b6fcb9 100644 --- a/docs/source/Gauge.html +++ b/docs/source/Gauge.html @@ -1,194 +1,474 @@ -
+ +/** - * @class Ext.chart.axis.Gauge - * @extends Ext.chart.axis.Abstract - * - * Gauge Axis is the axis to be used with a Gauge series. The Gauge axis - * displays numeric data from an interval defined by the `minimum`, `maximum` and - * `step` configuration properties. The placement of the numeric data can be changed - * by altering the `margin` option that is set to `10` by default. - * - * A possible configuration for this axis would look like: + + + + +\ No newline at end of file +}); + +The source code + + + + + + +/** + * @class Ext.chart.series.Gauge + * @extends Ext.chart.series.Series + * + * Creates a Gauge Chart. Gauge Charts are used to show progress in a certain variable. There are two ways of using the Gauge chart. + * One is setting a store element into the Gauge and selecting the field to be used from that store. Another one is instanciating the + * visualization and using the `setValue` method to adjust the value you want. * - * axes: [{ - * type: 'gauge', - * position: 'gauge', - * minimum: 0, - * maximum: 100, - * steps: 10, - * margin: 7 - * }], + * A chart/series configuration for the Gauge visualization could look like this: + * + * { + * xtype: 'chart', + * store: store, + * axes: [{ + * type: 'gauge', + * position: 'gauge', + * minimum: 0, + * maximum: 100, + * steps: 10, + * margin: -10 + * }], + * series: [{ + * type: 'gauge', + * field: 'data1', + * donut: false, + * colorSet: ['#F49D10', '#ddd'] + * }] + * } + * + * In this configuration we create a special Gauge axis to be used with the gauge visualization (describing half-circle markers), and also we're + * setting a maximum, minimum and steps configuration options into the axis. The Gauge series configuration contains the store field to be bound to + * the visual display and the color set to be used with the visualization. + * + * @xtype gauge */ -Ext.define('Ext.chart.axis.Gauge', { +Ext.define('Ext.chart.series.Gauge', { /* Begin Definitions */ - extend: 'Ext.chart.axis.Abstract', + extend: 'Ext.chart.series.Series', /* End Definitions */ - - /** - * @cfg {Number} minimum (required) the minimum value of the interval to be displayed in the axis. - */ - /** - * @cfg {Number} maximum (required) the maximum value of the interval to be displayed in the axis. - */ + type: "gauge", + alias: 'series.gauge', - /** - * @cfg {Number} steps (required) the number of steps and tick marks to add to the interval. + rad: Math.PI / 180, + + /** + * @cfg {Number} highlightDuration + * The duration for the pie slice highlight effect. */ + highlightDuration: 150, - /** - * @cfg {Number} margin (optional) the offset positioning of the tick marks and labels in pixels. Default's 10. + /** + * @cfg {String} angleField + * The store record field name to be used for the pie angles. + * The values bound to this field name must be positive real numbers. + * This parameter is required. */ + angleField: false, - position: 'gauge', + /** + * @cfg {Boolean} needle + * Use the Gauge Series as an area series or add a needle to it. Default's false. + */ + needle: false, + + /** + * @cfg {Boolean|Number} donut + * Use the entire disk or just a fraction of it for the gauge. Default's false. + */ + donut: false, - alias: 'axis.gauge', + /** + * @cfg {Boolean} showInLegend + * Whether to add the pie chart elements as legend items. Default's false. + */ + showInLegend: false, - drawAxis: function(init) { - var chart = this.chart, + /** + * @cfg {Object} style + * An object containing styles for overriding series styles from Theming. + */ + style: {}, + + constructor: function(config) { + this.callParent(arguments); + var me = this, + chart = me.chart, surface = chart.surface, - bbox = chart.chartBBox, - centerX = bbox.x + (bbox.width / 2), - centerY = bbox.y + bbox.height, - margin = this.margin || 10, - rho = Math.min(bbox.width, 2 * bbox.height) /2 + margin, - sprites = [], sprite, - steps = this.steps, - i, pi = Math.PI, - cos = Math.cos, - sin = Math.sin; - - if (this.sprites && !chart.resizing) { - this.drawLabel(); - return; - } - - if (this.margin >= 0) { - if (!this.sprites) { - //draw circles - for (i = 0; i <= steps; i++) { - sprite = surface.add({ - type: 'path', - path: ['M', centerX + (rho - margin) * cos(i / steps * pi - pi), - centerY + (rho - margin) * sin(i / steps * pi - pi), - 'L', centerX + rho * cos(i / steps * pi - pi), - centerY + rho * sin(i / steps * pi - pi), 'Z'], - stroke: '#ccc' - }); - sprite.setAttributes({ - hidden: false - }, true); - sprites.push(sprite); + store = chart.store, + shadow = chart.shadow, i, l, cfg; + Ext.apply(me, config, { + shadowAttributes: [{ + "stroke-width": 6, + "stroke-opacity": 1, + stroke: 'rgb(200, 200, 200)', + translate: { + x: 1.2, + y: 2 } - } else { - sprites = this.sprites; - //draw circles - for (i = 0; i <= steps; i++) { - sprites[i].setAttributes({ - path: ['M', centerX + (rho - margin) * cos(i / steps * pi - pi), - centerY + (rho - margin) * sin(i / steps * pi - pi), - 'L', centerX + rho * cos(i / steps * pi - pi), - centerY + rho * sin(i / steps * pi - pi), 'Z'], - stroke: '#ccc' - }, true); + }, + { + "stroke-width": 4, + "stroke-opacity": 1, + stroke: 'rgb(150, 150, 150)', + translate: { + x: 0.9, + y: 1.5 + } + }, + { + "stroke-width": 2, + "stroke-opacity": 1, + stroke: 'rgb(100, 100, 100)', + translate: { + x: 0.6, + y: 1 } + }] + }); + me.group = surface.getGroup(me.seriesId); + if (shadow) { + for (i = 0, l = me.shadowAttributes.length; i < l; i++) { + me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i)); } } - this.sprites = sprites; - this.drawLabel(); - if (this.title) { - this.drawTitle(); - } + surface.customAttributes.segment = function(opt) { + return me.getSegment(opt); + }; }, - drawTitle: function() { + //@private updates some onbefore render parameters. + initialize: function() { var me = this, - chart = me.chart, - surface = chart.surface, - bbox = chart.chartBBox, - labelSprite = me.titleSprite, - labelBBox; - - if (!labelSprite) { - me.titleSprite = labelSprite = surface.add({ - type: 'text', - zIndex: 2 - }); + store = me.chart.substore || me.chart.store; + //Add yFields to be used in Legend.js + me.yField = []; + if (me.label.field) { + store.each(function(rec) { + me.yField.push(rec.get(me.label.field)); + }); } - labelSprite.setAttributes(Ext.apply({ - text: me.title - }, me.label || {}), true); - labelBBox = labelSprite.getBBox(); - labelSprite.setAttributes({ - x: bbox.x + (bbox.width / 2) - (labelBBox.width / 2), - y: bbox.y + bbox.height - (labelBBox.height / 2) - 4 - }, true); }, - /** - * Updates the {@link #title} of this axis. - * @param {String} title - */ - setTitle: function(title) { - this.title = title; - this.drawTitle(); + // @private returns an object with properties for a Slice + getSegment: function(opt) { + var me = this, + rad = me.rad, + cos = Math.cos, + sin = Math.sin, + abs = Math.abs, + x = me.centerX, + y = me.centerY, + x1 = 0, x2 = 0, x3 = 0, x4 = 0, + y1 = 0, y2 = 0, y3 = 0, y4 = 0, + delta = 1e-2, + r = opt.endRho - opt.startRho, + startAngle = opt.startAngle, + endAngle = opt.endAngle, + midAngle = (startAngle + endAngle) / 2 * rad, + margin = opt.margin || 0, + flag = abs(endAngle - startAngle) > 180, + a1 = Math.min(startAngle, endAngle) * rad, + a2 = Math.max(startAngle, endAngle) * rad, + singleSlice = false; + + x += margin * cos(midAngle); + y += margin * sin(midAngle); + + x1 = x + opt.startRho * cos(a1); + y1 = y + opt.startRho * sin(a1); + + x2 = x + opt.endRho * cos(a1); + y2 = y + opt.endRho * sin(a1); + + x3 = x + opt.startRho * cos(a2); + y3 = y + opt.startRho * sin(a2); + + x4 = x + opt.endRho * cos(a2); + y4 = y + opt.endRho * sin(a2); + + if (abs(x1 - x3) <= delta && abs(y1 - y3) <= delta) { + singleSlice = true; + } + //Solves mysterious clipping bug with IE + if (singleSlice) { + return { + path: [ + ["M", x1, y1], + ["L", x2, y2], + ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4], + ["Z"]] + }; + } else { + return { + path: [ + ["M", x1, y1], + ["L", x2, y2], + ["A", opt.endRho, opt.endRho, 0, +flag, 1, x4, y4], + ["L", x3, y3], + ["A", opt.startRho, opt.startRho, 0, +flag, 0, x1, y1], + ["Z"]] + }; + } + }, + + // @private utility function to calculate the middle point of a pie slice. + calcMiddle: function(item) { + var me = this, + rad = me.rad, + slice = item.slice, + x = me.centerX, + y = me.centerY, + startAngle = slice.startAngle, + endAngle = slice.endAngle, + radius = Math.max(('rho' in slice) ? slice.rho: me.radius, me.label.minMargin), + donut = +me.donut, + a1 = Math.min(startAngle, endAngle) * rad, + a2 = Math.max(startAngle, endAngle) * rad, + midAngle = -(a1 + (a2 - a1) / 2), + xm = x + (item.endRho + item.startRho) / 2 * Math.cos(midAngle), + ym = y - (item.endRho + item.startRho) / 2 * Math.sin(midAngle); + + item.middle = { + x: xm, + y: ym + }; }, - drawLabel: function() { - var chart = this.chart, + /** + * Draws the series for the current chart. + */ + drawSeries: function() { + var me = this, + chart = me.chart, + store = chart.substore || chart.store, + group = me.group, + animate = me.chart.animate, + axis = me.chart.axes.get(0), + minimum = axis && axis.minimum || me.minimum || 0, + maximum = axis && axis.maximum || me.maximum || 0, + field = me.angleField || me.field || me.xField, surface = chart.surface, - bbox = chart.chartBBox, - centerX = bbox.x + (bbox.width / 2), - centerY = bbox.y + bbox.height, - margin = this.margin || 10, - rho = Math.min(bbox.width, 2 * bbox.height) /2 + 2 * margin, - round = Math.round, - labelArray = [], label, - maxValue = this.maximum || 0, - steps = this.steps, i = 0, - adjY, - pi = Math.PI, + chartBBox = chart.chartBBox, + rad = me.rad, + donut = +me.donut, + values = {}, + items = [], + seriesStyle = me.seriesStyle, + seriesLabelStyle = me.seriesLabelStyle, + colorArrayStyle = me.colorArrayStyle, + colorArrayLength = colorArrayStyle && colorArrayStyle.length || 0, + gutterX = chart.maxGutter[0], + gutterY = chart.maxGutter[1], cos = Math.cos, sin = Math.sin, - labelConf = this.label, - renderer = labelConf.renderer || function(v) { return v; }; - - if (!this.labelArray) { - //draw scale - for (i = 0; i <= steps; i++) { - // TODO Adjust for height of text / 2 instead - adjY = (i === 0 || i === steps) ? 7 : 0; - label = surface.add({ - type: 'text', - text: renderer(round(i / steps * maxValue)), - x: centerX + rho * cos(i / steps * pi - pi), - y: centerY + rho * sin(i / steps * pi - pi) - adjY, - 'text-anchor': 'middle', - 'stroke-width': 0.2, - zIndex: 10, - stroke: '#333' + rendererAttributes, centerX, centerY, slice, slices, sprite, value, + item, ln, record, i, j, startAngle, endAngle, middleAngle, sliceLength, path, + p, spriteOptions, bbox, splitAngle, sliceA, sliceB; + + Ext.apply(seriesStyle, me.style || {}); + + me.setBBox(); + bbox = me.bbox; + + //override theme colors + if (me.colorSet) { + colorArrayStyle = me.colorSet; + colorArrayLength = colorArrayStyle.length; + } + + //if not store or store is empty then there's nothing to draw + if (!store || !store.getCount()) { + return; + } + + centerX = me.centerX = chartBBox.x + (chartBBox.width / 2); + centerY = me.centerY = chartBBox.y + chartBBox.height; + me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y); + me.slices = slices = []; + me.items = items = []; + + if (!me.value) { + record = store.getAt(0); + me.value = record.get(field); + } + + value = me.value; + if (me.needle) { + sliceA = { + series: me, + value: value, + startAngle: -180, + endAngle: 0, + rho: me.radius + }; + splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum)); + slices.push(sliceA); + } else { + splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum)); + sliceA = { + series: me, + value: value, + startAngle: -180, + endAngle: splitAngle, + rho: me.radius + }; + sliceB = { + series: me, + value: me.maximum - value, + startAngle: splitAngle, + endAngle: 0, + rho: me.radius + }; + slices.push(sliceA, sliceB); + } + + //do pie slices after. + for (i = 0, ln = slices.length; i < ln; i++) { + slice = slices[i]; + sprite = group.getAt(i); + //set pie slice properties + rendererAttributes = Ext.apply({ + segment: { + startAngle: slice.startAngle, + endAngle: slice.endAngle, + margin: 0, + rho: slice.rho, + startRho: slice.rho * +donut / 100, + endRho: slice.rho + } + }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {})); + + item = Ext.apply({}, + rendererAttributes.segment, { + slice: slice, + series: me, + storeItem: record, + index: i + }); + items[i] = item; + // Create a new sprite if needed (no height) + if (!sprite) { + spriteOptions = Ext.apply({ + type: "path", + group: group + }, Ext.apply(seriesStyle, colorArrayStyle && { fill: colorArrayStyle[i % colorArrayLength] } || {})); + sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes)); + } + slice.sprite = slice.sprite || []; + item.sprite = sprite; + slice.sprite.push(sprite); + if (animate) { + rendererAttributes = me.renderer(sprite, record, rendererAttributes, i, store); + sprite._to = rendererAttributes; + me.onAnimate(sprite, { + to: rendererAttributes }); - label.setAttributes({ + } else { + rendererAttributes = me.renderer(sprite, record, Ext.apply(rendererAttributes, { hidden: false - }, true); - labelArray.push(label); + }), i, store); + sprite.setAttributes(rendererAttributes, true); } } - else { - labelArray = this.labelArray; - //draw values - for (i = 0; i <= steps; i++) { - // TODO Adjust for height of text / 2 instead - adjY = (i === 0 || i === steps) ? 7 : 0; - labelArray[i].setAttributes({ - text: renderer(round(i / steps * maxValue)), - x: centerX + rho * cos(i / steps * pi - pi), - y: centerY + rho * sin(i / steps * pi - pi) - adjY - }, true); + + if (me.needle) { + splitAngle = splitAngle * Math.PI / 180; + + if (!me.needleSprite) { + me.needleSprite = me.chart.surface.add({ + type: 'path', + path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle), + centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)), + 'L', centerX + me.radius * cos(splitAngle), + centerY + -Math.abs(me.radius * sin(splitAngle))], + 'stroke-width': 4, + 'stroke': '#222' + }); + } else { + if (animate) { + me.onAnimate(me.needleSprite, { + to: { + path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle), + centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)), + 'L', centerX + me.radius * cos(splitAngle), + centerY + -Math.abs(me.radius * sin(splitAngle))] + } + }); + } else { + me.needleSprite.setAttributes({ + type: 'path', + path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle), + centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)), + 'L', centerX + me.radius * cos(splitAngle), + centerY + -Math.abs(me.radius * sin(splitAngle))] + }); + } } + me.needleSprite.setAttributes({ + hidden: false + }, true); } - this.labelArray = labelArray; + + delete me.value; + }, + + /** + * Sets the Gauge chart to the current specified value. + */ + setValue: function (value) { + this.value = value; + this.drawSeries(); + }, + + // @private callback for when creating a label sprite. + onCreateLabel: function(storeItem, item, i, display) {}, + + // @private callback for when placing a label sprite. + onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {}, + + // @private callback for when placing a callout. + onPlaceCallout: function() {}, + + // @private handles sprite animation for the series. + onAnimate: function(sprite, attr) { + sprite.show(); + return this.callParent(arguments); + }, + + isItemInPoint: function(x, y, item, i) { + return false; + }, + + // @private shows all elements in the series. + showAll: function() { + if (!isNaN(this._index)) { + this.__excludes[this._index] = false; + this.drawSeries(); + } + }, + + /** + * Returns the color of the series (to be displayed as color for the series legend item). + * @param item {Object} Info about the item; same format as returned by #getItemForPoint + */ + getLegendColor: function(index) { + var me = this; + return me.colorArrayStyle[index % me.colorArrayStyle.length]; } -});