<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>The source code</title>
- <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
- <script type="text/javascript" src="../prettify/prettify.js"></script>
+ <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
+ <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
<style type="text/css">
.highlight { display: block; background-color: #ddd; }
</style>
</script>
</head>
<body onload="prettyPrint(); highlight();">
- <pre class="prettyprint lang-js"><span id='Ext-chart-axis-Gauge'>/**
-</span> * @class Ext.chart.axis.Gauge
- * @extends Ext.chart.axis.Abstract
+ <pre class="prettyprint lang-js"><span id='Ext-chart-series-Gauge'>/**
+</span> * @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 instantiating the
+ * visualization and using the `setValue` method to adjust the value you want.
*
- * 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:
- *
- * 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 */
-
-<span id='Ext-chart-axis-Gauge-cfg-minimum'> /**
-</span> * @cfg {Number} minimum (required) the minimum value of the interval to be displayed in the axis.
- */
-<span id='Ext-chart-axis-Gauge-cfg-maximum'> /**
-</span> * @cfg {Number} maximum (required) the maximum value of the interval to be displayed in the axis.
- */
+ type: "gauge",
+ alias: 'series.gauge',
+
+ rad: Math.PI / 180,
-<span id='Ext-chart-axis-Gauge-cfg-steps'> /**
-</span> * @cfg {Number} steps (required) the number of steps and tick marks to add to the interval.
+<span id='Ext-chart-series-Gauge-cfg-highlightDuration'> /**
+</span> * @cfg {Number} highlightDuration
+ * The duration for the pie slice highlight effect.
*/
+ highlightDuration: 150,
-<span id='Ext-chart-axis-Gauge-cfg-margin'> /**
-</span> * @cfg {Number} margin (optional) the offset positioning of the tick marks and labels in pixels. Default's 10.
+<span id='Ext-chart-series-Gauge-cfg-angleField'> /**
+</span> * @cfg {String} angleField (required)
+ * The store record field name to be used for the pie angles.
+ * The values bound to this field name must be positive real numbers.
*/
+ angleField: false,
- position: 'gauge',
+<span id='Ext-chart-series-Gauge-cfg-needle'> /**
+</span> * @cfg {Boolean} needle
+ * Use the Gauge Series as an area series or add a needle to it. Default's false.
+ */
+ needle: false,
+
+<span id='Ext-chart-series-Gauge-cfg-donut'> /**
+</span> * @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',
+<span id='Ext-chart-series-Gauge-cfg-showInLegend'> /**
+</span> * @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,
+<span id='Ext-chart-series-Gauge-cfg-style'> /**
+</span> * @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.getChartStore();
+ //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);
},
-<span id='Ext-chart-axis-Gauge-method-setTitle'> /**
-</span> * 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,
+<span id='Ext-chart-series-Gauge-method-drawSeries'> /**
+</span> * Draws the series for the current chart.
+ */
+ drawSeries: function() {
+ var me = this,
+ chart = me.chart,
+ store = chart.getChartStore(),
+ 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;
+ },
+
+<span id='Ext-chart-series-Gauge-method-setValue'> /**
+</span> * 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();
+ }
+ },
+
+<span id='Ext-chart-series-Gauge-method-getLegendColor'> /**
+</span> * 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];
}
-});</pre>
+});
+
+</pre>
</body>
</html>