Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / examples / charts / ScatterRenderer.js
1 Ext.require('Ext.chart.*');
2 Ext.require(['Ext.Window', 'Ext.fx.target.Sprite', 'Ext.layout.container.Fit']);
3
4 var Renderers = {};
5
6 (function() {
7      var ColorManager = {
8        rgbToHsv: function(rgb) {
9            var r = rgb[0] / 255,
10                g = rgb[1] / 255,
11                b = rgb[2] / 255,
12                rd = Math.round,
13                minVal = Math.min(r, g, b),
14                maxVal = Math.max(r, g, b),
15                delta = maxVal - minVal,
16                h = 0, s = 0, v = 0,
17                deltaRgb;
18
19            v = maxVal;
20
21            if (delta == 0) {
22              return [0, 0, v];
23            } else {
24              s = delta / maxVal;
25              deltaRgb = {
26                  r: (((maxVal - r) / 6) + (delta / 2)) / delta,
27                  g: (((maxVal - g) / 6) + (delta / 2)) / delta,
28                  b: (((maxVal - b) / 6) + (delta / 2)) / delta
29              };
30              if (r == maxVal) {
31                  h = deltaRgb.b - deltaRgb.g;
32              } else if (g == maxVal) {
33                  h = (1 / 3) + deltaRgb.r - deltaRgb.b;
34              } else if (b == maxVal) {
35                  h = (2 / 3) + deltaRgb.g - deltaRgb.r;
36              }
37              //handle edge cases for hue
38              if (h < 0) {
39                  h += 1;
40              }
41              if (h > 1) {
42                  h -= 1;
43              }
44            }
45
46            h = rd(h * 360);
47            s = rd(s * 100);
48            v = rd(v * 100);
49
50            return [h, s, v];
51        },
52
53        hsvToRgb : function(hsv) {
54            var h = hsv[0] / 360,
55                s = hsv[1] / 100,
56                v = hsv[2] / 100,
57                r, g, b, rd = Math.round;
58
59            if (s == 0) {
60              v *= 255;
61              return [v, v, v];
62            } else {
63              var vh = h * 6,
64                  vi = vh >> 0,
65                  v1 = v * (1 - s),
66                  v2 = v * (1 - s * (vh - vi)),
67                  v3 = v * (1 - s * (1 - (vh - vi)));
68
69              switch(vi) {
70                  case 0:
71                      r = v; g = v3; b = v1;
72                      break;
73                  case 1:
74                      r = v2; g = v; b = v1;
75                      break;
76                  case 2:
77                      r = v1; g = v; b = v3;
78                      break;
79                  case 3:
80                      r = v1; g = v2; b = v;
81                      break;
82                  case 4:
83                      r = v3; g = v1; b = v;
84                      break;
85                  default:
86                      r = v; g = v1; b = v2;
87              }
88              return [rd(r * 255),
89                      rd(g * 255),
90                      rd(b * 255)];
91            }
92        }
93     };
94     //Generic number interpolator
95     var delta = function(x, y, a, b, theta) {
96             return a + (b - a) * (y - theta) / (y - x);
97     };
98     //Add renderer methods.
99     Ext.apply(Renderers, {
100         color: function(fieldName, minColor, maxColor, minValue, maxValue) {
101             var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)\s*/,
102                 minColorMatch = minColor.match(re),
103                 maxColorMatch = maxColor.match(re),
104                 interpolate = function(theta) {
105                     return [ delta(minValue, maxValue, minColor[0], maxColor[0], theta),
106                              delta(minValue, maxValue, minColor[1], maxColor[1], theta),
107                              delta(minValue, maxValue, minColor[2], maxColor[2], theta) ];
108                 };
109             minColor = ColorManager.rgbToHsv([ +minColorMatch[1], +minColorMatch[2], +minColorMatch[3] ]);
110             maxColor = ColorManager.rgbToHsv([ +maxColorMatch[1], +maxColorMatch[2], +maxColorMatch[3] ]);
111             //Return the renderer
112             return function(sprite, record, attr, index, store) {
113                 var value = +record.get(fieldName),
114                     rgb = ColorManager.hsvToRgb(interpolate(value)),
115                     rgbString = 'rgb(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ')';
116                 return Ext.apply(attr, {
117                     fill: rgbString
118                 });
119             };
120         },
121
122         grayscale: function(fieldName, minColor, maxColor, minValue, maxValue) {
123             var re = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)\s*/,
124             minColorMatch = minColor.match(re),
125             maxColorMatch = maxColor.match(re),
126             interpolate = function(theta) {
127                 var ans = delta(minValue, maxValue, +minColorMatch[1], +maxColorMatch[1], theta) >> 0;
128                 return [ ans, ans, ans ];
129             };
130             //Return the renderer
131             return function(sprite, record, attr, index, store) {
132                 var value = +record.get(fieldName),
133                     rgb = interpolate(value),
134                     rgbString = 'rgb(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ')';
135
136                 return Ext.apply(attr, {
137                     fill: rgbString,
138                     strokeFill: 'rgb(0, 0, 0)'
139                 });
140             };
141         },
142
143         radius: function(fieldName, minRadius, maxRadius, minValue, maxValue) {
144             var interpolate = function(theta) {
145                 return delta(minValue, maxValue, minRadius, maxRadius, theta);
146             };
147             //Return the renderer
148             return function(sprite, record, attr, index, store) {
149                 var value = +record.get(fieldName),
150                     radius = interpolate(value);
151
152                 return Ext.apply(attr, {
153                     radius: radius,
154                     size: radius
155                 });
156             };
157         }
158     });
159 })();
160
161 Ext.onReady(function () {
162     //current renderer configuration
163     var rendererConfiguration = {
164         xField: 'data1',
165         yField: 'data2',
166         color: false,
167         colorFrom: 'rgb(250, 20, 20)',
168         colorTo: 'rgb(127, 0, 240)',
169         scale: false,
170         scaleFrom: 'rgb(20, 20, 20)',
171         scaleTo: 'rgb(220, 220, 220)',
172         radius: false,
173         radiusSize: 50
174     };
175     //update the visualization with the new renderer configuration
176     function refresh() {
177         var chart = Ext.getCmp('chartCmp'),
178             series = chart.series.items,
179             len = series.length,
180             rc = rendererConfiguration,
181             color, grayscale, radius, s;
182
183         for(var i = 0; i < len; i++) {
184             s = series[i];
185             s.xField = rc.xField;
186             s.yField = rc.yField;
187             color = rc.color? Renderers.color(rc.color, rc.colorFrom, rc.colorTo, 0, 100) : function(a, b, attr) { return attr; };
188             grayscale = rc.grayscale? Renderers.grayscale(rc.grayscale, rc.scaleFrom, rc.scaleTo, 0, 100) : function(a, b, attr) { return attr; };
189             radius = rc.radius? Renderers.radius(rc.radius, 10, rc.radiusSize, 0, 100) : function(a, b, attr) { return attr; };
190             s.renderer = function(sprite, record, attr, index, store) {
191                 return radius(sprite, record, grayscale(sprite, record, color(sprite, record, attr, index, store), index, store), index, store);
192             };
193         }
194         chart.redraw();
195     }
196     //form selection callbacks/handlers.
197     var xAxisHandler = function(elem) {
198         var xField = elem.text;
199         rendererConfiguration.xField = xField;
200         refresh();
201     };
202
203     var yAxisHandler = function(elem) {
204         var yField = elem.text;
205         rendererConfiguration.yField = yField;
206         refresh();
207     };
208
209     var colorVariableHandler = function(elem) {
210         var color = elem.text;
211         rendererConfiguration.color = color;
212         rendererConfiguration.grayscale = false;
213         refresh();
214     };
215
216     var grayscaleVariableHandler = function(elem) {
217         var color = elem.text;
218         rendererConfiguration.grayscale = color;
219         rendererConfiguration.color = false;
220         refresh();
221     };
222
223     var scaleFromHandler = function(elem) {
224         var from = elem.text;
225         rendererConfiguration.scaleFrom = from;
226         refresh();
227     };
228
229     var scaleToHandler = function(elem) {
230         var to = elem.text;
231         rendererConfiguration.scaleTo = to;
232         refresh();
233     };
234
235     var colorFromHandler = function(elem) {
236         var from = elem.text;
237         rendererConfiguration.colorFrom = from;
238         refresh();
239     };
240
241     var colorToHandler = function(elem) {
242         var to = elem.text;
243         rendererConfiguration.colorTo = to;
244         refresh();
245     };
246
247     var radiusHandler = function(elem) {
248         var radius = elem.text;
249         rendererConfiguration.radius = radius;
250         refresh();
251     };
252
253     var radiusSizeHandler = function(elem) {
254         var radius = elem.text;
255         rendererConfiguration.radiusSize = parseInt(radius, 10);
256         refresh();
257     };
258
259     var xAxisMenu = Ext.create('Ext.menu.Menu', {
260         id: 'xAxisMenu',
261         items: [ {
262              text: 'data1',
263              handler: xAxisHandler,
264              checked: true,
265              group: 'x'
266            },
267            {
268              text: 'data2',
269              handler: xAxisHandler,
270                checked: false,
271                group: 'x'
272            },
273            {
274              text: 'data3',
275              handler: xAxisHandler,
276              checked: false,
277              group: 'x'
278            } ]
279     });
280
281     var yAxisMenu = Ext.create('Ext.menu.Menu', {
282         id: 'yAxisMenu',
283         items: [ {
284             text: 'data1',
285             handler: yAxisHandler,
286             checked: false,
287             group: 'y'
288           },
289           {
290         text: 'data2',
291             handler: yAxisHandler,
292             checked: true,
293             group: 'y'
294           },
295           {
296             text: 'data3',
297             handler: yAxisHandler,
298             checked: false,
299             group: 'y'
300           } ]
301     });
302
303     var colorMenu = Ext.create('Ext.menu.Menu', {
304         id: 'colorMenu',
305         items: [ { text: 'data1', handler: colorVariableHandler, checked: false, group: 'color' },
306                  { text: 'data2', handler: colorVariableHandler, checked: false, group: 'color' },
307                  { text: 'data3', handler: colorVariableHandler, checked: false, group: 'color' },
308                  { text: 'Color From',
309                    menu: {
310                      items: [{ text: 'rgb(250, 20, 20)', handler: colorToHandler, checked: true, group: 'colorrange' },
311                              { text: 'rgb(20, 250, 20)', handler: colorToHandler, checked: false, group: 'colorrange' },
312                              { text: 'rgb(20, 20, 250)', handler: colorToHandler, checked: false, group: 'colorrange' },
313                              { text: 'rgb(127, 0, 240)', handler: colorFromHandler, checked: false, group: 'colorrange' },
314                              { text: 'rgb(213, 70, 121)', handler: colorToHandler, checked: false, group: 'colorrange' },
315                              { text: 'rgb(44, 153, 201)', handler: colorFromHandler, checked: false, group: 'colorrange' },
316                              { text: 'rgb(146, 6, 157)', handler: colorFromHandler, checked: false, group: 'colorrange' },
317                              { text: 'rgb(49, 149, 0)', handler: colorFromHandler, checked: false, group: 'colorrange' },
318                              { text: 'rgb(249, 153, 0)', handler: colorFromHandler, checked: false, group: 'colorrange' }]
319                    }
320                  },
321                  { text: 'Color To',
322                      menu: {
323                        items: [{ text: 'rgb(250, 20, 20)', handler: colorToHandler, checked: false, group: 'tocolorrange' },
324                                { text: 'rgb(20, 250, 20)', handler: colorToHandler, checked: false, group: 'tocolorrange' },
325                                { text: 'rgb(20, 20, 250)', handler: colorToHandler, checked: false, group: 'tocolorrange' },
326                                { text: 'rgb(127, 0, 220)', handler: colorFromHandler, checked: true, group: 'tocolorrange' },
327                                { text: 'rgb(213, 70, 121)', handler: colorToHandler, checked: false, group: 'tocolorrange' },
328                                { text: 'rgb(44, 153, 201)', handler: colorToHandler, checked: false, group: 'tocolorrange' },
329                                { text: 'rgb(146, 6, 157)' , handler: colorToHandler, checked: false, group: 'tocolorrange' },
330                                { text: 'rgb(49, 149, 0)', handler: colorToHandler, checked: false, group: 'tocolorrange' },
331                                { text: 'rgb(249, 153, 0)', handler: colorToHandler, checked: false, group: 'tocolorrange' }]
332                      }
333                    }
334                ]
335     });
336
337     var grayscaleMenu = Ext.create('Ext.menu.Menu', {
338         id: 'grayscaleMenu',
339         items: [ { text: 'data1', handler: grayscaleVariableHandler, checked: false, group: 'gs' },
340                  { text: 'data2', handler: grayscaleVariableHandler, checked: false, group: 'gs' },
341                  { text: 'data3', handler: grayscaleVariableHandler, checked: false, group: 'gs' },
342                  { text: 'Scale From',
343                    menu: {
344                      items: [{ text: 'rgb(20, 20, 20)', handler: scaleFromHandler, checked: true, group: 'gsrange' },
345                              { text: 'rgb(80, 80, 80)', handler: scaleFromHandler, checked: false, group: 'gsrange' },
346                              { text: 'rgb(120, 120, 120)', handler: scaleFromHandler, checked: false, group: 'gsrange' },
347                              { text: 'rgb(180, 180, 180)', handler: scaleFromHandler, checked: false, group: 'gsrange' },
348                              { text: 'rgb(220, 220, 220)', handler: scaleFromHandler, checked: false, group: 'gsrange' },
349                              { text: 'rgb(250, 250, 250)', handler: scaleFromHandler, checked: false, group: 'gsrange' }]
350                    }
351                  },
352                  { text: 'Scale To',
353                      menu: {
354                      items: [{ text: 'rgb(20, 20, 20)', handler: scaleToHandler, checked: false, group: 'togsrange' },
355                              { text: 'rgb(80, 80, 80)', handler: scaleToHandler, checked: false, group: 'togsrange' },
356                              { text: 'rgb(120, 120, 120)', handler: scaleToHandler, checked: false, group: 'togsrange' },
357                              { text: 'rgb(180, 180, 180)', handler: scaleToHandler, checked: false, group: 'togsrange' },
358                              { text: 'rgb(220, 220, 220)', handler: scaleToHandler, checked: true, group: 'togsrange' },
359                              { text: 'rgb(250, 250, 250)', handler: scaleToHandler, checked: false, group: 'togsrange' }]
360                      }
361                    }
362                ]
363     });
364
365     var radiusMenu = Ext.create('Ext.menu.Menu', {
366         id: 'radiusMenu',
367         style: {
368             overflow: 'visible'     // For the Combo popup
369         },
370         items: [ { text: 'data1', handler: radiusHandler, checked: false, group: 'radius' },
371                  { text: 'data2', handler: radiusHandler, checked: false, group: 'radius' },
372                  { text: 'data3', handler: radiusHandler, checked: false, group: 'radius' },
373                  { text: 'Max Radius',
374                    menu: {
375                      items: [{ text: '20', handler: radiusSizeHandler, checked: false, group: 'sradius' },
376                              { text: '30', handler: radiusSizeHandler, checked: false, group: 'sradius' },
377                              { text: '40', handler: radiusSizeHandler, checked: false, group: 'sradius' },
378                              { text: '50', handler: radiusSizeHandler, checked: true, group: 'sradius' },
379                              { text: '60', handler: radiusSizeHandler, checked: false, group: 'sradius' }]
380                    }
381                  }
382                ]
383     });
384
385     var win = Ext.create('Ext.Window', {
386         width: 800,
387         height: 600,
388         hidden: false,
389         maximizable: true,
390         title: 'Scatter Chart Renderer',
391         renderTo: Ext.getBody(),
392         layout: 'fit',
393         tbar: [
394         {
395             text: 'Reload Data',
396             handler: function() {
397                 store1.loadData(generateData());
398             }
399         },
400         {
401             text: 'Select X Axis',
402             menu: xAxisMenu
403         },
404         {
405             text: 'Select Y Axis',
406             menu: yAxisMenu
407         },
408
409         {
410             text: 'Select Color',
411             menu: colorMenu
412         },
413         {
414             text: 'Select Grayscale',
415             menu: grayscaleMenu
416         },
417         {
418             text: 'Select Radius',
419             menu: radiusMenu
420         }
421         ],
422         items: {
423             id: 'chartCmp',
424             xtype: 'chart',
425             style: 'background:#fff',
426             animate: true,
427             store: store1,
428             axes: false,
429             insetPadding: 50,
430             series: [{
431                 type: 'scatter',
432                 axis: false,
433                 xField: 'data1',
434                 yField: 'data2',
435                 color: '#ccc',
436                 markerConfig: {
437                     type: 'circle',
438                     radius: 20,
439                     size: 20
440                 }
441             }]
442         }
443     });
444 });