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