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 rose chart constuctor |
---|
19 | * |
---|
20 | * @param object canvas |
---|
21 | * @param array data |
---|
22 | */ |
---|
23 | RGraph.Rose = 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 = 'rose'; |
---|
31 | this.isRGraph = true; |
---|
32 | |
---|
33 | |
---|
34 | /** |
---|
35 | * Compatibility with older browsers |
---|
36 | */ |
---|
37 | RGraph.OldBrowserCompat(this.context); |
---|
38 | |
---|
39 | |
---|
40 | this.centerx = 0; |
---|
41 | this.centery = 0; |
---|
42 | this.radius = 0; |
---|
43 | this.max = 0; |
---|
44 | |
---|
45 | this.properties = { |
---|
46 | 'chart.radius': null, |
---|
47 | 'chart.colors': ['red', 'rgb(0,255,255)', 'rgb(0,255,0)', 'gray', 'blue', 'rgb(255,128,255)','green', 'pink', 'gray', 'aqua'], |
---|
48 | 'chart.colors.sequential': false, |
---|
49 | 'chart.colors.alpha': null, |
---|
50 | 'chart.strokestyle': 'rgba(0,0,0,0.5)', |
---|
51 | 'chart.gutter.left': 25, |
---|
52 | 'chart.gutter.right': 25, |
---|
53 | 'chart.gutter.top': 25, |
---|
54 | 'chart.gutter.bottom': 25, |
---|
55 | 'chart.title': '', |
---|
56 | 'chart.title.background': null, |
---|
57 | 'chart.title.hpos': null, |
---|
58 | 'chart.title.vpos': null, |
---|
59 | 'chart.labels': null, |
---|
60 | 'chart.labels.position': 'center', |
---|
61 | 'chart.labels.axes': 'nsew', |
---|
62 | 'chart.labels.offset': 0, |
---|
63 | 'chart.text.color': 'black', |
---|
64 | 'chart.text.font': 'Verdana', |
---|
65 | 'chart.text.size': 10, |
---|
66 | 'chart.key': null, |
---|
67 | 'chart.key.background': 'white', |
---|
68 | 'chart.key.position': 'graph', |
---|
69 | 'chart.key.halign': 'right', |
---|
70 | 'chart.key.shadow': false, |
---|
71 | 'chart.key.shadow.color': '#666', |
---|
72 | 'chart.key.shadow.blur': 3, |
---|
73 | 'chart.key.shadow.offsetx': 2, |
---|
74 | 'chart.key.shadow.offsety': 2, |
---|
75 | 'chart.key.position.gutter.boxed': true, |
---|
76 | 'chart.key.position.x': null, |
---|
77 | 'chart.key.position.y': null, |
---|
78 | 'chart.key.color.shape': 'square', |
---|
79 | 'chart.key.rounded': true, |
---|
80 | 'chart.key.linewidth': 1, |
---|
81 | 'chart.contextmenu': null, |
---|
82 | 'chart.tooltips': null, |
---|
83 | 'chart.tooltips.event': 'onclick', |
---|
84 | 'chart.tooltips.effect': 'fade', |
---|
85 | 'chart.tooltips.css.class': 'RGraph_tooltip', |
---|
86 | 'chart.tooltips.highlight': true, |
---|
87 | 'chart.highlight.stroke': 'black', |
---|
88 | 'chart.highlight.fill': 'rgba(255,255,255,0.5)', |
---|
89 | 'chart.annotatable': false, |
---|
90 | 'chart.annotate.color': 'black', |
---|
91 | 'chart.zoom.factor': 1.5, |
---|
92 | 'chart.zoom.fade.in': true, |
---|
93 | 'chart.zoom.fade.out': true, |
---|
94 | 'chart.zoom.hdir': 'right', |
---|
95 | 'chart.zoom.vdir': 'down', |
---|
96 | 'chart.zoom.frames': 10, |
---|
97 | 'chart.zoom.delay': 50, |
---|
98 | 'chart.zoom.shadow': true, |
---|
99 | 'chart.zoom.mode': 'canvas', |
---|
100 | 'chart.zoom.thumbnail.width': 75, |
---|
101 | 'chart.zoom.thumbnail.height': 75, |
---|
102 | 'chart.zoom.background': true, |
---|
103 | 'chart.zoom.action': 'zoom', |
---|
104 | 'chart.resizable': false, |
---|
105 | 'chart.resize.handle.adjust': [0,0], |
---|
106 | 'chart.resize.handle.background': null, |
---|
107 | 'chart.adjustable': false, |
---|
108 | 'chart.ymax': null, |
---|
109 | 'chart.ymin': 0, |
---|
110 | 'chart.scale.decimals': null, |
---|
111 | 'chart.variant': 'stacked' |
---|
112 | } |
---|
113 | |
---|
114 | |
---|
115 | /** |
---|
116 | * Set the .getShape commonly named method |
---|
117 | */ |
---|
118 | this.getShape = this.getSegment; |
---|
119 | } |
---|
120 | |
---|
121 | |
---|
122 | /** |
---|
123 | * A simple setter |
---|
124 | * |
---|
125 | * @param string name The name of the property to set |
---|
126 | * @param string value The value of the property |
---|
127 | */ |
---|
128 | RGraph.Rose.prototype.Set = function (name, value) |
---|
129 | { |
---|
130 | this.properties[name.toLowerCase()] = value; |
---|
131 | } |
---|
132 | |
---|
133 | |
---|
134 | /** |
---|
135 | * A simple getter |
---|
136 | * |
---|
137 | * @param string name The name of the property to get |
---|
138 | */ |
---|
139 | RGraph.Rose.prototype.Get = function (name) |
---|
140 | { |
---|
141 | return this.properties[name.toLowerCase()]; |
---|
142 | } |
---|
143 | |
---|
144 | |
---|
145 | /** |
---|
146 | * This method draws the rose chart |
---|
147 | */ |
---|
148 | RGraph.Rose.prototype.Draw = function () |
---|
149 | { |
---|
150 | /** |
---|
151 | * Fire the onbeforedraw event |
---|
152 | */ |
---|
153 | RGraph.FireCustomEvent(this, 'onbeforedraw'); |
---|
154 | |
---|
155 | /** |
---|
156 | * Clear all of this canvases event handlers (the ones installed by RGraph) |
---|
157 | */ |
---|
158 | RGraph.ClearEventListeners(this.id); |
---|
159 | |
---|
160 | |
---|
161 | /** |
---|
162 | * This doesn't affect the chart, but is used for compatibility |
---|
163 | */ |
---|
164 | this.gutterLeft = this.Get('chart.gutter.left'); |
---|
165 | this.gutterRight = this.Get('chart.gutter.right'); |
---|
166 | this.gutterTop = this.Get('chart.gutter.top'); |
---|
167 | this.gutterBottom = this.Get('chart.gutter.bottom'); |
---|
168 | |
---|
169 | // Calculate the radius |
---|
170 | this.radius = (Math.min(RGraph.GetWidth(this) - this.gutterLeft - this.gutterRight, RGraph.GetHeight(this) - this.gutterTop - this.gutterBottom) / 2); |
---|
171 | this.centerx = RGraph.GetWidth(this) / 2; |
---|
172 | this.centery = RGraph.GetHeight(this) / 2; |
---|
173 | this.angles = []; |
---|
174 | this.total = 0; |
---|
175 | this.startRadians = 0; |
---|
176 | |
---|
177 | // User specified radius |
---|
178 | if (typeof(this.Get('chart.radius')) == 'number') { |
---|
179 | this.radius = this.Get('chart.radius'); |
---|
180 | } |
---|
181 | |
---|
182 | /** |
---|
183 | * Change the centerx marginally if the key is defined |
---|
184 | */ |
---|
185 | if (this.Get('chart.key') && this.Get('chart.key').length > 0 && this.Get('chart.key').length >= 3) { |
---|
186 | this.centerx = this.centerx - this.Get('chart.gutter.right') + 5; |
---|
187 | } |
---|
188 | |
---|
189 | this.DrawBackground(); |
---|
190 | this.DrawRose(); |
---|
191 | this.DrawLabels(); |
---|
192 | |
---|
193 | /** |
---|
194 | * Setup the context menu if required |
---|
195 | */ |
---|
196 | if (this.Get('chart.contextmenu')) { |
---|
197 | RGraph.ShowContext(this); |
---|
198 | } |
---|
199 | |
---|
200 | /** |
---|
201 | * Tooltips |
---|
202 | */ |
---|
203 | if (this.Get('chart.tooltips')) { |
---|
204 | |
---|
205 | /** |
---|
206 | * Register this object for redrawing |
---|
207 | */ |
---|
208 | RGraph.Register(this); |
---|
209 | |
---|
210 | /** |
---|
211 | * The onclick event |
---|
212 | */ |
---|
213 | var canvas_onclick_func = function (e) |
---|
214 | { |
---|
215 | var obj = e.target.__object__; |
---|
216 | var canvas = e.target; |
---|
217 | var context = canvas.getContext('2d'); |
---|
218 | |
---|
219 | e = RGraph.FixEventObject(e); |
---|
220 | |
---|
221 | RGraph.Redraw(); |
---|
222 | |
---|
223 | var segment = obj.getSegment(e); |
---|
224 | |
---|
225 | if (segment && obj.Get('chart.tooltips')) { |
---|
226 | |
---|
227 | /** |
---|
228 | * Get the tooltip text |
---|
229 | */ |
---|
230 | if (typeof(obj.Get('chart.tooltips')) == 'function') { |
---|
231 | var text = String(obj.Get('chart.tooltips')(segment[6])); |
---|
232 | } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[segment[6]]) == 'function') { |
---|
233 | var text = String(obj.Get('chart.tooltips')[segment[6]](segment[6])); |
---|
234 | } else if (typeof(obj.Get('chart.tooltips')) == 'object' && (typeof(obj.Get('chart.tooltips')[segment[6]]) == 'string' || typeof(obj.Get('chart.tooltips')[segment[6]]) == 'number')) { |
---|
235 | var text = String(obj.Get('chart.tooltips')[segment[6]]); |
---|
236 | } else { |
---|
237 | var text = null; |
---|
238 | } |
---|
239 | |
---|
240 | if (text) { |
---|
241 | context.beginPath(); |
---|
242 | context.strokeStyle = obj.Get('chart.highlight.stroke'); |
---|
243 | context.fillStyle = obj.Get('chart.highlight.fill'); |
---|
244 | |
---|
245 | // This highlights the chart |
---|
246 | context.arc(obj.centerx, obj.centery, segment[3], segment[4] / 57.3, segment[5] / 57.3, false); |
---|
247 | context.arc(obj.centerx, obj.centery, segment[2] + 0.01, segment[5] / 57.3, segment[4] / 57.3, true); |
---|
248 | |
---|
249 | context.closePath(); |
---|
250 | |
---|
251 | context.fill(); |
---|
252 | context.stroke(); |
---|
253 | |
---|
254 | context.strokeStyle = 'rgba(0,0,0,0)'; |
---|
255 | |
---|
256 | // Taken out on 12th June 2011 |
---|
257 | //obj.DrawLabels(); |
---|
258 | |
---|
259 | /** |
---|
260 | * Show the tooltip |
---|
261 | */ |
---|
262 | RGraph.Tooltip(canvas, text, e.pageX, e.pageY, segment[6]); |
---|
263 | |
---|
264 | e.stopPropagation(); |
---|
265 | } |
---|
266 | |
---|
267 | return; |
---|
268 | } |
---|
269 | } |
---|
270 | this.canvas.addEventListener('click', canvas_onclick_func, false); |
---|
271 | RGraph.AddEventListener(this.id, 'click', canvas_onclick_func); |
---|
272 | |
---|
273 | |
---|
274 | /** |
---|
275 | * The onmousemove event |
---|
276 | */ |
---|
277 | var canvas_onmousemove_func = function (e) |
---|
278 | { |
---|
279 | |
---|
280 | var obj = e.target.__object__; |
---|
281 | var canvas = e.target; |
---|
282 | var context = canvas.getContext('2d'); |
---|
283 | |
---|
284 | e = RGraph.FixEventObject(e); |
---|
285 | |
---|
286 | var segment = obj.getSegment(e); |
---|
287 | |
---|
288 | if (segment && obj.Get('chart.tooltips')) { |
---|
289 | |
---|
290 | /** |
---|
291 | * Get the tooltip text |
---|
292 | */ |
---|
293 | if (typeof(obj.Get('chart.tooltips')) == 'function') { |
---|
294 | var text = String(obj.Get('chart.tooltips')(segment[6])); |
---|
295 | } else if (typeof(obj.Get('chart.tooltips')) == 'object' && typeof(obj.Get('chart.tooltips')[segment[6]]) == 'function') { |
---|
296 | var text = String(obj.Get('chart.tooltips')[segment[6]](segment[6])); |
---|
297 | } else if (typeof(obj.Get('chart.tooltips')) == 'object' && (typeof(obj.Get('chart.tooltips')[segment[6]]) == 'string' || typeof(obj.Get('chart.tooltips')[segment[6]]) == 'number')) { |
---|
298 | var text = String(obj.Get('chart.tooltips')[segment[6]]); |
---|
299 | } else { |
---|
300 | var text = null; |
---|
301 | } |
---|
302 | |
---|
303 | if (text) { |
---|
304 | canvas.style.cursor = 'pointer'; |
---|
305 | |
---|
306 | /******************************************************* |
---|
307 | * This is here in case tooltips are using the |
---|
308 | * onmousemove event |
---|
309 | *******************************************************/ |
---|
310 | if (obj.Get('chart.tooltips.event') == 'onmousemove') { |
---|
311 | if (!RGraph.Registry.Get('chart.tooltip') || RGraph.Registry.Get('chart.tooltip').__index__ != segment[6]) { |
---|
312 | canvas_onclick_func(e); |
---|
313 | } |
---|
314 | } |
---|
315 | |
---|
316 | } else { |
---|
317 | canvas.style.cursor = 'default'; |
---|
318 | } |
---|
319 | |
---|
320 | return; |
---|
321 | } |
---|
322 | |
---|
323 | canvas.style.cursor = 'default'; |
---|
324 | } |
---|
325 | this.canvas.addEventListener('mousemove', canvas_onmousemove_func, false); |
---|
326 | RGraph.AddEventListener(this.id, 'mousemove', canvas_onmousemove_func); |
---|
327 | } |
---|
328 | |
---|
329 | /** |
---|
330 | * If the canvas is annotatable, do install the event handlers |
---|
331 | */ |
---|
332 | if (this.Get('chart.annotatable')) { |
---|
333 | RGraph.Annotate(this); |
---|
334 | } |
---|
335 | |
---|
336 | /** |
---|
337 | * This bit shows the mini zoom window if requested |
---|
338 | */ |
---|
339 | if (this.Get('chart.zoom.mode') == 'thumbnail' || this.Get('chart.zoom.mode') == 'area') { |
---|
340 | RGraph.ShowZoomWindow(this); |
---|
341 | } |
---|
342 | |
---|
343 | |
---|
344 | /** |
---|
345 | * This function enables resizing |
---|
346 | */ |
---|
347 | if (this.Get('chart.resizable')) { |
---|
348 | RGraph.AllowResizing(this); |
---|
349 | } |
---|
350 | |
---|
351 | |
---|
352 | /** |
---|
353 | * This function enables adjusting |
---|
354 | */ |
---|
355 | if (this.Get('chart.adjustable')) { |
---|
356 | RGraph.AllowAdjusting(this); |
---|
357 | } |
---|
358 | |
---|
359 | /** |
---|
360 | * Fire the RGraph ondraw event |
---|
361 | */ |
---|
362 | RGraph.FireCustomEvent(this, 'ondraw'); |
---|
363 | } |
---|
364 | |
---|
365 | /** |
---|
366 | * This method draws the rose charts background |
---|
367 | */ |
---|
368 | RGraph.Rose.prototype.DrawBackground = function () |
---|
369 | { |
---|
370 | this.context.lineWidth = 1; |
---|
371 | |
---|
372 | // Draw the background grey circles |
---|
373 | this.context.strokeStyle = '#ccc'; |
---|
374 | for (var i=15; i<this.radius - (RGraph.isIE8() ? 5 : 0); i+=15) {// Radius must be greater than 0 for Opera to work |
---|
375 | //this.context.moveTo(this.centerx + i, this.centery); |
---|
376 | |
---|
377 | // Radius must be greater than 0 for Opera to work |
---|
378 | this.context.arc(this.centerx, this.centery, i, 0, (2 * Math.PI), 0); |
---|
379 | } |
---|
380 | this.context.stroke(); |
---|
381 | |
---|
382 | // Draw the background lines that go from the center outwards |
---|
383 | this.context.beginPath(); |
---|
384 | for (var i=15; i<360; i+=15) { |
---|
385 | |
---|
386 | // Radius must be greater than 0 for Opera to work |
---|
387 | this.context.arc(this.centerx, this.centery, this.radius, i / 57.3, (i + 0.1) / 57.3, 0); // The 0.1 avoids a bug in Chrome 6 |
---|
388 | |
---|
389 | this.context.lineTo(this.centerx, this.centery); |
---|
390 | } |
---|
391 | this.context.stroke(); |
---|
392 | |
---|
393 | this.context.beginPath(); |
---|
394 | this.context.strokeStyle = 'black'; |
---|
395 | |
---|
396 | // Draw the X axis |
---|
397 | this.context.moveTo(this.centerx - this.radius, this.centery); |
---|
398 | this.context.lineTo(this.centerx + this.radius, this.centery); |
---|
399 | |
---|
400 | // Draw the X ends |
---|
401 | this.context.moveTo(this.centerx - this.radius, this.centery - 5); |
---|
402 | this.context.lineTo(this.centerx - this.radius, this.centery + 5); |
---|
403 | this.context.moveTo(this.centerx + this.radius, this.centery - 5); |
---|
404 | this.context.lineTo(this.centerx + this.radius, this.centery + 5); |
---|
405 | |
---|
406 | // Draw the X check marks |
---|
407 | for (var i=(this.centerx - this.radius); i<(this.centerx + this.radius); i+=20) { |
---|
408 | this.context.moveTo(i, this.centery - 3); |
---|
409 | this.context.lineTo(i, this.centery + 3); |
---|
410 | } |
---|
411 | |
---|
412 | // Draw the Y check marks |
---|
413 | for (var i=(this.centery - this.radius); i<(this.centery + this.radius); i+=20) { |
---|
414 | this.context.moveTo(this.centerx - 3, i); |
---|
415 | this.context.lineTo(this.centerx + 3, i); |
---|
416 | } |
---|
417 | |
---|
418 | // Draw the Y axis |
---|
419 | this.context.moveTo(this.centerx, this.centery - this.radius); |
---|
420 | this.context.lineTo(this.centerx, this.centery + this.radius); |
---|
421 | |
---|
422 | // Draw the Y ends |
---|
423 | this.context.moveTo(this.centerx - 5, this.centery - this.radius); |
---|
424 | this.context.lineTo(this.centerx + 5, this.centery - this.radius); |
---|
425 | |
---|
426 | this.context.moveTo(this.centerx - 5, this.centery + this.radius); |
---|
427 | this.context.lineTo(this.centerx + 5, this.centery + this.radius); |
---|
428 | |
---|
429 | // Stroke it |
---|
430 | this.context.closePath(); |
---|
431 | this.context.stroke(); |
---|
432 | } |
---|
433 | |
---|
434 | |
---|
435 | /** |
---|
436 | * This method draws the data on the graph |
---|
437 | */ |
---|
438 | RGraph.Rose.prototype.DrawRose = function () |
---|
439 | { |
---|
440 | var max = 0; |
---|
441 | var data = this.data; |
---|
442 | |
---|
443 | // Must be at least two data points |
---|
444 | if (data.length < 2) { |
---|
445 | alert('[ROSE] Must be at least two data points! [' + data + ']'); |
---|
446 | return; |
---|
447 | } |
---|
448 | |
---|
449 | // Work out the maximum value and the sum |
---|
450 | if (!this.Get('chart.ymax')) { |
---|
451 | // Work out the max |
---|
452 | for (var i=0; i<data.length; ++i) { |
---|
453 | if (typeof(data[i]) == 'number') { |
---|
454 | max = Math.max(max, data[i]); |
---|
455 | } else if (typeof(data[i]) == 'object' && this.Get('chart.variant') == 'non-equi-angular') { |
---|
456 | max = Math.max(max, data[i][0]); |
---|
457 | |
---|
458 | // Fallback is stacked |
---|
459 | } else { |
---|
460 | max = Math.max(max, RGraph.array_sum(data[i])); |
---|
461 | } |
---|
462 | } |
---|
463 | |
---|
464 | this.scale = RGraph.getScale(max, this); |
---|
465 | this.max = this.scale[4]; |
---|
466 | } else { |
---|
467 | var ymax = this.Get('chart.ymax'); |
---|
468 | var ymin = this.Get('chart.ymin'); |
---|
469 | |
---|
470 | this.scale = [ |
---|
471 | ((ymax - ymin) * 0.2) + ymin, |
---|
472 | ((ymax - ymin) * 0.4) + ymin, |
---|
473 | ((ymax - ymin) * 0.6) + ymin, |
---|
474 | ((ymax - ymin) * 0.8) + ymin, |
---|
475 | ((ymax - ymin) * 1.0) + ymin |
---|
476 | ]; |
---|
477 | this.max = this.scale[4]; |
---|
478 | } |
---|
479 | |
---|
480 | this.sum = RGraph.array_sum(data); |
---|
481 | |
---|
482 | // Move to the centre |
---|
483 | this.context.moveTo(this.centerx, this.centery); |
---|
484 | |
---|
485 | this.context.stroke(); // Stroke the background so it stays grey |
---|
486 | |
---|
487 | // Transparency |
---|
488 | if (this.Get('chart.colors.alpha')) { |
---|
489 | this.context.globalAlpha = this.Get('chart.colors.alpha'); |
---|
490 | } |
---|
491 | |
---|
492 | /******************************************************* |
---|
493 | * A non-equi-angular Rose chart |
---|
494 | *******************************************************/ |
---|
495 | if (typeof(this.Get('chart.variant')) == 'string' && this.Get('chart.variant') == 'non-equi-angular') { |
---|
496 | /******************************************************* |
---|
497 | * NON-EQUI-ANGULAR GOES HERE |
---|
498 | *******************************************************/ |
---|
499 | var total=0; |
---|
500 | for (var i=0; i<data.length; ++i) { |
---|
501 | total += data[i][1]; |
---|
502 | } |
---|
503 | |
---|
504 | |
---|
505 | for (var i=0; i<this.data.length; ++i) { |
---|
506 | |
---|
507 | var segmentRadians = (this.data[i][1] / total) * (2 * Math.PI); |
---|
508 | var radius = ((this.data[i][0] - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin'))) * (this.radius - 10); |
---|
509 | |
---|
510 | this.context.strokeStyle = this.Get('chart.strokestyle'); |
---|
511 | this.context.fillStyle = this.Get('chart.colors')[0]; |
---|
512 | |
---|
513 | if (this.Get('chart.colors.sequential')) { |
---|
514 | this.context.fillStyle = this.Get('chart.colors')[i]; |
---|
515 | } |
---|
516 | |
---|
517 | this.context.beginPath(); // Begin the segment |
---|
518 | this.context.arc(this.centerx, this.centery, radius, this.startRadians - (Math.PI / 2), this.startRadians + segmentRadians - (Math.PI / 2), 0); |
---|
519 | this.context.lineTo(this.centerx, this.centery); |
---|
520 | this.context.closePath(); // End the segment |
---|
521 | |
---|
522 | this.context.stroke(); |
---|
523 | this.context.fill(); |
---|
524 | |
---|
525 | // Store the start and end angles |
---|
526 | this.angles.push([ |
---|
527 | ((this.startRadians - (Math.PI / 2)) * 57.3) + 90, |
---|
528 | (((this.startRadians + segmentRadians) - (Math.PI / 2)) * 57.3) + 90, |
---|
529 | 0, |
---|
530 | radius |
---|
531 | ]); |
---|
532 | |
---|
533 | this.startRadians += segmentRadians; |
---|
534 | } |
---|
535 | } else { |
---|
536 | /******************************************************* |
---|
537 | * Draw the segments |
---|
538 | *******************************************************/ |
---|
539 | for (var i=0; i<this.data.length; ++i) { |
---|
540 | |
---|
541 | this.context.strokeStyle = this.Get('chart.strokestyle'); |
---|
542 | this.context.fillStyle = this.Get('chart.colors')[0]; |
---|
543 | |
---|
544 | /******************************************************* |
---|
545 | * This allows sequential colors |
---|
546 | *******************************************************/ |
---|
547 | if (this.Get('chart.colors.sequential')) { |
---|
548 | this.context.fillStyle = this.Get('chart.colors')[i]; |
---|
549 | } |
---|
550 | |
---|
551 | var segmentRadians = (1 / this.data.length) * (2 * Math.PI); |
---|
552 | |
---|
553 | if (typeof(this.data[i]) == 'number') { |
---|
554 | this.context.beginPath(); // Begin the segment |
---|
555 | var radius = ((this.data[i] - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin'))) * (this.radius - 10); |
---|
556 | |
---|
557 | this.context.arc(this.centerx, this.centery, radius, this.startRadians - (Math.PI / 2), this.startRadians + segmentRadians - (Math.PI / 2), 0); |
---|
558 | this.context.lineTo(this.centerx, this.centery); |
---|
559 | this.context.closePath(); // End the segment |
---|
560 | this.context.stroke(); |
---|
561 | this.context.fill(); |
---|
562 | |
---|
563 | // Store the start and end angles |
---|
564 | this.angles.push([ |
---|
565 | ((this.startRadians - (Math.PI / 2)) * 57.3) + 90, |
---|
566 | (((this.startRadians + segmentRadians) - (Math.PI / 2)) * 57.3) + 90, |
---|
567 | 0, |
---|
568 | radius |
---|
569 | ]); |
---|
570 | /******************************************************* |
---|
571 | * Draw a stacked segment |
---|
572 | *******************************************************/ |
---|
573 | } else if (typeof(this.data[i]) == 'object') { |
---|
574 | |
---|
575 | for (var j=0; j<this.data[i].length; ++j) { |
---|
576 | |
---|
577 | this.context.fillStyle = this.Get('chart.colors')[j]; |
---|
578 | if (j == 0) { |
---|
579 | this.context.beginPath(); // Begin the segment |
---|
580 | var startRadius = 0; |
---|
581 | var endRadius = ((this.data[i][j] - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin'))) * (this.radius - 10); |
---|
582 | |
---|
583 | this.context.arc(this.centerx, |
---|
584 | this.centery, |
---|
585 | endRadius, |
---|
586 | this.startRadians - (Math.PI / 2), |
---|
587 | this.startRadians + segmentRadians - (Math.PI / 2), |
---|
588 | 0); |
---|
589 | this.context.lineTo(this.centerx, this.centery); |
---|
590 | this.context.closePath(); // End the segment |
---|
591 | this.context.stroke(); |
---|
592 | this.context.fill(); |
---|
593 | |
---|
594 | this.angles.push([ |
---|
595 | ((this.startRadians - (Math.PI / 2)) * 57.3) + 90, |
---|
596 | (((this.startRadians + segmentRadians) - (Math.PI / 2)) * 57.3) + 90, |
---|
597 | 0, |
---|
598 | endRadius |
---|
599 | ]); |
---|
600 | |
---|
601 | } else { |
---|
602 | |
---|
603 | this.context.beginPath(); // Begin the segment |
---|
604 | var startRadius = endRadius; // This comes from the prior iteration of this loop |
---|
605 | var endRadius = (((this.data[i][j] - this.Get('chart.ymin')) / (this.max - this.Get('chart.ymin'))) * (this.radius - 10)) + startRadius; |
---|
606 | |
---|
607 | this.context.arc(this.centerx, |
---|
608 | this.centery, |
---|
609 | startRadius, |
---|
610 | this.startRadians - (Math.PI / 2), |
---|
611 | this.startRadians + segmentRadians - (Math.PI / 2), |
---|
612 | 0); |
---|
613 | |
---|
614 | this.context.arc(this.centerx, |
---|
615 | this.centery, |
---|
616 | endRadius, |
---|
617 | this.startRadians + segmentRadians - (Math.PI / 2), |
---|
618 | this.startRadians - (Math.PI / 2), |
---|
619 | true); |
---|
620 | |
---|
621 | this.context.closePath(); // End the segment |
---|
622 | this.context.stroke(); |
---|
623 | this.context.fill(); |
---|
624 | |
---|
625 | this.angles.push([ |
---|
626 | ((this.startRadians - (Math.PI / 2)) * 57.3) + 90, |
---|
627 | ((this.startRadians + segmentRadians - (Math.PI / 2)) * 57.3) + 90, |
---|
628 | startRadius, |
---|
629 | endRadius |
---|
630 | ]); |
---|
631 | } |
---|
632 | } |
---|
633 | } |
---|
634 | |
---|
635 | this.startRadians += segmentRadians; |
---|
636 | } |
---|
637 | } |
---|
638 | |
---|
639 | // Turn off the transparency |
---|
640 | if (this.Get('chart.colors.alpha')) { |
---|
641 | this.context.globalAlpha = 1; |
---|
642 | } |
---|
643 | |
---|
644 | // Draw the title if any has been set |
---|
645 | if (this.Get('chart.title')) { |
---|
646 | RGraph.DrawTitle(this.canvas, |
---|
647 | this.Get('chart.title'), |
---|
648 | (this.canvas.height / 2) - this.radius, |
---|
649 | this.centerx, |
---|
650 | this.Get('chart.text.size') + 2); |
---|
651 | } |
---|
652 | } |
---|
653 | |
---|
654 | |
---|
655 | /** |
---|
656 | * Unsuprisingly, draws the labels |
---|
657 | */ |
---|
658 | RGraph.Rose.prototype.DrawLabels = function () |
---|
659 | { |
---|
660 | this.context.lineWidth = 1; |
---|
661 | var key = this.Get('chart.key'); |
---|
662 | |
---|
663 | if (key && key.length) { |
---|
664 | RGraph.DrawKey(this, key, this.Get('chart.colors')); |
---|
665 | } |
---|
666 | |
---|
667 | // Set the color to black |
---|
668 | this.context.fillStyle = 'black'; |
---|
669 | this.context.strokeStyle = 'black'; |
---|
670 | |
---|
671 | var r = this.radius - 10; |
---|
672 | var font_face = this.Get('chart.text.font'); |
---|
673 | var font_size = this.Get('chart.text.size'); |
---|
674 | var context = this.context; |
---|
675 | var axes = this.Get('chart.labels.axes').toLowerCase(); |
---|
676 | |
---|
677 | // Draw any labels |
---|
678 | |
---|
679 | if (typeof(this.Get('chart.labels')) == 'object' && this.Get('chart.labels')) { |
---|
680 | this.DrawCircularLabels(context, this.Get('chart.labels'), font_face, font_size, r + 10); |
---|
681 | } |
---|
682 | |
---|
683 | |
---|
684 | var color = 'rgba(255,255,255,0.8)'; |
---|
685 | |
---|
686 | // The "North" axis labels |
---|
687 | if (axes.indexOf('n') > -1) { |
---|
688 | RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.2), String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
689 | RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.4), String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
690 | RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.6), String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
691 | RGraph.Text(context, font_face, font_size, this.centerx, this.centery - (r * 0.8), String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
692 | RGraph.Text(context, font_face, font_size, this.centerx, this.centery - r, String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
693 | } |
---|
694 | |
---|
695 | // The "South" axis labels |
---|
696 | if (axes.indexOf('s') > -1) { |
---|
697 | RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.2), String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
698 | RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.4), String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
699 | RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.6), String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
700 | RGraph.Text(context, font_face, font_size, this.centerx, this.centery + (r * 0.8), String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
701 | RGraph.Text(context, font_face, font_size, this.centerx, this.centery + r, String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
702 | } |
---|
703 | |
---|
704 | // The "East" axis labels |
---|
705 | if (axes.indexOf('e') > -1) { |
---|
706 | RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.2), this.centery, String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
707 | RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.4), this.centery, String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
708 | RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.6), this.centery, String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
709 | RGraph.Text(context, font_face, font_size, this.centerx + (r * 0.8), this.centery, String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
710 | RGraph.Text(context, font_face, font_size, this.centerx + r, this.centery, String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
711 | } |
---|
712 | |
---|
713 | // The "West" axis labels |
---|
714 | if (axes.indexOf('w') > -1) { |
---|
715 | RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.2), this.centery, String(Number(this.scale[0]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
716 | RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.4), this.centery, String(Number(this.scale[1]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
717 | RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.6), this.centery, String(Number(this.scale[2]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
718 | RGraph.Text(context, font_face, font_size, this.centerx - (r * 0.8), this.centery, String(Number(this.scale[3]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
719 | RGraph.Text(context, font_face, font_size, this.centerx - r, this.centery, String(Number(this.scale[4]).toFixed(this.Get('chart.scale.decimals'))), 'center', 'center', true, false, color); |
---|
720 | } |
---|
721 | |
---|
722 | RGraph.Text(context, font_face, font_size, this.centerx, this.centery, typeof(this.Get('chart.ymin')) == 'number' ? String(Number(this.Get('chart.ymin')).toFixed(this.Get('chart.scale.decimals'))) : '0', 'center', 'center', true, false, color); |
---|
723 | } |
---|
724 | |
---|
725 | |
---|
726 | /** |
---|
727 | * Draws the circular labels that go around the charts |
---|
728 | * |
---|
729 | * @param labels array The labels that go around the chart |
---|
730 | */ |
---|
731 | RGraph.Rose.prototype.DrawCircularLabels = function (context, labels, font_face, font_size, r) |
---|
732 | { |
---|
733 | var variant = this.Get('chart.variant'); |
---|
734 | var position = this.Get('chart.labels.position'); |
---|
735 | var r = r + 10 + this.Get('chart.labels.offset'); |
---|
736 | |
---|
737 | for (var i=0; i<labels.length; ++i) { |
---|
738 | |
---|
739 | if (typeof(variant) == 'string' && variant == 'non-equi-angular') { |
---|
740 | |
---|
741 | var a = Number(this.angles[i][0]) + ((this.angles[i][1] - this.angles[i][0]) / 2); |
---|
742 | a -= 90; |
---|
743 | var halign = 'center'; // Default halign |
---|
744 | |
---|
745 | var x = Math.cos(a / 57.29577866666) * (r + 10); |
---|
746 | var y = Math.sin(a / 57.29577866666) * (r + 10); |
---|
747 | |
---|
748 | RGraph.Text(context, font_face, font_size, this.centerx + x, this.centery + y, String(labels[i]), 'center', halign); |
---|
749 | |
---|
750 | } else { |
---|
751 | |
---|
752 | var a = (360 / labels.length) * (i + 1) - (360 / (labels.length * 2)); |
---|
753 | var a = a - 90 + (this.Get('chart.labels.position') == 'edge' ? ((360 / labels.length) / 2) : 0); |
---|
754 | var halign = 'center'; // Default halign |
---|
755 | |
---|
756 | // Horizontal alignment |
---|
757 | //if (a == 0) { |
---|
758 | // var halign = 'left'; |
---|
759 | //} else if (a == 180) { |
---|
760 | // var halign = 'right'; |
---|
761 | //} |
---|
762 | |
---|
763 | var x = Math.cos(a / 57.29577866666) * (r + 10); |
---|
764 | var y = Math.sin(a / 57.29577866666) * (r + 10); |
---|
765 | |
---|
766 | RGraph.Text(context, font_face, font_size, this.centerx + x, this.centery + y, String(labels[i]), 'center', halign); |
---|
767 | } |
---|
768 | } |
---|
769 | } |
---|
770 | |
---|
771 | |
---|
772 | /** |
---|
773 | * This function is for use with circular graph types, eg the Pie or Rose. Pass it your event object |
---|
774 | * and it will pass you back the corresponding segment details as an array: |
---|
775 | * |
---|
776 | * [x, y, r, startAngle, endAngle] |
---|
777 | * |
---|
778 | * Angles are measured in degrees, and are measured from the "east" axis (just like the canvas). |
---|
779 | * |
---|
780 | * @param object e Your event object |
---|
781 | */ |
---|
782 | RGraph.Rose.prototype.getSegment = function (e) |
---|
783 | { |
---|
784 | RGraph.FixEventObject(e); |
---|
785 | |
---|
786 | // The optional arg provides a way of allowing some accuracy (pixels) |
---|
787 | var accuracy = arguments[1] ? arguments[1] : 0; |
---|
788 | |
---|
789 | var obj = e.target.__object__; |
---|
790 | var canvas = obj.canvas; |
---|
791 | var context = obj.context; |
---|
792 | var mouseCoords = RGraph.getMouseXY(e); |
---|
793 | var x = mouseCoords[0] - obj.centerx; |
---|
794 | var y = mouseCoords[1] - obj.centery; |
---|
795 | var r = obj.radius; |
---|
796 | var theta = Math.atan(y / x); // RADIANS |
---|
797 | var hyp = y / Math.sin(theta); |
---|
798 | var angles = obj.angles; |
---|
799 | var ret = []; |
---|
800 | var hyp = (hyp < 0) ? hyp + accuracy : hyp - accuracy; |
---|
801 | |
---|
802 | |
---|
803 | |
---|
804 | // Put theta in DEGREES |
---|
805 | theta *= 57.3 |
---|
806 | |
---|
807 | // hyp should not be greater than radius if it's a Rose chart |
---|
808 | if (obj.type == 'rose') { |
---|
809 | if ( (isNaN(hyp) && Math.abs(mouseCoords[0]) < (obj.centerx - r) ) |
---|
810 | || (isNaN(hyp) && Math.abs(mouseCoords[0]) > (obj.centerx + r)) |
---|
811 | || (!isNaN(hyp) && Math.abs(hyp) > r)) { |
---|
812 | return; |
---|
813 | } |
---|
814 | } |
---|
815 | |
---|
816 | /** |
---|
817 | * Account for the correct quadrant |
---|
818 | */ |
---|
819 | if (x < 0 && y >= 0) { |
---|
820 | theta += 180; |
---|
821 | } else if (x < 0 && y < 0) { |
---|
822 | theta += 180; |
---|
823 | } else if (x > 0 && y < 0) { |
---|
824 | theta += 360; |
---|
825 | } |
---|
826 | |
---|
827 | /** |
---|
828 | * Account for the rose chart angle displacement |
---|
829 | */ |
---|
830 | theta += 90; |
---|
831 | |
---|
832 | if (theta > 360) { |
---|
833 | theta -= 360; |
---|
834 | } |
---|
835 | |
---|
836 | hyp = Math.abs(hyp); |
---|
837 | |
---|
838 | for (var i=0; i<angles.length; ++i) { |
---|
839 | if (theta >= angles[i][0] && theta < angles[i][1] && hyp > angles[i][2] && hyp < angles[i][3]) { |
---|
840 | |
---|
841 | if (!(hyp > angles[i][2] && hyp < angles[i][3])) { |
---|
842 | return null; |
---|
843 | } |
---|
844 | |
---|
845 | if (!hyp) { |
---|
846 | return null; |
---|
847 | } |
---|
848 | |
---|
849 | ret[0] = obj.centerx; |
---|
850 | ret[1] = obj.centery; |
---|
851 | ret[2] = angles[i][2]; // Start angle |
---|
852 | ret[3] = angles[i][3]; // End angle |
---|
853 | |
---|
854 | ret[4] = angles[i][0]; // Start radius |
---|
855 | ret[5] = angles[i][1]; // End radius |
---|
856 | ret[6] = i; |
---|
857 | |
---|
858 | ret[4] -= 90; |
---|
859 | ret[5] -= 90; |
---|
860 | |
---|
861 | if (x > 0 && y < 0) { |
---|
862 | ret[4] += 360; |
---|
863 | ret[5] += 360; |
---|
864 | } |
---|
865 | |
---|
866 | if (ret[4] < 0) ret[4] += 360; |
---|
867 | if (ret[5] > 360) ret[5] -= 360; |
---|
868 | |
---|
869 | return ret; |
---|
870 | } |
---|
871 | } |
---|
872 | |
---|
873 | return null; |
---|
874 | } |
---|