Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Gauge2.html
index 1a1eff6..022b18d 100644 (file)
@@ -3,8 +3,8 @@
 <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: &quot;gauge&quot;,
+    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 &amp;&amp; !chart.resizing) {
-            this.drawLabel();
-            return;
-        }
-
-        if (this.margin &gt;= 0) {
-            if (!this.sprites) {
-                //draw circles
-                for (i = 0; i &lt;= 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: [{
+                &quot;stroke-width&quot;: 6,
+                &quot;stroke-opacity&quot;: 1,
+                stroke: 'rgb(200, 200, 200)',
+                translate: {
+                    x: 1.2,
+                    y: 2
                 }
-            } else {
-                sprites = this.sprites;
-                //draw circles
-                for (i = 0; i &lt;= 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);
+            },
+            {
+                &quot;stroke-width&quot;: 4,
+                &quot;stroke-opacity&quot;: 1,
+                stroke: 'rgb(150, 150, 150)',
+                translate: {
+                    x: 0.9,
+                    y: 1.5
                 }
+            },
+            {
+                &quot;stroke-width&quot;: 2,
+                &quot;stroke-opacity&quot;: 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 &lt; 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) &gt; 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) &lt;= delta &amp;&amp; abs(y1 - y3) &lt;= delta) {
+            singleSlice = true;
+        }
+        //Solves mysterious clipping bug with IE
+        if (singleSlice) {
+            return {
+                path: [
+                [&quot;M&quot;, x1, y1],
+                [&quot;L&quot;, x2, y2],
+                [&quot;A&quot;, opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
+                [&quot;Z&quot;]]
+            };
+        } else {
+            return {
+                path: [
+                [&quot;M&quot;, x1, y1],
+                [&quot;L&quot;, x2, y2],
+                [&quot;A&quot;, opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
+                [&quot;L&quot;, x3, y3],
+                [&quot;A&quot;, opt.startRho, opt.startRho, 0, +flag, 0, x1, y1],
+                [&quot;Z&quot;]]
+            };
+        }
+    },
+
+    // @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 &amp;&amp; axis.minimum || me.minimum || 0,
+            maximum = axis &amp;&amp; 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 &amp;&amp; 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 &lt;= 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 &lt; 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 &amp;&amp; { 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: &quot;path&quot;,
+                    group: group
+                }, Ext.apply(seriesStyle, colorArrayStyle &amp;&amp; { 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 &lt;= 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>