Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / examples / charts / ScatterRenderer.js
diff --git a/examples/charts/ScatterRenderer.js b/examples/charts/ScatterRenderer.js
new file mode 100644 (file)
index 0000000..77412d3
--- /dev/null
@@ -0,0 +1,444 @@
+Ext.require('Ext.chart.*');
+Ext.require(['Ext.Window', 'Ext.fx.target.Sprite', 'Ext.layout.container.Fit']);
+
+var Renderers = {};
+
+(function() {
+     var ColorManager = {
+       rgbToHsv: function(rgb) {
+           var r = rgb[0] / 255,
+               g = rgb[1] / 255,
+               b = rgb[2] / 255,
+               rd = Math.round,
+               minVal = Math.min(r, g, b),
+               maxVal = Math.max(r, g, b),
+               delta = maxVal - minVal,
+               h = 0, s = 0, v = 0,
+               deltaRgb;
+
+           v = maxVal;
+
+           if (delta == 0) {
+             return [0, 0, v];
+           } else {
+             s = delta / maxVal;
+             deltaRgb = {
+                 r: (((maxVal - r) / 6) + (delta / 2)) / delta,
+                 g: (((maxVal - g) / 6) + (delta / 2)) / delta,
+                 b: (((maxVal - b) / 6) + (delta / 2)) / delta
+             };
+             if (r == maxVal) {
+                 h = deltaRgb.b - deltaRgb.g;
+             } else if (g == maxVal) {
+                 h = (1 / 3) + deltaRgb.r - deltaRgb.b;
+             } else if (b == maxVal) {
+                 h = (2 / 3) + deltaRgb.g - deltaRgb.r;
+             }
+             //handle edge cases for hue
+             if (h < 0) {
+                 h += 1;
+             }
+             if (h > 1) {
+                 h -= 1;
+             }
+           }
+
+           h = rd(h * 360);
+           s = rd(s * 100);
+           v = rd(v * 100);
+
+           return [h, s, v];
+       },
+
+       hsvToRgb : function(hsv) {
+           var h = hsv[0] / 360,
+               s = hsv[1] / 100,
+               v = hsv[2] / 100,
+               r, g, b, rd = Math.round;
+
+           if (s == 0) {
+             v *= 255;
+             return [v, v, v];
+           } else {
+             var vh = h * 6,
+                 vi = vh >> 0,
+                 v1 = v * (1 - s),
+                 v2 = v * (1 - s * (vh - vi)),
+                 v3 = v * (1 - s * (1 - (vh - vi)));
+
+             switch(vi) {
+                 case 0:
+                     r = v; g = v3; b = v1;
+                     break;
+                 case 1:
+                     r = v2; g = v; b = v1;
+                     break;
+                 case 2:
+                     r = v1; g = v; b = v3;
+                     break;
+                 case 3:
+                     r = v1; g = v2; b = v;
+                     break;
+                 case 4:
+                     r = v3; g = v1; b = v;
+                     break;
+                 default:
+                     r = v; g = v1; b = v2;
+             }
+             return [rd(r * 255),
+                     rd(g * 255),
+                     rd(b * 255)];
+           }
+       }
+    };
+    //Generic number interpolator
+    var delta = function(x, y, a, b, theta) {
+            return a + (b - a) * (y - theta) / (y - x);
+    };
+    //Add renderer methods.
+    Ext.apply(Renderers, {
+        color: function(fieldName, minColor, maxColor, minValue, maxValue) {
+            var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)\s*/,
+                minColorMatch = minColor.match(re),
+                maxColorMatch = maxColor.match(re),
+                interpolate = function(theta) {
+                    return [ delta(minValue, maxValue, minColor[0], maxColor[0], theta),
+                             delta(minValue, maxValue, minColor[1], maxColor[1], theta),
+                             delta(minValue, maxValue, minColor[2], maxColor[2], theta) ];
+                };
+            minColor = ColorManager.rgbToHsv([ +minColorMatch[1], +minColorMatch[2], +minColorMatch[3] ]);
+            maxColor = ColorManager.rgbToHsv([ +maxColorMatch[1], +maxColorMatch[2], +maxColorMatch[3] ]);
+            //Return the renderer
+            return function(sprite, record, attr, index, store) {
+                var value = +record.get(fieldName),
+                    rgb = ColorManager.hsvToRgb(interpolate(value)),
+                    rgbString = 'rgb(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ')';
+                return Ext.apply(attr, {
+                    fill: rgbString
+                });
+            };
+        },
+
+        grayscale: function(fieldName, minColor, maxColor, minValue, maxValue) {
+            var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)\s*/,
+            minColorMatch = minColor.match(re),
+            maxColorMatch = maxColor.match(re),
+            interpolate = function(theta) {
+                var ans = delta(minValue, maxValue, +minColorMatch[1], +maxColorMatch[1], theta) >> 0;
+                return [ ans, ans, ans ];
+            };
+            //Return the renderer
+            return function(sprite, record, attr, index, store) {
+                var value = +record.get(fieldName),
+                    rgb = interpolate(value),
+                    rgbString = 'rgb(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ')';
+
+                return Ext.apply(attr, {
+                    fill: rgbString,
+                    strokeFill: 'rgb(0, 0, 0)'
+                });
+            };
+        },
+
+        radius: function(fieldName, minRadius, maxRadius, minValue, maxValue) {
+            var interpolate = function(theta) {
+                return delta(minValue, maxValue, minRadius, maxRadius, theta);
+            };
+            //Return the renderer
+            return function(sprite, record, attr, index, store) {
+                var value = +record.get(fieldName),
+                    radius = interpolate(value);
+
+                return Ext.apply(attr, {
+                    radius: radius,
+                    size: radius
+                });
+            };
+        }
+    });
+})();
+
+Ext.onReady(function () {
+    //current renderer configuration
+    var rendererConfiguration = {
+        xField: 'data1',
+        yField: 'data2',
+        color: false,
+        colorFrom: 'rgb(250, 20, 20)',
+        colorTo: 'rgb(127, 0, 240)',
+        scale: false,
+        scaleFrom: 'rgb(20, 20, 20)',
+        scaleTo: 'rgb(220, 220, 220)',
+        radius: false,
+        radiusSize: 50
+    };
+    //update the visualization with the new renderer configuration
+    function refresh() {
+        var chart = Ext.getCmp('chartCmp'),
+            series = chart.series.items,
+            len = series.length,
+            rc = rendererConfiguration,
+            color, grayscale, radius, s;
+
+        for(var i = 0; i < len; i++) {
+            s = series[i];
+            s.xField = rc.xField;
+            s.yField = rc.yField;
+            color = rc.color? Renderers.color(rc.color, rc.colorFrom, rc.colorTo, 0, 100) : function(a, b, attr) { return attr; };
+            grayscale = rc.grayscale? Renderers.grayscale(rc.grayscale, rc.scaleFrom, rc.scaleTo, 0, 100) : function(a, b, attr) { return attr; };
+            radius = rc.radius? Renderers.radius(rc.radius, 10, rc.radiusSize, 0, 100) : function(a, b, attr) { return attr; };
+            s.renderer = function(sprite, record, attr, index, store) {
+                return radius(sprite, record, grayscale(sprite, record, color(sprite, record, attr, index, store), index, store), index, store);
+            };
+        }
+        chart.redraw();
+    }
+    //form selection callbacks/handlers.
+    var xAxisHandler = function(elem) {
+        var xField = elem.text;
+        rendererConfiguration.xField = xField;
+        refresh();
+    };
+
+    var yAxisHandler = function(elem) {
+        var yField = elem.text;
+        rendererConfiguration.yField = yField;
+        refresh();
+    };
+
+    var colorVariableHandler = function(elem) {
+        var color = elem.text;
+        rendererConfiguration.color = color;
+        rendererConfiguration.grayscale = false;
+        refresh();
+    };
+
+    var grayscaleVariableHandler = function(elem) {
+        var color = elem.text;
+        rendererConfiguration.grayscale = color;
+        rendererConfiguration.color = false;
+        refresh();
+    };
+
+    var scaleFromHandler = function(elem) {
+        var from = elem.text;
+        rendererConfiguration.scaleFrom = from;
+        refresh();
+    };
+
+    var scaleToHandler = function(elem) {
+        var to = elem.text;
+        rendererConfiguration.scaleTo = to;
+        refresh();
+    };
+
+    var colorFromHandler = function(elem) {
+        var from = elem.text;
+        rendererConfiguration.colorFrom = from;
+        refresh();
+    };
+
+    var colorToHandler = function(elem) {
+        var to = elem.text;
+        rendererConfiguration.colorTo = to;
+        refresh();
+    };
+
+    var radiusHandler = function(elem) {
+        var radius = elem.text;
+        rendererConfiguration.radius = radius;
+        refresh();
+    };
+
+    var radiusSizeHandler = function(elem) {
+        var radius = elem.text;
+        rendererConfiguration.radiusSize = parseInt(radius, 10);
+        refresh();
+    };
+
+    var xAxisMenu = Ext.create('Ext.menu.Menu', {
+        id: 'xAxisMenu',
+        items: [ {
+             text: 'data1',
+             handler: xAxisHandler,
+             checked: true,
+             group: 'x'
+           },
+           {
+             text: 'data2',
+             handler: xAxisHandler,
+               checked: false,
+               group: 'x'
+           },
+           {
+             text: 'data3',
+             handler: xAxisHandler,
+             checked: false,
+             group: 'x'
+           } ]
+    });
+
+    var yAxisMenu = Ext.create('Ext.menu.Menu', {
+        id: 'yAxisMenu',
+        items: [ {
+            text: 'data1',
+            handler: yAxisHandler,
+            checked: false,
+            group: 'y'
+          },
+          {
+        text: 'data2',
+            handler: yAxisHandler,
+            checked: true,
+            group: 'y'
+          },
+          {
+            text: 'data3',
+            handler: yAxisHandler,
+            checked: false,
+            group: 'y'
+          } ]
+    });
+
+    var colorMenu = Ext.create('Ext.menu.Menu', {
+        id: 'colorMenu',
+        items: [ { text: 'data1', handler: colorVariableHandler, checked: false, group: 'color' },
+                 { text: 'data2', handler: colorVariableHandler, checked: false, group: 'color' },
+                 { text: 'data3', handler: colorVariableHandler, checked: false, group: 'color' },
+                 { text: 'Color From',
+                   menu: {
+                     items: [{ text: 'rgb(250, 20, 20)', handler: colorToHandler, checked: true, group: 'colorrange' },
+                             { text: 'rgb(20, 250, 20)', handler: colorToHandler, checked: false, group: 'colorrange' },
+                             { text: 'rgb(20, 20, 250)', handler: colorToHandler, checked: false, group: 'colorrange' },
+                             { text: 'rgb(127, 0, 240)', handler: colorFromHandler, checked: false, group: 'colorrange' },
+                             { text: 'rgb(213, 70, 121)', handler: colorToHandler, checked: false, group: 'colorrange' },
+                             { text: 'rgb(44, 153, 201)', handler: colorFromHandler, checked: false, group: 'colorrange' },
+                             { text: 'rgb(146, 6, 157)', handler: colorFromHandler, checked: false, group: 'colorrange' },
+                             { text: 'rgb(49, 149, 0)', handler: colorFromHandler, checked: false, group: 'colorrange' },
+                             { text: 'rgb(249, 153, 0)', handler: colorFromHandler, checked: false, group: 'colorrange' }]
+                   }
+                 },
+                 { text: 'Color To',
+                     menu: {
+                       items: [{ text: 'rgb(250, 20, 20)', handler: colorToHandler, checked: false, group: 'tocolorrange' },
+                               { text: 'rgb(20, 250, 20)', handler: colorToHandler, checked: false, group: 'tocolorrange' },
+                               { text: 'rgb(20, 20, 250)', handler: colorToHandler, checked: false, group: 'tocolorrange' },
+                               { text: 'rgb(127, 0, 220)', handler: colorFromHandler, checked: true, group: 'tocolorrange' },
+                               { text: 'rgb(213, 70, 121)', handler: colorToHandler, checked: false, group: 'tocolorrange' },
+                               { text: 'rgb(44, 153, 201)', handler: colorToHandler, checked: false, group: 'tocolorrange' },
+                               { text: 'rgb(146, 6, 157)' , handler: colorToHandler, checked: false, group: 'tocolorrange' },
+                               { text: 'rgb(49, 149, 0)', handler: colorToHandler, checked: false, group: 'tocolorrange' },
+                               { text: 'rgb(249, 153, 0)', handler: colorToHandler, checked: false, group: 'tocolorrange' }]
+                     }
+                   }
+               ]
+    });
+
+    var grayscaleMenu = Ext.create('Ext.menu.Menu', {
+        id: 'grayscaleMenu',
+        items: [ { text: 'data1', handler: grayscaleVariableHandler, checked: false, group: 'gs' },
+                 { text: 'data2', handler: grayscaleVariableHandler, checked: false, group: 'gs' },
+                 { text: 'data3', handler: grayscaleVariableHandler, checked: false, group: 'gs' },
+                 { text: 'Scale From',
+                   menu: {
+                     items: [{ text: 'rgb(20, 20, 20)', handler: scaleFromHandler, checked: true, group: 'gsrange' },
+                             { text: 'rgb(80, 80, 80)', handler: scaleFromHandler, checked: false, group: 'gsrange' },
+                             { text: 'rgb(120, 120, 120)', handler: scaleFromHandler, checked: false, group: 'gsrange' },
+                             { text: 'rgb(180, 180, 180)', handler: scaleFromHandler, checked: false, group: 'gsrange' },
+                             { text: 'rgb(220, 220, 220)', handler: scaleFromHandler, checked: false, group: 'gsrange' },
+                             { text: 'rgb(250, 250, 250)', handler: scaleFromHandler, checked: false, group: 'gsrange' }]
+                   }
+                 },
+                 { text: 'Scale To',
+                     menu: {
+                     items: [{ text: 'rgb(20, 20, 20)', handler: scaleToHandler, checked: false, group: 'togsrange' },
+                             { text: 'rgb(80, 80, 80)', handler: scaleToHandler, checked: false, group: 'togsrange' },
+                             { text: 'rgb(120, 120, 120)', handler: scaleToHandler, checked: false, group: 'togsrange' },
+                             { text: 'rgb(180, 180, 180)', handler: scaleToHandler, checked: false, group: 'togsrange' },
+                             { text: 'rgb(220, 220, 220)', handler: scaleToHandler, checked: true, group: 'togsrange' },
+                             { text: 'rgb(250, 250, 250)', handler: scaleToHandler, checked: false, group: 'togsrange' }]
+                     }
+                   }
+               ]
+    });
+
+    var radiusMenu = Ext.create('Ext.menu.Menu', {
+        id: 'radiusMenu',
+        style: {
+            overflow: 'visible'     // For the Combo popup
+        },
+        items: [ { text: 'data1', handler: radiusHandler, checked: false, group: 'radius' },
+                 { text: 'data2', handler: radiusHandler, checked: false, group: 'radius' },
+                 { text: 'data3', handler: radiusHandler, checked: false, group: 'radius' },
+                 { text: 'Max Radius',
+                   menu: {
+                     items: [{ text: '20', handler: radiusSizeHandler, checked: false, group: 'sradius' },
+                             { text: '30', handler: radiusSizeHandler, checked: false, group: 'sradius' },
+                             { text: '40', handler: radiusSizeHandler, checked: false, group: 'sradius' },
+                             { text: '50', handler: radiusSizeHandler, checked: true, group: 'sradius' },
+                             { text: '60', handler: radiusSizeHandler, checked: false, group: 'sradius' }]
+                   }
+                 }
+               ]
+    });
+
+    var win = Ext.create('Ext.Window', {
+        width: 800,
+        height: 600,
+        hidden: false,
+        maximizable: true,
+        title: 'Scatter Chart Renderer',
+        renderTo: Ext.getBody(),
+        layout: 'fit',
+        tbar: [
+        {
+            text: 'Reload Data',
+            handler: function() {
+                store1.loadData(generateData());
+            }
+        },
+        {
+            text: 'Select X Axis',
+            menu: xAxisMenu
+        },
+        {
+            text: 'Select Y Axis',
+            menu: yAxisMenu
+        },
+
+        {
+            text: 'Select Color',
+            menu: colorMenu
+        },
+        {
+            text: 'Select Grayscale',
+            menu: grayscaleMenu
+        },
+        {
+            text: 'Select Radius',
+            menu: radiusMenu
+        }
+        ],
+        items: {
+            id: 'chartCmp',
+            xtype: 'chart',
+            style: 'background:#fff',
+            animate: true,
+            store: store1,
+            axes: false,
+            insetPadding: 50,
+            series: [{
+                type: 'scatter',
+                axis: false,
+                xField: 'data1',
+                yField: 'data2',
+                color: '#ccc',
+                markerConfig: {
+                    type: 'circle',
+                    radius: 20,
+                    size: 20
+                }
+            }]
+        }
+    });
+});