source: Dev/branches/jQueryUI/client/RGraph/libraries/RGraph.rscatter.js @ 249

Last change on this file since 249 was 249, checked in by hendrikvanantwerpen, 13 years ago

This one's for Subversion, because it's so close...

First widget (stripped down sequencer).
Seperated client and server code in two direcotry trees.

File size: 24.5 KB
Line 
1    /**
2    * o------------------------------------------------------------------------------o
3    * | This file is part of the RGraph package - you can learn more at:             |
4    * |                                                                              |
5    * |                          http://www.rgraph.net                               |
6    * |                                                                              |
7    * | This package is licensed under the RGraph license. For all kinds of business |
8    * | purposes there is a small one-time licensing fee to pay and for non          |
9    * | commercial  purposes it is free to use. You can read the full license here:  |
10    * |                                                                              |
11    * |                      http://www.rgraph.net/LICENSE.txt                       |
12    * o------------------------------------------------------------------------------o
13    */
14   
15    if (typeof(RGraph) == 'undefined') RGraph = {};
16   
17    /**
18    * The chart constuctor
19    *
20    * @param object canvas
21    * @param array data
22    */
23    RGraph.Rscatter = function (id, data)
24    {
25        this.id                = id;
26        this.canvas            = document.getElementById(id);
27        this.context           = this.canvas.getContext('2d');
28        this.data              = data;
29        this.canvas.__object__ = this;
30        this.type              = 'rscatter';
31        this.hasTooltips       = false;
32        this.isRGraph          = true;
33
34
35        /**
36        * Compatibility with older browsers
37        */
38        RGraph.OldBrowserCompat(this.context);
39
40
41        this.centerx = 0;
42        this.centery = 0;
43        this.radius  = 0;
44        this.max     = 0;
45       
46        this.properties = {
47            'chart.radius':                 null,
48            'chart.colors':                 [], // This is used internally for the key
49            'chart.colors.default':         'black',
50            'chart.gutter.left':            25,
51            'chart.gutter.right':           25,
52            'chart.gutter.top':             25,
53            'chart.gutter.bottom':          25,
54            'chart.title':                  '',
55            'chart.title.background':       null,
56            'chart.title.hpos':             null,
57            'chart.title.vpos':             null,
58            'chart.labels':                 null,
59            'chart.labels.position':       'center',
60            'chart.labels.axes':            'nsew',
61            'chart.text.color':             'black',
62            'chart.text.font':              'Verdana',
63            'chart.text.size':              10,
64            'chart.key':                    null,
65            'chart.key.background':         'white',
66            'chart.key.position':           'graph',
67            'chart.key.halign':             'right',
68            'chart.key.shadow':             false,
69            'chart.key.shadow.color':       '#666',
70            'chart.key.shadow.blur':        3,
71            'chart.key.shadow.offsetx':     2,
72            'chart.key.shadow.offsety':     2,
73            'chart.key.position.gutter.boxed': true,
74            'chart.key.position.x':         null,
75            'chart.key.position.y':         null,
76            'chart.key.color.shape':        'square',
77            'chart.key.rounded':            true,
78            'chart.key.linewidth':          1,
79            'chart.contextmenu':            null,
80            'chart.tooltips.effect':        'fade',
81            'chart.tooltips.css.class':     'RGraph_tooltip',
82            'chart.tooltips.highlight':     true,
83            'chart.tooltips.hotspot':       3,
84            'chart.annotatable':            false,
85            'chart.annotate.color':         'black',
86            'chart.zoom.factor':            1.5,
87            'chart.zoom.fade.in':           true,
88            'chart.zoom.fade.out':          true,
89            'chart.zoom.hdir':              'right',
90            'chart.zoom.vdir':              'down',
91            'chart.zoom.frames':            10,
92            'chart.zoom.delay':             50,
93            'chart.zoom.shadow':            true,
94            'chart.zoom.mode':              'canvas',
95            'chart.zoom.thumbnail.width':   75,
96            'chart.zoom.thumbnail.height':  75,
97            'chart.zoom.background':        true,
98            'chart.zoom.action':            'zoom',
99            'chart.resizable':              false,
100            'chart.resize.handle.adjust':   [0,0],
101            'chart.resize.handle.background': null,
102            'chart.adjustable':             false,
103            'chart.ymax':                   null,
104            'chart.ymin':                   0,
105            'chart.tickmarks':              'cross',
106            'chart.ticksize':               3,
107            'chart.scale.decimals':         null,
108            'chart.scale.round':            false
109        }
110    }
111
112
113    /**
114    * A simple setter
115    *
116    * @param string name  The name of the property to set
117    * @param string value The value of the property
118    */
119    RGraph.Rscatter.prototype.Set = function (name, value)
120    {
121        this.properties[name.toLowerCase()] = value;
122    }
123   
124   
125    /**
126    * A simple getter
127    *
128    * @param string name The name of the property to get
129    */
130    RGraph.Rscatter.prototype.Get = function (name)
131    {
132        return this.properties[name.toLowerCase()];
133    }
134
135   
136    /**
137    * This method draws the rose chart
138    */
139    RGraph.Rscatter.prototype.Draw = function ()
140    {
141        /**
142        * Fire the onbeforedraw event
143        */
144        RGraph.FireCustomEvent(this, 'onbeforedraw');
145
146        /**
147        * Clear all of this canvases event handlers (the ones installed by RGraph)
148        */
149        RGraph.ClearEventListeners(this.id);
150
151       
152        /**
153        * This doesn't affect the chart, but is used for compatibility
154        */
155        this.gutterLeft   = this.Get('chart.gutter.left');
156        this.gutterRight  = this.Get('chart.gutter.right');
157        this.gutterTop    = this.Get('chart.gutter.top');
158        this.gutterBottom = this.Get('chart.gutter.bottom');
159
160        // Calculate the radius
161        this.radius  = (Math.min(RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight, RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2);
162        this.centerx = RGraph.GetWidth(this) / 2;
163        this.centery = RGraph.GetHeight(this) / 2;
164        this.coords  = [];
165       
166        /**
167        * If there's a user specified radius, use that
168        */
169        if (typeof(this.Get('chart.radius')) == 'number') {
170            this.radius = this.Get('chart.radius');
171        }
172       
173        /**
174        * Work out the scale
175        */
176        var max = this.Get('chart.ymax');
177        var min = this.Get('chart.ymin');
178       
179        if (typeof(max) == 'number') {
180            this.max   = max;
181            this.scale = [((max - min) * 0.2) + min,((max - min) * 0.4) + min,((max - min) * 0.6) + min,((max - min) * 0.8) + min,((max - min) * 1.0) + min,];
182           
183        } else {
184            for (var i=0; i<this.data.length; ++i) {
185                this.max = Math.max(this.max, this.data[i][1]);
186            }
187            this.scale = RGraph.getScale(this.max, this);
188            this.max   = this.scale[4];
189
190            // Hmmmmmmmm
191            if (String(this.scale[0]).indexOf('e') == -1) {
192                this.scale[0] = Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'));
193                this.scale[1] = Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'));
194                this.scale[2] = Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'));
195                this.scale[3] = Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'));
196                this.scale[4] = Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'));
197            }
198        }
199
200        /**
201        * Change the centerx marginally if the key is defined
202        */
203        if (this.Get('chart.key') && this.Get('chart.key').length > 0 && this.Get('chart.key').length >= 3) {
204            this.centerx = this.centerx - this.Get('chart.gutter.right') + 5;
205        }
206       
207        /**
208        * Populate the colors array for the purposes of generating the key
209        */
210        if (typeof(this.Get('chart.key')) == 'object' && RGraph.is_array(this.Get('chart.key')) && this.Get('chart.key')[0]) {
211            for (var i=0; i<this.data.length; ++i) {
212                if (this.data[i][2] && typeof(this.data[i][2]) == 'string') {
213                    this.Get('chart.colors').push(this.data[i][2]);
214                }
215            }
216        }
217
218        this.DrawBackground();
219        this.DrawRscatter();
220        this.DrawLabels();
221
222        /**
223        * Setup the context menu if required
224        */
225        if (this.Get('chart.contextmenu')) {
226            RGraph.ShowContext(this);
227        }
228
229        /**
230        * Tooltips
231        */
232        if (this.hasTooltips) {
233
234            /**
235            * Register this object for redrawing
236            */
237            RGraph.Register(this);
238           
239            /**
240            * The onmousemove event
241            */
242            var canvas_onmousemove_func = function (event)
243            {
244                event = RGraph.FixEventObject(event);
245
246                var mouseCoords = RGraph.getMouseXY(event);
247                var mouseX      = mouseCoords[0];
248                var mouseY      = mouseCoords[1];
249                var obj         = event.target.__object__;
250                var canvas      = obj.canvas;
251                var context     = obj.context;
252                var overHotspot = false;
253                var offset      = obj.Get('chart.tooltips.hotspot'); // This is how far the hotspot extends
254
255                for (var i=0; i<obj.coords.length; ++i) {
256               
257                    var xCoord  = obj.coords[i][0];
258                    var yCoord  = obj.coords[i][1];
259                    var tooltip = obj.coords[i][3];
260
261                    if (
262                        mouseX < (xCoord + offset) &&
263                        mouseX > (xCoord - offset) &&
264                        mouseY < (yCoord + offset) &&
265                        mouseY > (yCoord - offset) &&
266                        typeof(tooltip) == 'string' &&
267                        tooltip.length > 0
268                       ) {
269
270                        overHotspot = true;
271                        canvas.style.cursor = 'pointer';
272
273                        if (!RGraph.Registry.Get('chart.tooltip') || RGraph.Registry.Get('chart.tooltip').__text__ != tooltip) {
274   
275                            if (obj.Get('chart.tooltips.highlight')) {
276                                RGraph.Redraw();
277                            }
278   
279                            /**
280                            * Get the tooltip text
281                            */
282                            if (typeof(tooltip) == 'function') {
283                                var text = String(tooltip(i));
284   
285                            } else {
286                                var text = String(tooltip);
287                            }
288   
289                            RGraph.Tooltip(canvas, text, event.pageX + 5, event.pageY - 5, i);
290   
291                            /**
292                            * Highlight the tickmark
293                            */
294                            if (obj.Get('chart.tooltips.highlight')) {
295                                context.beginPath();
296                                context.fillStyle = 'rgba(255,255,255,0.5)';
297                                context.arc(xCoord, yCoord, 3, 0, 6.2830, 0);
298                                context.fill();
299                            }
300                        }
301                    }
302                }
303               
304                if (!overHotspot) {
305                    canvas.style.cursor = 'default';
306                }
307            }
308            this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false);
309            RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func);
310        }
311
312        // Draw the title if any has been set
313        if (this.Get('chart.title')) {
314            RGraph.DrawTitle(this.canvas,
315                             this.Get('chart.title'),
316                             (this.canvas.height / 2) - this.radius - 5,
317                             this.centerx,
318                             this.Get('chart.text.size') + 2);
319        }
320       
321        /**
322        * If the canvas is annotatable, do install the event handlers
323        */
324        if (this.Get('chart.annotatable')) {
325            RGraph.Annotate(this);
326        }
327       
328        /**
329        * This bit shows the mini zoom window if requested
330        */
331        if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') {
332            RGraph.ShowZoomWindow(this);
333        }
334
335       
336        /**
337        * This function enables resizing
338        */
339        if (this.Get('chart.resizable')) {
340            RGraph.AllowResizing(this);
341        }
342       
343        /**
344        * Fire the RGraph ondraw event
345        */
346        RGraph.FireCustomEvent(this, 'ondraw');
347    }
348
349    /**
350    * This method draws the rose charts background
351    */
352    RGraph.Rscatter.prototype.DrawBackground = function ()
353    {
354        this.context.lineWidth = 1;
355   
356        // Draw the background grey circles
357        this.context.strokeStyle = '#ccc';
358        for (var i=15; i<this.radius - (document.all ? 5 : 0); i+=15) {// Radius must be greater than 0 for Opera to work
359            //this.context.moveTo(this.centerx + i, this.centery);
360   
361            // Radius must be greater than 0 for Opera to work
362            this.context.arc(this.centerx, this.centery, i, 0, (2 * Math.PI), 0);
363        }
364        this.context.stroke();
365
366        // Draw the background lines that go from the center outwards
367        this.context.beginPath();
368        for (var i=15; i<360; i+=15) {
369       
370            // Radius must be greater than 0 for Opera to work
371            this.context.arc(this.centerx, this.centery, this.radius, i / 57.3, (i + 0.01) / 57.3, 0);
372       
373            this.context.lineTo(this.centerx, this.centery);
374        }
375        this.context.stroke();
376       
377        this.context.beginPath();
378        this.context.strokeStyle = 'black';
379   
380        // Draw the X axis
381        this.context.moveTo(this.centerx - this.radius, this.centery);
382        this.context.lineTo(this.centerx + this.radius, this.centery);
383   
384        // Draw the X ends
385        this.context.moveTo(this.centerx - this.radius, this.centery - 5);
386        this.context.lineTo(this.centerx - this.radius, this.centery + 5);
387        this.context.moveTo(this.centerx + this.radius, this.centery - 5);
388        this.context.lineTo(this.centerx + this.radius, this.centery + 5);
389       
390        // Draw the X check marks
391        for (var i=(this.centerx - this.radius); i<(this.centerx + this.radius); i+=20) {
392            this.context.moveTo(i,  this.centery - 3);
393            this.context.lineTo(i,  this.centery + 3);
394        }
395       
396        // Draw the Y check marks
397        for (var i=(this.centery - this.radius); i<(this.centery + this.radius); i+=20) {
398            this.context.moveTo(this.centerx - 3, i);
399            this.context.lineTo(this.centerx + 3, i);
400        }
401   
402        // Draw the Y axis
403        this.context.moveTo(this.centerx, this.centery - this.radius);
404        this.context.lineTo(this.centerx, this.centery + this.radius);
405   
406        // Draw the Y ends
407        this.context.moveTo(this.centerx - 5, this.centery - this.radius);
408        this.context.lineTo(this.centerx + 5, this.centery - this.radius);
409   
410        this.context.moveTo(this.centerx - 5, this.centery + this.radius);
411        this.context.lineTo(this.centerx + 5, this.centery + this.radius);
412       
413        // Stroke it
414        this.context.closePath();
415        this.context.stroke();
416    }
417
418
419    /**
420    * This method draws a set of data on the graph
421    */
422    RGraph.Rscatter.prototype.DrawRscatter = function ()
423    {
424        var data = this.data;
425
426        for (var i=0; i<data.length; ++i) {
427
428            var d1 = data[i][0];
429            var d2 = data[i][1];
430            var a   = d1 / (180 / Math.PI); // RADIANS
431            var r   = ( (d2 - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin')) ) * this.radius;
432            var x   = Math.sin(a) * r;
433            var y   = Math.cos(a) * r;
434            var color = data[i][2] ? data[i][2] : this.Get('chart.colors.default');
435            var tooltip = data[i][3] ? data[i][3] : null;
436           
437            if (tooltip && tooltip.length) {
438                this.hasTooltips = true;
439            }
440
441            /**
442            * Account for the correct quadrant
443            */
444            x = x + this.centerx;
445            y = this.centery - y;
446
447
448            this.DrawTick(x, y, color);
449           
450            // Populate the coords array with the coordinates and the tooltip
451            this.coords.push([x, y, color, tooltip]);
452        }
453    }
454
455
456    /**
457    * Unsuprisingly, draws the labels
458    */
459    RGraph.Rscatter.prototype.DrawLabels = function ()
460    {
461        this.context.lineWidth = 1;
462        var key = this.Get('chart.key');
463       
464        // Set the color to black
465        this.context.fillStyle = 'black';
466        this.context.strokeStyle = 'black';
467       
468        var r         = this.radius;
469        var color     = this.Get('chart.text.color');
470        var font_face = this.Get('chart.text.font');
471        var font_size = this.Get('chart.text.size');
472        var context   = this.context;
473        var axes      = this.Get('chart.labels.axes').toLowerCase();
474       
475        this.context.fillStyle = this.Get('chart.text.color');
476
477        // Draw any labels
478        if (typeof(this.Get('chart.labels')) == 'object' && this.Get('chart.labels')) {
479            this.DrawCircularLabels(context, this.Get('chart.labels'), font_face, font_size, r);
480        }
481
482
483        var color = 'rgba(255,255,255,0.8)';
484
485        // The "North" axis labels
486        if (axes.indexOf('n') > -1) {
487            RGraph.Text(context,font_face,font_size,this.centerx,this.centery - ((r) * 0.2),String(this.scale[0]),'center','center',true,false,color);
488            RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.4), String(this.scale[1]), 'center', 'center', true, false, color);
489            RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.6), String(this.scale[2]), 'center', 'center', true, false, color);
490            RGraph.Text(context, font_face, font_size, this.centerx, this.centery - ((r) * 0.8), String(this.scale[3]), 'center', 'center', true, false, color);
491            RGraph.Text(context, font_face, font_size, this.centerx, this.centery - r, String(this.scale[4]), 'center', 'center', true, false, color);
492        }
493
494        // The "South" axis labels
495        if (axes.indexOf('s') > -1) {
496            RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.2), String(this.scale[0]), 'center', 'center', true, false, color);
497            RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.4), String(this.scale[1]), 'center', 'center', true, false, color);
498            RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.6), String(this.scale[2]), 'center', 'center', true, false, color);
499            RGraph.Text(context, font_face, font_size, this.centerx, this.centery + ((r) * 0.8), String(this.scale[3]), 'center', 'center', true, false, color);
500            RGraph.Text(context, font_face, font_size, this.centerx, this.centery + r, String(this.scale[4]), 'center', 'center', true, false, color);
501        }
502       
503        // The "East" axis labels
504        if (axes.indexOf('e') > -1) {
505            RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color);
506            RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color);
507            RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color);
508            RGraph.Text(context, font_face, font_size, this.centerx + ((r) * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color);
509            RGraph.Text(context, font_face, font_size, this.centerx + r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color);
510        }
511
512        // The "West" axis labels
513        if (axes.indexOf('w') > -1) {
514            RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.2), this.centery, String(this.scale[0]), 'center', 'center', true, false, color);
515            RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.4), this.centery, String(this.scale[1]), 'center', 'center', true, false, color);
516            RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.6), this.centery, String(this.scale[2]), 'center', 'center', true, false, color);
517            RGraph.Text(context, font_face, font_size, this.centerx - ((r) * 0.8), this.centery, String(this.scale[3]), 'center', 'center', true, false, color);
518            RGraph.Text(context, font_face, font_size, this.centerx - r, this.centery, String(this.scale[4]), 'center', 'center', true, false, color);
519        }
520       
521        // Draw the center minimum value (but only if there's at least one axes labels stipulated)
522        if (this.Get('chart.labels.axes').length > 0) {
523            RGraph.Text(context, font_face, font_size, this.centerx,  this.centery, this.Get('chart.ymin') > 0 ? String(this.Get('chart.ymin').toFixed(this.Get('chart.scale.decimals'))) : '0', 'center', 'center', true, false, color);
524        }
525
526        /**
527        * Draw the key
528        */
529        if (key && key.length) {
530            RGraph.DrawKey(this, key, this.Get('chart.colors'));
531        }
532    }
533
534
535    /**
536    * Draws the circular labels that go around the charts
537    *
538    * @param labels array The labels that go around the chart
539    */
540    RGraph.Rscatter.prototype.DrawCircularLabels = function (context, labels, font_face, font_size, r)
541    {
542        var position = this.Get('chart.labels.position');
543        var r        = r + 10;
544
545        for (var i=0; i<labels.length; ++i) {
546
547
548            var a = (360 / labels.length) * (i + 1) - (360 / (labels.length * 2));
549            var a = a - 90 + (this.Get('chart.labels.position') == 'edge' ? ((360 / labels.length) / 2) : 0);
550
551            var x = Math.cos(a / 57.29577866666) * (r + 10);
552            var y = Math.sin(a / 57.29577866666) * (r + 10);
553
554            RGraph.Text(context, font_face, font_size, this.centerx + x, this.centery + y, String(labels[i]), 'center', 'center');
555        }
556    }
557
558
559    /**
560    * Draws a single tickmark
561    */
562    RGraph.Rscatter.prototype.DrawTick = function (x, y, color)
563    {
564        var tickmarks    = this.Get('chart.tickmarks');
565        var ticksize     = this.Get('chart.ticksize');
566
567        this.context.strokeStyle = color;
568        this.context.fillStyle   = color;
569
570        // Cross
571        if (tickmarks == 'cross') {
572
573            this.context.beginPath();
574            this.context.moveTo(x + ticksize, y + ticksize);
575            this.context.lineTo(x - ticksize, y - ticksize);
576            this.context.stroke();
577   
578            this.context.beginPath();
579            this.context.moveTo(x - ticksize, y + ticksize);
580            this.context.lineTo(x + ticksize, y - ticksize);
581            this.context.stroke();
582       
583        // Circle
584        } else if (tickmarks == 'circle') {
585
586            this.context.beginPath();
587            this.context.arc(x, y, ticksize, 0, 6.2830, false);
588            this.context.fill();
589
590        // Square
591        } else if (tickmarks == 'square') {
592
593            this.context.beginPath();
594            this.context.fillRect(x - ticksize, y - ticksize, 2 * ticksize, 2 * ticksize);
595            this.context.fill();
596       
597        // Diamond shape tickmarks
598         } else if (tickmarks == 'diamond') {
599
600            this.context.beginPath();
601                this.context.moveTo(x, y - ticksize);
602                this.context.lineTo(x + ticksize, y);
603                this.context.lineTo(x, y + ticksize);
604                this.context.lineTo(x - ticksize, y);
605            this.context.closePath();
606            this.context.fill();
607
608        // Plus style tickmarks
609        } else if (tickmarks == 'plus') {
610       
611            this.context.lineWidth = 1;
612
613            this.context.beginPath();
614                this.context.moveTo(x, y - ticksize);
615                this.context.lineTo(x, y + ticksize);
616                this.context.moveTo(x - ticksize, y);
617                this.context.lineTo(x + ticksize, y);
618            this.context.stroke();
619        }
620    }
Note: See TracBrowser for help on using the repository browser.